Skip to content

$settings

import { $settings } from "dreamkit";

Define your settings and use them throughout the application (services, middlewares, APIs) with strong typing and validation.

In every application there comes a time when we want to record data outside the repository, either because it is private (e.g. passwords) and/or is very specific (e.g. connection parameters). Normally this is done using environment variables or a JSON file.

The problem with this method is that, in addition to not having strong typing in its declaration and use, there is no validation and there is no way to see all the available settings.

$settings solves all these problems and goes one step further.

In development mode, when creating settings, a JSON schema will automatically be generated so that your IDE auto-completes the available parameters based on all the settings created. Also, if the configuration file is modified, the settings will be reloaded.

The default settings path is {root}/settings.json, where root is the package directory, but this can be changed with the settingsPath option of dreamkitPlugin in the app.config.ts.

In production mode, it is possible to change the path from which the settings are loaded with the environment variable DK_SETTINGS_PATH.

If you want to get/set the settings params directly you can use SettingsHandler.


Definition

const $settings: {
name(value: string): typeof $settings;
params(value: object): typeof $settings;
optional(value?: boolean): typeof $settings;
defaults(cb: (params: object) => object): typeof $settings;
generate(cb: (unsafeParams: object) => object): typeof $settings;
create(): {
new (params: object): {
params: object;
};
};
};

name

You can have as many settings as you want, since each one of them will use the name option that you have assigned to save its data.

params

The settings parameters are defined with the params option and their values ​​are loaded and validated directly from the settings.json file.

If the data types of the settings do not match the defined ones, a fatal error will be generated which will prevent the application from starting, thus early detection of errors.

generator

The settings file will be automatically generated if the settings have the generator option, so you could initialize certain settings as random keys if these have not been defined already.

optional

Sometimes it may be necessary to have optional settings and we want the application to start even if the settings have not been defined. This can be done by enabling the optional option.

When someone consumes the settings if you leave them undefined, it will throw a local error. You can also consume the settings optionally with iocParam (see examples below).

create

Creates a strongly typed and validated class which allows receiving the configured parameters as the first argument.

This simple class can be instantiated directly if desired for testing or other purposes.

const AppSettings = $settings.name("app").params({ name: s.string() }).create();
const appSettings = new AppSettings({ name: "my-app" });
console.log(appSettings.params.name); // my-app

Examples

Basic usage

import { $api, $route, $settings, s } from "dreamkit";
import { createResource } from "solid-js";
export const AppSettings = $settings
.name("app")
.params({ name: s.string() })
// generates default values (optional)
.generate((input) => ({
...(!input.name && { name: "My App" }),
}))
.create();
const fetchAppSettings = $api.self({ AppSettings }).create(function () {
return this.appSettings.params;
});
export default $route
.path("/")
.api({ fetchAppSettings })
.create(({ api }) => {
const [data] = createResource(api.fetchAppSettings);
return <>{data.latest?.name}</>;
});

Optional

import {
$api,
$route,
$settings,
createAction,
iocParam,
s,
SettingsHandler,
} from "dreamkit";
export const MailSettings = $settings
.name("mail")
.params({ hostname: s.string(), username: s.string(), password: s.string() })
// With optional, MailSettings is not required for startup the application
.optional()
.create();
const sendMail = $api
.title("Send mail")
// MailSettings is required, so it will throw an error if it is not defined before of calling this action
.self({ MailSettings })
.create(function () {
console.log("Sending mail with settings:", this.mailSettings.params);
});
const trySendMail = $api
.title("Try send mail")
.self({ mailSettings: iocParam(MailSettings).optional() })
.create(function () {
if (!this.mailSettings) throw new Error("Mail settings is not defined");
console.log("Sending mail with settings:", this.mailSettings.params);
});
const initMailSettings = $api
.title("Init mail settings")
.self({ SettingsHandler })
.create(async function () {
await this.settingsHandler.set(MailSettings, {
hostname: "smtp.localhost",
username: "admin",
password: "admin",
});
});
export default $route
.path("/")
.api({ sendMail, trySendMail, initMailSettings })
.create(({ api }) => {
const sendMail = createAction(api.sendMail);
const trySendMail = createAction(api.trySendMail);
const initMailSettings = createAction(api.initMailSettings);
return (
<>
<p>
<button
onClick={sendMail}
children={
<>
{sendMail.title} ({sendMail.state})
</>
}
/>
</p>
<p>
<button
onClick={trySendMail}
children={
<>
{trySendMail.title} ({trySendMail.state})
</>
}
/>
</p>
<p>
{initMailSettings.state === "success" && <p>MailSettings ready</p>}
<button
onClick={initMailSettings}
children={initMailSettings.title}
/>
</p>
</>
);
});