Hono
Hono Recipe
Section titled “Hono Recipe”Hono is fetch-native, so it mounts the HTTP transport core directly: .mount() binds the handler to a prefix and strips that prefix before it sees the request. Nothing to install beyond uql-orm.
import { Hono } from 'hono';import { cors } from 'hono/cors';import { createFetchHandler } from 'uql-orm/http';import './uql.config.js'; // setQuerierPool + entity importsimport { Post, User } from './shared/models/index.js';
const handler = createFetchHandler({ include: [User, Post] });
const app = new Hono();app.use('*', cors());app.get('/health', (c) => c.text('ok'));app.post('/checkout', (c) => c.json(runCheckout(c.req))); // custom business logicapp.mount('/api', handler); // entity CRUD under /api
export default app; // Bun.serve, Deno.serve, Cloudflare Workers, Node via @hono/node-serverThat serves the full wire protocol per entity. Because .mount() forwards every method, the QUERY transport works with no extra routing. .mount() claims only the /api prefix, so your own routes and middleware sit beside the generated CRUD; keep them for read-modify-write logic, multi-entity transactions, aggregations, uploads and streaming.
Authorization and tenant scoping
Section titled “Authorization and tenant scoping”getContext receives the web Request and returns the ambient context for the whole request, so a security filter scopes every query it runs, non-bypassable from the wire and fail-closed:
const handler = createFetchHandler({ include: [User, Post], async getContext(request) { const user = await authenticate(request.headers.get('authorization')); if (!user) { throw Object.assign(new Error('unauthorized'), { status: 401 }); // numeric status becomes the HTTP status } return { tenantId: user.tenantId, userId: user.id }; },});The core’s hooks cover everything else: shaping a response, stamping a field, rejecting a payload.