Type-safe backend for Next.js
Next.js handles your frontend. SPFN handles your backend.
📖 superfunction.xyz — docs, package guides, and a full-stack tutorial.
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.
Building a mobile app? → Next.js (landing page) + SPFN (API) = Complete solution
Building a SaaS product? → Next.js (marketing + dashboard) + SPFN (backend) = Full-stack
Need these features?
- Complex business logic with transactions
- Connection pools (PostgreSQL, Redis)
- Background jobs & scheduled tasks
- End-to-end type safety
- Authentication & CMS out of the box
If you just need simple API routes, Next.js is enough. If you need a real backend, Next.js + SPFN.
# Create new project
npx spfn@beta create my-app
cd my-app
# Start databases
docker compose up -d
# Start dev server
npm run spfn:dev✅ Backend: http://localhost:8790 ✅ Frontend: http://localhost:3790
// 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 };
});// 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;The typed client needs no codegen. The RPC proxy that forwards browser calls to the
SPFN server does — run pnpm codegen to (re)generate the route map after changing routes.
// src/lib/api-client.ts — the typed client (metadata-free)
import { createApi } from '@spfn/core/nextjs';
import type { AppRouter } from '@/server/router';
export const api = createApi<AppRouter>();// src/app/api/rpc/[routeName]/route.ts — forwards api.*.call() to the SPFN server
import { createRpcProxy } from '@spfn/core/nextjs/server';
import { routeMap } from '@/generated/route-map'; // generated by `pnpm codegen`
export const { GET, POST } = createRpcProxy({ routeMap });// app/page.tsx — fully typed call, no manual types
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>;
}Type-safe Routes
- Chainable API:
route.get().input().handler() - TypeBox validation
- Metadata-free typed client; RPC proxy resolves routes from a generated map
Database
- Drizzle ORM with helpers
- Transaction support (AsyncLocalStorage)
- Repository pattern
Authentication (@spfn/auth)
- JWT sessions
- RBAC (roles & permissions)
- Email/SMS verification
CMS (@spfn/cms)
- Type-safe labels
- Version control
- Multi-locale support
Storage (@spfn/storage)
- Provider-agnostic object storage (S3 / GCS / local)
- Upload, download, and deletion APIs
Notifications (@spfn/notification)
- Multi-channel: Email, SMS, Slack, Push
Monitoring (@spfn/monitor)
- Error tracking, log management, dashboard
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`
├── lib/
│ └── api-client.ts # createApi<AppRouter>() — typed client
└── app/
├── api/rpc/[routeName]/route.ts # RPC proxy → SPFN server
└── page.tsx # Next.js pages
| Package | Version | Description |
|---|---|---|
| @spfn/core | Beta | Routing, DB, Transactions |
| spfn | Beta | CLI & Dev tools |
| @spfn/auth | Beta | Authentication & RBAC |
| @spfn/cms | Beta | Content Management |
| @spfn/storage | Beta | Object storage (S3 / GCS / local) |
| @spfn/notification | Beta | Email, SMS, Slack, Push notifications |
| @spfn/monitor | Beta | Error tracking & 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/i18n | Beta | Small server and React internationalization runtime |
| @spfn/mcp | Beta | Official-SDK-based MCP route adapter |
| @spfn/workflow | Alpha | Pipeline orchestration |
# Auth
npm i @spfn/auth@beta
# CMS
npm i @spfn/cms@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]);- Node.js >= 18.18.0
- Next.js >= 15.1.9 or >= 16.0.7 (patched for CVE-2025-66478)
- PostgreSQL 14+
- superfunction.xyz — the documentation site
- Full-stack tutorial — from
spfn createto auth-guarded pages - Examples — a step-by-step ladder, start at
01-minimal-api - Core API
- CLI Guide
- Auth Package
- CMS Package
- i18n Package
- MCP Package
Every package has a deep-dive README in packages/ — see the table above.
Contributions are welcome. See CONTRIBUTING.md for setup, the development workflow, and the PR checklist — and AGENTS.md if you work with an AI coding agent. By participating you agree to our Code of Conduct.
Found a security issue? Please report it privately — see SECURITY.md.
MIT © FXY Inc.