---
type: concept
title: Server API
description: Details of the Hono + oRPC server setup, including routing, middleware, context creation, and service integration.
tags: [server, API, Hono, oRPC, middleware]
verified:
  - by: openwiki/0.7.2
    at: 2026-10-14T20:42:32.326Z
---

## Overview

The server API is built with [Hono](https://hono.dev/) and integrates [oRPC](https://orpc.dev/) 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.  
   ```ts
   app.use(
     evlog({
       drain: process.env.NODE_ENV === "production" ? undefined : createFsDrain(),
     })
   );
   ```
   *Evidence: [index.ts#L32-L35](repo://apps/server/src/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.  
   ```ts
   app.use("*", async (c, next) => {
     await identifyUser(c.get("log"), c.req.raw.headers, c.req.path);
     return next();
   });
   ```
   *Evidence: [index.ts#L36-L39](repo://apps/server/src/index.ts#L36-L39)*  
   The middleware is constructed from the auth service:  
   ```ts
   const identifyUser = createAuthMiddleware(auth as BetterAuthInstance, {
     exclude: ["/api/auth/**"],
     maskEmail: true,
   });
   ```
   *Evidence: [index.ts#L24-L27](repo://apps/server/src/index.ts#L24-L27)*

3. **CORS**  
   Configures cross-origin requests with specific headers, methods, credentials, and the origin from environment variables.  
   ```ts
   app.use(
     "/*",
     cors({
       allowHeaders: ["Content-Type", "Authorization"],
       allowMethods: ["GET", "POST", "OPTIONS"],
       credentials: true,
       origin: [ENV.CORS_ORIGIN],
     })
   );
   ```
   *Evidence: [index.ts#L41-L49](repo://apps/server/src/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

```ts
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](repo://apps/server/src/context.ts#L10-L20)*  
The context type is inferred as:  
```ts
export type Context = Awaited<ReturnType<typeof createContext>>;
```
*Evidence: [context.ts#L22](repo://apps/server/src/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.  
   ```ts
   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](repo://apps/server/src/index.ts#L76-L86)*

2. **OpenAPI Handler (`/api-reference` prefix)**  
   Serves OpenAPI documentation if the RPC handler does not match.  
   ```ts
   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](repo://apps/server/src/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:  
```ts
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](repo://apps/server/src/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:  
```ts
app.get("/", (c) => c.text("OK"));
```
*Evidence: [index.ts#L100](repo://apps/server/src/index.ts#L100)*

## Service Integration

The server initializes core services (database and authentication) from environment variables:  
```ts
export const db = createDb(ENV);
export const auth = createAuth(ENV, db);
```
*Evidence: [services.ts#L6-L7](repo://apps/server/src/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:  
```ts
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](repo://apps/server/src/index.ts#L56-L58) and [index.ts#L69-L71](repo://apps/server/src/index.ts#L69-L71)*

## Diagram: Request Flow

```mermaid
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"]
```
