Getting started
Configuration
Your app's settings live in the config folder, one TypeScript file per part of the framework, with the values that
differ per machine in .env.
Introduction#
Each file in config is a TypeScript module whose default export is an object. The file's name is the first part of
every key it holds: name in config/ is app.name. A new app has app, auth, cache, database,
filesystems, hashing, http, logging, mail, queue, services, session and view, and the Inertia kits
add inertia:
import { env } from '@orieljs/framework'export default { name: env('APP_NAME', 'Oriel'), env: env('APP_ENV', 'production'), debug: env('APP_DEBUG', false), url: env('APP_URL', 'http://localhost'), timezone: 'UTC', locale: env('APP_LOCALE', 'en'), cipher: 'AES-256-GCM', key: env('APP_KEY'), previous_keys: String(env('APP_PREVIOUS_KEYS', '')).split(',').filter(Boolean),}Oriel imports every .ts, .js and .mjs file directly in config when it creates your app, before any service
provider registers. A file is plain code, so it can compute a value, as previous_keys does, or build a path from
import.meta.dirname, as config/ does for the SQLite file. Each app gets its own copy of the values, so
a value you change in one test never reaches the next.
A file runs once per process. A change to it, or to .env, applies when the process starts again, which
node oriel serve --watch does for you.
The about command#
about shows an overview of your app: its name, the Oriel and Node versions, the environment, whether debug mode is
on, and its URL:
node oriel aboutLaravel's --only option and config:show command aren't in Oriel.
Environment configuration#
Some values differ between your machine and your servers: the database's password, the mail server, the debug mode.
They go in the .env file at the root of your app, and the config files read them with env(). A new app has a
.env.example file listing the variables it uses, and the installer copies it to .env for you.
If you work in a team, keep .env.example in your repository with placeholder values, so the others see which
variables your app needs.
Environment file security#
.env is ignored by git in a new app, and should stay out of your repository: each machine has its own, and its
secrets would be exposed to anyone who can read the repository. Laravel can encrypt an environment file so that it can
be committed (env:encrypt); Oriel can't, so give your servers their values through their environment or their own
.env.
Additional environment files#
When APP_ENV is set in the environment before your app starts, and a .env.<APP_ENV> file exists, Oriel loads that
file instead of .env, not as well: with APP_ENV=staging, a variable that is only in .env isn't set. When the
file doesn't exist, .env is loaded. APP_ENV inside .env doesn't choose a file, and there is no --env option.
Your tests run with APP_ENV=testing, so a .env.testing file, when you add one, replaces .env in your tests.
Environment variable types#
The values in .env are text. env() turns a few words into other values, whatever their case:
.env value | env() value |
|---|---|
true, (true) | true |
false, (false) | false |
empty, (empty) | '' |
null, (null) | null |
Nothing else is converted. A number stays text: with SESSION_LIFETIME=120 in .env, env('SESSION_LIFETIME', 120) is
the string '120', and only the default, used when the variable isn't set, is the number. Wrap the call where you need
a number, as a new app's config files do: Number(env('SESSION_LIFETIME', 120)). In the same way, debug mode is on only
for APP_DEBUG=true: APP_DEBUG=1 is the string '1'.
A value with spaces goes in double quotes:
APP_NAME="My Application"A value can use another variable, set earlier in the file or in the environment, as ${NAME}. Single quotes keep a
value as written on Node; Bun, which reads .env itself, replaces ${NAME} in single quotes too:
MAIL_FROM_NAME="${APP_NAME}"PASSWORD_HINT='${not a variable}'Retrieving environment configuration#
env() reads a variable, with a default for when it isn't set. The config files call it for most of their values:
const debug = env('APP_DEBUG', false)A function as the default is called only when the variable isn't set. The result is typed as the default's type, a
string, a boolean or null, since the variable could hold any of them.
Call env() in your config files, and read config() everywhere else. A config value can be changed while your app
runs, in a test for one, and every part of your app then reads the same value.
Determining the current environment#
The current environment is app.env in your config, which config/ reads from APP_ENV. A new app's .env
says local; without the variable, the environment is production. app().environment() returns it, and with names
it tells you whether it is one of them:
const environment = app().environment()if (app().environment('local')) { // The environment is local}if (app().environment('local', 'staging')) { // The environment is either local OR staging...}A name may use * as a wildcard: app().environment('stag*'). Where Laravel takes a list, pass the names as separate
arguments. The application has shortcuts too:
| Method | Is true when |
|---|---|
app().isLocal() | The environment is local |
app().isProduction() | The environment is production |
app().runningUnitTests() | The environment is testing, as in your tests |
app().runningInConsole() | The app was started by an oriel command, oriel serve included, or by your tests |
Since a variable set in the environment wins over .env, your server can set APP_ENV=production even when its
.env says local.
Accessing configuration values#
Read a value with config(), anywhere in your app. A key is the file's name, then the keys inside it, with dots. A
second argument is the default, returned when the key doesn't exist:
const timezone = config('app.timezone')// Retrieve a default value if the configuration value does not exist...const fallback = config('app.timezone', 'Asia/Seoul')A value is typed unknown, unless you give it a type, which TypeScript takes on trust, config<string>('app.name'), or
a default, whose type it takes: config('app.name', 'Oriel').
To set values while your app runs, pass an object to config(). A value you set lasts as long as the app:
config({ 'app.timezone': 'America/Chicago' })With no arguments, config() returns the configuration itself, with get(), set(), has(), push() and all().
The Config facade#
The Config facade reaches the same values:
const timezone = Config.get('app.timezone')Config.set('app.timezone', 'America/Chicago')if (Config.has('services.stripe')) { // ...}It also has getMany(), which reads several keys at once, push() and prepend(), which add a value at the end or
the start of a list, and typed getters. A typed getter returns the value as its type, or throws a TypeError when the
value isn't of that type:
const name = Config.string('app.name')const debug = Config.boolean('app.debug')const maxBodySize = Config.integer('http.max_body_size')const proxies = Config.array('http.trusted_proxies')float() takes any number, as JavaScript has one number type, and array() takes a list or an object, such as a
config section. Each typed getter takes a default for a missing key. Laravel's collection() getter isn't in Oriel.
A typed getter checks the value it reads: Config.integer() on a value read from .env as text throws "Configuration
value for key [session.lifetime] must be an integer, string given.". A new app's config files wrap their numeric
settings in Number(), as Laravel's cast them, so its own values read as numbers; do the same for a setting of yours.
Third-party services#
config/ holds the credentials of the services your app talks to, such as a payment gateway or a mail
provider, so your code and your packages find them in one place. A new app's file is empty. Add a section per
service, with its values from .env:
import { env } from '@orieljs/framework'export default { stripe: { key: env('STRIPE_KEY'), secret: env('STRIPE_SECRET') },}Read them as any other value: config('services.stripe.key'), or Config.string('services.stripe.key'), which throws
when the variable isn't set.
The HTTP settings#
config/ holds settings of the HTTP layer that Laravel sets in bootstrap/ or in PHP's own
configuration:
export default { trusted_proxies: [], csrf_except: [], redirect_guests_to: '/login', max_body_size: 8 * 1024 * 1024,}trusted_proxies: the proxies whoseX-Forwarded-*headers your app believes (see Configuring trusted proxies).csrf_except: the URIs the CSRF check skips (see Excluding URIs from CSRF protection).redirect_guests_to: where theauthmiddleware sends a guest (see Redirecting guests and signed-in users).max_body_size: the largest request body your app reads, in bytes, ornullfor no limit. A larger body gets a 413 (see Body size limits).
The limit is 8 MB, and it holds for every route, uploads included. A Live component accepts uploads of up to 12 MB,
so to take a file larger than 8 MB, raise max_body_size above the largest file you accept, with room for the rest of
the form:
export default { trusted_proxies: [], csrf_except: [], redirect_guests_to: '/login', max_body_size: 16 * 1024 * 1024,}With the default limit, a Live upload of 9 MB gets a 413, and the field shows "The photo failed to upload.", with your
property's name in place of photo.
Configuration caching#
Configuration publishing#
The framework's packages merge their defaults with your config files, so a key you leave out still has its value:
config/ has no max_json_depth, and config('http.max_json_depth') is 512. A package of your own does the
same with mergeConfigFrom(), in its service provider's register():
import { ServiceProvider } from '@orieljs/framework'export class CourierServiceProvider extends ServiceProvider { override register(): void { this.mergeConfigFrom({ channel: 'mail', timeout: 30, options: { retries: 3 } }, 'courier') }}The merge is shallow, as in Laravel: each top-level key of the app's config/ wins over the default, and an
object such as options is replaced whole, not merged key by key.
Debug mode#
debug in config/ decides how much an error response shows. It reads APP_DEBUG, which a new app's .env
sets to true: a server error then shows the debug page, with your code around the error and its stack.
Error handling describes what it shows.
The application key#
APP_KEY in .env is your app's secret key. It encrypts your cookies, your sessions when session.encrypt is on and
what you encrypt with Crypt, and it signs signed URLs, such as the email verification links, and the
state of your Live components. The installer sets it. To set a new one, run key:generate:
node oriel key:generateIt writes a random key for the cipher in config/, AES-256-GCM, into the APP_KEY= line of .env, as
base64: and the key's bytes. .env needs that line: without it, the command stops with "Unable to set application
key. No APP_KEY variable was found in the .env file.". --show prints a key and changes nothing. In production, the
command refuses to replace a key that is set, unless you pass --force.
A new key can't decrypt what the old one encrypted, so signed-in users are signed out. To keep reading the old values,
list the old keys in APP_PREVIOUS_KEYS, separated by commas: Oriel then decrypts with them too, and accepts the
signed URLs they signed, but encrypts and signs with the new key only. An app with no key fails the first time it
needs one, with "No application encryption key has been specified.".