Skip to content
Published on

TypeScript Ecosystem 2026 Complete Guide — Bun · Deno · Hono · Elysia · NestJS · Effect · tRPC · Drizzle · Prisma · Zod · Vitest Deep Dive

Authors

"Take the freedom of JavaScript, add the safety of TypeScript, then layer Effect for functional effects, Bun for speed, and Hono for the edge — that is the TypeScript backend of 2026." — Paraphrased from the TypeScript Conf 2025 keynote

Fourteen years have passed since TypeScript first appeared as 0.8 from Anders Hejlsberg in 2012. In that span TypeScript has gone from "JavaScript with types attached" to the standard language of an enormous backend and full-stack ecosystem. As of May 2026, the TypeScript compiler is downloaded more than 100 million times per week on npm, and Stack Overflow's 2025 Developer Survey placed TypeScript fourth as "most loved" and third as "most used."

The shift in the backend space is especially dramatic. Node.js now has 22 LTS and 24 Active running side by side, Bun 1.2 is no longer an "experimental fast runtime" but a real production alternative, and Deno 2.x has returned to the stage after completing npm compatibility. Hono is now the most-used framework on V8 isolates, Elysia is the high-performance choice for Bun, and NestJS 11 is the enterprise standard. This article covers TypeScript 5.6, three runtimes, seven web frameworks, five ORMs, five validation libraries, workflow engines, test tools, build tools, and real adoption stories from Korea and Japan — all in one place.

1. The 2026 TypeScript Backend Map

As of May 2026, TypeScript backends largely sort into five axes.

AxisRepresentative ToolsKey Themes
RuntimeNode.js 22/24, Bun 1.2, Deno 2.xCompatibility, speed, security
Web frameworkHono, Elysia, NestJS 11, Fastify 5, h3/NitroRouting, DI, middleware
ValidationZod 4, ArkType, Valibot, Effect Schema, TypeBoxType inference, runtime checks
ORMDrizzle 0.40, Prisma 6, Kysely, MikroORM 6SQL DSL, migrations
WorkflowEffect, Temporal, Inngest, Trigger.dev, HatchetRetries, durability, scheduling

The biggest change is this: runtime choice is no longer "Node or not Node." Bun 1.2 runs almost every npm package through its Node compatibility mode, and Deno 2.x absorbs the npm world through npm: / jsr: schemes. So in 2026, the question is less "which runtime is fastest" and more "which runtime does our team's tooling, observability, and platform actually support."

2. TypeScript 5.6 — using declarations · isolated declarations · ISO 8601 Date

TypeScript 5.6 shipped in late 2025, and as of May 2026 the 5.7 release candidate is out. From a backend perspective, three features in 5.6 are worth highlighting.

First, using declarations (ECMAScript Explicit Resource Management). Declaring something like using db = await pool.connect() automatically calls Symbol.dispose or Symbol.asyncDispose when the value goes out of scope, closing the resource. DB connections, file handles, and transactions can now be managed with a pattern that is close to RAII.

Second, isolated declarations (--isolatedDeclarations). This mode forces every export to carry an explicit type annotation, so build tools can extract type information without doing inference. Non-tsc bundlers like Bun, esbuild, swc, and oxc can emit .d.ts files directly, which dramatically reduces the tsc -b bottleneck in monorepos.

Third, improved ISO 8601 Date handling. Parsing strings like new Date('2026-05-16T00:00:00Z') has tightened timezone inference, and the Intl API has been bolstered, which improves timestamp consistency across logging and observability tools.

// using declarations example
import { Pool } from 'pg'

const pool = new Pool({ connectionString: process.env.DATABASE_URL })

async function fetchUser(id: string) {
  await using client = await pool.connect()
  const result = await client.query('SELECT * FROM users WHERE id = $1', [id])
  return result.rows[0]
  // client[Symbol.asyncDispose]() runs automatically at scope exit
}

5.6 also added --noUncheckedSideEffectImports, which prevents side-effect imports like import './styles.css' from being silently dropped at type-check time.

3. Node.js 22 LTS and 24 Active — built-in fetch, test runner, watch mode

As of May 2026, Node.js 22.x is in Active LTS, 24.x is Active (and the next LTS candidate), and 26.x is preparing to branch off soon. The 22 LTS changes that matter most for backends:

  • Built-in fetch and WebSocket: The undici-based fetch was promoted to stable in 22, and the WebSocket client was standardized.
  • node:test: The built-in test runner went stable in 22. Light unit tests now work without Jest or Mocha.
  • --watch mode: node --watch app.ts is enough to detect file changes and restart. A nodemon replacement.
  • --env-file=.env: Load env files without dotenv.
  • Permission Model: Permission isolation in Deno style, via flags like --permission --allow-fs-read=....

The 24.x line updates to V8 12.8 and ICU 75 and exposes a beta SQLite via node --experimental-sqlite. 24 also adds node --experimental-strip-types so TypeScript files can be executed without a transpile step (type checking is a separate concern).

# Run TypeScript directly on Node 22 LTS (experimental)
node --experimental-strip-types app.ts

# Run *.test.ts with the built-in test runner
node --test --experimental-strip-types '**/*.test.ts'

# Load .env file + permission model
node --env-file=.env --permission --allow-fs-read=./data app.ts

Now that TypeScript execution is landing in Node itself, many observers feel the need for "Node + tsx" is shrinking. In production, however, the standard remains "pre-build with tsc."

4. Bun 1.2 — Node-compatible, fast startup, bundler · SQLite · Redis built in

Bun is a Zig-based JavaScript runtime that Jarred Sumner started in 2021, built on the JavaScriptCore (WebKit) engine. 1.0 shipped in 2024, and a year and a half later 1.2 has reframed Bun from "experimental fast runtime" to "Node replacement."

Highlights of 1.2:

  • Node compatibility: Full support for node_modules, CommonJS, ESM, and every field of package.json. Most of Express, Fastify, NestJS, and Next.js runs as-is.
  • Speed: Cold start is about 4x faster than Node, and bun install is up to 25x faster than npm (official benchmarks).
  • Built-in bundler: bun build provides esbuild-class bundling without a separate tool.
  • Built-in test: bun test offers a Jest-compatible API.
  • Built-in SQLite: import { Database } from "bun:sqlite".
  • Built-in Redis/HTTP/WebSocket: Bun.serve(), Bun.redis() minimize external dependencies.
  • bun.lockb to bun.lock: 1.2 adds a text lockfile option, which is friendlier to code review.
// HTTP server via Bun.serve() (pure Bun, no Hono)
import { Database } from "bun:sqlite"

const db = new Database(":memory:")
db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
db.run("INSERT INTO users (name) VALUES (?)", ["Toss"])

Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url)
    if (url.pathname === "/users") {
      const rows = db.query("SELECT * FROM users").all()
      return Response.json(rows)
    }
    return new Response("Not Found", { status: 404 })
  },
})

console.log("Listening on http://localhost:3000")

In 2026 every major PaaS — Vercel, Cloudflare, Railway, Fly.io — supports Bun as a first-class build runtime. In Korea, some of Toss's side projects and live backends run on Bun; in Japan, some microservices at Mercari and LayerX do too.

5. Deno 2.x — npm · jsr compatible, Permissions, Deno Deploy

Deno is the runtime Ryan Dahl released in 2020 after writing up his "10 things I regret about Node." In the 1.x era npm incompatibility was a major drag, but the mood shifted with 2.0 (October 2024).

Core points of 2.x:

  • npm/jsr compatibility: You can import directly with import express from "npm:express" or import zod from "jsr:@zod/zod". Auto-generating node_modules is also an option.
  • Workspace monorepos: A workspaces field in deno.json.
  • Permission model: No permissions by default. Grant via --allow-net, --allow-read, and so on.
  • deno compile: Single-binary builds, perfect for Lambda/Worker deployment.
  • Deno Deploy: A V8-isolate-based edge platform across 35 regions worldwide.
  • Built-in deno test, deno lint, deno fmt.
// Declare deps in deno.json + run with permission isolation
// $ deno run --allow-net --allow-env main.ts
import { Hono } from "jsr:@hono/hono"
import { z } from "npm:zod@4"

const app = new Hono()

const UserSchema = z.object({ id: z.string(), email: z.string().email() })

app.post("/users", async (c) => {
  const body = UserSchema.parse(await c.req.json())
  return c.json({ ok: true, user: body })
})

Deno.serve({ port: 8000 }, app.fetch)

In 2026 Deno has landed the jsr.io package registry and cemented its identity as a "TypeScript-first secure runtime." Adoption is still below Node and Bun, and the main use cases are edge, CLIs, and scripts.

6. Package managers — npm 11 · pnpm 10 · Yarn 5 Berry · Bun

As of May 2026, the four JavaScript package managers have each carved out their own territory.

ManagerVersionTraits
npm11Bundled with Node, registry standard, simple
pnpm10Symlink-based, disk-efficient, monorepo
Yarn5 Berry (PnP/nm)Plug'n'Play, enterprise
Bun1.2Fastest, integrated with Bun runtime

pnpm 10 brought the most interesting changes. pnpm install now blocks lifecycle scripts by default for security, pnpm dlx has standardized as the npx replacement, and catalogs centralize dependency versions across monorepos. npm 11 improved workspace command consistency and audit-fix accuracy. Yarn 5 keeps Plug'n'Play as the default while stabilizing the nodeLinker: node-modules compat mode.

# pnpm 10 catalog example (pnpm-workspace.yaml)
# Define catalog → reference catalog:default in every package
#
# packages:
#   - 'apps/*'
#   - 'packages/*'
# catalog:
#   typescript: ^5.6.0
#   zod: ^4.0.0

# Install a package across the whole workspace
pnpm -r add zod@catalog:

# Bun workspaces
bun install --frozen-lockfile

# Yarn 5 (Berry) Plug'n'Play
yarn install --immutable

Bun wins the performance benchmarks (25x faster than npm on cold install), but in the large monorepos of Korea and Japan pnpm 10 is the most broadly used. Toss, Coupang, Mercari, and freee are all pnpm-based.

7. Hono — the most-used edge framework on V8 isolates

Hono (炎) is an ultra-light web framework built by Yusuke Wada, a Japan-based developer of Korean heritage. It combines an Express-style API with an ultra-fast router and the ability to run on any runtime.

It supports an exceptionally broad set of runtimes: Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, Vercel Edge, Fastly Compute. The core value is that the same code moves between them unchanged.

Key features as of May 2026:

  • Ultra-fast Router: TrieRouter / RegExpRouter / LinearRouter — automatically selects the best router for the environment.
  • Type-safe middleware: Values that middleware adds to the context propagate into the next handler's types.
  • hono/zod-validator, hono/typebox-validator: Built-in integrations with validation libraries.
  • hono/jsx: Server-side JSX rendering with no React dependency.
  • HonoX: A Hono-based meta-framework (a Next.js-style full-stack alternative).
// Minimal REST API written in Hono
import { Hono } from "hono"
import { zValidator } from "@hono/zod-validator"
import { z } from "zod"

const app = new Hono()

const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1),
})

app.get("/", (c) => c.text("Hello Hono!"))

app.post("/users", zValidator("json", CreateUserSchema), async (c) => {
  const body = c.req.valid("json")
  // body is inferred as { email: string; name: string }
  return c.json({ ok: true, user: body }, 201)
})

// Same code on Cloudflare Workers, Deno, Bun, and Node
export default app

After Cloudflare officially adopted it, more than 70% of Workers code in 2026 is written in Hono, and it is spreading fast across Vercel, Deno Deploy, Bun, and Fastly. npm downloads grew 7x from 500k per week in 2024 to about 3.5M per week in May 2026.

8. Elysia — the Bun-native high-performance framework

Elysia is often compared with Hono, but the directions differ. Elysia is specialized for Bun, aggressively uses code generation that the JIT can optimize, and provides end-to-end type safety via Eden.

What sets it apart:

  • Bun-native: It gives up multi-runtime support in exchange for maximum performance on Bun.
  • Compile-time routing: Generated code, fastest routing.
  • Eden: tRPC-like feature where server route types are inferred on the client.
  • TypeBox integration: TypeBox is the default validation library.
  • Automatic OpenAPI generation.
import { Elysia, t } from "elysia"

const app = new Elysia()
  .get("/", () => "Hello Elysia")
  .post(
    "/users",
    ({ body }) => ({ ok: true, user: body }),
    {
      body: t.Object({
        email: t.String({ format: "email" }),
        name: t.String({ minLength: 1 }),
      }),
    }
  )
  .listen(3000)

export type App = typeof app
// On the client, import the App type via Eden to share inference

Benchmarks show throughput on simple JSON responses 1.5x higher than Hono on Bun and 20x higher than Express. The downside: heavy Bun dependence makes it a poor fit for multi-runtime strategies.

9. NestJS 11 — enterprise standard with Angular-style DI

NestJS is the Angular-style DI-container-driven backend framework Kamil Myśliwiec started in 2017. As of May 2026, the major line is 11.x, with these core changes:

  • First-class support for both Express 5 and Fastify 5: pick an adapter.
  • Official support for TypeScript 5.6 + Stage 3 decorators.
  • Standardized Microservices, GraphQL, WebSocket, and Bull-queue modules.
  • Improved stability for automatic OpenAPI/Swagger generation.
  • Standalone application mode — usable as a CLI tool without HTTP.

NestJS's value is "a structure where a large team can work together without convention collisions." Module / Controller / Service / Provider layers are explicit and DI is mandatory, which makes unit tests easy.

import { Controller, Get, Post, Body, Injectable, Module } from "@nestjs/common"
import { IsEmail, MinLength } from "class-validator"

class CreateUserDto {
  @IsEmail()
  email!: string

  @MinLength(1)
  name!: string
}

@Injectable()
class UsersService {
  private users: CreateUserDto[] = []
  create(dto: CreateUserDto) {
    this.users.push(dto)
    return dto
  }
  findAll() {
    return this.users
  }
}

@Controller("users")
class UsersController {
  constructor(private readonly users: UsersService) {}

  @Get()
  list() {
    return this.users.findAll()
  }

  @Post()
  create(@Body() body: CreateUserDto) {
    return this.users.create(body)
  }
}

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class AppModule {}

In Korea, Kakao Enterprise, some teams at Coupang, and Yanolja use NestJS; in Japan, services at CyberAgent and LINE do. The most common comment is "this is the easiest shape for Java/Spring Boot engineers to move into TypeScript."

10. Fastify 5 · Express 5 · h3/Nitro · Encore.ts — the rest of the backend frameworks

Fastify 5 is the high-performance standard Node framework created by Matteo Collina and Tomas Della Vedova. It is more than 2x faster than Express, with strong JSON-schema validation and a rich plugin ecosystem. In v5 (May 2026 baseline), ESM and TypeScript typings improved significantly.

Express 5 is the first major Express update in 17 years (GA in 2024). It standardizes async error handling, splits Express Router out as a library, and bundles a body parser. It is still the #1 backend framework by npm downloads.

h3/Nitro is a stack from the Nuxt team (UnJS): H3 provides ASGI-style request handlers and Nitro wraps it as a server builder. Nuxt is built on Nitro, and you can use it directly as a backend. Cloudflare/Vercel/Deno/Bun adapters are provided.

Encore.ts is a full-stack backend that "expresses backend data infrastructure as code." DB, queues, cron, and APIs are all declared with TypeScript types, and Encore provisions the infrastructure. Adoption is small but DX is widely praised.

// Fastify 5 + Zod 4 plugin
import Fastify from "fastify"
import { z } from "zod"
import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "fastify-type-provider-zod"

const app = Fastify({ logger: true }).withTypeProvider<ZodTypeProvider>()
app.setValidatorCompiler(validatorCompiler)
app.setSerializerCompiler(serializerCompiler)

app.route({
  method: "POST",
  url: "/users",
  schema: {
    body: z.object({ email: z.string().email(), name: z.string().min(1) }),
    response: { 201: z.object({ ok: z.literal(true) }) },
  },
  handler: async (req, reply) => {
    // req.body is auto-inferred via z.infer<...>
    return reply.code(201).send({ ok: true })
  },
})

await app.listen({ port: 3000 })

11. Effect — the functional effect system for TypeScript

Effect (formerly Effect-TS) is a TypeScript effect-system library led by Michael Arnaldi, inspired by ZIO, Cats Effect, and RIO. 3.0 shipped in 2024, and by May 2026 it has stabilized through the 3.10 line.

Core concepts:

  • Effect<A, E, R>: success type A, failure type E, dependency type R. Errors and deps appear in the type.
  • Structured concurrency: Fiber-based concurrency via Effect.all, Effect.race, Effect.fork.
  • Retry/Schedule: Declarative retries like Effect.retry(Schedule.exponential("100 millis")).
  • Layer: A dependency injection system. Compose Rs across modules.
  • Schema: Validation, decode, and encode in one (an alternative to Zod).
  • STM: Software transactional memory.
import { Effect, Schedule, Console, Layer } from "effect"

const fetchUser = (id: string) =>
  Effect.tryPromise({
    try: () => fetch(`/api/users/${id}`).then((r) => r.json()),
    catch: (e) => new Error(`fetch failed: ${String(e)}`),
  })

const program = Effect.gen(function* () {
  const user = yield* fetchUser("u_1").pipe(
    Effect.retry(Schedule.exponential("100 millis").pipe(Schedule.compose(Schedule.recurs(3))))
  )
  yield* Console.log(`got user: ${JSON.stringify(user)}`)
})

Effect.runPromise(program)

Teams that adopt Effect end-to-end (for example Effectful Tech and some fintechs) say "exceptions are gone." Because dependencies, errors, and concurrency live in the type, large-scale backend refactors become safer. The trade-off is a steep learning curve, so Effect can be overkill for small teams.

12. tRPC 11 — end-to-end type-safe RPC

tRPC is the "connect client and server using only TypeScript types, no REST or GraphQL" RPC project from Alex Johansson. v11 (2024) completed integrations with React Query v5, Tanstack Router, and the Next.js App Router.

Key traits:

  • Type inference only: No code-generation step. The client imports the server type as-is.
  • Procedures: query, mutation, and subscription.
  • Adapters: Express, Fastify, Next.js, Bun, Hono, AWS Lambda all supported.
  • Links: HTTP, WebSocket, batch, splitLink.
  • v11 changes: AbortSignal support, enhanced output transformers, FormData inputs.
// Server
import { initTRPC } from "@trpc/server"
import { z } from "zod"

const t = initTRPC.create()

export const appRouter = t.router({
  user: t.router({
    byId: t.procedure
      .input(z.object({ id: z.string() }))
      .query(({ input }) => ({ id: input.id, name: "Toss user" })),

    create: t.procedure
      .input(z.object({ email: z.string().email(), name: z.string() }))
      .mutation(({ input }) => ({ ok: true, ...input })),
  }),
})

export type AppRouter = typeof appRouter

// Client
// import type { AppRouter } from "./server"
// const trpc = createTRPCClient<AppRouter>({...})
// const u = await trpc.user.byId.query({ id: "u_1" })
// → u is inferred as { id: string; name: string }

tRPC offers overwhelming DX for full-stack teams that ship BE and FE from the same monorepo. Some full-stack teams at Toss in Korea and Mercari in Japan have adopted it. For integrations with other languages or external clients, GraphQL/OpenAPI tend to fit better.

13. GraphQL — Pothos · Yoga · Apollo Server 5

GraphQL is the query language Facebook open-sourced in 2015. It is one of the most mature areas of the TypeScript ecosystem. The 2026 core stack:

  • Pothos: A code-first schema builder, with decorators and a plugin system.
  • GraphQL Yoga 5: A light GraphQL server maintained by The Guild. Hono and Fastify adapters.
  • Apollo Server 5: The most-used GraphQL server. v5 (2025) added Express 5 / Fastify 5 adapters.
  • GraphQL Codegen: Generates TS types and React Query hooks from a schema.
// Pothos + GraphQL Yoga (on top of Hono)
import SchemaBuilder from "@pothos/core"
import { createYoga } from "graphql-yoga"
import { Hono } from "hono"

const builder = new SchemaBuilder({})

builder.queryType({
  fields: (t) => ({
    hello: t.string({
      args: { name: t.arg.string({ required: true }) },
      resolve: (_, { name }) => `Hello, ${name}`,
    }),
  }),
})

const yoga = createYoga({ schema: builder.toSchema() })
const app = new Hono()
app.all("/graphql", (c) => yoga.fetch(c.req.raw, c.env))

export default app

GraphQL remains strong for large-scale BFF patterns, mobile-facing backends, and GraphQL gateways that stitch micro-frontends.

14. Validation libraries — Zod 4 · ArkType · Valibot · Effect Schema · TypeBox

Runtime validation is a core piece of TypeScript backend infrastructure. A five-way comparison:

LibraryBundle sizeNotes
Zod 4MediumMost used, rich ecosystem, v4 halved its size
ArkTypeSmallValidation expressed in TypeScript-like syntax
ValibotVery smallTree-shaking friendly, function composition
Effect SchemaMediumIntegrates with Effect, two-way decoder
TypeBoxSmallEmits JSON Schema directly, default in Elysia

Zod 4 shipped formally in 2025 and reduced its bundle size by 50% versus v3, with up to 3x faster validation in some cases. ArkType takes the unusual approach of expressing TS-style syntax as strings, e.g. type({ email: "string.email", age: "number<150" }). Valibot uses tree-shaking to drop unused validation code at build time. Effect Schema plugs naturally into the Effect effect system. TypeBox is the top pick when you need JSON Schema right away (Fastify, Elysia).

// Comparing the same schema in Zod 4, Valibot, and ArkType
import { z } from "zod"
const ZodUser = z.object({ email: z.string().email(), age: z.number().min(0).max(150) })

import * as v from "valibot"
const ValibotUser = v.object({
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.minValue(0), v.maxValue(150)),
})

import { type } from "arktype"
const ArkUser = type({ email: "string.email", age: "0 <= number <= 150" })

// All three yield the same result: { email: string; age: number }

By npm downloads in May 2026, Zod is dominant at #1, Valibot is climbing fast at #2, and ArkType is the rising dark horse.

15. ORM — Drizzle 0.40 · Prisma 6 · Kysely · MikroORM 6

The JS/TS ORM space changed the most during 2024–2025.

  • Drizzle 0.40+: The fastest-growing ORM. SQL DSL expressed directly in TypeScript. Build-time migration generation. First-class edge runtime support.
  • Prisma 6: The most widely used ORM. From 2025 onward Prisma is deprecating its Rust engine and moving to TypeScript-only. Prisma Postgres (its own serverless database) launched.
  • Kysely: A type-safe SQL builder. Closer to a query builder than an ORM, minimalist. Even closer to SQL than Drizzle.
  • MikroORM 6: A data-mapper ORM that filled the gap TypeORM left. Unit of Work pattern.
  • TypeORM: Still used but new adoption is dropping. The decorator-friendly ORM that NestJS popularized.
// Drizzle 0.40 — schema and a query
import { drizzle } from "drizzle-orm/postgres-js"
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"
import { eq } from "drizzle-orm"
import postgres from "postgres"

export const users = pgTable("users", {
  id: serial("id").primaryKey(),
  email: text("email").notNull().unique(),
  name: text("name").notNull(),
  createdAt: timestamp("created_at").defaultNow(),
})

const sql = postgres(process.env.DATABASE_URL!)
const db = drizzle(sql)

// Inferred type: { id: number; email: string; name: string; createdAt: Date }[]
const rows = await db.select().from(users).where(eq(users.email, "a@toss.im"))

Since late 2024, Drizzle has been the officially recommended ORM at Vercel, Cloudflare, and Neon, and for new projects in Korea and Japan it now sits roughly even with or ahead of Prisma. Prisma is now focused on the "ORM + DB bundle" model via Prisma Postgres.

16. Workflow engines — Temporal · Inngest · Trigger.dev · Hatchet · Restate

Long-running workflows (approvals, payments, batch jobs, multi-step AI pipelines) were among the hardest pain points for TS backends. In 2026, these five tools are the standard candidates.

ToolTraits
Temporal TS SDKMost mature. Workers, task queues, signals, durable retries
InngestFunctional event-based. Serverless-friendly
Trigger.devTS-only. Code becomes a job immediately
HatchetPostgres-backed open-source queue + workflow
RestateDurable execution + RPC + state
// Inngest workflow (multi-step + retry + wait)
import { Inngest } from "inngest"

const inngest = new Inngest({ id: "toss-app" })

export const onboardUser = inngest.createFunction(
  { id: "onboard-user", retries: 3 },
  { event: "user/signup" },
  async ({ event, step }) => {
    await step.run("send-welcome-email", async () => {
      // This step retries automatically on failure; the result is durable
      return sendEmail(event.data.email)
    })

    await step.sleep("wait-1-day", "24h")

    await step.run("send-day1-tips", async () => sendDay1Tips(event.data.email))
  }
)

The key axis is "where do step results get persisted." Temporal uses its own server, Inngest is cloud SaaS plus open source, Trigger.dev can be self-hosted, Hatchet uses Postgres, Restate uses its own distributed KV.

17. Testing — Vitest 3 · Bun test · node:test · Playwright · Jest

Testing is the area integrating most rapidly with build tooling.

  • Vitest 3: A Vite-based TS-friendly test runner. Jest-compatible API. The de facto standard for 2025–2026.
  • Bun test: Built into the Bun runtime. Vitest-compatible API. The first choice on Bun teams.
  • node:test: Built into Node 22+. Lightweight tests with no external dependencies.
  • Playwright: The standard for browser E2E. Maintained by Microsoft.
  • Jest 30: Still used, but new project adoption is dropping.
// Vitest 3 unit + Playwright component testing
import { describe, it, expect } from "vitest"
import { sum } from "./math"

describe("sum", () => {
  it("adds two numbers", () => {
    expect(sum(1, 2)).toBe(3)
  })

  it("handles negatives", () => {
    expect(sum(-1, -2)).toBe(-3)
  })
})

// Playwright (e2e/login.spec.ts)
// import { test, expect } from "@playwright/test"
// test("login", async ({ page }) => {
//   await page.goto("/login")
//   await page.getByLabel("email").fill("a@b.com")
//   await page.click("text=Sign in")
//   await expect(page.locator("h1")).toHaveText("Welcome")
// })

Vitest's biggest strength is sharing the same transform pipeline as Vite. Unit tests against Next.js, Remix, SvelteKit, and Astro feel seamless.

18. Build tools — tsup · tsc · esbuild · swc · oxc · Bun bundle

Library and application build tools are also layered.

ToolRoleNotes
tscType check + compileStandard, slowest
tsupLibrary bundlerOn top of esbuild, minimal config
esbuildTranspilerGo-based, very fast
swcTranspilerRust-based, built into Next.js
oxcTS/linter/formatterRust-based, next-gen
Bun bundleBundlerZig-based, integrated with the runtime

As of May 2026, the de facto backend build workflow is "tsc for type checks, tsup/esbuild for output." oxc is rising quickly together with oxlint (a fast ESLint replacement); some teams that swapped ESLint for oxlint cut their lint time to about 1/30.

# Library: tsup
pnpm add -D tsup
pnpm tsup src/index.ts --format esm,cjs --dts

# App: esbuild directly
pnpm add -D esbuild
node --experimental-strip-types build.ts

# oxc-based lint
pnpm add -D oxlint
pnpm oxlint .

19. Monorepos — Turborepo · Nx · Bun workspaces · pnpm workspaces

A quick comparison of monorepo tools for managing many TS packages in one repo.

  • Turborepo 2.x: Maintained by Vercel. Strong at task caching and parallel execution.
  • Nx 20: Rich plugins and code generators. Widely used in enterprises.
  • pnpm workspaces 10: The package manager's own workspaces.
  • Bun workspaces: Fast install, simple.

Most teams pick a combo: "package manager pnpm/Bun, task runner Turborepo/Nx."

20. AI/ML SDKs — Vercel AI SDK · transformers.js · vec-tor

A standard has emerged for plugging LLMs and embeddings into TS backends.

  • Vercel AI SDK 4: An abstraction layer over OpenAI, Anthropic, Google, Mistral, Bedrock, and more. First-class streaming, tool use, and RAG.
  • transformers.js: The Hugging Face model runner for browsers and Node from Xenova. Backed by ONNX Runtime.
  • vec-tor / DIY vector libraries: Combined with Drizzle/Prisma + pgvector to store embeddings.
// Streaming LLM responses with the Vercel AI SDK + tool calls
import { generateText, tool } from "ai"
import { anthropic } from "@ai-sdk/anthropic"
import { z } from "zod"

const result = await generateText({
  model: anthropic("claude-3-5-sonnet"),
  messages: [{ role: "user", content: "What is the weather in Seoul?" }],
  tools: {
    getWeather: tool({
      description: "Get weather for a city",
      parameters: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city, tempC: 22 }),
    }),
  },
  maxSteps: 5,
})

console.log(result.text)

With LLM integration standardized, backend TS developers no longer have to jump into Python to enjoy LangChain-level abstractions.

21. Adoption in Korea — Toss · NAVER · Coupang · Kakao

Toss: 100% TypeScript on the frontend across the company. The backend is gradually migrating some Spring Boot services to NestJS / Hono. Since 2024 the Toss Tech blog has published many case studies on operating NestJS, monorepo structure, and ts-pattern. Part of Toss Securities' BFF runs on Bun.

NAVER: Some BFF and gateway layers across the Whale browser, LINE WORKS, and Naver Pay run on TypeScript. NAVER has built its own internal monorepo tooling. Some teams run edge APIs with Hono + Cloudflare.

Coupang: Frontend is fully TypeScript. Some new backend microservices are written in NestJS. Coupang Play's BFF is a representative case.

Kakao: Kakao Enterprise and some Kakao Pay teams have adopted NestJS. Kakao Style is full-stack TS.

The Korean market follows a pattern: "SI and banking stay on Java/Spring, while fintech, commerce, and content shift to TS step by step."

22. Adoption in Japan — Mercari · freee · LayerX · CyberAgent

Mercari: Many microservices started in Go, but new BFFs and media-conversion services are TypeScript microservices. Some services adopted Bun. Their internal GraphQL gateway is TS.

freee: Accounting SaaS. Frontend is TS, and parts of the Rails backend are migrating to TS. Uses NestJS and tRPC.

LayerX: Bakuraku (an invoice SaaS). A very high share of TypeScript backends, with public case studies on Effect and Drizzle adoption.

CyberAgent: Subsidiaries include AbemaTV and Tapple. Uses NestJS and Hono, with internal monorepo tooling.

The Japanese market keeps a multi-stack base (Go, Rails, etc.) and expands TS into BFF, real-time, and edge.

23. Security — dependency audits, lockfiles, permission models

TS backend supply-chain security got renewed attention after the 2024 xz utils incident.

  • npm audit / pnpm audit: Scan for known CVEs.
  • pnpm install lifecycle script blocking (10.x): Stops malicious postinstall scripts.
  • Sigstore / Provenance: Verify the build origin of npm packages.
  • Node 22's Permission Model: Filesystem isolation with flags like --allow-fs-read.
  • Deno's deny-by-default permissions.
  • Snyk, Socket, GitGuardian: External security scanners.

By 2026 attestation (signed build provenance) is the default for both npm and jsr, and most large companies run their own npm registry mirror (Verdaccio, Sonatype Nexus, Artifactory) to cache external dependencies.

24. Observability — OpenTelemetry · pino · Sentry

The standard observability stack for TS backends looks like this:

  • OpenTelemetry JS SDK: The standard for traces, metrics, and logs. Supports both Node and Bun.
  • pino: The fastest JSON logger, often paired with Fastify.
  • Sentry: Error tracking, performance monitoring, session replay.
  • Datadog · New Relic · Grafana Cloud: Commercial APM.
// OpenTelemetry + Pino
import { NodeSDK } from "@opentelemetry/sdk-node"
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"
import pino from "pino"

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({ url: "http://otel-collector:4318/v1/traces" }),
  instrumentations: [getNodeAutoInstrumentations()],
})
sdk.start()

export const log = pino({ level: process.env.LOG_LEVEL || "info" })

Hono and Elysia ship OpenTelemetry middleware packages, and NestJS has an official OpenTelemetry module.

25. Comparison tables — picking what's right for you

Finally, a big-picture comparison.

Web frameworks

FrameworkRuntimeDIValidationRecommended for
HonoAny runtimeMiddlewareZod/Valibot/TypeBoxEdge, multi-runtime
ElysiaBun onlyDecoratorsTypeBoxBun full-stack, max performance
NestJS 11Node/BunAngular-styleclass-validator/ZodEnterprise, large teams
Fastify 5NodePluginsJSON Schema/ZodHigh-performance standard Node
Express 5NodeNoneFree-formLegacy / compatibility

ORMs

ORMStyleMigrationsEdge support
Drizzle 0.40+SQL DSLBuild-time generationFirst-class
Prisma 6Code-firstBuilt-in Prisma MigratePartial (driver adapter)
KyselyQuery builderManual / externalFirst-class
MikroORM 6Unit of WorkBuilt-inPartial

Validation

LibraryBundle sizeDSLStrength
Zod 4MediumMethod chainEcosystem
ValibotVery smallFunction compositionTree-shaking
ArkTypeSmallTS-like stringReadability
Effect SchemaMediumEffect-integratedWhen using Effect
TypeBoxSmallJSON SchemaOpenAPI / Elysia

26. Migration roadmap — from legacy Node to the 2026 stack

To move an Express + TypeORM service to the 2026 stack, this order is sensible.

  1. Upgrade to TypeScript 5.6+ and enable strict: true, noUncheckedIndexedAccess: true.
  2. Switch the package manager to pnpm 10, regenerate lockfiles, unify versions via catalog.
  3. Move tests to Vitest 3 (Jest-compatible API).
  4. Standardize input validation on Zod 4. Enforce Zod schemas at handler inputs and outputs.
  5. Build new modules on Hono or Fastify 5. Gradually migrate Express routers.
  6. Switch the ORM to Drizzle or Prisma 6. The largest piece of work.
  7. Consolidate observability on OpenTelemetry, with Sentry for error tracking.
  8. Run oxlint + tsc + vitest in parallel in CI to cut build time.
  9. Move long-running work to Inngest or Temporal.
  10. Treat Effect adoption as a separate decision. It is not for every team — pick carefully.

The most important principle: don't change everything at once. Move one step at a time and verify with measurable indicators (build time, p99 latency, error rate).

27. Closing — choosing in 2026

The TypeScript backend of 2026 is no longer "Node + Express + Jest." You pick a runtime, a framework, an ORM, a validator, a workflow tool, and a build tool. Having more choices is good, but it also means you have to answer "which combination fits our team's operations."

A one-line summary of this article: for a new project, "Bun or Node 22 + Hono + Drizzle + Zod 4 + Vitest 3 + pnpm 10" is the lowest-friction default. Add NestJS 11 at enterprise scale, Effect if you want a functional effect system, Hono + Cloudflare if you're going to the edge, and tRPC 11 if you're shipping a single full-stack monorepo.

Freedom of choice carries responsibility. Whichever tools you pick, first ask whether your team will still enjoy operating them a year from now.

References