# Backend Setup & Configuration Guide

> Complete guide to setting up and running the Hono + tRPC API server with authentication, middleware, and observability

## Table of Contents

1. [Server Setup](#server-setup)
2. [Hono Framework Configuration](#hono-framework-configuration)
3. [Middleware & Request Processing](#middleware--request-processing)
4. [Logging & Observability](#logging--observability)

---

## Server Setup

The Izri API server is a production-ready backend built with **Hono** (fast web framework) and **tRPC** (type-safe RPC).

### Overview

**Default Port**: `4000`

**Key Features:**
- Ultra-fast request handling with Hono
- End-to-end type safety with tRPC
- Built-in OpenTelemetry support
- Better Auth integration with session management
- Comprehensive error handling and logging

### Architecture

```
┌─────────────────────────────────────────────┐
│          Hono HTTP Server                   │
│  (CORS, Logging, Error Handling)           │
└────────────┬────────────────────────────────┘
             │
    ┌────────┴────────┐
    │                 │
┌───▼────────┐  ┌────▼─────────┐
│ REST APIs  │  │ Better Auth  │
│  /health   │  │ /api/auth/** │
└────────────┘  └──────────────┘
             │
    ┌────────▼────────┐
    │  tRPC Server    │
    │   /trpc/*       │
    └────────┬────────┘
             │
    ┌────────┴────────────────┐
    │                         │
┌───▼─────────┐  ┌───────────▼──────────┐
│  Routers    │  │   Database (Drizzle) │
│ - auth      │  │   - PostgreSQL       │
│ - projects  │  │   - Migrations       │
│ - orgs      │  └──────────────────────┘
│ - analysis  │
└─────────────┘
```

### Quick Start

**Prerequisites:**
- Node.js 18+
- PostgreSQL 15+ (or Docker)
- pnpm 8+

**Installation:**

```bash
# Install dependencies from repo root
pnpm install

# Start PostgreSQL (via Docker)
pnpm docker:up
# Postgres exposed on localhost:5433
```

**Run Development Server:**

```bash
# From repo root
pnpm dev:api

# Or from apps/api directory
cd apps/api
pnpm dev
```

**Verify Server:**

```bash
# Health check
curl http://localhost:4000/health
# Response: {"ok":true}

# tRPC endpoint
curl http://localhost:4000/trpc/health
```

### Environment Variables

Create a `.env` file in the repo root:

```bash
# Server Configuration
API_PORT=4000
LOG_LEVEL=info  # debug | info | warn | error
NODE_ENV=development

# Database
DATABASE_URL=postgresql://postgres:password@localhost:5432/izri

# Authentication
AUTH_URL=http://localhost:4000
BETTER_AUTH_SECRET=your-secret-key-here

# Telemetry
OTEL_ENABLED=false
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
```

### File Structure

```
apps/api/
├── src/
│   ├── index.ts           # Server entry point
│   ├── telemetry.ts       # OpenTelemetry setup
│   ├── middleware/
│   │   ├── auth.ts        # Authentication middleware
│   │   ├── cors.ts        # CORS configuration
│   │   └── error.ts       # Error handling middleware
│   ├── routers/
│   │   ├── auth.ts        # Auth procedures
│   │   ├── projects.ts    # Project procedures
│   │   ├── organizations.ts
│   │   └── index.ts       # Combined router
│   └── lib/
│       ├── auth.ts        # Auth helpers
│       ├── db.ts          # Database connection
│       └── errors.ts      # Error definitions
├── package.json
└── tsconfig.json
```

---

## Hono Framework Configuration

### Basic Setup

File: apps/api/src/index.ts

```ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { createTRPCServer } from './trpc'

const app = new Hono()

// Middleware
app.use('*', cors())
app.use('*', logger())

// Health check
app.get('/health', (c) => c.json({ ok: true }))

// tRPC routes
const trpcServer = createTRPCServer()
app.use('/trpc/*', trpcServer)

// Error handling
app.onError((err, c) => {
  console.error(err)
  return c.json(
    { error: 'Internal Server Error' },
    { status: 500 }
  )
})

export default app
```

### CORS Configuration

```ts
import { cors } from 'hono/cors'

app.use('*', cors({
  origin: process.env.WEB_URL || 'http://localhost:3000',
  credentials: true,
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400,
}))
```

### Request Logging

```ts
import { logger } from 'hono/logger'

app.use('*', logger((message) => {
  const timestamp = new Date().toISOString()
  console.log(`[${timestamp}] ${message}`)
}))
```

### Static File Serving

```ts
import { serveStatic } from 'hono/serve-static'

app.use('/public/*', serveStatic({ root: './' }))
```

---

## Middleware & Request Processing

### Authentication Middleware

File: apps/api/src/middleware/auth.ts

```ts
import { auth } from '@izri/auth'
import type { Context, Next } from 'hono'
import type { AuthenticatedUser } from '@izri/shared'

declare global {
  namespace HonoRequest {
    Variables: {
      user?: AuthenticatedUser
    }
  }
}

export async function withAuth(c: Context, next: Next) {
  try {
    const sessionData = await auth.api.getSession({ headers: c.req.raw.headers })
    if (sessionData?.user) {
      c.set('user', {
        id: sessionData.user.id,
        email: sessionData.user.email,
        name: sessionData.user.name,
        image: sessionData.user.image,
      })
    }
  } catch (error) {
    console.error('Auth middleware error:', error)
  }
  await next()
}

export function requireAuth(c: Context) {
  const user = c.get('user')
  if (!user) {
    throw new Error('Unauthorized')
  }
  return user
}
```

**Usage:**

```ts
app.use('/api/protected/*', withAuth)

app.get('/api/protected/profile', (c) => {
  const user = requireAuth(c)
  return c.json({ user })
})
```

### Custom Middleware

```ts
import type { Context, Next } from 'hono'

export async function requestId(c: Context, next: Next) {
  const id = crypto.randomUUID()
  c.set('requestId', id)
  c.header('X-Request-Id', id)
  await next()
}

export async function timing(c: Context, next: Next) {
  const start = Date.now()
  await next()
  const duration = Date.now() - start
  c.header('X-Response-Time', `${duration}ms`)
}
```

### Error Handling Middleware

```ts
import type { Context, Next } from 'hono'

export async function errorHandler(c: Context, next: Next) {
  try {
    await next()
  } catch (error) {
    const requestId = c.get('requestId')
    console.error(`[${requestId}] Error:`, error)

    if (error instanceof ValidationError) {
      return c.json(
        { error: error.message, details: error.details },
        { status: 400 }
      )
    }

    if (error instanceof AuthorizationError) {
      return c.json(
        { error: 'Unauthorized' },
        { status: 401 }
      )
    }

    return c.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    )
  }
}
```

### Database Query Middleware

```ts
import type { Context, Next } from 'hono'
import { db } from '@/lib/db'

export async function withDb(c: Context, next: Next) {
  c.set('db', db)
  await next()
}

export function getDb(c: Context) {
  return c.get('db')
}
```

**Usage:**

```ts
app.use('/api/*', withDb)

app.get('/api/users/:id', async (c) => {
  const db = getDb(c)
  const user = await db.query.users.findFirst({
    where: (users, { eq }) => eq(users.id, c.req.param('id'))
  })
  return c.json(user)
})
```

---

## Logging & Observability

### Logger Setup

File: apps/api/src/lib/logger.ts

```ts
import { pino } from 'pino'

const isProduction = process.env.NODE_ENV === 'production'

export const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: isProduction ? undefined : {
    target: 'pino-pretty',
    options: {
      colorize: true,
      translateTime: 'SYS:standard',
      ignore: 'pid,hostname',
    },
  },
})
```

### Using Logger

```ts
import { logger } from '@/lib/logger'

// Info
logger.info({ userId: '123' }, 'User created')

// Error
logger.error({ error: err }, 'Failed to fetch user')

// Debug
logger.debug({ query }, 'Executing database query')
```

### Request Logging Middleware

```ts
import type { Context, Next } from 'hono'
import { logger } from '@/lib/logger'

export async function requestLogger(c: Context, next: Next) {
  const start = Date.now()
  const method = c.req.method
  const path = c.req.path
  
  await next()
  
  const duration = Date.now() - start
  const status = c.res.status
  
  logger.info({
    method,
    path,
    status,
    duration,
  }, `${method} ${path}`)
}
```

### OpenTelemetry Integration

File: apps/api/src/telemetry.ts

```ts
import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'

if (process.env.OTEL_ENABLED === 'true') {
  const sdk = new NodeSDK({
    traceExporter: new OTLPTraceExporter({
      url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
    }),
    instrumentations: [getNodeAutoInstrumentations()],
  })
  
  sdk.start()
  
  process.on('SIGTERM', () => {
    sdk.shutdown().catch(console.error)
  })
}
```

### Structured Logging Best Practices

```ts
// ✓ Good: Structured with context
logger.info({
  userId: user.id,
  organizationId: org.id,
  action: 'project_created',
  projectId: project.id,
}, 'Project creation successful')

// ✗ Avoid: Unstructured strings
logger.info(`User ${userId} created project ${projectId}`)
```

### Performance Monitoring

```ts
import { performance } from 'perf_hooks'

export async function measurePerformance(name: string, fn: () => Promise<any>) {
  const start = performance.now()
  try {
    const result = await fn()
    const duration = performance.now() - start
    logger.debug({ duration }, `${name} took ${duration.toFixed(2)}ms`)
    return result
  } catch (error) {
    const duration = performance.now() - start
    logger.error({ duration, error }, `${name} failed after ${duration.toFixed(2)}ms`)
    throw error
  }
}
```

---

**Related:** [Authentication & API](./authentication-and-api.md) | [Testing](./testing-backend.md) | [README](./README.md)
