Shelf framework guardrails, patterns, and best practices for AI-assisted development.
Use when working with Shelf (Dart HTTP server) projects, or when the user mentions Shelf.
Provides middleware patterns, request handling, pipeline composition, and server guidelines.
Shelf framework guardrails, patterns, and best practices for AI-assisted development.
Use when working with Shelf (Dart HTTP server) projects, or when the user mentions Shelf.
Provides middleware patterns, request handling, pipeline composition, and server guidelines.
Parse query parameters defensively with int.tryParse and defaults
Read the body only once (it is a stream); do not call readAsString() twice
Use request.change(context:) for downstream data, never mutable globals
Set Content-Type on every response that has a body
Routing with shelf_router
final router = Router()
..get('/health', _health)
..get('/users', _listUsers)
..get('/users/<id>', _getUser)
..post('/users', _createUser)
..put('/users/<id>', _updateUser)
..delete('/users/<id>', _deleteUser);
// Mount sub-routers with path prefix
final root = Router()
..mount('/api/v1', apiRouter.call)
..mount('/ws', webSocketHandler);
Routing Rules
Use ..method('/path', handler) cascade syntax for readability
Mount sub-routers with ..mount('/prefix', router.call)
Path parameters use angle brackets: /<id>, /<slug>
Always version API routes: /api/v1/...
Group related routes in a single handler class
Cascade (Fallback Routing)
Cascade tries handlers in order until one returns a non-404 response.
import 'package:shelf/shelf.dart';
import 'package:shelf_static/shelf_static.dart';
final cascade = Cascade()
.add(apiRouter)
.add(createStaticHandler('public', defaultDocument: 'index.html'));
final handler = const Pipeline()
.addMiddleware(loggingMiddleware())
.addHandler(cascade.handler);
Cascade Rules
Place specific handlers (API) before generic handlers (static files)
Cascade treats 404 and 405 as "not handled" by default
Use statusCodes parameter to customize which codes trigger fallthrough
Useful for SPAs: API routes first, then static file handler as fallback
Custom Exception Types
class UnauthorizedException implements Exception {
final String message;
UnauthorizedException([this.message = 'Unauthorized']);
}
class NotFoundException implements Exception {
final String message;
NotFoundException(this.message);
}
class ValidationException implements Exception {
final String message;
final Map<String, List<String>> errors;
ValidationException(this.message, [this.errors = const {}]);
}
Exception Rules
Define domain exceptions that implement Exception (not Error)
Map exceptions to HTTP status codes in error-handling middleware only
Never throw generic Exception('message') -- use typed exceptions
Include structured error details (field-level validation errors)
Commands
# Development
dart run bin/server.dart # Start server
dart run --enable-vm-service bin/server.dart # With debugger# Code generation (freezed, json_serializable)
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch # Watch mode for codegen# Testing
dart test# Run all tests
dart testtest/handlers/ # Run specific directory
dart test --coverage # With coverage# Quality
dart format . # Format all files
dart analyze # Static analysis
dart fix --apply # Auto-fix lint issues# Build
dart compile exe bin/server.dart -o server # AOT compile to native binary
Best Practices Summary
Pipeline: Compose middleware with Pipeline; order matters (outermost runs first)
Handlers: Keep thin; delegate to services; one class per resource
Error Handling: Centralize in middleware; catch all exceptions; return structured JSON errors
Context: Pass data between middleware and handlers via request.change(context:)
Testing: Use shelf directly in tests (no HTTP server needed); mock services with mocktail
Security: Validate all inputs; use parameterized database queries; never log secrets
Performance: Compile to native executable for production; use streaming for large responses
Advanced Topics
For detailed middleware examples, WebSocket support, static files, authentication flows, rate limiting, and testing patterns, see: