Skip to content
Company OS
Esc
↑↓navigate↵open⌘Jpreview
On this page

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:

  1. 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

  2. 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

  3. 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:

  1. RPC Handler (/rpc prefix)
    Uses oRPC’s RPCHandler to 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

  2. OpenAPI Handler (/api-reference prefix)
    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

  3. 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"]

Was this page helpful?