Server API
Details of the Hono + oRPC server setup, including routing, middleware, context creation, and service integration.
Overview
The server API is built with Hono and integrates oRPC for type-safe RPC and OpenAPI documentation. It defines middleware for authentication, logging, and CORS, creates a request context with database and session data, and routes requests to appropriate handlers.
Middleware
The server applies middleware in the following order:
-
Logging (
evlog)
Initializes structured logging with a file system drain in development and no drain in production.app.use( evlog({ drain: process.env.NODE_ENV === "production" ? undefined : createFsDrain(), }) );Evidence: index.ts#L32-L35
-
Authentication (
identifyUser)
Extracts user session from request headers using the auth service, excluding/api/auth/**routes to allow public access to authentication endpoints.app.use("*", async (c, next) => { await identifyUser(c.get("log"), c.req.raw.headers, c.req.path); return next(); });Evidence: index.ts#L36-L39
The middleware is constructed from the auth service:const identifyUser = createAuthMiddleware(auth as BetterAuthInstance, { exclude: ["/api/auth/**"], maskEmail: true, });Evidence: index.ts#L24-L27
-
CORS
Configures cross-origin requests with specific headers, methods, credentials, and the origin from environment variables.app.use( "/*", cors({ allowHeaders: ["Content-Type", "Authorization"], allowMethods: ["GET", "POST", "OPTIONS"], credentials: true, origin: [ENV.CORS_ORIGIN], }) );Evidence: index.ts#L41-L49
Context Creation
For each request, the server creates an API context containing:
- A database connection (
db) from the services module - The user session (
session) derived from the authentication middleware
export const createContext = async ({
context,
}: CreateContextOptions): Promise<ApiContext> => {
const session = await auth.api.getSession({
headers: context.req.raw.headers,
});
return {
db,
session,
};
};
Evidence: context.ts#L10-L20
The context type is inferred as:
export type Context = Awaited<ReturnType<typeof createContext>>;
Evidence: context.ts#L22
Request Handling
After middleware, the server attempts to handle requests in this order:
-
RPC Handler (
/rpcprefix)
Uses oRPC’sRPCHandlerto process RPC requests. If matched, returns the RPC response.const rpcResult = await rpcHandler.handle(c.req.raw, { context, prefix: "/rpc", }); if (rpcResult.matched) { return c.newResponse(rpcResult.response.body, rpcResult.response); }Evidence: index.ts#L76-L86
-
OpenAPI Handler (
/api-referenceprefix)
Serves OpenAPI documentation if the RPC handler does not match.const apiResult = await apiHandler.handle(c.req.raw, { context, prefix: "/api-reference", }); if (apiResult.matched) { return c.newResponse(apiResult.response.body, apiResult.response); }Evidence: index.ts#L88-L95
-
Fallback
If neither handler matches, invokes the next middleware (leading to a 404 or the root handler).
Authentication Routes
Requests to /api/auth/* are bypassed by the RPC/OpenAPI handling logic and passed directly to the auth service’s handler:
app.use("/*", async (c, next) => {
// ... context creation and handler attempts ...
});
app.on(["POST", "GET"], "/api/auth/*", (c) => auth.handler(c.req.raw));
Evidence: index.ts#L51
This ensures authentication endpoints (e.g., sign-in, sign-out) are accessible without requiring a valid session.
Root Endpoint
A simple health check endpoint returns “OK” for GET requests to the root path:
app.get("/", (c) => c.text("OK"));
Evidence: index.ts#L100
Service Integration
The server initializes core services (database and authentication) from environment variables:
export const db = createDb(ENV);
export const auth = createAuth(ENV, db);
Evidence: services.ts#L6-L7
These services are imported and used in the context creation and authentication middleware.
Error Handling
Both RPC and OpenAPI handlers include an interceptor that logs errors to the console:
interceptors: [
// oxlint-disable-next-line promise/prefer-await-to-callbacks -- oRPC interceptor API is callback-shaped
onError((error) => {
console.error(error);
}),
],
Evidence: index.ts#L56-L58 and index.ts#L69-L71
Diagram: Request Flow
flowchart TD
A[Incoming Request] --> B[Logging Middleware]
B --> C[Authentication Middleware]
C --> D{CORS Middleware}
D --> E{Path /api/auth/*?}
E -->|Yes| F[Auth Service Handler]
E -->|No| G[Create Context: db + session]
G --> H{RPC Handler /rpc}
H -->|Matched| I[Return RPC Response]
H -->|Not Matched| J{OpenAPI Handler /api-reference}
J -->|Matched| K[Return OpenAPI Response]
J -->|Not Matched| L[Next Middleware / 404]
L --> M[Root Handler /]
M --> N[Return "OK"]