Take your AI-built app from prototype to production
📖 superfunction.xyz — docs, package guides, and a full-stack tutorial.
spfn create --mode full wires the baseline into one application — core (server,
database, routes, codegen, typed client), auth (sessions, social login,
authorization), i18n (translation catalogs), and ops (your own operations, run
from the terminal with spfn ops, instead of an admin dashboard). Everything else is
installed only when you reach for it.
SPFN (Superfunction) is a TypeScript full-stack framework for products built with AI coding agents. A Next.js app handles the frontend; an SPFN backend — built on Hono, PostgreSQL and Drizzle ORM — handles the server side, either as its own long-lived process or as serverless functions on the same origin. Requests are validated with TypeBox and reach the browser through a typed client, so a route's input and output types are the same object on both sides.
What makes it different from a stack you assemble yourself is that the shape is fixed.
Every feature is one vertical slice with the same four parts in the same four places:
an entity, a repository, a route, and a router registration. Nothing is left for a
human or an agent to invent per feature. On top of that, the parts every product needs
— authentication, storage, notifications, CMS, i18n, background jobs, and an MCP server
for operating the app — ship as @spfn/* packages rather than being rebuilt each time.
The point is that the first version and the production version are the same application. You scaffold something deployable and authenticated on day one, then keep changing it — rather than building a prototype and rebuilding it once it stops holding.
Status — Beta (
0.x). SPFN is in active development and runs in production for its authors. The public API is stabilizing, but may still change between minor releases before1.0— pin your versions and skim the CHANGELOG before upgrading. Install from the@betatag, e.g.npm i @spfn/core@beta.
Because nothing tells it where things go. Ask for "add orders" twice and you get two different shapes: business logic in a route handler one time, in a service the next; queries inline here, in a helper there. Each answer is reasonable on its own. Together they become a codebase nobody — human or agent — can hold in their head.
The name for this is architecture drift: the gap that opens between the structure a codebase was supposed to have and the structure it actually has. Agents accelerate it for three ordinary reasons — they answer your prompt rather than reading your whole codebase, they copy whatever shape they happen to read, and they produce faster than anyone reviews. The long version is here, including the usual answers to it and why they are all detection after the fact.
This is the wall most AI-built apps hit. The prototype works. The tenth feature is where it stops being changeable.
SPFN answers it by removing the choice. One feature is one vertical slice:
src/server/
entities/order.ts # the data shape
repositories/order.ts # persistence
routes/orders.ts # the validated API contract
router.ts # registration
The agent is not asked to design an architecture. It is asked to fill in a known one. Type safety is one guardrail here, alongside runtime schemas, generated migrations, explicit module boundaries, and a build that fails when the contract breaks.
Next.js alone is enough if nobody signs in and nobody has to operate it — a marketing site, a public tool, a dashboard over data that is already public.
Everything else meets two gates, in this order.
Gate 1 — accounts. Nothing ships until people can sign in. Sessions and their expiry. A callback flow per social provider, and the redirect URLs that break on deploy. Email or phone verification. Roles and permissions on every route. Account deletion and recovery. Device and key management.
Gate 2 — operations. The day after you deploy, someone has to refund an order, look up a user, publish a change, retry a failed job. That is a second application: an admin dashboard with its own auth, its own screens, and its own maintenance — and it keeps growing for as long as the product does.
Neither gate is your product. Both have a known correct shape. Getting the first one subtly wrong is a security incident rather than a bug.
So the question is where your tokens go. Building with an agent puts an explicit price
on both gates — every prompt, every regeneration, every fix that follows. SPFN's answer is
that they should cost you close to nothing:
@spfn/auth clears the first, and the ops surface clears the second by
running your operations from the terminal you already have open, instead of a dashboard you
have to build. What is left is the part only you can write.
Both give you a structured TypeScript backend. They differ in how much they decide for you and in what they assume about the frontend.
| SPFN | NestJS | |
|---|---|---|
| Frontend | Assumes Next.js; ships a typed client and RPC proxy | Frontend-agnostic |
| Structure | One fixed vertical slice per feature | Modules and providers you design |
| Validation | TypeBox, built in | Your choice, commonly class-validator |
| Database | Drizzle ORM, fixed | Your choice — TypeORM, Prisma, Drizzle |
| Client types | Route types flow to the browser directly | You define DTOs and keep them in sync |
| Auth, storage, CMS, MCP | Installable @spfn/* packages |
Assembled from the ecosystem |
| Ecosystem size | Small and young | Large and mature |
NestJS is the better fit when you need freedom in how the backend is organized, or when the frontend is not Next.js. SPFN is the better fit when you want one decided shape that a coding agent can follow without being told again each time.
tRPC solves a narrower problem — typed calls between a client and server — and leaves structure, persistence, and auth to you.
# Create a new project — full mode wires core, auth, i18n and ops together
npx spfn@beta create my-app --mode full
cd my-app
# Start PostgreSQL and Redis
docker compose up -d
# Start both sides
npm run spfn:devBackend: http://localhost:8790 · Frontend: http://localhost:3790
Use --mode bare for the core architecture only. For an existing Next.js app, use
npx spfn@beta init --mode full.
// src/server/routes/users.ts
import { route } from '@spfn/core/route';
import { Type } from '@sinclair/typebox';
export const listUsers = route.get('/users')
.input({
query: Type.Object({
limit: Type.Optional(Type.Number()),
}),
})
.handler(async (c) => {
const { query } = await c.data();
return { users: [], limit: query.limit ?? 10 };
});
export const getUser = route.get('/users/:id')
.input({
params: Type.Object({
id: Type.String(),
}),
})
.handler(async (c) => {
const { params } = await c.data();
return { id: params.id, name: 'John' };
});
export const createUser = route.post('/users')
.input({
body: Type.Object({
name: Type.String(),
email: Type.String(),
}),
})
.handler(async (c) => {
const { body } = await c.data();
return { id: '1', ...body };
});Register them in the router — this is the API contract:
// src/server/router.ts
import { defineRouter } from '@spfn/core/route';
import { listUsers, getUser, createUser } from './routes/users';
export const appRouter = defineRouter({
listUsers,
getUser,
createUser,
});
export type AppRouter = typeof appRouter;Through the AppRouter type, not through generated client code. The client is created
once and infers everything from that type:
// src/lib/api-client.ts
import { createApi } from '@spfn/core/nextjs';
import type { AppRouter } from '@/server/router';
export const api = createApi<AppRouter>();Calls are fully typed, with no manual types anywhere:
// app/page.tsx
import { api } from '@/lib/api-client';
export default async function Page() {
const { users } = await api.listUsers.call({ query: { limit: 10 } });
const user = await api.getUser.call({ params: { id: '123' } });
return <div>{users.length} users</div>;
}One piece does need codegen: the RPC proxy that forwards browser calls to the SPFN
server resolves routes from a generated map. Run pnpm codegen after changing routes
and commit the output.
// src/app/api/rpc/[routeName]/route.ts
import { createRpcProxy } from '@spfn/core/nextjs/server';
import { routeMap } from '@/generated/route-map'; // generated by `pnpm codegen`
export const { GET, POST } = createRpcProxy({ routeMap });src/
├── server/
│ ├── router.ts # defineRouter — the API contract
│ ├── server.config.ts # server configuration
│ ├── routes/ # route.get/post/... definitions
│ ├── entities/ # Drizzle database schemas
│ └── repositories/ # data access layer
├── generated/
│ └── route-map.ts # produced by `pnpm codegen` — never hand-edit
├── lib/
│ └── api-client.ts # createApi<AppRouter>() — typed client
└── app/
├── api/rpc/[routeName]/route.ts # RPC proxy → SPFN server
└── page.tsx # Next.js pages
Generated files are output, not editing targets.
Point it at CONTRIBUTING.md. That file is the contract an AI coding agent is expected to follow here: what the repo is, the commands, the vertical-slice pattern, and the hard rules (never hand-edit generated files, migrations come from the schema).
One file answers to people and agents alike, so there is no second copy of the rules to
drift out of sync. Each package's own README.md carries the authoritative detail for
that package — an agent changing @spfn/auth reads packages/auth/README.md as well.
Pointing an agent at a file like this is worth more than a long prompt describing a new architecture for every feature.
npm i @spfn/auth@beta// src/server/router.ts
import { defineRouter } from '@spfn/core/route';
import { authRouter, authenticate } from '@spfn/auth/server';
export const appRouter = defineRouter({
// your routes...
})
.packages([authRouter])
.use([authenticate]);That brings sessions, social login, and RBAC. See
packages/auth/README.md for the four wiring points and the
environment variables each provider needs.
Yes. Write the operations an operator is allowed to perform as ops routes, and run them
from the terminal — spfn ops reads them from the running app, so nothing is generated
or configured locally:
spfn ops list --app https://api.example.com # what can this app do?
spfn ops call refundOrder --param id=42
spfn ops call listCustomers --describe # what does it take?Each command uses the same repositories and services as the product, with its own input schema and authorization rule, and answers only to a scoped, revocable ops token. Do not expose tables as generic CRUD — expose operations.
spfn create --mode full scaffolds this surface. To drive the same operations from an
agent instead of a terminal, add @spfn/mcp with
spfn add @spfn/mcp and expose them as MCP tools too.
In the default setup:
| Package | Stage | Description |
|---|---|---|
| @spfn/core | Beta | Routing, database, transactions, typed client |
| @spfn/auth | Beta | Sessions, social login, RBAC |
| @spfn/i18n | Beta | Server and React internationalization runtime |
| @spfn/mcp | Beta | MCP route adapter built on the official SDK |
| spfn | Beta | CLI and dev tools |
Install when you need them:
| Package | Stage | Description |
|---|---|---|
| @spfn/storage | Beta | Object storage (S3 / GCS / local) |
| @spfn/notification | Beta | Email, SMS, Slack, push |
| @spfn/cms | Beta | Content management |
| @spfn/monitor | Beta | Error tracking and monitoring dashboard |
| @spfn/migrate | Beta | Code-based data migrations with a run-once ledger |
| @spfn/pages | Beta | Serve a markdown site from a GitHub repo |
| @spfn/pages-next | Beta | Next.js integration for @spfn/pages |
| @spfn/signing | Alpha | Token signing with local, GCP KMS and AWS KMS key providers |
| @spfn/workflow | Alpha | Pipeline orchestration |
Every package has a deep-dive README in packages/. Exact versions live on
npm — install from the @beta tag.
Two targets. Both ship from the same repository, versioned together.
Vercel — serverless, one origin, no container
spfn add vercelThat scaffolds src/app/api/backend/[[...route]]/route.ts (a hono/vercel adapter) and
vercel.json. The SPFN app mounts under /api/backend, so the Next.js frontend and the
backend live on a single Vercel origin — point SPFN_API_URL at
https://<deployment>/api/backend. It runs on the Node runtime, not edge, because SPFN
needs pg and native bcrypt. If you use the Vercel Supabase integration, the adapter
maps the injected POSTGRES_URL onto DATABASE_URL for you.
One caveat: the in-process job worker does not run on serverless. Enqueuing still works, but nothing drains the queue — schedule a route that processes a batch (Vercel Cron), or run jobs on an always-on target. Seed and RBAC provisioning move to a deploy-time step instead of running per cold start.
Always-on — a long-lived process
spfn build && spfn start # or the generated Docker files
docker compose -f docker-compose.production.yml up --build -dBackground jobs, WebSocket events, and the periodic database health-check all need this path. See the deployment guide.
- Node.js >= 20.0.0 —
@spfn/coreruns on@hono/node-server2, which requires it - Next.js >= 16.2.11 — only if you use the Next.js integration
- PostgreSQL 14+
- Redis — only when the features you enable need it
Next.js 15 is not supported. Its fixes for CVE-2025-66478
and the Server Components denial of service landed separately on each minor line
(15.0.5, 15.1.9, 15.2.6, …), so no single range can express "patched" — the packages
require ^16.2.11 instead.
PostgreSQL 13 is the oldest release the code actually runs on: gen_random_uuid() is a
column default and moved into the server in 13. The floor is 14 because 13 stopped
receiving fixes in November 2025.
I built a Next.js app with an AI agent. Can I add SPFN to it?
Yes. Run npx spfn@beta init --mode full in the existing project. It adds the server
directory, the RPC proxy route, Docker files, and the codegen config, and updates your
package.json.
My agent keeps inventing new folder structures. Does this actually fix that?
It removes the decision. There is one place for entities, repositories, routes and the
router, and CONTRIBUTING.md states it. An agent that reads the file has nothing left to
invent. It does not stop an agent from writing bad logic inside a slice — that is the
line between the drift this removes and the drift it does not, set out in
architecture drift.
Can I use SPFN without Next.js?
Yes. next is an optional peer dependency of @spfn/core, and the Next.js integration
is one module (@spfn/core/nextjs). The server runs on Hono on its own. You give up the
typed client and RPC proxy, which are the Next.js-side pieces.
Can I use Prisma instead of Drizzle?
No. drizzle-orm is a required peer dependency, and the repository layer is built on it.
Can I deploy to Vercel?
Yes — run spfn add vercel. See How do I deploy this? for what
it scaffolds and the one caveat about background jobs.
Do I have to use Docker?
No. Vercel is a first-class target and needs no Docker. Docker is the always-on path, and
docker compose up -d is the convenient way to get PostgreSQL and Redis locally —
pointing at your own PostgreSQL works too. PostgreSQL itself is not optional.
Do I need to run codegen after every change?
Only after changing routes, and after schema changes run pnpm db:generate. The typed
client needs no codegen — it infers from AppRouter.
I have never built a backend. Is SPFN too much?
It is built for the opposite case. spfn create --mode full hands you a working,
authenticated, deployable app before you write a line — you are not assembling sessions,
OAuth, migrations and a data layer yourself, and you are not choosing an architecture.
The shape is fixed and written down in CONTRIBUTING.md, so your coding agent fills it
in instead of asking you to design it.
What SPFN removes is the architecture and the solved problems. What it does not remove is the infrastructure: you still need a PostgreSQL database, and a setup step with real environment variables in it. Knowing TypeScript helps, but the agent writes most of it.
What database does SPFN require? PostgreSQL 14 or later. Redis is optional and only needed by some features.
- superfunction.xyz — the documentation site
- Architecture drift — the problem this framework is shaped around, and where fixing the shape stops helping
- Prototype to Production — the whole loop, scaffold to MCP operations
- Full-stack tutorial — from
spfn createto auth-guarded pages - Examples — a step-by-step ladder, start at
01-minimal-api - CONTRIBUTING.md — setup, the SPFN pattern, the hard rules, and the contract for AI coding agents
Contributions are welcome. See CONTRIBUTING.md for setup, the development workflow, the PR checklist, and the rules an AI coding agent is expected to follow. By participating you agree to our Code of Conduct.
Found a security issue? Please report it privately — see SECURITY.md.
MIT © FXY Inc.