Documentation
Complete Goddo API reference, with practical examples for every feature of the core (@goddo/core).
Syntax (Elysia-compatible)
new Goddo()
.state('version', '1.0') // shared state
.decorate('logger', console.log) // decorates the context
.onRequest(({ path }) => console.log(path)) // lifecycle hook
.get('/', () => 'text') // text/plain
.get('/json', () => ({ ok: true })) // application/json (auto)
.get('/user/:id', ({ params: { id } }) => id) // route params
.get('/files/*', ({ params }) => params['*']) // wildcard
.get('/q', ({ query }) => query) // query string
.post('/body', ({ body }) => body) // automatic body parse
.get('/error', ({ error }) => {
throw error(418)
})
.get('/go', ({ redirect }) => redirect('/'))
.group('/api', (app) => app.get('/health', () => 'ok'))
.use(plugin) // plugin composition
.onError(({ code }) => code === 'NOT_FOUND' ? 'Not found' : 'Error')
.listen(3000)Validation (t module)
TypeBox-style schemas for body, query, params, headers, and response, featuring end-to-end type inference in the handler and automatic coercion in query/params/headers. Invalid inputs return 422 (error code VALIDATION in onError).
import { Goddo, t } from '@goddo/core'
new Goddo()
.post('/user', ({ body }) => body.name, { // body: { name: string; age: number }
body: t.Object({ name: t.String(), age: t.Number() }),
})
.get('/user/:id', ({ params: { id } }) => id * 2, { // id: number (coerced from URL)
params: t.Object({ id: t.Numeric() }),
})
.get('/list', ({ query }) => query, {
query: t.Object({
page: t.Numeric({ default: 1 }),
active: t.Optional(t.Boolean()),
}),
})
.listen(3000)Available builders: t.String (minLength, maxLength, pattern, format), t.Number/t.Integer (minimum, maximum, multipleOf), t.Numeric (number with string coercion), t.Boolean, t.Null, t.Any, t.Unknown, t.Literal, t.Union, t.Enum, t.Nullable, t.Array (minItems, maxItems), t.Object (additionalProperties),t.Optional. All of them accept custom error messages and a default value.
Guard
Apply shared hooks and schemas to a group of routes:
new Goddo()
.guard(
{
headers: t.Object({ authorization: t.String() }),
beforeHandle: ({ headers, error }) => {
if (!headers.authorization.startsWith('Bearer ')) throw error(401)
},
},
(app) =>
app
.get('/admin', () => 'admin')
.get('/settings', () => 'settings'),
)
.get('/public', () => 'public') // not affected by guard
.listen(3000)Derive & Resolve
derive extends the context before validation (runs in the transform queue):
new Goddo()
.derive(({ headers }) => ({
bearer: headers.authorization?.replace('Bearer ', ''),
}))
.get('/token', ({ bearer }) => bearer)resolve extends the context after validation (runs in the beforeHandle queue):
new Goddo()
.resolve(async ({ headers }) => ({
user: await getUser(headers.authorization),
}))
.get('/me', ({ user }) => user.name)Divergence from Elysia: onCleanup (Teardown)
onCleanup is a context method that registers a teardown function, run asynchronously in the finally block after the request finishes. Ideal for cleaning up request-scoped resources.
new Goddo()
.derive(({ onCleanup }) => {
const tx = db.transaction()
onCleanup(() => tx.release())
return { tx }
})Macro
Create reusable route-level options that expand into lifecycle hooks:
new Goddo()
.macro({
auth: (enabled: boolean) => ({
beforeHandle({ headers, error }) {
if (enabled && !headers.authorization) throw error(401)
},
}),
})
.get('/', () => 'public')
.get('/admin', () => 'secret', { auth: true }) // macro applied
.listen(3000)WebSockets (.ws)
Elysia-compatible WebSocket support, with built-in schema validation and pub/sub rooms, powered natively by Deno.upgradeWebSocket:
import { Goddo, t } from '@goddo/core'
new Goddo()
.ws('/chat', {
body: t.Object({ text: t.String() }),
open(ws) {
ws.subscribe('general')
ws.publish('general', { text: 'A user joined' })
},
message(ws, msg) {
ws.publish('general', msg)
},
close(ws) {
ws.unsubscribe('general')
},
})
.listen(3000)Lifecycle
onRequest → onParse → onTransform → derive → validation → resolve → onBeforeHandle → handler → onAfterHandle → response validation → mapResponse → onAfterResponse (and onError for exceptions), mirroring Elysia's lifecycle.
Performance (AOT Compilation)
Goddo compiles all routes into a single optimized handler at listen() time (or when compile() is called manually):
- Pre-merged hooks: global and route-level hooks merged once, not per-request
- Pre-computed flags: validation uses booleans instead of truthiness checks
- Static route map: routes without dynamic segments use a
Mapfor O(1) lookup - Sucrose detection: sync handlers avoid
awaitoverhead - V8-optimized context:
GoddoContextwith hidden classes and lazy evaluation - Fast URL parsing: manual extraction using string indices
- Method-aware execution: skips body parsing for
GET/HEAD
const app = new Goddo()
.get('/', () => 'Hello')
.get('/user/:id', ({ params }) => params.id)
app.compile() // optional — listen() calls this automatically
app.listen(3000)