<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcus Kohlberg</title>
    <description>The latest articles on DEV Community by Marcus Kohlberg (@marcuskohlberg).</description>
    <link>https://dev.to/marcuskohlberg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F577527%2Fdc97c6e3-cd4f-4618-b7a4-b04753829937.jpg</url>
      <title>DEV Community: Marcus Kohlberg</title>
      <link>https://dev.to/marcuskohlberg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcuskohlberg"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Thu, 12 Mar 2026 07:28:28 +0000</pubDate>
      <link>https://dev.to/marcuskohlberg/-21ha</link>
      <guid>https://dev.to/marcuskohlberg/-21ha</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leonardo_marciano" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3111927%2Fa111cac7-a8a1-44eb-9d80-037f84f1de95.jpeg" alt="leonardo_marciano"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leonardo_marciano/encorets-effectts-building-a-production-ready-task-management-system-with-functional-1fg7" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Encore.ts + Effect.ts : Building a Production-Ready Task Management System with Functional TypeScript&lt;/h2&gt;
      &lt;h3&gt;Leonardo Marciano ・ Mar 12&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#encore&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#effect&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#functional&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>typescript</category>
      <category>encore</category>
      <category>effect</category>
      <category>functional</category>
    </item>
    <item>
      <title>Build a production-ready SaaS app (that will scale!)</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Mon, 09 Feb 2026 13:42:15 +0000</pubDate>
      <link>https://dev.to/encore/build-a-production-ready-saas-app-that-will-scale-e16</link>
      <guid>https://dev.to/encore/build-a-production-ready-saas-app-that-will-scale-e16</guid>
      <description>&lt;p&gt;&lt;strong&gt;Thinking of building a SaaS application? This starter template will help you create a scalable and production ready application using &lt;a href="https://encore.dev" rel="noopener noreferrer"&gt;Encore.ts&lt;/a&gt; and &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It uses &lt;a href="https://clerk.com/" rel="noopener noreferrer"&gt;Clerk&lt;/a&gt; for authentication, has a &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; integration for payments, and uses &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; and &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; for styling and components.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://encorets-saas-starter.vercel.app" rel="noopener noreferrer"&gt;https://encorets-saas-starter.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/encoredev/examples/tree/main/ts/saas-starter" rel="noopener noreferrer"&gt;Open on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6l6bjxt6i423760rh5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6l6bjxt6i423760rh5f.png" alt="SaaS Starter" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Marketing landing page (/)&lt;/li&gt;
&lt;li&gt;Pricing page (/pricing) which connects to Stripe Checkout&lt;/li&gt;
&lt;li&gt;Dashboard pages with (/dashboard)&lt;/li&gt;
&lt;li&gt;Subscription management (/dashboard/subscription)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend framework:&lt;/strong&gt; &lt;a href="https://encore.dev" rel="noopener noreferrer"&gt;Encore.ts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend framework:&lt;/strong&gt; &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; &lt;a href="https://clerk.com/" rel="noopener noreferrer"&gt;Clerk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Library:&lt;/strong&gt; &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; &amp;amp; &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install or Update Encore
&lt;/h3&gt;

&lt;p&gt;Install Encore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS:&lt;/strong&gt; &lt;code&gt;brew install encoredev/tap/encore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux:&lt;/strong&gt; &lt;code&gt;curl -L https://encore.dev/install.sh | bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; &lt;code&gt;iwr https://encore.dev/install.ps1 | iex&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This starter requires Encore v1.46.9+, if you have an older version installed, update using &lt;code&gt;encore version update&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create app
&lt;/h3&gt;

&lt;p&gt;Create a local app from this template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;encore app create my-app-name &lt;span class="nt"&gt;--example&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ts/saas-starter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the frontend dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-app-name/frontend
pnpm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up Clerk
&lt;/h2&gt;

&lt;p&gt;Create a Clerk account if you haven't already. Then, in the Clerk dashboard, create a new application.&lt;/p&gt;

&lt;p&gt;Ensure the application is configured to support organizations, which can be enabled on the &lt;strong&gt;Organization management settings&lt;/strong&gt; page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;

&lt;p&gt;If you host your app on Vercel (see deployment instructions further down), you can connect your Clerk application with your Vercel project under &lt;strong&gt;Integrations&lt;/strong&gt; in the project settings on Vercel.&lt;br&gt;
Don't forget to run &lt;code&gt;vercel env pull&lt;/code&gt; afterward.&lt;/p&gt;

&lt;p&gt;Otherwise, go to the &lt;em&gt;API Keys&lt;/em&gt; page for your app. Copy the "Publishable Key" and one of the "Secret keys". To your frontend project, add them to the &lt;code&gt;.env.local&lt;/code&gt; as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY={PUBLISHABLE_KEY}
CLERK_SECRET_KEY={SECRET_KEY}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to add the following environment variables to your frontend project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_CLERK_SIGN_IN_URL="/sign-in"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add them to Encore, go to the &lt;em&gt;API Keys&lt;/em&gt; page for your app. Copy the "Publishable Key" and one of the "Secret keys".&lt;/p&gt;

&lt;p&gt;From your terminal (inside the &lt;code&gt;backend&lt;/code&gt; directory), run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;encore secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--prod&lt;/span&gt; ClerkSecretKey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, do the same for the development secret. The most secure way is to create another secret key (Clerk allows you to have multiple).&lt;br&gt;
Once you have a client secret for development, set it similarly to before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;encore secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--dev&lt;/span&gt; ClerkSecretKey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up Stripe
&lt;/h2&gt;

&lt;p&gt;Create a Stripe account if you don't have one already. Then, create your account and make sure to set up the products and pricing plans. You must also configure the billing customer portal.&lt;/p&gt;

&lt;p&gt;You will also need to add your backend as a webhook endpoint in Stripe and set the endpoint URL to &lt;code&gt;https://YOUR_DOMAIN/stripe/webhook&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Currently, the pricing plans are stored in &lt;a href="//./frontend/lib/plans.ts"&gt;frontend/lib/plans.ts&lt;/a&gt;, but you can also read them from Stripe or store them in a database if you prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;

&lt;p&gt;On the Stripe dashboard, go to the &lt;em&gt;Developers&lt;/em&gt; page and create a new API key. Copy the "Secret Key".&lt;/p&gt;

&lt;p&gt;From your terminal (inside the &lt;code&gt;backend&lt;/code&gt; directory), run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;encore secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--dev&lt;/span&gt; StripeSecretKey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to verify the incoming webhook requests, you need to copy the signing secret from the Webhook page on the Stripe dashboard and add it to Encore by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;encore secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--dev&lt;/span&gt; StripeWebhookSigningSecret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you want to set it up for production, create a separate Secret key and Webhook Signing Secret for production and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;encore secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--prod&lt;/span&gt; StripeSecretKey
&lt;span class="nv"&gt;$ &lt;/span&gt;encore secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--prod&lt;/span&gt; StripeWebhookSigningSecret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing payments
&lt;/h3&gt;

&lt;p&gt;To test Stripe payments, use the following test card details:&lt;/p&gt;

&lt;p&gt;Card Number: 4242 4242 4242 4242&lt;br&gt;
Expiration: Any future date&lt;br&gt;
CVC: Any 3-digit number&lt;/p&gt;
&lt;h3&gt;
  
  
  Vercel Environment Variables
&lt;/h3&gt;

&lt;p&gt;Vercel-specific environment variables are not set on local by default, so you need to add them yourself. This can be done on the project settings page on Vercel.&lt;/p&gt;

&lt;p&gt;Please add the following environment variables &lt;strong&gt;for development&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VERCEL_ENV="development"
NEXT_PUBLIC_VERCEL_ENV="development"
VERCEL_GIT_PULL_REQUEST_ID=""
NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID=""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run locally
&lt;/h2&gt;

&lt;p&gt;Run your Encore backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend
encore run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This will fail unless you set up the Stripe and Clerk secrets according to the instructions above.&lt;/p&gt;

&lt;p&gt;In a different terminal window, run the React frontend using &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
pnpm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generating a request client
&lt;/h3&gt;

&lt;p&gt;Keep the contract between the backend and frontend in sync by regenerating the request client whenever you make a change to an Encore endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm run gen &lt;span class="c"&gt;# Deployed Encore staging environment&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
pnpm run gen:local &lt;span class="c"&gt;# Locally running Encore backend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Encore
&lt;/h3&gt;

&lt;p&gt;Deploy your backend to a staging environment in Encore's free development cloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'Commit message'&lt;/span&gt;
git push encore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then head over to the &lt;a href="https://app.encore.dev" rel="noopener noreferrer"&gt;Cloud Dashboard&lt;/a&gt; to monitor your deployment and find your production URL.&lt;/p&gt;

&lt;p&gt;From there you can also see metrics, traces, connect your app to a GitHub repo to get automatic deploys on new commits, and connect your own AWS or GCP account to use for deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nice job, your SaaS backend is now deployed to the cloud!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js on Vercel
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a repo and push the project to GitHub.&lt;/li&gt;
&lt;li&gt;Create a new project on Vercel and point it to your GitHub repo.&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;frontend&lt;/code&gt; as the root directory for the Vercel project.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Frontend &amp;amp; CORS configuration
&lt;/h2&gt;

&lt;p&gt;The backend needs to know where the frontend is hosted, in order to serve the correct redirect URLs&lt;br&gt;
back from Stripe. It is also necessary for CORS to work correctly.&lt;/p&gt;

&lt;p&gt;To handle this, you need to update two configuration files.&lt;/p&gt;

&lt;p&gt;First, update the &lt;code&gt;FRONTEND_URL&lt;/code&gt; constant in &lt;code&gt;backend/config.ts&lt;/code&gt; to point to where your frontend is hosted.&lt;/p&gt;

&lt;p&gt;Secondly, if you are running into CORS issues when calling your Encore API from your frontend then you may need to specify which origins are allowed to access your API (via browsers). You do this by specifying the &lt;code&gt;global_cors&lt;/code&gt; key in the &lt;code&gt;encore.app&lt;/code&gt; file, which has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"global_cors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;allow_origins_without_credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;allowed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;origins&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;requests&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;don't&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;credentials.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;defaults&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;allowing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;domains&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(equivalent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;).&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow_origins_without_credentials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ORIGIN-GOES-HERE&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;allow_origins_with_credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;specifies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;allowed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;origins&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;requests&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;credentials.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;request&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;made&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;an&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Encore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;responds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Access-Control-Allow-Origin:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;Origin&amp;gt;.&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;URLs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;wildcards&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(e.g.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://*.example.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://*-myapp.example.com"&lt;/span&gt;&lt;span class="err"&gt;).&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow_origins_with_credentials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;DOMAIN-GOES-HERE&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both of these default to "&lt;a href="https://encorets-saas-starter.vercel.app" rel="noopener noreferrer"&gt;https://encorets-saas-starter.vercel.app&lt;/a&gt;" which is the hosted demo application.&lt;/p&gt;

&lt;p&gt;More information on CORS configuration can be found here: &lt;a href="https://encore.dev/docs/ts/develop/cors" rel="noopener noreferrer"&gt;https://encore.dev/docs/ts/develop/cors&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;You now have a scalable and production-ready SaaS application up and running. Now keep going and develop your SaaS product!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you have questions: &lt;a href="https://encore.dev/discord" rel="noopener noreferrer"&gt;Join our Discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/encoredev/encore" rel="noopener noreferrer"&gt;Star Encore on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/encoredev/examples" rel="noopener noreferrer"&gt;Find more Open-source App Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dly.to/RHOdnsc6KxH" rel="noopener noreferrer"&gt;Join our Squad&lt;/a&gt; if you want to contribute content to Encore's blog&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Mon, 09 Feb 2026 13:39:59 +0000</pubDate>
      <link>https://dev.to/marcuskohlberg/-4med</link>
      <guid>https://dev.to/marcuskohlberg/-4med</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/encore" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4094%2F34ea76ef-564a-401f-85bd-4ae600dc8955.png" alt="Encore" width="400" height="400"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F577527%2Fdc97c6e3-cd4f-4618-b7a4-b04753829937.jpg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/encore/the-end-of-heroku-what-it-means-for-your-apps-5a8i" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;The End of Heroku: What It Means for Your Apps&lt;/h2&gt;
      &lt;h3&gt;Marcus Kohlberg for Encore ・ Feb 9&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#heroku&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cloud&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#backend&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>heroku</category>
      <category>cloud</category>
      <category>backend</category>
      <category>devops</category>
    </item>
    <item>
      <title>The End of Heroku: What It Means for Your Apps</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Mon, 09 Feb 2026 13:39:47 +0000</pubDate>
      <link>https://dev.to/encore/the-end-of-heroku-what-it-means-for-your-apps-5a8i</link>
      <guid>https://dev.to/encore/the-end-of-heroku-what-it-means-for-your-apps-5a8i</guid>
      <description>&lt;p&gt;Heroku &lt;a href="https://www.heroku.com/blog/an-update-on-heroku/" rel="noopener noreferrer"&gt;recently moved&lt;/a&gt; to a "sustaining engineering model." No new features, no new enterprise contracts. The platform stays running for existing customers, but active development has stopped.&lt;/p&gt;

&lt;p&gt;Heroku launched in 2007 and was acquired by &lt;a href="https://www.salesforce.com" rel="noopener noreferrer"&gt;Salesforce&lt;/a&gt; for $212 million in 2010. After &lt;a href="https://www.heroku.com/blog/new-low-cost-plans" rel="noopener noreferrer"&gt;removing its free tier&lt;/a&gt; in November 2022 and years of slowing feature development, the February 2026 announcement made the trajectory official.&lt;/p&gt;

&lt;p&gt;For teams planning a move, the biggest shift is toward infrastructure ownership: deploying to your own AWS or GCP account rather than renting another managed platform. But there are several directions and here's what each option offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encore Cloud
&lt;/h2&gt;

&lt;p&gt;If you want to replicate Heroku's &lt;code&gt;git push&lt;/code&gt; workflow while owning your infrastructure, &lt;a href="https://encore.cloud" rel="noopener noreferrer"&gt;Encore Cloud&lt;/a&gt; provisions managed resources in your own AWS or GCP account (powered by &lt;a href="https://encore.dev" rel="noopener noreferrer"&gt;Encore&lt;/a&gt;, an open-source framework with 11k+ GitHub stars). You declare infrastructure as type-safe objects in your TypeScript or Go code, and Encore provisions the corresponding managed services. Everything else is standard TypeScript or Go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SQLDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/storage/sqldb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/pubsub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CronJob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/cron&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// These declarations are all Encore needs to provision your infrastructure.&lt;/span&gt;
&lt;span class="c1"&gt;// Everything else in your codebase is standard TypeScript (or Go).&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./migrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OrderEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;events&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;deliveryGuarantee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;at-least-once&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CronJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;daily-cleanup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0 0 * * *&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cleanup&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;a href="https://encore.dev/docs/platform/infrastructure/aws" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, this provisions RDS, SNS/SQS, and CloudWatch Events. On &lt;a href="https://encore.dev/docs/platform/infrastructure/gcp" rel="noopener noreferrer"&gt;GCP&lt;/a&gt;, it provisions Cloud SQL, Pub/Sub, and Cloud Scheduler. Networking, IAM, encryption, and backups are configured automatically following cloud provider best practices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencore.cloud%2Fassets%2Fimg%2Foverview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencore.cloud%2Fassets%2Fimg%2Foverview.png" alt="Infrastructure from Code: define resources in TypeScript, deploy to AWS or GCP" width="800" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deployment works like Heroku: &lt;code&gt;git push encore main&lt;/code&gt;. The infrastructure runs in your account, visible in the AWS or GCP console. Local development runs everything with &lt;code&gt;encore run&lt;/code&gt;, every pull request gets an automatic &lt;a href="https://encore.dev/docs/platform/deploy/preview-environments" rel="noopener noreferrer"&gt;preview environment&lt;/a&gt;, and built-in distributed tracing and metrics come included.&lt;/p&gt;

&lt;p&gt;For teams using AI agents like &lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; and &lt;a href="https://claude.ai" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, infrastructure-from-code means infrastructure doesn't drift from application logic. When AI changes your code, infrastructure updates automatically to match, and guardrails like type validation and compile-time checks are built in so there's no separate Terraform or YAML falling out of sync.&lt;/p&gt;

&lt;p&gt;Companies like &lt;a href="https://encore.cloud/customers/groupon" rel="noopener noreferrer"&gt;Groupon&lt;/a&gt; already use this at scale with TypeScript and Go, deploying to both AWS and GCP (Azure is not yet supported).&lt;/p&gt;

&lt;h2&gt;
  
  
  Managed Platforms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Render
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://render.com" rel="noopener noreferrer"&gt;Render&lt;/a&gt; is the most direct Heroku replacement. The concepts are nearly identical: git-push deploys, managed Postgres, background workers, cron jobs. If you want the smallest possible change from your current workflow, Render is the shortest path.&lt;/p&gt;

&lt;p&gt;The mental model carries over cleanly as you connect a Git repo, define services in &lt;code&gt;render.yaml&lt;/code&gt; or through the dashboard, and push to deploy. Render handles SSL, load balancing, and auto-deploys from your branch, and preview environments work similarly to Heroku Review Apps.&lt;/p&gt;

&lt;p&gt;The main tradeoff is that you're still on someone else's infrastructure. If Render changes direction, raises prices, or gets acquired, you're in a similar position to where you are with Heroku. Per-service and per-seat pricing can also add up for larger teams running multiple services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fly.io
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://fly.io" rel="noopener noreferrer"&gt;Fly.io&lt;/a&gt; takes a different approach. Instead of abstracting away infrastructure, it gives you lightweight VMs (Machines) in 30+ regions worldwide. The deployment workflow has a similar feel (&lt;code&gt;fly deploy&lt;/code&gt; and your app is live) but the underlying model is closer to running containers than Heroku-style dynos.&lt;/p&gt;

&lt;p&gt;Fly.io is well-suited for applications where latency matters, with fast cold starts, automatic placement near users, and a global Anycast network. The community is active and the team is responsive on support.&lt;/p&gt;

&lt;p&gt;The managed Postgres offering has had some reliability challenges, though the team is actively improving this with their new MPG product. Pricing is usage-based, which can make monthly bills harder to predict compared to Heroku's flat per-dyno model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Railway
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://railway.com" rel="noopener noreferrer"&gt;Railway&lt;/a&gt; focuses on modern developer experience. The dashboard is clean, deploys are fast, and the project model is intuitive. It supports any language via Docker or Nixpacks (similar to Heroku buildpacks) and deploys from GitHub, a CLI, or templates.&lt;/p&gt;

&lt;p&gt;Railway stands out for teams: paid plans include unlimited seats, which is a meaningful differentiator over Render and Heroku where each team member adds to the bill. Support for monorepos and multi-service projects is solid.&lt;/p&gt;

&lt;p&gt;Like Render and Fly.io, you're on Railway's infrastructure. It's a managed platform with the same category of tradeoffs around ownership and portability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Hosted: Coolify, Dokku, Dokploy
&lt;/h2&gt;

&lt;p&gt;Tools like &lt;a href="https://coolify.io" rel="noopener noreferrer"&gt;Coolify&lt;/a&gt;, &lt;a href="https://dokku.com" rel="noopener noreferrer"&gt;Dokku&lt;/a&gt;, and &lt;a href="https://dokploy.com" rel="noopener noreferrer"&gt;Dokploy&lt;/a&gt; run a Heroku-like experience on your own VPS. The typical setup is a Hetzner or DigitalOcean server with one of these tools installed. You get git-push deploys, automatic SSL, and database provisioning.&lt;/p&gt;

&lt;p&gt;This path gives you the most control at the lowest price. Dokku is the most mature option (built in 2013, supports Heroku buildpacks directly). Coolify and Dokploy offer web dashboards for teams that prefer a GUI.&lt;/p&gt;

&lt;p&gt;The tradeoff is operational responsibility. You manage the server: OS updates, security patches, backups, monitoring. For side projects and internal tools this is fine. For production applications that need high availability, it takes significantly more effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Choose
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you need...&lt;/th&gt;
&lt;th&gt;Consider&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure ownership, compliance&lt;/td&gt;
&lt;td&gt;Encore Cloud (your AWS/GCP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fastest migration from Heroku&lt;/td&gt;
&lt;td&gt;Render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global edge performance&lt;/td&gt;
&lt;td&gt;Fly.io&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modern DX, unlimited team seats&lt;/td&gt;
&lt;td&gt;Railway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lowest possible cost&lt;/td&gt;
&lt;td&gt;Self-hosted (Coolify/Dokku)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Heroku's concepts (dynos, Postgres, worker processes, config vars, pipelines) map cleanly to every alternative. The migration is more about choosing the right destination than dealing with technical complexity.&lt;/p&gt;

&lt;p&gt;For step-by-step migration guides:&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/resources/end-of-heroku" rel="noopener noreferrer"&gt;The End of Heroku: What It Means for Your Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/resources/heroku-alternatives-2026" rel="noopener noreferrer"&gt;Top Heroku Alternatives in 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/resources/migrate-heroku-to-aws" rel="noopener noreferrer"&gt;How to Migrate from Heroku to AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/resources/migrate-heroku-to-gcp" rel="noopener noreferrer"&gt;How to Migrate from Heroku to GCP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>heroku</category>
      <category>cloud</category>
      <category>backend</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Deploy to AWS in 2026</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Tue, 27 Jan 2026 09:26:40 +0000</pubDate>
      <link>https://dev.to/encore/how-to-deploy-to-aws-in-2026-55bn</link>
      <guid>https://dev.to/encore/how-to-deploy-to-aws-in-2026-55bn</guid>
      <description>&lt;p&gt;Deploying to AWS in 2026 looks different than it did five years ago. The ecosystem has matured, new tools have emerged, and the "right" approach depends more than ever on your team's size, experience, and what you're building.&lt;/p&gt;

&lt;p&gt;This guide covers the main options for deploying backend applications to AWS, from clicking around in the console to fully automated infrastructure-as-code. We'll look at the trade-offs, show code examples, and help you pick the right approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: AWS Console (Click-Ops)
&lt;/h2&gt;

&lt;p&gt;The AWS Console is where most developers start. You log in, click through wizards, and configure services manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning AWS concepts&lt;/li&gt;
&lt;li&gt;Quick experiments and prototypes&lt;/li&gt;
&lt;li&gt;One-off tasks that don't need to be repeatable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When it doesn't:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anything you need to reproduce (staging, production, disaster recovery)&lt;/li&gt;
&lt;li&gt;Team environments where multiple people need to understand the setup&lt;/li&gt;
&lt;li&gt;Compliance requirements that need audit trails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The console is fine for learning, but you'll quickly hit limits. There's no version control, no way to review changes, and no easy path to reproduce what you built. Most teams graduate to something else within months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: Infrastructure from Code (Encore)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://encore.dev" rel="noopener noreferrer"&gt;Encore&lt;/a&gt; takes a different approach than traditional infrastructure-as-code. Instead of writing separate infrastructure definitions, you declare what your application needs directly in your backend code. &lt;a href="https://encore.cloud" rel="noopener noreferrer"&gt;Encore Cloud&lt;/a&gt; then provisions AWS resources automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SQLDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/storage/sqldb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/pubsub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This creates an RDS PostgreSQL database&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./migrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// This creates SNS/SQS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SignupEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;deliveryGuarantee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;at-least-once&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// This creates a Fargate service or Lambda&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="s2"&gt;`INSERT INTO users ...`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you push code, Encore analyzes it and provisions the corresponding AWS resources: RDS for databases, SNS/SQS for Pub/Sub, Fargate or Lambda for compute, S3 for object storage, CloudWatch for cron jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No Terraform, CloudFormation, or YAML to write&lt;/li&gt;
&lt;li&gt;Infrastructure changes through code review (not manual config)&lt;/li&gt;
&lt;li&gt;Local development with Docker-based versions of AWS services&lt;/li&gt;
&lt;li&gt;Preview environments for every pull request&lt;/li&gt;
&lt;li&gt;Built-in observability (tracing, metrics, logs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less granular control over resource configuration than raw Terraform&lt;/li&gt;
&lt;li&gt;Newer ecosystem than Terraform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Encore works well for teams that want AWS infrastructure ownership without becoming infrastructure experts. Companies like &lt;a href="https://encore.cloud/customers/groupon" rel="noopener noreferrer"&gt;Groupon&lt;/a&gt; use this approach to power their backends at scale. The open-source framework has 11k+ GitHub stars.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencore.cloud%2Fassets%2Fimg%2Foverview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencore.cloud%2Fassets%2Fimg%2Foverview.png" alt="Infrastructure from Code: define resources in TypeScript, deploy to AWS" width="800" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: AWS CloudFormation
&lt;/h2&gt;

&lt;p&gt;CloudFormation is AWS's native infrastructure-as-code service. You define resources in YAML or JSON templates, and CloudFormation creates and manages them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MyDatabase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::RDS::DBInstance&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DBInstanceClass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db.t3.micro&lt;/span&gt;
      &lt;span class="na"&gt;Engine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;MasterUsername&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
      &lt;span class="na"&gt;MasterUserPassword&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;DBPassword&lt;/span&gt;
      &lt;span class="na"&gt;AllocatedStorage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

  &lt;span class="na"&gt;MyLambda&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs18.x&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;S3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-deployment-bucket&lt;/span&gt;
        &lt;span class="na"&gt;S3Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda.zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native AWS integration, no third-party dependencies&lt;/li&gt;
&lt;li&gt;Drift detection (CloudFormation can tell if someone changed resources manually)&lt;/li&gt;
&lt;li&gt;Stack management for grouping related resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verbose YAML/JSON syntax&lt;/li&gt;
&lt;li&gt;Slow feedback loops (deployments can take minutes)&lt;/li&gt;
&lt;li&gt;Limited programming constructs (no loops, limited conditionals)&lt;/li&gt;
&lt;li&gt;AWS-only (can't manage GCP or other providers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CloudFormation is reliable but painful to write. Most teams either use a wrapper (like CDK) or switch to Terraform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 4: AWS CDK
&lt;/h2&gt;

&lt;p&gt;The Cloud Development Kit (CDK) lets you write CloudFormation in real programming languages. You write TypeScript, Python, or Go, and CDK synthesizes CloudFormation templates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;rds&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-rds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DatabaseInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DatabaseInstanceEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PostgresEngineVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VER_15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;instanceType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;InstanceType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InstanceClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;T3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InstanceSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MICRO&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_18_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instanceEndpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real programming language instead of YAML&lt;/li&gt;
&lt;li&gt;IDE autocomplete and type checking&lt;/li&gt;
&lt;li&gt;Reusable constructs and abstractions&lt;/li&gt;
&lt;li&gt;Still generates CloudFormation under the hood&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a compilation step (CDK → CloudFormation → AWS)&lt;/li&gt;
&lt;li&gt;Debugging means understanding both CDK and CloudFormation&lt;/li&gt;
&lt;li&gt;AWS-only&lt;/li&gt;
&lt;li&gt;Still requires understanding AWS service configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CDK is a significant improvement over raw CloudFormation, but you still need to understand AWS services deeply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 5: Terraform
&lt;/h2&gt;

&lt;p&gt;Terraform is the industry standard for infrastructure-as-code. It uses HashiCorp Configuration Language (HCL) and works across AWS, GCP, Azure, and hundreds of other providers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Terraform HCL syntax&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws_db_instance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;identifier&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;engine_version&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_class&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.t3.micro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;allocated_storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="nx"&gt;username&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs18.x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.handler&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lambda.zip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_db_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-cloud support&lt;/li&gt;
&lt;li&gt;Massive ecosystem (providers for everything)&lt;/li&gt;
&lt;li&gt;State management for tracking what's deployed&lt;/li&gt;
&lt;li&gt;Plan/apply workflow for reviewing changes before deploying&lt;/li&gt;
&lt;li&gt;Large community and extensive documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning HCL (it's not hard, but it's another language)&lt;/li&gt;
&lt;li&gt;State file management (remote state, locking, etc.)&lt;/li&gt;
&lt;li&gt;Can get complex for large infrastructures&lt;/li&gt;
&lt;li&gt;Separate from your application code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-off is that you're maintaining two codebases: your application and your infrastructure. This adds overhead but gives you more control over individual resource settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 6: Pulumi
&lt;/h2&gt;

&lt;p&gt;Pulumi is like Terraform but uses real programming languages instead of HCL. You write TypeScript, Python, Go, or C# to define infrastructure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/pulumi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@pulumi/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;engineVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instanceClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.t3.micro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;allocatedStorage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requireSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dbPassword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs18.x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.handler&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FileArchive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./lambda&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real programming language (loops, conditionals, functions)&lt;/li&gt;
&lt;li&gt;Type safety and IDE support&lt;/li&gt;
&lt;li&gt;Multi-cloud support&lt;/li&gt;
&lt;li&gt;Can share code between infrastructure and application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller ecosystem than Terraform&lt;/li&gt;
&lt;li&gt;Still requires understanding cloud service configuration&lt;/li&gt;
&lt;li&gt;Separate from your application code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pulumi offers an alternative to Terraform's HCL syntax if you prefer using TypeScript or Python for infrastructure definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 7: SST (Serverless Stack)
&lt;/h2&gt;

&lt;p&gt;SST focuses on serverless AWS deployments. It's built on CDK but optimized for Lambda, API Gateway, and related services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sst/constructs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;API&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;StackContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;primaryIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST /users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;packages/functions/src/create.handler&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimized for serverless (Lambda, DynamoDB, etc.)&lt;/li&gt;
&lt;li&gt;Live Lambda development (code changes without redeploy)&lt;/li&gt;
&lt;li&gt;Good developer experience for serverless patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless-focused (less suited for containers or traditional servers)&lt;/li&gt;
&lt;li&gt;Built on CDK, so inherits some of its complexity&lt;/li&gt;
&lt;li&gt;AWS-only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SST targets serverless applications on AWS. It's built on CDK, so you'll still need to understand AWS concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Learning Curve&lt;/th&gt;
&lt;th&gt;Flexibility&lt;/th&gt;
&lt;th&gt;Maintenance&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Console&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;None (not repeatable)&lt;/td&gt;
&lt;td&gt;Learning, experiments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Encore&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Teams wanting AWS without DevOps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudFormation&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;AWS-native shops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDK&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Teams comfortable with AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Multi-cloud, large infra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pulumi&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Developers who prefer real code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SST&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Serverless applications&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Which Should You Choose?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose the Console if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're learning AWS&lt;/li&gt;
&lt;li&gt;It's a one-time experiment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Encore if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to focus on backend code, not infrastructure&lt;/li&gt;
&lt;li&gt;You want AWS ownership without becoming an AWS expert&lt;/li&gt;
&lt;li&gt;You value fast iteration and built-in observability&lt;/li&gt;
&lt;li&gt;Your infrastructure needs are covered by databases, queues, cron, storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose CloudFormation/CDK if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're already invested in the AWS ecosystem&lt;/li&gt;
&lt;li&gt;You need fine-grained control over every AWS setting&lt;/li&gt;
&lt;li&gt;Your organization mandates AWS-native tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Terraform if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need multi-cloud support&lt;/li&gt;
&lt;li&gt;You have complex infrastructure requirements&lt;/li&gt;
&lt;li&gt;Your team has (or wants to build) infrastructure expertise&lt;/li&gt;
&lt;li&gt;You need to manage non-AWS resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Pulumi if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want Terraform's flexibility with real programming languages&lt;/li&gt;
&lt;li&gt;Your team prefers TypeScript/Python over HCL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose SST if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building serverless applications&lt;/li&gt;
&lt;li&gt;You want the best Lambda development experience&lt;/li&gt;
&lt;li&gt;You're okay being AWS-only&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Most teams should start with the simplest option that meets their needs, then evolve if necessary. If you're building a backend application and don't want to become an infrastructure expert, &lt;a href="https://encore.cloud" rel="noopener noreferrer"&gt;Encore&lt;/a&gt; is worth trying. If you need maximum flexibility or multi-cloud support, Terraform is the industry standard.&lt;/p&gt;

&lt;p&gt;Whatever you choose, avoid the console for anything you need to reproduce. Infrastructure-as-code isn't optional in 2026—it's how professional teams ship software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/resources/how-to-deploy-to-gcp-2026" rel="noopener noreferrer"&gt;How to Deploy to GCP in 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/comparison/terraform" rel="noopener noreferrer"&gt;Encore vs Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://encore.cloud/resources/deploy-aws-without-terraform" rel="noopener noreferrer"&gt;How to Deploy to AWS Without Terraform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>opensource</category>
      <category>cloud</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Fri, 23 Jan 2026 14:42:31 +0000</pubDate>
      <link>https://dev.to/marcuskohlberg/-4578</link>
      <guid>https://dev.to/marcuskohlberg/-4578</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/encore" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4094%2F34ea76ef-564a-401f-85bd-4ae600dc8955.png" alt="Encore" width="400" height="400"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F577527%2Fdc97c6e3-cd4f-4618-b7a4-b04753829937.jpg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/encore/best-typescript-backend-frameworks-in-2026-2jpi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Best TypeScript Backend Frameworks in 2026&lt;/h2&gt;
      &lt;h3&gt;Marcus Kohlberg for Encore ・ Jan 23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#backend&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>typescript</category>
      <category>node</category>
      <category>javascript</category>
      <category>backend</category>
    </item>
    <item>
      <title>Best TypeScript Backend Frameworks in 2026</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Fri, 23 Jan 2026 14:42:24 +0000</pubDate>
      <link>https://dev.to/encore/best-typescript-backend-frameworks-in-2026-2jpi</link>
      <guid>https://dev.to/encore/best-typescript-backend-frameworks-in-2026-2jpi</guid>
      <description>&lt;p&gt;TypeScript has become the default choice for backend development in the Node.js ecosystem. The combination of type safety and JavaScript's flexibility makes it ideal for building production APIs. But with so many frameworks available, picking the right one can be challenging.&lt;/p&gt;

&lt;p&gt;In this guide, we break down the most popular TypeScript backend frameworks in 2026, comparing their features, ideal use cases, and potential drawbacks to help you make an informed decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript Backend Frameworks: An Overview
&lt;/h2&gt;

&lt;p&gt;Here's a high-level comparison of the frameworks included in this article, comparing built-in support for common backend requirements.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Express.js&lt;/th&gt;
&lt;th&gt;Encore.ts&lt;/th&gt;
&lt;th&gt;Fastify&lt;/th&gt;
&lt;th&gt;NestJS&lt;/th&gt;
&lt;th&gt;Hono&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General purpose APIs&lt;/td&gt;
&lt;td&gt;Distributed systems&lt;/td&gt;
&lt;td&gt;Schema-validated APIs&lt;/td&gt;
&lt;td&gt;Enterprise applications&lt;/td&gt;
&lt;td&gt;Edge &amp;amp; serverless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built-in Validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (JSON Schema)&lt;/td&gt;
&lt;td&gt;Yes (decorators)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type-safe Service Calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built-in Tracing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure from Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto API Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (via plugins)&lt;/td&gt;
&lt;td&gt;Yes (via plugins)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Express.js
&lt;/h2&gt;

&lt;p&gt;Express is the grandfather of Node.js frameworks. Released in 2010, it remains the most widely used Node.js framework with millions of weekly npm downloads. Express takes a minimalist approach, providing the core building blocks for web applications while leaving architectural decisions to the developer.&lt;/p&gt;

&lt;p&gt;The framework's longevity has resulted in an unmatched ecosystem. Whatever functionality you need, there's likely Express middleware for it. This makes Express a safe choice for teams who value proven solutions and extensive community support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getUserById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Minimal core with extensive middleware ecosystem&lt;/li&gt;
&lt;li&gt;Flexible routing with support for various HTTP methods&lt;/li&gt;
&lt;li&gt;Template engine support for server-rendered applications&lt;/li&gt;
&lt;li&gt;Static file serving out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Express shines in situations where flexibility is paramount. Its unopinionated nature means you can structure your application however you like, using whatever libraries you prefer. The massive community means finding help, tutorials, and solutions to common problems is straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;Express was designed before TypeScript became mainstream, so type safety requires additional setup. Request validation needs external libraries like Zod or Joi. The framework's age also means some patterns feel dated compared to modern alternatives.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Consider Express
&lt;/h3&gt;

&lt;p&gt;Consider Express when you need maximum flexibility, want to leverage existing middleware, or have a team already experienced with the framework. It's also a reasonable choice for simple APIs where the overhead of more opinionated frameworks isn't justified.&lt;/p&gt;

&lt;p&gt;If you're evaluating Express against modern alternatives, see our detailed &lt;a href="https://encore.dev/articles/expressjs-vs-encore" rel="noopener noreferrer"&gt;Express.js vs Encore.ts comparison&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Encore.ts
&lt;/h2&gt;

&lt;p&gt;Encore.ts takes a fundamentally different approach to backend development. Rather than just providing HTTP routing, Encore is designed for building distributed systems with multiple services. You declare infrastructure like databases, Pub/Sub, and cron jobs directly in TypeScript, and Encore provisions them automatically during local development.&lt;/p&gt;

&lt;p&gt;The framework excels at making microservices feel like a monolith to develop. Service-to-service calls are type-safe function calls with automatic service discovery. Built-in distributed tracing shows you exactly how requests flow through your system. And because Encore understands your infrastructure, you get a &lt;a href="https://encore.dev/docs/ts/observability/service-catalog" rel="noopener noreferrer"&gt;service catalog&lt;/a&gt; and &lt;a href="https://encore.dev/docs/ts/observability/api-docs" rel="noopener noreferrer"&gt;auto-generated API documentation&lt;/a&gt; that stay in sync with your code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SQLDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/storage/sqldb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./migrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryRow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Type-safe request validation based on TypeScript types&lt;/li&gt;
&lt;li&gt;Infrastructure primitives (databases, Pub/Sub, cron) with automatic local provisioning&lt;/li&gt;
&lt;li&gt;Type-safe service-to-service communication with generated clients&lt;/li&gt;
&lt;li&gt;Built-in distributed tracing across services&lt;/li&gt;
&lt;li&gt;Service catalog and auto-generated API documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://encore.dev/docs/ts/primitives/streaming-apis" rel="noopener noreferrer"&gt;Streaming APIs&lt;/a&gt; with WebSocket support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Encore makes building distributed systems significantly easier. When you create a new service, you just create a new folder. Service calls become type-safe function calls with full IDE autocomplete. Databases provision automatically in local development without Docker configuration. Distributed tracing works out of the box across all your services.&lt;/p&gt;

&lt;p&gt;The consistent infrastructure SDKs mean you use the same patterns for databases, Pub/Sub, cron jobs, and more. This makes the codebase easier to understand and onboard new developers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencore.dev%2Fassets%2Fblog%2Fenc2-trace.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencore.dev%2Fassets%2Fblog%2Fenc2-trace.png" alt="Encore distributed tracing showing request flow" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;Encore is more opinionated than Express or Fastify. If you need to integrate with infrastructure that Encore doesn't support natively, you can use standard libraries alongside Encore's primitives. The framework works best when you embrace its conventions for building services.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Consider Encore.ts
&lt;/h3&gt;

&lt;p&gt;Consider Encore when you're building microservices or distributed systems, when you want type-safe service communication with automatic service discovery, when local infrastructure automation matters (databases, Pub/Sub without Docker), or when you want built-in observability across multiple services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try Encore.ts
&lt;/h3&gt;

&lt;p&gt;You can get started with Encore in minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://encore.dev/install.sh | bash
encore app create my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
encore run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;a href="https://encore.dev/docs/ts" rel="noopener noreferrer"&gt;Encore.ts documentation&lt;/a&gt; for more details, or follow the &lt;a href="https://encore.dev/docs/ts/tutorials/rest-api" rel="noopener noreferrer"&gt;REST API tutorial&lt;/a&gt; to build a complete application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fastify
&lt;/h2&gt;

&lt;p&gt;Fastify positions itself as a high-performance alternative to Express, though &lt;a href="https://encore.dev/blog/event-loops" rel="noopener noreferrer"&gt;recent benchmarks&lt;/a&gt; show this advantage has diminished. Modern alternatives like Encore.ts now significantly outperform both Fastify and Express in request throughput.&lt;/p&gt;

&lt;p&gt;That said, Fastify's value proposition goes beyond raw speed. It uses JSON Schema for validation, which provides both runtime checking and automatic serialization optimization. The plugin architecture keeps code organized and encapsulated, with each plugin having its own dependencies and lifecycle. Fastify also offers good TypeScript support and helpful error messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Fastify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Fastify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JSON Schema validation with automatic serialization&lt;/li&gt;
&lt;li&gt;Plugin-based architecture for modularity&lt;/li&gt;
&lt;li&gt;First-class TypeScript support&lt;/li&gt;
&lt;li&gt;Extensive logging with Pino integration&lt;/li&gt;
&lt;li&gt;OpenAPI generation from schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Fastify provides a good balance of performance and features. The JSON Schema validation provides runtime safety and can be used to generate OpenAPI documentation. The plugin system encourages good architectural patterns and makes testing easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;JSON Schema definitions can become verbose, especially for complex objects. The ecosystem is smaller than Express, so you may need to write custom plugins for less common use cases. The mental model for plugins takes some time to internalize.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Consider Fastify
&lt;/h3&gt;

&lt;p&gt;Consider Fastify when you appreciate a well-structured plugin architecture, when you want JSON Schema validation with OpenAPI generation, or when you're building a single-service API and are already familiar with the Fastify ecosystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  NestJS
&lt;/h2&gt;

&lt;p&gt;NestJS brings Angular-style architecture to the backend. It's highly opinionated, uses decorators extensively, and provides a comprehensive solution for building enterprise applications. The framework enforces consistent code organization through modules, controllers, and providers.&lt;/p&gt;

&lt;p&gt;The dependency injection system makes testing straightforward and encourages loose coupling between components. NestJS includes solutions for most backend requirements out of the box, including GraphQL, WebSockets, microservices, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Param&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UsersService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./users.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;usersService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UsersService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usersService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Angular-inspired module architecture&lt;/li&gt;
&lt;li&gt;Built-in dependency injection container&lt;/li&gt;
&lt;li&gt;Comprehensive decorator-based validation&lt;/li&gt;
&lt;li&gt;First-class GraphQL and WebSocket support&lt;/li&gt;
&lt;li&gt;Microservices patterns with multiple transport layers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;NestJS provides strong guardrails for large teams. The enforced architecture means code stays consistent as the team grows. The comprehensive feature set means you rarely need to look outside the framework for common requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;The learning curve is steep. Understanding modules, providers, guards, interceptors, and pipes takes time. The decorator-heavy approach can obscure what's actually happening, making debugging more challenging. For simple APIs, NestJS can feel like overkill.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Consider NestJS
&lt;/h3&gt;

&lt;p&gt;Consider NestJS for large enterprise applications where consistent architecture matters more than minimal boilerplate. It's particularly suitable for teams with Angular experience or those who value strong conventions.&lt;/p&gt;

&lt;p&gt;For a deeper comparison, see our &lt;a href="https://encore.dev/articles/nestjs-vs-encore" rel="noopener noreferrer"&gt;NestJS vs Encore.ts&lt;/a&gt; article.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hono
&lt;/h2&gt;

&lt;p&gt;Hono is a newer framework designed for modern JavaScript runtimes. It runs everywhere: Cloudflare Workers, Deno, Bun, and Node.js. The framework is ultrafast, with a minimal footprint that makes it perfect for edge deployments where bundle size matters.&lt;/p&gt;

&lt;p&gt;Despite its small size, Hono includes essential middleware for common tasks like JWT validation, CORS, and request logging. The API is clean and intuitive, borrowing good ideas from Express while modernizing the developer experience.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Hono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Hono&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Universal runtime support (Node.js, Deno, Bun, Cloudflare Workers)&lt;/li&gt;
&lt;li&gt;Minimal bundle size for edge deployment&lt;/li&gt;
&lt;li&gt;Built-in middleware for common requirements&lt;/li&gt;
&lt;li&gt;Clean, modern API design&lt;/li&gt;
&lt;li&gt;First-class TypeScript support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Hono shines at the edge. When deploying to Cloudflare Workers or similar platforms, bundle size directly impacts cold start times and costs. The framework's universal nature also means you can move between runtimes without rewriting your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;As a newer framework, Hono has a smaller community and fewer third-party integrations. Some Node.js-specific patterns may require adaptation. The edge-first design means certain traditional server patterns may feel awkward.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Consider Hono
&lt;/h3&gt;

&lt;p&gt;Consider Hono when deploying to edge platforms, when you need cross-runtime compatibility, or when building lightweight APIs where bundle size matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Choose
&lt;/h2&gt;

&lt;p&gt;The right framework depends on your specific situation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Express&lt;/strong&gt; remains the safe choice for general-purpose development, especially when flexibility and ecosystem maturity matter most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encore.ts&lt;/strong&gt; is ideal for building distributed systems with multiple services, when you want type-safe service communication, local infrastructure automation, and built-in observability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fastify&lt;/strong&gt; works well for single-service APIs where you want JSON Schema validation and a plugin architecture, though performance is no longer a differentiator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NestJS&lt;/strong&gt; fits large enterprise applications where consistent architecture and comprehensive features justify the learning investment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hono&lt;/strong&gt; is perfect for edge computing and serverless deployments where bundle size and cold start times matter.&lt;/p&gt;

&lt;p&gt;The best way to decide is to build a small project with your top two candidates. Each framework has excellent documentation, and the experience of building something real will clarify which tradeoffs matter most to your team.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building something with TypeScript? Join our &lt;a href="https://encore.dev/discord" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt; to discuss framework choices with other developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>javascript</category>
      <category>backend</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Tue, 13 Jan 2026 09:06:11 +0000</pubDate>
      <link>https://dev.to/marcuskohlberg/-5a36</link>
      <guid>https://dev.to/marcuskohlberg/-5a36</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/encore" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4094%2F34ea76ef-564a-401f-85bd-4ae600dc8955.png" alt="Encore" width="400" height="400"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F577527%2Fdc97c6e3-cd4f-4618-b7a4-b04753829937.jpg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/encore/moving-on-from-terraform-cdk-3aif" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Moving on from Terraform CDK&lt;/h2&gt;
      &lt;h3&gt;Marcus Kohlberg for Encore ・ Jan 13&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#terraform&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cloud&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#backend&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>terraform</category>
      <category>devops</category>
      <category>cloud</category>
      <category>backend</category>
    </item>
    <item>
      <title>Moving on from Terraform CDK</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Tue, 13 Jan 2026 09:06:02 +0000</pubDate>
      <link>https://dev.to/encore/moving-on-from-terraform-cdk-3aif</link>
      <guid>https://dev.to/encore/moving-on-from-terraform-cdk-3aif</guid>
      <description>&lt;p&gt;HashiCorp is &lt;a href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;sunsetting Terraform CDK&lt;/a&gt;. Terraform CDK let you write infrastructure in TypeScript instead of HCL - you'd define AWS resources in TypeScript, run &lt;code&gt;cdktf synth&lt;/code&gt; to generate Terraform JSON, then &lt;code&gt;terraform apply&lt;/code&gt; to provision everything. Writing infrastructure in a real programming language with type safety and IDE support instead of learning HCL syntax.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://encore.dev" rel="noopener noreferrer"&gt;Encore&lt;/a&gt;, we approach this differently. While Encore isn't a replacement for Terraform CDK, we think developers who appreciated CDK's approach might find our perspective interesting. Instead of writing infrastructure declarations that generate configs, you write application code with infrastructure primitives. Encore parses the application code to understand what infrastructure is needed, then provisions it directly through cloud provider APIs in your &lt;a href="https://encore.dev/docs/deploy/aws" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; or &lt;a href="https://encore.dev/docs/deploy/gcp" rel="noopener noreferrer"&gt;GCP&lt;/a&gt; account (or generates a &lt;a href="https://encore.dev/docs/deploy/docker" rel="noopener noreferrer"&gt;Docker image&lt;/a&gt; for self-hosting). The infrastructure declarations are part of your application code, not separate from it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article describes deploying through &lt;a href="https://encore.cloud" rel="noopener noreferrer"&gt;Encore Cloud&lt;/a&gt;, which provisions infrastructure directly in your &lt;a href="https://encore.dev/docs/deploy/aws" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; or &lt;a href="https://encore.dev/docs/deploy/gcp" rel="noopener noreferrer"&gt;GCP&lt;/a&gt; account without config files. &lt;a href="https://encore.dev/docs/deploy/self-hosting" rel="noopener noreferrer"&gt;Self-hosting&lt;/a&gt; is available, but requires you to set up your own deployment infrastructure. The application code and local development benefits apply either way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Terraform CDK Required
&lt;/h2&gt;

&lt;p&gt;Creating a database with Terraform CDK meant writing infrastructure configuration for the database instance, subnet groups, security groups, and all the wiring between them. Here's what that looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AwsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws/lib/provider&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DbInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws/lib/db-instance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DbSubnetGroup&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws/lib/db-subnet-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SecurityGroup&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws/lib/security-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subnetGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DbSubnetGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db-subnet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-db-subnet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subnetIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subnet-12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subnet-67890&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;securityGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db-sg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-db-sg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;vpcId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vpc-12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;fromPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;toPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tcp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cidrBlocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10.0.0.0/16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DbInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;engineVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;14.7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instanceClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;db.t3.micro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;allocatedStorage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myapp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dbSubnetGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subnetGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;vpcSecurityGroupIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;securityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;skipFinalSnapshot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application code that used this database was written separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two files, two different purposes. The infrastructure file declared AWS resources. The application file used those resources through environment variables. Running &lt;code&gt;cdktf synth&lt;/code&gt; generated Terraform JSON, then &lt;code&gt;terraform apply&lt;/code&gt; provisioned everything, then you'd deploy your application code separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  How We Approach This
&lt;/h2&gt;

&lt;p&gt;With Encore, application code includes infrastructure declarations. The database declaration and its usage are in the same file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SQLDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/storage/sqldb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myapp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./migrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM users`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://encore.dev/docs/ts/primitives/databases" rel="noopener noreferrer"&gt;&lt;code&gt;SQLDatabase&lt;/code&gt;&lt;/a&gt; declaration tells Encore this application needs a PostgreSQL database. When you run the app locally with &lt;code&gt;encore run&lt;/code&gt;, Encore starts a local PostgreSQL instance. When you deploy to AWS, Encore provisions RDS with backups, high availability, security groups, and least-privilege IAM policies configured automatically. The application code stays the same - locally it connects to the local database, in production it connects to RDS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bwilf86ghsx5ug0mda3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bwilf86ghsx5ug0mda3.png" alt="Local development dashboard" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Local development environment with database running&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Complete API Example
&lt;/h2&gt;

&lt;p&gt;Here's a user management API. With Terraform CDK, this would require separate infrastructure code for Lambda functions (or ECS tasks), API Gateway configuration, methods, integrations, IAM roles, security groups, and the database setup - easily several hundred lines of infrastructure declarations, plus the application code itself, plus state management setup.&lt;/p&gt;

&lt;p&gt;With Encore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SQLDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/storage/sqldb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SQLDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./migrations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="s2"&gt;`
      INSERT INTO users (email, name)
      VALUES (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryRow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;`
      SELECT id, email, name FROM users WHERE email = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users/:email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryRow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;`
      SELECT id, email, name FROM users WHERE email = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;encore run&lt;/code&gt; starts a local API server with a PostgreSQL database. The &lt;a href="https://encore.dev/docs/ts/primitives/apis" rel="noopener noreferrer"&gt;API&lt;/a&gt; is available at &lt;code&gt;http://localhost:4000&lt;/code&gt; and there's a &lt;a href="https://encore.dev/docs/develop/local-dev-dashboard" rel="noopener noreferrer"&gt;development dashboard&lt;/a&gt; at &lt;code&gt;http://localhost:9400&lt;/code&gt; showing your services, APIs, and database schema. Deploying to AWS provisions ECS tasks for running the services, an Application Load Balancer for routing requests, RDS for the database, TLS certificates, and IAM roles with appropriate permissions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b3dxo28ro03577czvmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b3dxo28ro03577czvmp.png" alt="API Explorer and Service Catalog" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;API Explorer with automatic documentation and testing interface&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://encore.dev/docs/ts/primitives/databases#defining-a-database-schema" rel="noopener noreferrer"&gt;Database migrations&lt;/a&gt; are standard SQL files. Here's &lt;code&gt;migrations/1_create_users.up.sql&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We run these migrations automatically when the application starts, both locally and in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pub/Sub
&lt;/h2&gt;

&lt;p&gt;Terraform CDK required configuring SNS topics, SQS queues, subscriptions, IAM policies, and then connecting them in your application code - usually 40+ lines of infrastructure configuration plus separate publisher and consumer code.&lt;/p&gt;

&lt;p&gt;With Encore's &lt;a href="https://encore.dev/docs/ts/primitives/pubsub" rel="noopener noreferrer"&gt;Pub/Sub&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Subscription&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/pubsub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;deliveryGuarantee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;at-least-once&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Publishing events&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user_123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;99.99&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Subscribing to events&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Subscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;process-orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="s2"&gt;`
      INSERT INTO processed_orders (order_id, user_id, total)
      VALUES (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same pattern applies - the &lt;code&gt;Topic&lt;/code&gt; declaration works locally with an in-memory pub/sub system and provisions SNS topics and SQS queues in AWS. Publishers and subscribers are type-safe through TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Object Storage &amp;amp; More
&lt;/h2&gt;

&lt;p&gt;The pattern extends to object storage and other primitives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;encore.dev/storage/objects&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-uploads&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;versioned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Upload a file&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;uploads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Download a file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;uploads&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Locally, this uses the filesystem. In AWS, it provisions S3 buckets with appropriate permissions and lifecycle policies.&lt;/p&gt;

&lt;p&gt;Encore supports more infrastructure primitives - secrets management, cron jobs, streaming APIs - all following the same pattern. See the &lt;a href="https://encore.dev/docs/ts/primitives" rel="noopener noreferrer"&gt;full list of primitives&lt;/a&gt; in the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  State Management
&lt;/h2&gt;

&lt;p&gt;Terraform CDK still used Terraform underneath, which meant managing state files. State was typically stored in S3 with DynamoDB for locking, and you'd occasionally hit state drift issues where your actual infrastructure didn't match what Terraform thought it provisioned. Team coordination required careful state locking, and migrating state between backends was manual work.&lt;/p&gt;

&lt;p&gt;We don't use state files in Encore. Instead, we analyze your application code to understand what infrastructure exists and what needs to be provisioned. When you deploy, we compare what's declared in your code to what's actually running in your cloud account and provision any differences. Infrastructure always matches code because the code is the source of truth - no separate state to drift out of sync, no locking mechanisms to configure, no state backend to manage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1s0f2wss00zy5bvftd16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1s0f2wss00zy5bvftd16.png" alt="Infrastructure approval flow" width="800" height="1019"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Infrastructure changes require approval before provisioning&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Type Safety
&lt;/h2&gt;

&lt;p&gt;Terraform CDK provided type safety for infrastructure declarations - TypeScript would catch errors if you misconfigured an RDS instance. But when application code connected to that infrastructure, type safety stopped. Connection strings came from environment variables, database queries were strings, API responses were &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With Encore, type safety extends throughout the stack. Database queries are type-checked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryRow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;`
  SELECT id, email, name FROM users WHERE email = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// user is User | undefined, fully type-safe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;API endpoints are type-safe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users/:email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryRow&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM users WHERE email = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://encore.dev/docs/ts/primitives/services-and-apis#calling-apis" rel="noopener noreferrer"&gt;Service-to-service calls&lt;/a&gt; are type-safe when you import other services from &lt;code&gt;~encore/clients&lt;/code&gt;. The TypeScript compiler catches type mismatches, your IDE provides autocomplete for API parameters and database columns, and runtime errors from type mismatches are caught at compile time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Development
&lt;/h2&gt;

&lt;p&gt;Terraform CDK wasn't the best experience locally. Local development meant setting up Docker containers manually, writing docker-compose files, and configuring environment variables differently for local vs production. The local environment approximated production but never quite matched.&lt;/p&gt;

&lt;p&gt;We designed Encore to run the same code locally and in production. Running &lt;code&gt;encore run&lt;/code&gt; starts your application with local infrastructure - PostgreSQL databases, pub/sub messaging, object storage buckets - whatever your code declares. The &lt;a href="https://encore.dev/docs/develop/local-dev-dashboard" rel="noopener noreferrer"&gt;local development dashboard&lt;/a&gt; shows your services, API documentation, database schema, distributed tracing, and logs. When you deploy, the same code runs with AWS resources instead of local ones, maintaining 1:1 parity between environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsix72dxcd7g7kj6ui1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsix72dxcd7g7kj6ui1b.png" alt="Flow architecture diagram" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automatic service flow visualization and architecture diagram&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Existing Infrastructure
&lt;/h2&gt;

&lt;p&gt;If you have existing Terraform managing infrastructure, Encore can run alongside it. Use Terraform for your existing resources, Encore for new &lt;a href="https://encore.dev/docs/ts/primitives/services-and-apis" rel="noopener noreferrer"&gt;services&lt;/a&gt;. Encore services are just TypeScript or Go code - they can connect to existing databases, Redis instances, external APIs, or any other infrastructure through standard connection strings and environment variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Install the Encore CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;encoredev/tap/encore

&lt;span class="c"&gt;# Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://encore.dev/install.sh | bash

&lt;span class="c"&gt;# Windows&lt;/span&gt;
iwr https://encore.dev/install.ps1 | iex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;encore app create my-app &lt;span class="nt"&gt;--example&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ts/hello-world
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;encore run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application runs at &lt;code&gt;http://localhost:4000&lt;/code&gt; with a development dashboard at &lt;code&gt;http://localhost:9400&lt;/code&gt;. The dashboard shows your API endpoints, lets you test them, displays &lt;a href="https://encore.dev/docs/develop/local-dev-dashboard#distributed-tracing" rel="noopener noreferrer"&gt;distributed tracing&lt;/a&gt; for requests, and includes a &lt;a href="https://encore.dev/docs/develop/local-dev-dashboard#database-inspector" rel="noopener noreferrer"&gt;database inspector&lt;/a&gt; for viewing your schema and data.&lt;/p&gt;

&lt;p&gt;When you're ready to deploy, connect your AWS account through &lt;a href="https://encore.cloud" rel="noopener noreferrer"&gt;Encore Cloud&lt;/a&gt; or &lt;a href="https://encore.dev/docs/deploy/self-hosting" rel="noopener noreferrer"&gt;self-host&lt;/a&gt; the deployment infrastructure. We handle provisioning everything your application declares.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Share with Terraform CDK
&lt;/h2&gt;

&lt;p&gt;Terraform CDK and Encore share a core belief: infrastructure definitions belong in real programming languages, not domain-specific configuration languages. If you chose Terraform CDK because you wanted type safety, IDE autocomplete, and familiar programming constructs for infrastructure, we built Encore with the same philosophy.&lt;/p&gt;

&lt;p&gt;Where we differ is in execution. Terraform CDK generated Terraform configs from your TypeScript, which meant managing state files, running synthesis steps, and deploying infrastructure separately from application code. We took the idea further: infrastructure declarations are part of the application code itself. When you write &lt;code&gt;new SQLDatabase()&lt;/code&gt; in Encore, that's both application code (using a database) and infrastructure declaration (needs a database). We analyze your code and provision what it declares, directly in your AWS or GCP account.&lt;/p&gt;

&lt;p&gt;If you were using Terraform CDK to provision databases, APIs, pub/sub, and similar resources, Encore can handle that workflow. You can't just swap CDK code for Encore code 1:1, but if you're starting fresh or migrating gradually, the TypeScript knowledge transfers directly - same language, same type safety, same IDE tooling. The main difference is that infrastructure declarations live in your application code rather than separate configuration files.&lt;/p&gt;

&lt;p&gt;To sum it up: Encore provisions standard AWS and GCP resources directly in your account - VPCs, ECS clusters, RDS databases, load balancers, IAM roles. Since everything runs in your infrastructure, there's minimal lock-in. You can see and manage resources through your cloud console, costs appear in your existing billing, and your ops team keeps full access and control. Companies like &lt;a href="https://encore.cloud/customers/groupon" rel="noopener noreferrer"&gt;Groupon&lt;/a&gt; use this in production, running it in their existing AWS organization alongside their current setup.&lt;/p&gt;

&lt;p&gt;Learn more at &lt;a href="https://encore.dev/docs" rel="noopener noreferrer"&gt;encore.dev/docs&lt;/a&gt; or try the &lt;a href="https://encore.dev/docs/quick-start" rel="noopener noreferrer"&gt;Quick Start guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>cloud</category>
      <category>backend</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Tue, 16 Dec 2025 15:44:06 +0000</pubDate>
      <link>https://dev.to/marcuskohlberg/-567f</link>
      <guid>https://dev.to/marcuskohlberg/-567f</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/encore" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4094%2F34ea76ef-564a-401f-85bd-4ae600dc8955.png" alt="Encore" width="400" height="400"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F577527%2Fdc97c6e3-cd4f-4618-b7a4-b04753829937.jpg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/encore/encore-cloud-20-development-platform-for-the-ai-era-552j" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Encore Cloud 2.0 - Development Platform for the AI Era&lt;/h2&gt;
      &lt;h3&gt;Marcus Kohlberg for Encore ・ Dec 16&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#cloud&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#backend&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>cloud</category>
      <category>devops</category>
      <category>backend</category>
      <category>node</category>
    </item>
    <item>
      <title>Encore Cloud 2.0 - Development Platform for the AI Era</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Tue, 16 Dec 2025 15:43:50 +0000</pubDate>
      <link>https://dev.to/encore/encore-cloud-20-development-platform-for-the-ai-era-552j</link>
      <guid>https://dev.to/encore/encore-cloud-20-development-platform-for-the-ai-era-552j</guid>
      <description>&lt;p&gt;We started Encore five years ago because developers were spending too much time configuring infrastructure and jumping through hoops, not building products.&lt;/p&gt;

&lt;p&gt;That problem persists. AI agents can now scaffold entire backends in minutes, but running them well - with proper observability, and reliable, secure, infrastructure across multiple environments - still requires significant work.&lt;/p&gt;

&lt;p&gt;Today, we're launching &lt;a href="https://encore.cloud" rel="noopener noreferrer"&gt;Encore Cloud 2.0&lt;/a&gt;, a big upgrade to our development platform that understands your code and automates the operations layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Before we get into what's new, we'd like to take a moment to expand on why we set out building Encore. I mean, what exactly is wrong with existing tools like Terraform?&lt;/p&gt;

&lt;p&gt;These days, AI is already helpful in writing Terraform. But its potential usefulness is limited, because it lacks context.&lt;/p&gt;

&lt;p&gt;For most teams, the "truth" of a system is scattered. Business logic lives in one place, infrastructure definitions in another, permissions somewhere else, plus there's those pesky &lt;em&gt;yet inevitable&lt;/em&gt; manual changes in cloud consoles. There's no single authoritative model of how the system runs.&lt;/p&gt;

&lt;p&gt;Without such a model, common questions require significant human effort to get right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which services &lt;em&gt;actually&lt;/em&gt; need to talk?&lt;/li&gt;
&lt;li&gt;What permissions are &lt;em&gt;truly&lt;/em&gt; required?&lt;/li&gt;
&lt;li&gt;How can we be &lt;em&gt;sure&lt;/em&gt; this infra change won't break something?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fragmentation turns operating your system into a game of "spot the difference" between your intent and your infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework-defined infrastructure
&lt;/h2&gt;

&lt;p&gt;We designed Encore to solve this by ensuring that the application code is the single source of truth for everything that impacts application behavior, so that we can derive a complete system model from it.&lt;/p&gt;

&lt;p&gt;It works by providing an open source backend framework for TypeScript and Go, which comes with packages to define services and APIs, and infrastructure like databases, message queues, and scheduled jobs.&lt;/p&gt;

&lt;p&gt;Unlike Terraform, you do not define specific cloud resources, configurations, or providers in code. You only express what is relevant to the application behavior.&lt;/p&gt;

&lt;p&gt;In Encore.ts, creating a database object is &lt;code&gt;new SQLDatabase("users")&lt;/code&gt;. An API endpoint is a function with the &lt;code&gt;api()&lt;/code&gt; decorator. This is enough for Encore to understand what you're building, identify which services need which permissions, and generate everything needed to run it.&lt;/p&gt;

&lt;p&gt;From your code, Encore Cloud automatically orchestrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Resources:&lt;/strong&gt; Databases, caches, and queues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Policies:&lt;/strong&gt; Least-privilege permissions, generated automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking:&lt;/strong&gt; Service discovery and secure connectivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability:&lt;/strong&gt; Tracing and metrics, wired out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; Service catalog, API docs, and architecture diagrams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because infrastructure details are not embedded in your code, the same application can run on different environments without modification. You create an environment with Encore Cloud and choose where it runs, on AWS or Google Cloud, and using what type of infrastructure, Kubernetes or serverless platforms.&lt;/p&gt;

&lt;p&gt;Everything deploys to your own AWS or Google Cloud account by integrating directly with their APIs. Encore provisions resources with sane defaults, which you can tune in Encore Cloud or directly in your cloud provider without them being overridden.&lt;/p&gt;

&lt;p&gt;With this approach, there's an intentional tradeoff; you partially give up having low-level control over individual cloud resources defined in code, in exchange for productivity, portability, and safety at the system level.&lt;/p&gt;

&lt;p&gt;For teams that want to hand-tune every single resource, Terraform is likely the right tool. For teams that want to focus on application behavior and evolve infrastructure over time without rewrites, Encore optimizes for that path while providing easy-to-reach knobs and configuration options for key things.&lt;/p&gt;

&lt;p&gt;Because your code is the source of truth, drift is effectively eliminated. If infrastructure is missing, Encore provisions it. If a resource is removed from your code, Encore marks it as "unused" so you can safely delete it when you're ready.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;— But, what about lock-in?&lt;/em&gt; We're engineers ourselves and get why this matters, so we've designed Encore to reduce lock-in from the start. &lt;a href="https://encore.dev/docs/platform/migration/migrate-away" rel="noopener noreferrer"&gt;Learn more about migrating away&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in 2.0
&lt;/h2&gt;

&lt;p&gt;Now let's get to the exciting bit, what's new.&lt;/p&gt;

&lt;p&gt;Encore Cloud 2.0 builds on the foundation we've created over the past five years, and delivers an upgraded platform that feels less like "managing servers" and more like shipping product.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A redesigned operations dashboard
&lt;/h3&gt;

&lt;p&gt;We've rebuilt the dashboard to reflect how engineers actually think about systems. It allows you to navigate large distributed applications across multiple environments, surfacing topology, deployments, and infrastructure state without the noise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ggg3cp6c1w24np9pl92.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ggg3cp6c1w24np9pl92.png" alt="New Cloud 2.0 dashboard" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Full-stack observability
&lt;/h3&gt;

&lt;p&gt;For teams using Encore.ts, metrics now live alongside traces and logs. You get a complete, correlated view of system behavior across services. No external agents to install, no dashboards to configure manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklbnn7t8c82lz11c7q4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklbnn7t8c82lz11c7q4q.png" alt="Traces" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For teams using Encore.go where metrics was already available, you get an upgraded dashboard with more performant and usable graphs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0pemxcymm4c0iv634qu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0pemxcymm4c0iv634qu.png" alt="Metrics" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. System transparency
&lt;/h3&gt;

&lt;p&gt;Because Encore understands your system model, it maintains the artifacts that usually go stale. In this release we've improved the performance and usability of these tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architecture Diagrams:&lt;/strong&gt; Always up-to-date, generated from the live graph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Catalog:&lt;/strong&gt; A real-time inventory of what's running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local MCP Server:&lt;/strong&gt; Allowing AI agents to inspect your system, and take actions like call endpoints, view traces, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw86fd68odumv9zu5vbdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw86fd68odumv9zu5vbdr.png" alt="Architecture diagram" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvbifc5vhexi6egtdlvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvbifc5vhexi6egtdlvw.png" alt="Service Catalog" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Zero-friction onboarding
&lt;/h3&gt;

&lt;p&gt;We've streamlined the path from "new repo" to "production URL." With tighter GitHub integration, you can go from code to a fully provisioned cloud environment in minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump in
&lt;/h2&gt;

&lt;p&gt;Encore Cloud 2.0 is available now. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://encore.cloud/sign-up" rel="noopener noreferrer"&gt;Deploy an example app&lt;/a&gt; to a free development environment, or &lt;a href="https://encore.cloud/book" rel="noopener noreferrer"&gt;book a demo&lt;/a&gt; to see how it works with a production system.&lt;/p&gt;

&lt;p&gt;For teams already using Encore, the 2.0 dashboard features are rolling out automatically. Update to the latest CLI version using &lt;code&gt;encore version update&lt;/code&gt; to start using metrics in Encore.ts.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>backend</category>
      <category>node</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcus Kohlberg</dc:creator>
      <pubDate>Thu, 11 Dec 2025 15:37:40 +0000</pubDate>
      <link>https://dev.to/marcuskohlberg/-4lbp</link>
      <guid>https://dev.to/marcuskohlberg/-4lbp</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/encore" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4094%2F34ea76ef-564a-401f-85bd-4ae600dc8955.png" alt="Encore" width="400" height="400"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3195131%2Fe1bc42ab-7aa8-4c13-9f6e-913d02b61ba1.png" alt="" width="96" height="96"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/encore/moving-on-from-terraform-cdk-5394" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Moving on from Terraform CDK&lt;/h2&gt;
      &lt;h3&gt;Ivan Cernja for Encore ・ Dec 11&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#terraform&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>terraform</category>
      <category>devops</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
