An app file sets where your server listens. Create one when the defaults (localhost:8080, prefix
api) aren't what you want, or when you need to register dependencies.
dart run revali create app # scaffolds an app file in routes/apps/
See revali create for more.
import 'package:revali_router/revali_router.dart';
@App()
final class MainApp extends AppConfig {
const MainApp()
: super(
host: 'localhost',
port: 8080,
prefix: 'api', // routes are served under /api
);
}
Revali finds the app from these rules:
- The file must be under
routes/. The convention isroutes/apps/. - The file must be named
app.dart,*_app.dartor*.app.dart. -
The class must be annotated with
@App()and extendAppConfig, imported frompackage:revali_router/revali_router.dart. -
The constructor may take an
Argsparameter. Nothing else is injected into it: the app is created before dependency injection is set up.
Host, Port and Prefix#
| Parameter | Default | Notes |
|---|---|---|
host |
required | Use localhost for local development and 0.0.0.0 in a container. |
port |
required | 0 picks a free port, which is useful in tests. |
prefix |
'api' |
Written without slashes:
'api'
or
'api/v1'
, never
'/api'
. A leading or trailing
/
throws when the server starts. Use
null
or
''
for no prefix.
|
workers |
1 |
See Worker Isolates. |
backlog |
0 |
The listen backlog. 0 uses the OS default. |
The prefix applies to every route except the health probes. A controller at
@Controller('users') is served at /api/users.
Reading Host and Port from the Environment#
In a deployment, the platform usually decides the port. AppConfig.fromEnv reads HOST
(default 0.0.0.0) and PORT (default 8080):
@App()
final class MainApp extends AppConfig {
MainApp() : super.fromEnv(); // not const: it reads the environment at runtime
}
revali up and revali compose pass each service its port through PORT. See
Environment Variables.
Trusted Proxy#
Behind a reverse proxy or load balancer, override trustedProxy. Then request.ip
and @Ip() report the client's address from the proxy headers instead of the proxy's own address:
@App()
final class MainApp extends AppConfig {
const MainApp() : super(host: '0.0.0.0', port: 8080);
@override
TrustedProxy get trustedProxy => const TrustedProxy(
headers: ['X-Forwarded-For'],
);
}
Keep the default (const TrustedProxy()) when clients connect to the server directly, so they can't spoof their IP through these headers. See
Client IP.
Flavors#
A project can define several apps, one per environment. Give each app a flavor and pick one at run time with
--flavor:
import 'package:revali_router/revali_router.dart';
@App(flavor: 'dev')
final class DevApp extends AppConfig {
const DevApp() : super(host: 'localhost', port: 8080);
@override
Future<void> configureDependencies(DI di) async {
di.registerLazySingleton<EmailService>(FakeEmailService.new);
}
}
import 'package:revali_router/revali_router.dart';
@App(flavor: 'prod')
final class ProdApp extends AppConfig {
ProdApp() : super.fromEnv();
@override
Future<void> configureDependencies(DI di) async {
di.registerLazySingleton<EmailService>(SmtpEmailService.new);
}
}
dart run revali dev --flavor dev
dart run revali build --flavor prod
Revali picks the app as follows:
- Only one server runs. The CLI selects a single app. Two apps can't serve different ports from one package.
-
Flavor names are case-sensitive.
devandDevare different flavors. -
Without
--flavor: if only one app exists, it runs. If there are several, the first app without a flavor runs. If every app has a flavor, generation fails withNo app found, did you forget pass the --flavor arg?and a list of the configured flavors. -
With
--flavorbut no matching app: generation fails withNo app found for flavor "<name>".
Put values that change between deployments of the same build, such as secrets and URLs, in environment variables rather than in flavors.