fletch 2.0.1
fletch: ^2.0.1 copied to clipboard
A fast, Express-inspired HTTP framework for Dart. Build production-ready REST APIs with built-in sessions, CORS, rate limiting, and middleware support.
Fletch
π¦ Package History Notice
This package was previously a jQuery-like library by Rob Kellett. As of version 2.0.0 (January 2025), it has been repurposed as an Express-inspired HTTP framework. If you're looking for the original jQuery-like library, please see version 0.3.0 or the original repository. Thank you to Rob for graciously transferring the package name!
An Express-inspired HTTP framework for Dart. It brings familiar routing,
middleware, and dependency-injection patterns to dart:io while remaining
lightweight and dependency-free beyond GetIt.
π Documentation | π Issues | π¬ Discussions
Why Fletch? #
If you're coming from Express.js or Node.js, Fletch will feel instantly familiar:
- β
Express-like API -
app.get(),app.post(), middleware, it's all here - β‘ Fast - Radix-tree routing, minimal overhead
- π Secure by default - HMAC-signed sessions, CORS, rate limiting built-in
- π― Production-ready - Graceful shutdown, request timeouts, error handling
- π§© Modular - Controllers, isolated containers, dependency injection
- π¦ Lightweight - Minimal dependencies, pure Dart
Features #
- Fast radix-tree router with support for path parameters and nested routers
- Middleware pipeline with global and per-route handlers
GetIt-powered dependency injection (supports async/lazy registrations)- Controller abstraction for modular route registration
- Optional isolated containers for mounting self-contained sub-apps
- Batteries-included middleware for CORS, rate limiting, and cookie parsing
Quick start #
dart pub add fletch
import 'dart:io';
import 'package:fletch/fletch.dart';
Future<void> main() async {
final app = Fletch();
app.use(app.cors(allowedOrigins: ['http://localhost:3000']));
app.get('/health', (req, res) => res.text('OK'));
final port = int.parse(Platform.environment['PORT'] ?? '8080');
await app.listen(port);
print('Listening on http://localhost:$port');
}
Routing essentials #
-
Use
app.get,app.post, etc. to register handlers. Supply optional middleware with themiddleware:argument. -
Controllers help organise routes:
class UsersController extends Controller { @override void registerRoutes(ControllerOptions options) { options.get('/', _list); options.get('/:id(\\d+)', _show); } } app.useController('/users', UsersController()); -
Throw one of the built-in
HttpErrortypes (NotFoundError,ValidationError, etc.) to short-circuit with a specific status code.
Working with dependencies #
The container is backed by GetIt. Register dependencies during startup and
retrieve them in handlers via request.container:
app.registerLazySingleton(() => Database(config));
app.get('/posts', (req, res) async {
final db = req.container.get<Database>();
final posts = await db.posts();
res.json({'data': posts});
});
Isolated modules #
IsolatedContainer lets you mount a self-contained sub-application that has its
own middleware, router, and DI scope while sharing the main server:
final admin = IsolatedContainer(prefix: '/admin');
admin.use((req, res, next) {
res.setHeader('X-Isolated', 'admin');
return next();
});
admin.get('/', (req, res) => res.text('Admin dashboard'));
admin.mount(app);
For integration testing or microservice setups you can host the isolated module by itself:
await admin.listen(9090); // optional
Example project #
See example/fletch_example.dart for
a full reference that demonstrates controllers, isolated modules, and common
middleware.
Error handling #
Install a global error handler to customise responses:
app.setErrorHandler((error, req, res) async {
if (error is ValidationError) {
res.json({'error': error.message, 'details': error.data},
statusCode: error.statusCode);
return;
}
res.setStatus(HttpStatus.internalServerError);
res.json({'error': 'Internal Server Error'});
});
Performance tips #
-
Deploy behind a reverse proxy (nginx, Caddy) that terminates TLS and handles static assets.
-
Reuse the same
Fletchinstance across isolates if you need more CPU headroomβeach isolate can callawait app.listen(port, address: ...)with a different binding. -
For load testing use tools like
wrkorhey`:wrk -t8 -c256 -d30s http://localhost:8080/healthTest both direct routes and isolated modules to compare overhead.
-
Request parsing currently buffers the entire body; set upstream limits (e.g. via load balancer) and prefer streaming uploads for very large payloads.
Documentation #
Full documentation is available at docs.fletch.mahawarkartikey.in.
Contributing #
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run
dart format .anddart analyze - Add tests for new features
- Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
File issues or feature requests in the repository issue tracker.
License #
MIT License.