Building microservices in TypeScript is less about the HTTP framework and more about everything around it: how services communicate, how you manage infrastructure per service, how you trace a request across the system, how you keep types consistent across service boundaries, and how you deploy without every release becoming an ops event.
Most "best TypeScript framework" guides focus on single-service APIs, where Express, Fastify, Hono, and NestJS look roughly equivalent. For microservices, the differences get sharper. Some frameworks hand you the HTTP layer and expect you to assemble the rest. Others cover service-to-service type safety, infrastructure provisioning, and observability inside the framework itself.
In this guide, we walk through the most relevant TypeScript frameworks for microservices in 2026 and show where each fits, with honest tradeoffs and code examples.
A high-level comparison of the frameworks covered, with a focus on what matters for distributed systems.
| Feature | Encore | NestJS | Moleculer | Fastify | Hono |
|---|---|---|---|---|---|
| Primary use case | Distributed systems on AWS/GCP | Enterprise monoliths and microservices | Node.js microservices with service discovery | High-throughput HTTP APIs | Edge and serverless functions |
| Cross-service type safety | Yes (compile-time) | No (Observable<any>) | Limited | No (build it yourself) | No (build it yourself) |
| Infrastructure from Code | Yes | No | No | No | No |
| Built-in distributed tracing | Yes | No | Yes | No | No |
| Local dev for all services | encore run starts everything | Docker Compose or per-service | Single-process mode | Docker Compose per service | Wrangler / runtime-specific |
| Service discovery | Automatic | Manual (ClientsModule) | Built in | Manual | Not applicable |
| Pub/Sub primitive | Native (SNS/SQS, Pub/Sub) | Via transporters (Redis, NATS, Kafka) | Built-in event system | DIY | DIY |
| Deployment model | git push to AWS/GCP in your account | DIY (K8s, Fargate, etc.) | DIY | DIY | Edge platform deploy |
| Learning curve | Low | High | Medium | Low | Low |
Encore is purpose-built for TypeScript backends that span multiple services. You declare APIs, databases, Pub/Sub topics, and cron jobs as typed TypeScript objects, and Encore handles the rest: service discovery, cross-service type safety, distributed tracing, local dev, and deployment to AWS or GCP in your own cloud account. The framework has grown to over 11,000 GitHub stars and is used in production by companies including Groupon.
Creating a new service is creating a folder. Calling another service is calling a typed function. Provisioning a database or message queue is declaring one in code. The framework fills in the operational layer that every microservices project otherwise builds from scratch.
// users/users.ts
import { api } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
// Provisions managed Postgres (RDS on AWS, Cloud SQL on GCP, Docker locally).
const db = new SQLDatabase("users", { migrations: "./migrations" });
export interface User { id: number; email: string; }
export const get = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: { id: number }): Promise<User> => {
return await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
},
);
// orders/orders.ts
import { users } from "~encore/clients";
export const createOrder = api(
{ method: "POST", path: "/orders", expose: true },
async (req: CreateOrder) => {
const user = await users.get({ id: req.userId }); // compile-time checked
},
);
encore runThe gap between single-service and multi-service development narrows significantly. Adding a new service doesn't require new brokers, new task definitions, new deploy pipelines, or a new way to read logs. The same patterns scale from one service to twenty, and the operational overhead that normally comes with microservices is handled by the framework.
Type safety across services is the practical day-to-day win. Rename a field in one service and the compiler flags every consumer. That's a class of production bug (silent schema drift) that the framework eliminates rather than mitigates.
Encore is opinionated. If you need to run non-Node workloads or integrate tightly with a cloud Encore doesn't support natively (AWS and GCP are first-class; other clouds work via Encore's Terraform provider), you'll meet the edges of the abstraction. It's most valuable for teams that embrace its conventions rather than trying to route around them.
Consider Encore when you're building a new microservice system in TypeScript and want the framework to handle cross-service type safety, infrastructure, and observability. Especially strong if your target is AWS or GCP.
Install the CLI and create an app.
See the Encore documentation or the microservices tutorial for a full walkthrough.
NestJS is the most common opinionated TypeScript backend framework. Its dedicated microservices package (@nestjs/microservices) supports TCP, Redis, NATS, Kafka, MQTT, gRPC, and RabbitMQ transports, and lets a single NestJS app listen on multiple transports at once. The architecture (modules, controllers, providers, DI) gives you a consistent shape across services, at the cost of significant per-service boilerplate.
// users.controller.ts
@Controller()
export class UsersController {
@MessagePattern({ cmd: "get_user" })
async getUser(@Payload() id: number) {
return this.usersService.findOne(id);
}
}
// orders.module.ts
@Module({
imports: [
ClientsModule.register([{
name: "USERS_SERVICE",
transport: Transport.TCP,
options: { host: "users-service", port: 3001 },
}]),
],
})
export class OrdersModule {}
@nestjs/* integrations (config, GraphQL, WebSockets, scheduled jobs)@nestjs/testing for unit tests with DI-aware mockingTeams that like convention-heavy architectures get a lot from NestJS. The module system makes large codebases coherent, and the DI container is a real testing win. The microservices transports cover every realistic messaging model.
Cross-service type safety isn't covered. ClientProxy.send returns Observable<any>, so renaming a field in a producing service fails silently in the consumer at runtime. Infrastructure (databases per service, brokers, observability, deployment) is entirely DIY. Teams usually build a platform layer around NestJS to make microservices workable at scale.
Consider NestJS when your team is committed to the ecosystem, you have platform-engineering capacity to build out infrastructure and observability, or you need one of the microservice transports it supports. For the full picture, see the NestJS microservices guide.
Moleculer is a Node.js framework built specifically for microservices. Service discovery, load balancing, fault tolerance, and event-driven patterns are baked into the core. It's the most "batteries included for microservices" of the TypeScript-capable frameworks, though its TypeScript story is weaker than NestJS or Encore.
module.exports = {
name: "users",
actions: {
get(ctx) {
return this.findById(ctx.params.id);
},
},
events: {
"user.created"(ctx) {
// ...
},
},
};
Moleculer was designed around microservices from day one, so a lot of the wiring other frameworks leave to you is already there. The local dev experience (running all services in one Node process) is a notable DX win.
TypeScript support exists but the API is JavaScript-shaped. Action calls between services aren't type-checked by default. The community is smaller than NestJS, and infrastructure (databases, caches, message brokers, deployment) is still assembled separately.
Consider Moleculer when you want a framework explicitly built for microservices, you can live with weaker TypeScript integration, and you prefer JavaScript-shaped APIs over decorator or typed-primitive approaches.
Fastify is a fast, schema-first HTTP framework. It doesn't ship a microservices package, but its performance and plugin architecture make it a reasonable HTTP foundation for a microservices fleet where each service is a standalone Fastify app.
import Fastify from "fastify";
const app = Fastify({ logger: true });
app.get<{ Params: { id: string } }>("/users/:id", async (req, reply) => {
const user = await db.users.findById(req.params.id);
if (!user) return reply.status(404).send({ error: "not found" });
return user;
});
app.listen({ port: 3000 });
Each service is small, fast, and uncomplicated. For teams that want an Express-shaped API with better performance and validation, Fastify is a clean fit. Its lack of opinion about how services relate is liberating if you already have strong patterns.
Fastify doesn't help with anything beyond HTTP. Cross-service type safety, service discovery, brokers, observability, and deployment are all your problem. For a microservices system specifically, you're building a framework on top of Fastify rather than using one.
Consider Fastify for a microservices fleet when you want minimal framework overhead per service, you have strong opinions about every other layer (ORM, auth, broker, observability), and you accept that you're assembling the microservices story yourself.
Hono is a lightweight, edge-native TypeScript framework. It's the strongest choice when "microservices" in your system are really serverless or edge functions deployed to Cloudflare Workers, Vercel Edge, Deno Deploy, or similar runtimes.
import { Hono } from "hono";
const app = new Hono();
app.get("/users/:id", async (c) => {
const id = c.req.param("id");
const user = await db.users.findById(id);
if (!user) return c.json({ error: "not found" }, 404);
return c.json(user);
});
export default app;
For edge-heavy architectures, Hono is the only framework here that fits natively. Deploys are fast and cold starts are measured in tens of milliseconds. The type inference through chained middleware is particularly nice with @hono/zod-validator.
Hono is a per-function framework, not a per-system one. Coordinating multiple edge functions as a coherent system, sharing types between them, or handling durable state is outside the framework's scope. Traditional microservice patterns (pub/sub, service discovery, distributed tracing) don't map cleanly to edge runtimes.
Consider Hono when your "microservices" are really serverless or edge functions. It's also the right choice for any Node-moving-to-Bun migration where forward compatibility matters. For traditional always-on microservices, one of the other frameworks on this list is a better fit.
For a new TypeScript microservice system in 2026, Encore is the recommended default. It covers the parts of microservices that other frameworks leave to platform engineering (cross-service type safety, infrastructure, tracing, local dev, deployment) and stays out of your way at the application layer.
Moleculer is worth considering if you want an explicitly microservices-first framework and can live with weaker TypeScript integration. NestJS makes sense when you're already committed to its ecosystem and have the capacity to build out the platform layer it doesn't provide. Fastify is a good HTTP foundation if you plan to assemble everything else yourself. Hono is the right choice specifically for edge and serverless function architectures.
For existing systems, stay put unless you're hitting real pain. Microservice framework migrations are hard, and the incremental path (rewriting one service at a time while the rest keeps running) is usually the only practical option.
# Encore
brew install encoredev/tap/encore
encore app create my-app --example=ts/empty
cd my-app && encore run
# NestJS microservices
npm install -g @nestjs/cli
nest new my-app --monorepo
# Moleculer
npm install moleculer
Want to jump straight to a running app? Clone this starter and deploy it to your own cloud.