AppConfig
import 'package:gisila/gisila.dart';AppConfig
Section titled “AppConfig”Application-wide configuration, passed to GisilaApp. Every field is optional. The defaults give you text logging at info, an access log, security headers, a request id, and no authentication (every guarded route answers 401).
class AppConfig { AppConfig({ Logger? logger, LogLevel logLevel = LogLevel.info, LogFormat logFormat = LogFormat.text, bool logRequests = true, bool logRequestBody = false, bool logResponseBody = false, Authenticator authenticator = const NullAuthenticator(), PermissionResolver permissionResolver = const ClaimsPermissionResolver(), DatabaseProvider<Object?>? database, DbErrorMapper? dbErrorMapper, CacheProvider? cache, String requestIdHeader = 'X-Request-Id', CorsConfig? cors, SecurityHeadersConfig? securityHeaders = const SecurityHeadersConfig(), CompressionConfig? compression, EtagConfig? etags, RateLimitConfig? rateLimit, RateLimitStore? rateLimitStore, Set<String> trustedProxies = const {'127.0.0.1', '::1'}, RouteConfig defaultRouteConfig = RouteConfig.empty, ErrorHandler? errorHandler, String serverHeader = '', String? healthCheckPath, Future<bool> Function()? healthCheck, MetricsConfig? metrics, bool poweredByHeader = false, Duration? requestTimeout, int? maxRequestBodyBytes, Map<String, Object?> extra = const {}, });}When to use: pass one to every GisilaApp. Build it from environment variables in main(); gisila has no config-file loader of its own.
Logging
Section titled “Logging”| Field | Type | Default | Description |
|---|---|---|---|
logger |
Logger |
ConsoleLogger(level: logLevel, format: logFormat) |
Sink for framework and access logs. Also has a setter, so you can replace it after construction. When you pass one, logLevel and logFormat are ignored. |
logLevel |
LogLevel |
LogLevel.info |
Minimum level of the default console logger. |
logFormat |
LogFormat |
LogFormat.text |
text for humans, json for log shippers. |
logRequests |
bool |
true |
Install the access-log middleware (one line per request at info). |
logRequestBody |
bool |
false |
Include the request body (truncated to 2048 characters) in the access log. Can leak secrets; keep it off in production. |
logResponseBody |
bool |
false |
Include response bodies of known length (truncated) in the access log. Streams and server-sent events are never read. |
Authentication and authorization
Section titled “Authentication and authorization”| Field | Type | Default | Description |
|---|---|---|---|
authenticator |
Authenticator |
NullAuthenticator() |
Resolves a request to a Principal. Only called for routes that carry a guard. The default returns null, so guarded routes answer 401. |
permissionResolver |
PermissionResolver |
ClaimsPermissionResolver() |
Decides whether a principal holds a permission or role. The default reads Principal.permissions and Principal.roles. |
| Field | Type | Default | Description |
|---|---|---|---|
database |
DatabaseProvider<Object?>? |
null |
When set, every request gets a session from open() and close(ok: status < 500) afterwards; reach it with ctx.db<T>(). dispose() runs on app.stop(). |
dbErrorMapper |
DbErrorMapper? |
null |
Turns driver errors (unique violations and the like) into HttpExceptions before the generic 500. |
cache |
CacheProvider? |
null |
Connected on init(), disconnected on stop(), exposed as ctx.cache. |
HTTP behaviour
Section titled “HTTP behaviour”| Field | Type | Default | Description |
|---|---|---|---|
requestIdHeader |
String |
'X-Request-Id' |
Header read for an incoming request id and echoed on every response. |
cors |
CorsConfig? |
null |
Turns on the CORS middleware. Off by default. |
securityHeaders |
SecurityHeadersConfig? |
SecurityHeadersConfig() |
HSTS, nosniff, X-Frame-Options: DENY, Referrer-Policy: no-referrer and a locked-down CSP on non-HTML responses. On by default; null turns them all off. |
compression |
CompressionConfig? |
null |
gzip responses for clients that accept it. Leave off when a proxy already compresses. |
etags |
EtagConfig? |
null |
Add ETag to successful GET/HEAD responses and answer 304 to a matching If-None-Match. |
rateLimit |
RateLimitConfig? |
null |
App-wide rate limit, applied to every request before routing. |
rateLimitStore |
RateLimitStore? |
null |
Where app-wide and per-route limiters keep buckets. null gives each limiter its own in-memory store (per isolate). Pass a shared store such as RedisRateLimitStore from gisila_redis to enforce one limit across processes. |
trustedProxies |
Set<String> |
{'127.0.0.1', '::1'} |
IPs or CIDR ranges allowed to report the client address through X-Forwarded-For. Used by the rate limiter’s default key. Add your load balancer’s range when it is not on the same host. |
defaultRouteConfig |
RouteConfig |
RouteConfig.empty |
Baseline merged into every generated route before class- and method-level config. |
errorHandler |
ErrorHandler? |
null |
Renders exceptions that are not HttpExceptions, form errors or mapped database errors. When set, the framework does not log those exceptions itself. |
serverHeader |
String |
'' |
Value of the Server header. Empty sends none. |
poweredByHeader |
bool |
false |
Send X-Powered-By: gisila. |
requestTimeout |
Duration? |
null |
App-wide ceiling per request; slower requests get 504. A per-route RouteConfig.timeout applies in addition, so the shorter one wins. |
maxRequestBodyBytes |
int? |
null |
Reject bodies above this size with 413. null means unlimited, so set it in production. A per-route RouteConfig.maxBodyBytes applies in addition. |
Operations
Section titled “Operations”| Field | Type | Default | Description |
|---|---|---|---|
healthCheckPath |
String? |
null |
Path answered for GET before auth, routing, metrics and the database, such as '/healthz'. Returns {"status":"ok"}, or 503 {"status":"unavailable"} when healthCheck returns false or throws. |
healthCheck |
Future<bool> Function()? |
null |
Readiness check for healthCheckPath, for example a database ping. |
metrics |
MetricsConfig? |
null |
Record HTTP metrics in app.metrics and serve them in Prometheus format at MetricsConfig.path. |
extra |
Map<String, Object?> |
{} |
Free-form settings for your own middleware, readable through GisilaApp.maybeOf(request)!.config.extra. |
Example
import 'dart:io';
import 'package:gisila/gisila.dart';
AppConfig buildConfig() { final env = Platform.environment; final prod = env['APP_ENV'] == 'production'; return AppConfig( logLevel: prod ? LogLevel.info : LogLevel.debug, logFormat: prod ? LogFormat.json : LogFormat.text, authenticator: JwtAuthenticator( key: JwtKey.hmac(env['JWT_SECRET']!), algorithms: {'HS256'}, issuer: 'my-api', ), cors: CorsConfig(allowedOrigins: {env['FRONTEND_ORIGIN']!}, allowCredentials: true), rateLimit: const RateLimitConfig(requestsPerMinute: 600), defaultRouteConfig: const RouteConfig(timeout: Duration(seconds: 10)), maxRequestBodyBytes: 2 * 1024 * 1024, requestTimeout: const Duration(seconds: 30), healthCheckPath: '/healthz', compression: prod ? null : const CompressionConfig(), );}See also: Configuration concept, Middleware reference.
ErrorHandler
Section titled “ErrorHandler”typedef ErrorHandler = Response Function(Object error, StackTrace stack);Signature of AppConfig.errorHandler. It receives exceptions that are not HttpException, not FormValidationException and not mapped by dbErrorMapper.
When to use: to report unexpected errors to an error tracker, or to render them in a different envelope. The framework skips its own error log line when you set one, so log the error yourself.
final logger = ConsoleLogger(format: LogFormat.json);
final config = AppConfig( logger: logger, errorHandler: (error, stack) { logger.error('Unhandled exception', error: error, stackTrace: stack); return jsonResponse( statusCode: 500, body: InternalServerError().toJson(), ); },);See also: Errors reference, Handle errors.