Deployment
Deploy the same ChatDefinition on Next, Hono, Express, and edge — Web-standard handlers.
Deployment modes
The shared ChatDefinition is unchanged across deployment modes. Only the adapter, trusted host context, storage, and native renderer vary.
Topology
Loading diagram...
Next.js App Router
code
// app/api/chat/route.ts
import { createChatHandler } from '@agentskit/chat/server'
import { verifyBearer } from '@/lib/auth'
import { chats } from '@/lib/chats'
import { sessions } from '@/lib/session-storage'
const handler = createChatHandler({
authenticate: async request => {
const identity = await verifyBearer(request)
return identity
? { ok: true, context: identity }
: { ok: false, response: new Response('Unauthorized', { status: 401 }) }
},
resolveDefinition: context => chats.forTenant(context?.tenantId),
sessionStorage: context => sessions.forTenant(context?.tenantId),
})
export const POST = (request: Request) => handler(request)Ask route: same pattern with createAskServiceHandler.
Hono
code
import { Hono } from 'hono'
import { createChatHandler } from '@agentskit/chat/server'
const chat = createChatHandler({ /* authenticate, resolveDefinition, sessionStorage */ })
const app = new Hono()
app.post('/api/chat', c => chat(c.req.raw))
export default appExpress (Node 18+)
code
import express from 'express'
import { createChatHandler } from '@agentskit/chat/server'
const chat = createChatHandler({ /* … */ })
const app = express()
app.post('/api/chat', async (req, res) => {
const disconnected = new AbortController()
const abort = () => { if (!res.writableEnded) disconnected.abort() }
req.once('aborted', abort)
res.once('close', abort)
const headers = new Headers()
for (const [key, value] of Object.entries(req.headers)) {
if (typeof value === 'string') headers.set(key, value)
else if (Array.isArray(value)) value.forEach(item => headers.append(key, item))
}
const request = new Request(`http://${req.headers.host}${req.url}`, {
method: 'POST',
headers,
body: req,
duplex: 'half',
signal: disconnected.signal,
} as RequestInit)
const response = await chat(request)
res.status(response.status)
response.headers.forEach((v, k) => res.setHeader(k, v))
if (!response.body) { res.end(); return }
const reader = response.body.getReader()
const pump = async (): Promise<void> => {
const { done, value } = await reader.read()
if (done) { res.end(); return }
res.write(Buffer.from(value))
return pump()
}
await pump()
})Cloudflare Worker
code
import { createChatHandler } from '@agentskit/chat/server'
const chat = createChatHandler({ /* … */ })
export default {
fetch(request: Request) {
if (new URL(request.url).pathname === '/api/chat' && request.method === 'POST') {
return chat(request)
}
return new Response('Not found', { status: 404 })
},
}Checklist
- Shared
defineChatdefinition (no secrets in the client) - Server-side adapter + keys via AgentsKit providers
- Auth before body parse
- CAS
SessionStorage - Native shell per surface (examples)