Nitro
Use Silgi with Nitro v3 — modular server entry with analytics, tracing, and typed procedures.
Nitro is the server engine behind Nuxt and TanStack Start. Silgi integrates with Nitro using the serverEntry config — Silgi becomes the server framework.
Project structure
nitro.config.ts
server.ts
instance.ts
router.ts
schema.ts
index.ts
Setup
1. Silgi instance
import { silgi } from 'silgi'
import { todos } from './todos/schema'
export const s = silgi({
context: () => ({ todos }),
})2. Procedures with tracing
import { } from 'silgi/analytics'
import { } from '../instance'
import { , , , , , } from './schema'
export const =
.$route({ : 'GET' })
.$output()
.$resolve(async ({ }) => {
return (, 'db.todos.findMany', () => [....todos])
})
export const =
.$input()
.$output()
.$resolve(async ({ , }) => {
const = { : (), : .title, : false, : new ().() }
await (, 'db.todos.insert', () => .todos.push())
return
})
export const =
.$input()
.$output()
.$errors({ : 404 })
.$resolve(async ({ , , }) => {
const = await (, 'db.todos.findById', () => .todos.find(() => .id === .id))
if (!) return ('NOT_FOUND')
.completed = !.completed
return
})
export const =
.$input()
.$output()
.$errors({ : 404 })
.$resolve(async ({ , , }) => {
const = .todos.findIndex(() => .id === .id)
if ( === -1) return ('NOT_FOUND')
.todos.splice(, 1)
return { : true }
})3. Router
import * as auth from './auth'
import { s } from './instance'
import * as todos from './todos'
export const appRouter = s.router({
auth: {
login: auth.doLogin,
logout: auth.doLogout,
me: auth.me,
},
todos: {
list: todos.list,
create: todos.create,
toggle: todos.toggle,
remove: todos.remove,
},
})
export type AppRouter = typeof appRouter4. Server entry
import { s } from './server/rpc/instance'
import { appRouter } from './server/rpc/router'
export default {
fetch: s.handler(appRouter, {
scalar: true,
analytics: {
auth: process.env.ANALYTICS_TOKEN,
},
}),
}5. Nitro config
import { defineNitroConfig } from 'nitro/config'
export default defineNitroConfig({
compatibilityDate: '2025-01-01',
})Nitro detects the fetch method on the default export and routes all requests through it. The analytics dashboard is
available at /analytics.
Coexisting with existing routes
When you add Silgi to an app that already has its own routes — GraphQL endpoints, REST API routes, file-system routes — the default fetch export takes over all requests. To let unmatched routes fall through to Nitro's normal routing:
import { } from 'silgi'
const = ()
const = .({
: .(() => ({ : 'ok' })),
})
const = .()
export default {
: async (: Request) => {
const = await ()
if (. === 404) return
return
},
}