Silgi

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

server/rpc/instance.ts
import { silgi } from 'silgi'
import { todos } from './todos/schema'

export const s = silgi({
  context: () => ({ todos }),
})

2. Procedures with tracing

server/rpc/todos/index.ts
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

server/rpc/router.ts
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 appRouter

4. Server entry

server.ts
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

nitro.config.ts
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:

server.ts
import {  } from 'silgi'

const  = ()
const  = .({
  : .(() => ({ : 'ok' })),
})

const  = .()

export default {
  : async (: Request) => {
    const  = await ()
    if (. === 404) return
    return 
  },
}

What's next?

  • Nuxt — Silgi with Nuxt 4 (Vue pages, typed client composable)
  • Analytics — built-in monitoring dashboard
  • Server — standalone serve() with full protocol support

On this page