The fastest backend framework for Next.js applications. Auto-generate type-safe REST APIs from Drizzle schemas with zero code generation.
- Auto-CRUD — Define a Drizzle schema, get
list,get,create,update,deleteendpoints instantly - Type-safe client — Full TypeScript inference from server to client via
typeof app - React hooks —
useQueryanduseMutationwith SWR caching and optimistic updates - Auth built-in — Declarative permissions (
public,authenticated,owner,admin) + custom functions - Security defaults — CORS, Helmet, rate limiting, body size limits — all ON by default
- Edge-ready — Works on Node.js 18+, Vercel Edge, Cloudflare Workers, Bun, Deno
npm install narsil @narsil/drizzle @narsil/serverimport { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
role: text('role').default('user'),
createdAt: timestamp('created_at').defaultNow(),
})import { createApp, defineModule } from 'narsil'
import { createDb } from '@narsil/drizzle'
import { users } from './schema'
const db = await createDb({ url: process.env.DATABASE_URL! })
const app = createApp({
db,
auth: async (token) => {
const payload = await verifyToken(token)
return payload ? { id: payload.sub, role: payload.role } : null
},
})
.module('users', defineModule({
schema: users,
permissions: {
list: 'authenticated',
get: 'authenticated',
create: 'admin',
update: 'owner',
delete: 'admin',
},
}))
export type AppType = typeof app
export default appimport { createClient } from '@narsil/client-sdk'
import type { AppType } from './server'
const api = createClient<AppType>('http://localhost:3000/api', {
getToken: () => localStorage.getItem('token'),
})
// Fully typed!
const users = await api.users.list()
const user = await api.users.get('uuid-here')
const created = await api.users.create({ name: 'John', email: 'john@example.com' })import { useQuery, useMutation } from '@narsil/react'
function UserList() {
const { data: users, isLoading } = useQuery(
() => api.users.list(),
{ tags: ['users'] }
)
const { mutate: createUser } = useMutation(
(data) => api.users.create(data),
{
invalidateTags: ['users'],
onOptimistic: (cache, input) => {
return cache.optimisticUpdate('users:list', (current) => [
...(current ?? []),
{ ...input, id: 'temp' },
])
},
}
)
if (isLoading) return <p>Loading...</p>
return (
<ul>
{users?.map((user) => <li key={user.id}>{user.name}</li>)}
</ul>
)
}| Package | Description |
|---|---|
narsil |
Core app factory, module system, permissions, hooks |
@narsil/server |
Router, middleware pipeline, adapters (Node/Vercel/Web Standard) |
@narsil/drizzle |
Drizzle connection factory and auto-CRUD generator |
@narsil/client-sdk |
Type-safe proxy-based API client |
@narsil/react |
React hooks (useQuery, useMutation) with SWR cache |
@narsil/cache |
LRU cache for rate limiting and response caching |
@narsil/cli |
CLI for init, dev, and db commands |
Pass an auth function to createApp. It receives the Bearer token and should return a user object or null:
createApp({
db,
auth: async (token) => {
const user = await verifyJWT(token)
return user // { id, email, role, ... } or null
},
})Permissions are checked per-operation using presets or custom functions:
permissions: {
list: 'public', // Anyone
get: 'authenticated', // Valid token required
create: 'admin', // role === 'admin'
update: (ctx) => ctx.user?.id === ctx.params.id, // Custom logic
delete: ['admin'], // Array = any match
}MIT
O framework backend mais rapido para aplicacoes Next.js. Gere REST APIs type-safe a partir de schemas Drizzle, sem code generation.
- Auto-CRUD — Defina um schema Drizzle e receba endpoints
list,get,create,update,deleteautomaticamente - Client type-safe — Inferencia completa de tipos do servidor ao cliente via
typeof app - React hooks —
useQueryeuseMutationcom cache SWR e updates otimistas - Auth integrado — Permissoes declarativas (
public,authenticated,owner,admin) + funcoes customizadas - Seguranca por padrao — CORS, Helmet, rate limiting, limite de body — tudo ativo por padrao
- Edge-ready — Funciona em Node.js 18+, Vercel Edge, Cloudflare Workers, Bun, Deno
npm install narsil @narsil/drizzle @narsil/serverimport { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
role: text('role').default('user'),
createdAt: timestamp('created_at').defaultNow(),
})import { createApp, defineModule } from 'narsil'
import { createDb } from '@narsil/drizzle'
import { users } from './schema'
const db = await createDb({ url: process.env.DATABASE_URL! })
const app = createApp({
db,
auth: async (token) => {
const payload = await verifyToken(token)
return payload ? { id: payload.sub, role: payload.role } : null
},
})
.module('users', defineModule({
schema: users,
permissions: {
list: 'authenticated',
get: 'authenticated',
create: 'admin',
update: 'owner',
delete: 'admin',
},
}))
export type AppType = typeof app
export default appimport { createClient } from '@narsil/client-sdk'
import type { AppType } from './server'
const api = createClient<AppType>('http://localhost:3000/api', {
getToken: () => localStorage.getItem('token'),
})
// Totalmente tipado!
const users = await api.users.list()
const user = await api.users.get('uuid-aqui')
const created = await api.users.create({ name: 'John', email: 'john@example.com' })import { useQuery, useMutation } from '@narsil/react'
function UserList() {
const { data: users, isLoading } = useQuery(
() => api.users.list(),
{ tags: ['users'] }
)
const { mutate: createUser } = useMutation(
(data) => api.users.create(data),
{
invalidateTags: ['users'],
onOptimistic: (cache, input) => {
return cache.optimisticUpdate('users:list', (current) => [
...(current ?? []),
{ ...input, id: 'temp' },
])
},
}
)
if (isLoading) return <p>Carregando...</p>
return (
<ul>
{users?.map((user) => <li key={user.id}>{user.name}</li>)}
</ul>
)
}| Pacote | Descricao |
|---|---|
narsil |
App factory, sistema de modulos, permissoes, hooks |
@narsil/server |
Router, middleware pipeline, adapters (Node/Vercel/Web Standard) |
@narsil/drizzle |
Connection factory Drizzle e gerador de CRUD automatico |
@narsil/client-sdk |
Client API type-safe baseado em Proxy |
@narsil/react |
Hooks React (useQuery, useMutation) com cache SWR |
@narsil/cache |
Cache LRU para rate limiting e cache de respostas |
@narsil/cli |
CLI para comandos init, dev e db |
Passe uma funcao auth para createApp. Ela recebe o Bearer token e deve retornar um objeto de usuario ou null:
createApp({
db,
auth: async (token) => {
const user = await verifyJWT(token)
return user // { id, email, role, ... } ou null
},
})Permissoes sao verificadas por operacao usando presets ou funcoes customizadas:
permissions: {
list: 'public', // Qualquer um
get: 'authenticated', // Token valido necessario
create: 'admin', // role === 'admin'
update: (ctx) => ctx.user?.id === ctx.params.id, // Logica customizada
delete: ['admin'], // Array = qualquer match
}MIT