🎉 RamAPI v1.0 is now available! Read the Getting Started Guide
Documentation
Middleware
Built-in Middleware

Built-in Middleware Reference

RamAPI comes with several built-in middleware functions for common use cases. This guide provides a complete reference with examples for each middleware.

Table of Contents

  1. Overview
  2. Validation Middleware
  3. CORS Middleware
  4. Logger Middleware
  5. Rate Limit Middleware
  6. Authentication Middleware
  7. Combining Middleware

Overview

All built-in middleware can be imported from ramapi:

import {
  validate,
  cors,
  logger,
  rateLimit,
  authenticate,
  optionalAuthenticate,
} from 'ramapi';

Validation Middleware

Validates request data using Zod schemas.

Import

import { validate } from 'ramapi';
import { z } from 'zod';

Signature

function validate(schema: ValidationSchema): Middleware
 
interface ValidationSchema {
  body?: ZodSchema;
  query?: ZodSchema;
  params?: ZodSchema;
}

Basic Usage

import { z } from 'zod';
 
const createUserSchema = {
  body: z.object({
    name: z.string().min(1),
    email: z.string().email(),
    age: z.number().int().min(18),
  }),
};
 
app.post('/users',
  validate(createUserSchema),
  async (ctx) => {
    // ctx.body is now typed and validated
    ctx.json({ user: ctx.body });
  }
);

Validate Query Parameters

const searchSchema = {
  query: z.object({
    q: z.string().min(1),
    page: z.string().regex(/^\d+$/).transform(Number).default('1'),
    limit: z.string().regex(/^\d+$/).transform(Number).default('10'),
  }),
};
 
app.get('/search',
  validate(searchSchema),
  async (ctx) => {
    const { q, page, limit } = ctx.query;
    ctx.json({ results: [], page, limit });
  }
);

Validate Route Parameters

const getUserSchema = {
  params: z.object({
    id: z.string().uuid(),
  }),
};
 
app.get('/users/:id',
  validate(getUserSchema),
  async (ctx) => {
    const { id } = ctx.params;
    ctx.json({ user: { id } });
  }
);

Complete Validation

const updateUserSchema = {
  params: z.object({
    id: z.string().uuid(),
  }),
  body: z.object({
    name: z.string().min(1).optional(),
    email: z.string().email().optional(),
  }),
  query: z.object({
    notify: z.string().transform((val) => val === 'true').default('false'),
  }),
};
 
app.patch('/users/:id',
  validate(updateUserSchema),
  async (ctx) => {
    const { id } = ctx.params;
    const updates = ctx.body;
    const { notify } = ctx.query;
 
    ctx.json({ user: { id, ...updates }, notified: notify });
  }
);

See: Validation Guide


CORS Middleware

Handles Cross-Origin Resource Sharing headers.

Import

import { cors } from 'ramapi';

Signature

function cors(config?: CorsConfig): Middleware
 
interface CorsConfig {
  origin?: string | string[] | ((origin: string) => boolean);
  methods?: HTTPMethod[];
  allowedHeaders?: string[];
  exposedHeaders?: string[];
  credentials?: boolean;
  maxAge?: number;
}

Basic Usage

// Allow all origins (development)
app.use(cors());
 
// Allow specific origins (production)
app.use(cors({
  origin: ['https://example.com'],
  credentials: true,
}));

Complete Configuration

app.use(cors({
  origin: ['https://example.com', 'https://app.example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  exposedHeaders: ['X-Request-ID'],
  credentials: true,
  maxAge: 86400, // 24 hours
}));

Dynamic Origin

app.use(cors({
  origin: (requestOrigin) => {
    const allowedOrigins = ['https://example.com', 'https://app.example.com'];
    return allowedOrigins.includes(requestOrigin);
  },
  credentials: true,
}));

See: CORS Middleware Guide


Logger Middleware

Logs incoming requests with response times and status codes.

Import

import { logger } from 'ramapi';

Signature

function logger(): Middleware

Basic Usage

app.use(logger());

Output

[200] GET / - 5ms
[404] GET /not-found - 2ms
[500] POST /error - 15ms

Custom Logger

function customLogger(): Middleware {
  return async (ctx, next) => {
    const start = Date.now();
 
    try {
      await next();
      const duration = Date.now() - start;
      console.log(`${ctx.method} ${ctx.path} - ${duration}ms`);
    } catch (error) {
      console.error(`${ctx.method} ${ctx.path} - Error:`, error);
      throw error;
    }
  };
}
 
app.use(customLogger());

See: Logger Middleware Guide


Rate Limit Middleware

Protects API from abuse by limiting request frequency.

Import

import { rateLimit } from 'ramapi';

Signature

function rateLimit(config?: RateLimitConfig): Middleware
 
interface RateLimitConfig {
  windowMs?: number;                    // Time window (default: 60000)
  maxRequests?: number;                 // Max requests (default: 100)
  keyGenerator?: (ctx: Context) => string; // Key function
  message?: string;                     // Error message
  skipSuccessfulRequests?: boolean;
  skipFailedRequests?: boolean;
}

Basic Usage

// 100 requests per minute per IP
app.use(rateLimit());
 
// Custom limits
app.use(rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  maxRequests: 100,
}));

Per-Route Rate Limiting

// Strict limit for login
app.post('/auth/login',
  rateLimit({
    windowMs: 15 * 60 * 1000,
    maxRequests: 5,
    message: 'Too many login attempts',
  }),
  loginHandler
);
 
// Normal limit for API
app.use('/api', rateLimit({
  windowMs: 60000,
  maxRequests: 100,
}));

Custom Key Generator

app.use(rateLimit({
  windowMs: 60000,
  maxRequests: 100,
  keyGenerator: (ctx) => {
    // Rate limit by API key instead of IP
    return ctx.headers['x-api-key'] as string || 'anonymous';
  },
}));

See: Rate Limiting Guide


Authentication Middleware

JWT-based authentication middleware.

Import

import { authenticate, optionalAuthenticate, JWTService } from 'ramapi';

Signature

function authenticate(jwtService: JWTService): Middleware
function optionalAuthenticate(jwtService: JWTService): Middleware
 
class JWTService {
  constructor(config: JWTConfig);
  sign(payload: JWTPayload): string;
  verify(token: string): JWTPayload;
  decode(token: string): JWTPayload | null;
}
 
interface JWTConfig {
  secret: string;
  expiresIn?: number;
  algorithm?: 'HS256' | 'HS384' | 'HS512' | 'RS256' | ...;
  issuer?: string;
  audience?: string;
}

Basic Usage

import { JWTService, authenticate } from 'ramapi';
 
const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
  expiresIn: 86400, // 24 hours
});
 
// Protected route
app.get('/api/profile',
  authenticate(jwtService),
  async (ctx) => {
    ctx.json({ user: ctx.user });
  }
);

Generate Token

app.post('/auth/login', async (ctx) => {
  const { email, password } = ctx.body;
 
  // Verify credentials (pseudo-code)
  const user = await verifyCredentials(email, password);
 
  if (!user) {
    throw new HTTPError(401, 'Invalid credentials');
  }
 
  // Generate JWT token
  const token = jwtService.sign({
    sub: user.id,
    email: user.email,
    role: user.role,
  });
 
  ctx.json({ token });
});

Optional Authentication

Use when you want to authenticate if a token is present, but not require it:

app.get('/api/posts',
  optionalAuthenticate(jwtService),
  async (ctx) => {
    // ctx.user is populated if token was provided
    const posts = ctx.user
      ? await getPrivatePosts(ctx.user.id)
      : await getPublicPosts();
 
    ctx.json({ posts });
  }
);

Complete Configuration

const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
  expiresIn: 86400, // 24 hours
  algorithm: 'HS256',
  issuer: 'my-api',
  audience: 'my-app',
});

See: Authentication Guide


Combining Middleware

Multiple Middleware on Route

app.post('/api/users',
  logger(),
  rateLimit({ maxRequests: 10 }),
  cors({ origin: ['https://example.com'] }),
  validate(createUserSchema),
  authenticate(jwtService),
  async (ctx) => {
    // All middleware applied in order
    ctx.json({ user: ctx.body });
  }
);

Global Middleware

const app = createApp();
 
// Apply to all routes
app.use(logger());
app.use(cors());
app.use(rateLimit());

Route Groups

// Public routes
app.group('/api/public', (router) => {
  router.use(cors());
  router.use(rateLimit({ maxRequests: 50 }));
 
  router.get('/posts', getPublicPosts);
});
 
// Private routes
app.group('/api/private', (router) => {
  router.use(cors({ origin: ['https://app.example.com'] }));
  router.use(authenticate(jwtService));
  router.use(rateLimit({ maxRequests: 500 }));
 
  router.get('/profile', getProfile);
  router.put('/profile', validate(updateProfileSchema), updateProfile);
});

Conditional Middleware

// Only apply in production
if (process.env.NODE_ENV === 'production') {
  app.use(rateLimit());
}
 
// Only log in development
if (process.env.NODE_ENV !== 'production') {
  app.use(logger());
}

Middleware Order Matters

// CORRECT ORDER
app.use(logger());          // 1. Log first
app.use(cors());            // 2. Handle CORS
app.use(rateLimit());       // 3. Rate limit
app.use(authenticate());    // 4. Authenticate
app.use(validate(schema));  // 5. Validate
 
// Routes
app.get('/api/data', handler);

Quick Reference Table

MiddlewarePurposeDefault ConfigProduction Ready
validate()Zod schema validationN/AYes
cors()CORS headersAllow all originsConfigure origins
logger()Request loggingColored console outputUse structured logging
rateLimit()Rate limiting100 req/min per IPUse Redis
authenticate()JWT authenticationN/AYes

Complete Example

import {
  createApp,
  validate,
  cors,
  logger,
  rateLimit,
  authenticate,
  JWTService,
} from 'ramapi';
import { z } from 'zod';
 
const app = createApp();
 
// Global middleware
app.use(logger());
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || ['*'],
  credentials: true,
}));
 
// JWT setup
const jwtService = new JWTService({
  secret: process.env.JWT_SECRET!,
  expiresIn: 86400,
});
 
// Public routes
app.post('/auth/login',
  rateLimit({ maxRequests: 5 }),
  validate({
    body: z.object({
      email: z.string().email(),
      password: z.string().min(8),
    }),
  }),
  async (ctx) => {
    const token = jwtService.sign({ sub: 'user-id' });
    ctx.json({ token });
  }
);
 
// Protected routes
app.get('/api/profile',
  authenticate(jwtService),
  rateLimit({ maxRequests: 100 }),
  async (ctx) => {
    ctx.json({ user: ctx.user });
  }
);
 
app.listen(3000);

Next Steps


Need help? Check the API Reference or GitHub Issues (opens in a new tab).