Open source, MIT licensed
The AI-native full-stack TypeScript framework.
Change a route, and the build fails until the client, the docs, the agent tools, and the security policy agree. A change made by you or by a coding agent cannot half-land.
Runs on Bun, Node, Deno, and the edge.
src/server.ts
A coding agent gets a plain request, and edits two routes.
The page that still reads the old field stops compiling, and the write nobody declared is flagged.
src/server.ts
Client fixed, write declared. Now policy objects: a domain write with no authentication.
src/server.ts
One middleware supplies the evidence. Every gate agrees, and the change ships.
- 24×dynamic SSR vs Next.js (React on Node, same machine)
- 120krequests per second on Bun - 98% of the raw-runtime ceiling
- 5 + 4UI frameworks + runtime families, from one app
- 55typed packages, all optional
One framework in place of a stack you wire together.
- express / fastify / hono
@nifrajs/core - tRPC / OpenAPI client codegen
@nifrajs/client - BullMQ (single-node jobs)
@nifrajs/jobs - multer
@nifrajs/uploads - dotenv + envalid
@nifrajs/env - supertest
@nifrajs/testing
01 · Agent-native
The same gates hold when an agent writes the code.
nifra mcp gives Claude Code, Cursor, and other MCP clients the live route table, a scaffolder that puts files in the right place, and the same check and assure gates your CI runs.
nifra_contextRead the live appRoutes, schemas, middleware, and conventions - the real API surface, not stale docs.
nifra_scaffoldWrite in the right placeURL patterns resolve to framework-correct files, handlers, loaders, and typed clients.
nifra_runVerify the behaviorHTTP, SSR, and WebSocket checks run against the current workspace.
nifra_checkBlock driftTypecheck and route-contract checks, with the fix suggested, before CI goes green.
nifra_assureProve the security posturePolicy classifies every route - an unauthenticated write fails the build, named.
02 · End-to-end types
The client is inferred from the server.
Import the app's type and the path, params, body, and response are typed. There is no codegen step to forget and no schema file to drift.
import { server } from "@nifrajs/core/server"
import { t } from "@nifrajs/schema"
const body = t.object({ name: t.string() })
// A typed API. No frontend required.
export const app = server()
.get("/users/:id", (c) => ({ id: c.params.id }))
.post("/users", { body }, (c) => {
// c.body is validated and typed before this runs.
return { id: crypto.randomUUID(), name: c.body.name }
})
// Bun. Node, Deno, and the edge are one line each.
export default { fetch: app.fetch }import { client } from "@nifrajs/client"
// A type import: server code never ships to the client.
import type { app } from "./server"
const api = client<typeof app>("https://api.example.com")
// The path autocompletes. Params and body are typed.
const res = await api.users({ id: "42" }).get()
if (res.ok) {
res.data.id // typed from the route's return
} else {
res.error // failures are returned, never thrown
}03 · Assurance
Security posture is a policy the build enforces.
Declare what a route may touch, write the rule once, and nifra assure names every route that breaks it. An unauthenticated write is a failed build, not a review comment.
nifra checkTypecheck plus contract rules: hand-rolled fetch to your own API, interpolated SQL, backend code imported by a page.nifra assureClassifies every route against policy and lists the evidence each one is missing.nifra levelsReports which assurance levels the app reaches: L0 typed contract, L1 route assurance, L2 capability lockfile.
// nifra.assurance.ts - security posture as policy, not convention
import {
defineAssuranceConfig,
NIFRA_ASSURANCE,
} from "@nifrajs/core/assurance"
import { backend } from "./backend/app"
export default defineAssuranceConfig({
source: backend,
capabilities: {
definitions: [{ id: "db.write", zone: "domain", access: "write" }],
provenance: {
// Classification needs a capability definition and static
// provenance coverage for the effect it protects.
imports: [{ specifier: "./db/write.ts", capabilities: ["db.write"] }],
forbiddenImports: [],
},
},
policy: {
rules: [
{
// Every domain write must prove authentication.
name: "authenticated-write",
match: { access: "write", zone: "domain" },
require: [NIFRA_ASSURANCE.AUTHENTICATED],
},
],
},
})
// $ nifra assure
// ✖ POST /notes (authenticated-write) is missing nifra.authenticated04 · Try it
Run a real Nifra app without installing anything.
This is the real @nifrajs/core, running in this tab. Nothing is sent to a server. Pick an example or edit the code, then press Run.
05 · Performance
Measured against the frameworks you would otherwise pick.
Same machine, same workload, every framework in its production mode. The method and the full tables are on the benchmarks page.
- Nifra vs Next.js
24× the dynamic SSR throughput on the same machine, and five UI frameworks instead of one. Building around React Server Components? Pick Next.js.
Read the comparison - Nifra vs Fastify
On Node, 65,905 vs 57,051 requests per second on the same route. Nifra adds the typed client, the UI layer, and three more runtimes.
Read the comparison - Nifra vs Elysia
On Bun, 120,294 vs 121,677 requests per second on the same route. Elysia is a backend; Nifra adds the UI layer and the gates.
Read the comparison - Nifra vs Hono
Hono is the smaller server bundle: 7.7 KB vs 34.2 KB gzipped. Pick it for a minimal router, and Nifra for the full stack.
Read the comparison
06 · Portable
One app. Five UI frameworks, four runtime families.
The adapter is one line. Loaders, actions, streaming, and islands stay the same across React, Solid, Vue, Preact, and Svelte, and the same app runs on Bun, Node, Deno, and the edge.
- BunNative dev speed
export default { fetch: app.fetch } - NodeMature, everywhere
serve(app, { port: 3000 }) - DenoSecure by default
serve(app, { port: 3000 }) - CloudflareWorkers / Pages
export default toFetchHandler(app) - VercelEdge functions
export default toFetchHandler(app)
// The adapter is the one line that changes per framework:
import { reactAdapter } from "@nifrajs/web-react"
export default createWebApp({ adapter: reactAdapter, manifest, clientEntry })
// routes/hello.tsx - your page, written in React
export function Page({ data }: { data: { name: string } }) {
return <h1>Hello {data.name}</h1>
}// The adapter is the one line that changes per framework:
import { solidAdapter } from "@nifrajs/web-solid"
export default createWebApp({ adapter: solidAdapter, manifest, clientEntry })
// routes/hello.tsx - same page, Solid's fine-grained JSX
export function Page(props: { data: { name: string } }) {
return <h1>Hello {props.data.name}</h1>
}// The adapter is the one line that changes per framework:
import { vueAdapter } from "@nifrajs/web-vue"
export default createWebApp({ adapter: vueAdapter, manifest, clientEntry })
<!-- routes/hello.vue - same page, as a Vue SFC -->
<script setup lang="ts">defineProps<{ data: { name: string } }>()</script>
<template><h1>Hello {{ data.name }}</h1></template>// The adapter is the one line that changes per framework:
import { preactAdapter } from "@nifrajs/web-preact"
export default createWebApp({ adapter: preactAdapter, manifest, clientEntry })
// routes/hello.tsx - same page; Preact's 3 KB runtime, identical React API
export function Page({ data }: { data: { name: string } }) {
return <h1>Hello {data.name}</h1>
}// The adapter is the one line that changes per framework:
import { svelteAdapter } from "@nifrajs/web-svelte"
export default createWebApp({ adapter: svelteAdapter, manifest, clientEntry })
<!-- routes/hello.svelte - same page, as a Svelte component -->
<script lang="ts">export let data: { name: string }</script>
<h1>Hello {data.name}</h1>07 · Ecosystem
Fewer dependencies to keep in agreement.
The common parts of a TypeScript stack ship as optional @nifrajs packages that share one set of types.
@nifrajs/core@nifrajs/middleware@nifrajs/schema/openapi@nifrajs/client@nifrajs/web@nifrajs/jobs@nifrajs/cron@nifrajs/uploads@nifrajs/storage@nifrajs/image@nifrajs/env@nifrajs/cache@nifrajs/i18n@nifrajs/content@nifrajs/testing@nifrajs/mockStart with one route.
Scaffold an app, change a route, and watch the build tell you what else has to change.