Revali is a Dart framework for HTTP APIs. You write annotated controller classes; Revali generates the server code from them and runs it. Optional constructs generate more from the same annotations: a typed Dart client, an OpenAPI document, a Dockerfile.
At a glance#
| Requires | Dart SDK 3.8+ |
| Packages | revali_router (dependency), revali (dev dependency) |
| Your code | Controllers and apps in routes/; everything else in lib/ |
| Generated code | .revali/, never edited by hand |
| Run | dart run revali dev, serving at http://localhost:8080/api |
| Ship | dart run revali build |
import 'package:revali_router/revali_router.dart';
@Controller('users')
class UsersController {
const UsersController(this.users);
final UserService users;
@Get(':id')
Future<User> find(@Param() String id) => users.find(id);
@Post()
Future<User> create(@Body() User user) => users.create(user);
}
That controller serves GET /api/users/:id and POST /api/users, with the
path parameter and body parsed, validated and injected. UserService comes
from dependency injection.
How it works#
revali devorrevali buildanalyzes the files inroutes/.-
The built-in server construct generates routing code into
.revali/server/. Any other constructs you depend on generate their own output alongside it. -
The generated server runs on
revali_router. Indevit restarts whenever a file changes.
Conventions#
-
URLs are
/{prefix}/{controller path}/{method path}. The prefix defaults toapi. Method names are never part of the URL. -
Responses: return values are JSON wrapped as
{"data": ...}. ReturnStringContentor setresponse.bodyto send something else. -
Bad input: a missing or invalid bound parameter is answered with
400 Bad Requestbefore your method runs. -
Cross-cutting logic (auth, logging, error mapping) goes in
lifecycle components
in
lib/components/.
Start here#
Then, as you need them: app configuration,
testing, the server reference,
and, for systems with several services, messaging,
revali up and revali compose.