Skill v1.0.1
currentAutomated scan100/1001 files
version: "1.0.1" name: relic-middleware description: Create and apply middleware for auth, CORS, logging, and other cross-cutting concerns. Use context properties for type-safe request-scoped data. Use when adding middleware, request/response transformations, or passing data between middleware and handlers.
Relic Middleware
A Middleware wraps a handler with additional logic. It can inspect/modify requests, transform responses, short-circuit with an early response, or handle errors.
typedef Middleware = Handler Function(Handler innerHandler);
Basic pattern
Middleware myMiddleware() {return (Handler innerHandler) {return (Request req) async {// Before: inspect or modify the requestfinal result = await innerHandler(req);// After: inspect or transform the responsereturn result;};};}
Applying middleware
Use router.use(path, middleware). Middleware only runs on requests that match a registered route -- unmatched requests (404s) go directly to the fallback.
final app = RelicApp()// Global -- applies to all matched routes..use('/', logRequests())..use('/', corsMiddleware())// Scoped -- only /api routes..use('/api', authMiddleware())// Routes..get('/', homeHandler) // logRequests + cors..get('/api/users', usersHandler) // logRequests + cors + auth
Execution order
Path hierarchy first, then registration order within the same path scope:
final app = RelicApp()..use('/api', middlewareC) // specific to /api..use('/', middlewareA) // all paths..use('/', middlewareB) // all paths..get('/api/foo', fooHandler);
For GET /api/foo, execution order is: middlewareA → middlewareB → middlewareC → fooHandler (and response flows back in reverse).
Example: Auth middleware
Middleware authMiddleware() {return (Handler next) {return (Request req) async {final apiKey = req.headers['X-API-Key']?.first;if (apiKey != 'secret123') {return Response.unauthorized(body: Body.fromString('Invalid API key'));}return await next(req);};};}final app = RelicApp()..get('/public', publicHandler)..use('/protected', authMiddleware())..get('/protected', protectedHandler);
Example: CORS middleware
Middleware corsMiddleware() {return (Handler next) {return (Request req) async {if (req.method == Method.options) {return Response.ok(headers: Headers.build((mh) {mh['Access-Control-Allow-Origin'] = ['*'];mh['Access-Control-Allow-Methods'] = ['GET, POST, OPTIONS'];mh['Access-Control-Allow-Headers'] = ['Content-Type'];}),);}final result = await next(req);if (result is Response) {return result.copyWith(headers: result.headers.transform((mh) => mh['Access-Control-Allow-Origin'] = ['*'],),);}return result;};};}
Example: Error handling
Middleware errorHandlingMiddleware() {return (Handler next) {return (Request req) async {try {return await next(req);} catch (error) {return Response.internalServerError(body: Body.fromString('Something went wrong'),);}};};}
Example: Add response header
Middleware addHeaderMiddleware() {return (Handler next) {return (Request req) async {final result = await next(req);if (result is Response) {return result.copyWith(headers: result.headers.transform((mh) => mh['X-Custom-Header'] = ['Hello from middleware!'],),);}return result;};};}
Context properties
ContextProperty<T> provides type-safe, request-scoped storage. Values exist only for the duration of the request and do not leak between requests.
final requestIdProperty = ContextProperty<String>('requestId');// Set in middlewarerequestIdProperty[req] = 'req_${DateTime.now().millisecondsSinceEpoch}';// Read in handlerfinal id = requestIdProperty[req]; // String? -- null if not setfinal id = requestIdProperty.get(req); // String -- throws if missing
Extension pattern for clean access
final _userProperty = ContextProperty<User>('user');final _sessionProperty = ContextProperty<Session>('session');extension AuthContext on Request {User get currentUser => _userProperty.get(this);Session get session => _sessionProperty.get(this);}// In middleware: set values_userProperty[req] = authenticatedUser;// In handler: read valuesfinal user = req.currentUser;
Complete example
extension on Request {String get requestId => _requestIdProperty.get(this);}final _requestIdProperty = ContextProperty<String>('requestId');Handler requestIdMiddleware(Handler next) {return (req) async {_requestIdProperty[req] = 'req_${DateTime.now().millisecondsSinceEpoch}';return await next(req);};}Future<Response> handler(Request req) async {return Response.ok(body: Body.fromString('Request ID: ${req.requestId}'));}final app = RelicApp()..use('/', requestIdMiddleware)..get('/', handler);
Built-in context properties
Relic sets these automatically during routing:
request.pathParameters-- path params from matched routerequest.queryParameters-- typed query param accessorsrequest.router-- theRelicRouterthat routed the requestrequest.matchedPath/request.remainingPath-- consumed and remaining path portions
Pipeline (legacy)
Pipeline is a legacy composition pattern from Shelf. Prefer router.use() for new code. Pipeline runs middleware on all requests including 404s, while router.use() only runs on matched routes.