Skip to content

AppConfig

import 'package:gisila/gisila.dart';

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.

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.
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.
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.
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.

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.