Skip to content

Latest commit

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Deno.serve Compatible Middleware System

This middleware system provides a flexible and powerful way to compose middleware functions that are fully compatible with Deno.serve handlers.

Features

  • 🔄 Full Deno.serve compatibility - Works seamlessly with standard Deno HTTP handlers
  • 🛠️ Flexible composition - Multiple ways to compose middleware (functional and fluent)
  • 🔧 Built-in middleware - Common middleware like CORS, logging, auth, rate limiting
  • 🚨 Error handling - Configurable error handling and recovery
  • 📊 Request context - Share data between middleware and handlers
  • Type-safe - Full TypeScript support with proper typing

Quick Start

Basic Usage

import{createMiddlewareChain,logger,cors,rateLimit,timeout}from"jsr:@snaapi/middleware";// Your application handlerfunctionmyHandler(req: Request): Response{returnnewResponse("Hello World!");}// Create middleware stackconsthandler=createMiddlewareChain().use(logger()).use(cors()).use(rateLimit({windowMs: 60000,maxRequests: 100})).use(timeout(30000)).handle(myHandler);// Start serverDeno.serve({port: 8000},handler);

Alternative Composition

import{compose,logger,cors,rateLimit,timeout}from"jsr:@snaapi/middleware";consthandler=compose([logger(),cors(),rateLimit({windowMs: 60000,maxRequests: 100}),timeout(30000),],myHandler,);Deno.serve({port: 8000},handler);

Built-in Middleware

Logger

Logs HTTP requests with configurable formats:

import{logger}from"jsr:@snaapi/middleware";// Simple logging.use(logger())// Detailed logging with headers and body.use(logger({format: "detailed",includeHeaders: true,includeBody: true}))// JSON structured logging.use(logger({format: "json"}))

CORS

Handles Cross-Origin Resource Sharing:

import{cors}from"jsr:@snaapi/middleware";// Basic CORS (allows all origins).use(cors())// Configured CORS.use(cors({origin: ["https://myapp.com","https://admin.myapp.com"],methods: ["GET","POST","PUT","DELETE"],headers: ["Content-Type","Authorization"],credentials: true}))

Rate Limiting

Request rate limiting per client:

import{rateLimit}from"jsr:@snaapi/middleware";// 100 requests per minute.use(rateLimit({windowMs: 60000,maxRequests: 100}))// Custom key generator (rate limit by IP).use(rateLimit({windowMs: 60000,maxRequests: 100,keyGenerator: (req)=>req.headers.get("X-Real-IP")||"unknown"}))

Request Timeout

Timeout long-running requests:

import{timeout}from"jsr:@snaapi/middleware";// 30 second timeout.use(timeout(30000))

Custom Middleware

Create your own middleware functions:

import{typeMiddleware}from"jsr:@snaapi/middleware";// Simple custom middlewareconstcustomMiddleware: Middleware=async(req,next)=>{console.log(`Processing ${req.method}${req.url}`);constresponse=awaitnext();console.log(`Responded with ${response.status}`);returnresponse;};// Middleware with configurationfunctioncustomAuth(apiKey: string): Middleware{returnasync(req,next)=>{constkey=req.headers.get("X-API-Key");if(key!==apiKey){returnnewResponse("Unauthorized",{status: 401});}returnnext();};}

Request Context

Share data between middleware:

import{withContext,getContext,setContext}from"jsr:@snaapi/middleware";// Add context middleware.use(withContext({startTime: Date.now()}))// Use context in subsequent middleware.use(async(req,next)=>{constcontext=getContext(req);console.log("Request started at:",context.startTime);setContext(req,"userId","user123");returnnext();})

Error Handling

Configure error handling behavior:

createMiddlewareChain().use(logger()).use(cors()).configure({continueOnError: false,errorHandler: (error,req)=>{console.error(`Error in ${req.url}:`,error);returnnewResponse(JSON.stringify({error: "Something went wrong"}),{status: 500,headers: {"Content-Type": "application/json"},},);},}).handle(myHandler);

Integration with Existing Code

Using with ResourceHandler

import{ResourceHandler}from"./server/resource.ts";import{createMiddlewareChain,logger,cors,rateLimit}from"jsr:@snaapi/middleware";constresourceHandler=newResourceHandler();consthandler=createMiddlewareChain().use(logger()).use(cors()).use(rateLimit({windowMs: 60000,maxRequests: 100})).handle(resourceHandler.handler.bind(resourceHandler));Deno.serve({port: 8000},handler);

Conditional Middleware

Apply different middleware to different routes:

consthandler=async(req: Request): Promise<Response>=>{consturl=newURL(req.url);if(url.pathname==="/health"){// Minimal middleware for health checkreturncreateMiddlewareChain().use(logger({format: "simple"})).handle(healthHandler)(req);}// Full middleware stack for API routesreturncreateMiddlewareChain().use(logger({format: "detailed"})).use(cors()).use(auth("my-token")).handle(apiHandler)(req);};

Type Definitions

Middleware

typeMiddleware=(req: Request,next: ()=>Promise<Response>,)=>Promise<Response>;

Handler

typeHandler=(req: Request)=>Response|Promise<Response>;

Request Context

interfaceRequestContext{[key: string]: unknown;}

Migration from Existing Code

To migrate existing code:

  1. Replace direct Deno.serve(handler) calls with middleware-wrapped handlers
  2. Move authentication logic to auth() middleware
  3. Replace manual CORS handling with cors() middleware
  4. Add logging with logger() middleware
  5. Configure error handling with .configure()

Before

Deno.serve({port: 8000},resourceHandler.handler.bind(resourceHandler));

After

consthandler=createMiddlewareChain().use(logger()).use(cors()).use(rateLimit({windowMs: 60000,maxRequests: 100})).handle(resourceHandler.handler.bind(resourceHandler));Deno.serve({port: 8000},handler);

About

A collection of middleware for Deno web applications.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages