Quickstart
Get Vision Server up and running in under 5 minutes
Quickstart
Get Vision Server up and running in under 5 minutes.
Installation
bun add @getvision/server zodAlready using Hono or Fastify? Check out Adapters to add Vision to your existing app.
Basic Setup
Create your first Vision Server app:
import { createVision, createModule } from '@getvision/server'
import { z } from 'zod'
const usersModule = createModule({ prefix: '/users' })
.get(
'/:id',
({ params }) => ({
id: params.id,
name: 'John Doe',
email: '[email protected]',
}),
{
params: z.object({ id: z.string() }),
response: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}),
}
)
const app = createVision({
service: {
name: 'My API',
version: '1.0.0',
description: 'My awesome API',
},
}).use(usersModule)
app.listen(3000)
/** Eden Treaty type for typed RPC on the client. */
export type AppType = typeof appThat's it! Start your app and visit:
- API: http://localhost:3000
- Dashboard: http://localhost:9500 🎉
Composing Modules
Modules are the building blocks. Each one lives in its own file, owns a path prefix, and is composed at the root via .use():
import { createModule } from '@getvision/server'
import { z } from 'zod'
export const usersModule = createModule({ prefix: '/users' })
.get('/', () => ({ users: [] }))
.post(
'/',
async ({ body }) => ({ id: crypto.randomUUID(), ...body }),
{ body: z.object({ name: z.string(), email: z.string().email() }) }
)import { createVision } from '@getvision/server'
import { usersModule } from './modules/users'
import { ordersModule } from './modules/orders'
import { productsModule } from './modules/products'
const app = createVision({ service: { name: 'My API' } })
.use(usersModule)
.use(ordersModule)
.use(productsModule)
app.listen(3000)
export type AppType = typeof appWith Database Tracing
Every handler receives span() for tracing arbitrary blocks:
import { db } from './db'
import { users } from './db/schema'
import { eq } from 'drizzle-orm'
export const usersModule = createModule({ prefix: '/users' })
.get(
'/:id',
({ params, span }) => {
const user = span(
'db.select',
{ 'db.system': 'sqlite', 'db.table': 'users' },
() => db.select().from(users).where(eq(users.id, params.id)).get()
)
return user
},
{ params: z.object({ id: z.string() }) }
)Vision Dashboard will show:
- Request → Handler → DB Query waterfall
- Query timing and attributes
- Full trace correlation
With Pub/Sub Events
Use defineEvents() to register typed event handlers, then emit() from any route in the same app:
import { createModule, defineEvents } from '@getvision/server'
import { z } from 'zod'
export const usersModule = createModule({ prefix: '/users' })
.use(
defineEvents({
'user/created': {
schema: z.object({
userId: z.string(),
email: z.string().email(),
}),
handler: async (event) => {
console.log('Send welcome email to:', event.email)
},
},
})
)
.post(
'/',
async ({ body, emit }) => {
const user = { id: crypto.randomUUID(), ...body }
await emit('user/created', { userId: user.id, email: user.email })
return user
},
{ body: z.object({ name: z.string(), email: z.string().email() }) }
)Pub/sub is powered by BullMQ — in-memory during development (pubsub: { devMode: true }), Redis-backed in production.
Eden Treaty Client
On the frontend, import the exported AppType and get a fully-typed RPC client — no codegen:
import { treaty } from '@elysia/eden'
import type { AppType } from '../server'
const api = treaty<AppType>('http://localhost:3000')
const { data } = await api.users({ id: '1' }).get()
// ^? { id: string; name: string; email: string }With Drizzle Studio
Auto-start Drizzle Studio alongside your app:
const app = createVision({
service: {
name: 'My API',
integrations: {
database: 'sqlite://./dev.db',
},
drizzle: {
autoStart: true, // Auto-start Drizzle Studio
port: 4983,
},
},
})Vision will:
- ✅ Detect
drizzle.config.ts - ✅ Start Drizzle Studio on port 4983
- ✅ Show a link in Dashboard → Integrations
Configuration
Full configuration options:
const app = createVision({
service: {
name: 'My API',
version: '1.0.0',
description: 'Optional description',
integrations: {
database: 'postgresql://localhost/mydb',
redis: 'redis://localhost:6379',
},
drizzle: {
autoStart: true,
port: 4983,
},
},
vision: {
enabled: true, // enable/disable dashboard
port: 9500, // dashboard port
maxTraces: 1000,
maxLogs: 10000,
},
pubsub: {
devMode: true, // in-memory queue, no Redis required
// redis: { host: 'localhost', port: 6379 }
},
})Next Steps
- Vision Server Overview — the big picture
- Modules — build and compose modules
- Rate Limiting — per-endpoint & module-level limits
- Core Concepts — traces, spans, wide events