Configuration
Maat separates environment input from application configuration. Read process
values with env() while building the configuration maps, then use config()
everywhere else:
final app = {'name': env('APP_NAME', 'Maat')};
final name = config('app.name');This gives your application one stable configuration repository after boot.
Environment Files
A generated application loads .env before evaluating files in config/:
APP_NAME=BlogAPP_ENV=localAPP_DEBUG=trueAPP_KEY=base64:...APP_PORT=8000Real process environment variables override values from .env. You can deploy
the same application artifact to several environments without editing files.
Never commit .env. It contains secrets and local values. Commit
.env.example with safe placeholders so every required key remains visible.
Read Environment Values
Use the helper that matches the type you need:
env('APP_NAME'); // String?env('MISSING', 'fallback');envBool('APP_DEBUG'); // boolenvInt('APP_PORT', 8000); // intenvBool() treats true, 1, yes, and on as true, ignoring case. Invalid
or missing integers use the default passed to envInt().
Call these helpers in configuration files. Application classes should read the
resolved value with config() instead of depending directly on the process
environment.
Configuration Files
Configuration files export ordinary Dart maps:
import 'package:maat/maat.dart';
final Map<String, dynamic> app = { 'name': env('APP_NAME', 'Maat'), 'env': env('APP_ENV', 'production'), 'debug': envBool('APP_DEBUG'), 'url': env('APP_URL', 'http://localhost:8000'), 'key': env('APP_KEY'),};config/config.dart collects every file under its top-level key:
import 'app.dart';import 'database.dart';
final Map<String, dynamic> appConfig = { 'app': app, 'database': database,};Maat does not discover files through reflection. A configuration file becomes
available only when it is added to appConfig.
Read Configuration
Use dot notation to read nested values:
config('app.name');config('app.debug');config('database.default');config('services.stripe.key');config('app.missing', 'fallback');The helper returns dynamic, so cast at the boundary when a class requires a
specific type:
final debug = config('app.debug', false) as bool;final appName = config('app.name', 'Maat') as String;The application also exposes its repository directly:
app.config.has('app.debug');app.config.get('app.name');app.config.set('app.name', 'Test Blog');set() changes the in-memory repository only. It does not edit .env or a
configuration file, which makes it useful for focused tests.
Add a Configuration File
Create config/services.dart:
import 'package:maat/maat.dart';
final Map<String, dynamic> services = { 'stripe': {'key': env('STRIPE_KEY')},};Then register it in config/config.dart:
import 'services.dart';
final Map<String, dynamic> appConfig = { // Existing entries... 'services': services,};The value is now available through config('services.stripe.key').
Generate the Application Key
The installer generates APP_KEY automatically. Generate a new key manually
when creating an environment from an existing checkout:
maat key:generateUse --show to print a key without editing .env:
maat key:generate --showDo not reuse production application keys in local or test environments.
Debug Mode
app.debug controls exception detail. In local development, a 500 response
may include the exception message and stack trace. In production, disable debug
mode so the same failure returns only a safe message:
{"message":"Server Error"}See Error Handling for custom reporting and rendering.
Determine the Environment
The application reads APP_ENV, defaulting to production:
app.environment;Maat also includes the environment in log lines, such as local.INFO or
production.ERROR, so logs from different deployments remain distinguishable.