Middleware Patterns
Middleware Concept
Request Pipeline
┌─────────────────────────────────────────────────────────────┐
│ MIDDLEWARE PIPELINE │
├─────────────────────────────────────────────────────────────┤
│ │
│ Request │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ CORS │ ──▶ Add CORS headers │
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Auth │ ──▶ Verify JWT │
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Logging │ ──▶ Log request │
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Handler │ ──▶ Process request │
│ └─────────────┘ │
│ │ │
│ ▼ │
│ Response │
│ │
└─────────────────────────────────────────────────────────────┘
Manual Middleware
Simple Implementation
type Middleware = (
request: Request,
env: Env,
next: () => Promise<Response>
) => Promise<Response>;
// CORS Middleware
const cors: Middleware = async (request, env, next) => {
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
});
}
// Continue to next middleware
const response = await next();
// Add CORS headers to response
const newResponse = new Response(response.body, response);
newResponse.headers.set('Access-Control-Allow-Origin', '*');
return newResponse;
};
// Auth Middleware
const auth: Middleware = async (request, env, next) => {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// Verify token...
return next();
};
Chain Middlewares
function createHandler(middlewares: Middleware[], handler: Handler) {
return async (request: Request, env: Env): Promise<Response> => {
let index = 0;
const next = async (): Promise<Response> => {
if (index < middlewares.length) {
const middleware = middlewares[index++];
return middleware(request, env, next);
}
return handler(request, env);
};
return next();
};
}
// Usage
const handler = createHandler(
[cors, auth, logging],
async (request, env) => {
return Response.json({ data: 'protected' });
}
);
Hono Middleware (Recommended)
Built-in Middleware
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { jwt } from 'hono/jwt';
import { secureHeaders } from 'hono/secure-headers';
const app = new Hono();
// Global middleware
app.use('*', logger());
app.use('*', cors());
app.use('*', secureHeaders());
// Route-specific middleware
app.use('/api/*', jwt({ secret: 'my-secret' }));
app.get('/api/users', (c) => {
const payload = c.get('jwtPayload');
return c.json({ user: payload.sub });
});
export default app;
Custom Hono Middleware
import { Hono } from 'hono';
import { createMiddleware } from 'hono/factory';
interface Env {
SUPABASE_URL: string;
SUPABASE_ANON_KEY: string;
}
// Custom auth middleware
const supabaseAuth = createMiddleware<{ Bindings: Env }>(async (c, next) => {
const authHeader = c.req.header('Authorization');
if (!authHeader) {
return c.json({ error: 'Missing authorization header' }, 401);
}
// Verify with Supabase
const response = await fetch(`${c.env.SUPABASE_URL}/auth/v1/user`, {
headers: { Authorization: authHeader },
});
if (!response.ok) {
return c.json({ error: 'Invalid token' }, 401);
}
const user = await response.json();
c.set('user', user);
await next();
});
// Rate limiting middleware
const rateLimit = createMiddleware<{ Bindings: Env }>(async (c, next) => {
const ip = c.req.header('CF-Connecting-IP') || 'unknown';
const key = `rate:${ip}`;
// Check rate limit in KV
const count = parseInt(await c.env.MY_KV.get(key) || '0');
if (count > 100) {
return c.json({ error: 'Rate limit exceeded' }, 429);
}
// Increment count
await c.env.MY_KV.put(key, (count + 1).toString(), { expirationTtl: 60 });
await next();
});
const app = new Hono<{ Bindings: Env }>();
app.use('/api/*', rateLimit);
app.use('/api/*', supabaseAuth);
app.get('/api/me', (c) => {
const user = c.get('user');
return c.json(user);
});
Common Middleware Patterns
CORS
import { cors } from 'hono/cors';
app.use('/api/*', cors({
origin: ['https://myapp.com', 'http://localhost:3000'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400,
}));
Error Handling
import { HTTPException } from 'hono/http-exception';
// Error handler middleware
app.onError((err, c) => {
console.error('Error:', err);
if (err instanceof HTTPException) {
return c.json({ error: err.message }, err.status);
}
return c.json({ error: 'Internal Server Error' }, 500);
});
// Usage
app.get('/api/users/:id', async (c) => {
const user = await findUser(c.req.param('id'));
if (!user) {
throw new HTTPException(404, { message: 'User not found' });
}
return c.json(user);
});
Request Validation
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2),
password: z.string().min(8),
});
app.post(
'/api/users',
zValidator('json', createUserSchema),
async (c) => {
const data = c.req.valid('json');
// data is typed and validated
return c.json({ created: true });
}
);
Timing Middleware
const timing = createMiddleware(async (c, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
c.res.headers.set('X-Response-Time', `${duration}ms`);
console.log(`${c.req.method} ${c.req.path} - ${duration}ms`);
});
app.use('*', timing);
Middleware Order
Execution Order Matters
const app = new Hono();
// Order of middleware matters!
// 1. Logger first (logs all requests)
app.use('*', logger());
// 2. CORS (handles preflight)
app.use('*', cors());
// 3. Rate limiting
app.use('/api/*', rateLimit);
// 4. Authentication
app.use('/api/*', auth);
// 5. Routes
app.get('/api/data', handler);
// Error handler last
app.onError(errorHandler);
Tổng kết
Common Middleware
| Middleware |
Purpose |
| CORS |
Cross-origin requests |
| Auth |
JWT/token validation |
| Logger |
Request logging |
| Rate Limit |
Prevent abuse |
| Validation |
Input validation |
| Error Handler |
Consistent errors |
Best Practices
✅ Use Hono for cleaner middleware
✅ Order middleware carefully
✅ Handle errors globally
✅ Validate inputs early
✅ Add timing for monitoring
Q&A
- Middleware nào cần thiết nhất?
- Dùng Hono hay manual?
- Questions về patterns?