Docs

Migrating a backend

Nifra's server() is a chainable, Web-standard router — the move from Express, Hono, Fastify, or Elysia is mostly mechanical renames. What you gain: an end-to-end-typed client with zero codegen, schema validation built into the route, the same app on Bun / Node / Deno / the edge, and an SSR frontend whenever you want one.

How the concepts map

Express / Hono / FastifyNifra
`app.get(path, handler)`server().get(path, handler) — chainable, fully type-inferred
`req` / `res` · Hono `Context`c — `c.req`, `c.params`, `c.query`; return a value (JSON) or a Response
`app.use(mw)` · Hono middleware.use() + the @nifrajs/middleware pack (CORS, auth, rate limit, …)
`express.json()` / body parsingbuilt in — declare a schema and the parsed body is typed
Fastify JSON schema · `zod` middleware · Elysia `t`{ body, query, params } validated by t (TypeBox) or any Standard Schema
`res.json(x)` / `reply.send(x)` / `c.json(x)`return x
`app.listen(3000)`app.listen() (Bun) · @nifrajs/node · @nifrajs/deno · toFetchHandler (edge)
OpenAPI plugins / swaggergenerated from the same `t` schemas — no second source of truth

Express → Nifra

The handler returns its result instead of calling res.json, and validation moves onto the route. The client then infers all of this for free.

TS
// Express
app.get("/users/:id", (req, res) => res.json({ id: req.params.id }))
app.post("/users", express.json(), (req, res) => res.status(201).json(create(req.body)))

// nifra — return the value (or a Response); c is Web-standard; the body is parsed + validated
server()
  .get("/users/:id", (c) => ({ id: c.params.id }))
  .post("/users", { body: t.object({ name: t.string() }) }, (c) => create(c.body))

Hono → Nifra

The closest of the routers — c.req.param("id") becomes a typed c.params.id, and you return the value rather than c.json(...). Nifra adds the typed client, schema validation, multi-runtime deploy, and (if you want it) SSR.

TS
// Hono
app.get("/users/:id", (c) => c.json({ id: c.req.param("id") }))

// nifra — nearly identical; params are a typed object, you return the value
server().get("/users/:id", (c) => ({ id: c.params.id }))

Fastify & Elysia

  • Fastify — its per-route JSON-schema validation maps directly to Nifra's { body, query, params }; plugins become Nifra plugins; reply.send becomes a return.
  • Elysia — the closest peer (Bun, typed, TypeBox t). The shapes are nearly 1:1: .get/.post + a t schema, and an Eden-style typed client. You gain Node / Deno / edge portability and an optional SSR frontend on five UI libraries.

Then connect a database (Database) and, if you're going full-stack, see Migrating a meta-framework.

Proudly built with Nifra — server-rendered on Cloudflare Pages.MIT