Snapshot of CorentinTh/figue: 23★, TypeScript. Configuration management library, like convict but with modern validation libraries
Snapshot summary built from the project's own GitHub metadata — there's no written TopGit review yet. The page will update automatically when a full review is published.
WHY NO REVIEW YET
TopGit writes full reviews for the most-starred, most-requested repositories. This page is a snapshot until then — see the READ ME tab for the original README in full.
The modern way to handle and validate your application configuration with any standard-schema-compliant validation library.
Introduction
Figue is a modern configuration management library for Node.js. It is designed to be easy to use, flexible, it can used in any environment, and can be used with any standard-schema-compliant validation library, like zod or valibot.
Think of it as a modern version of convict but simpler, cross env and using battle tested validation libraries.
Features
Environment variables support
Validation with any standard-schema-compliant validation library
If, for some reason, you have multiple sources of environment variables, you can use the envSources key of the second argument of defineConfig to specify an array of sources:
You can specify multiple environment variable names for a single configuration field by providing an array of strings. Figue will use the first environment variable that is found in your environment sources.
This feature is particularly useful when:
Supporting multiple deployment environments with different environment variable naming conventions
Migrating from legacy environment variable names while maintaining backward compatibility
Providing fallback options for missing environment variables
const { config } = defineConfig(
{
port: {
doc: 'Application port to listen',
schema: z.coerce.number(),
default: 3000,
// Will use PORT if available, otherwise APP_PORT, otherwise SERVER_PORT
env: ['PORT', 'APP_PORT', 'SERVER_PORT'],
},
database: {
host: {
doc: 'Database host',
schema: z.string(),
default: 'localhost',
// Useful for supporting legacy environment variable names
env: ['DATABASE_HOST', 'DB_HOST', 'LEGACY_DB_URL'],
},
},
workerId: {
doc: 'Worker identifier',
schema: z.string().optional(),
// Or using some plateform specific environment variable
env: ['WORKER_ID', 'HEROKU_DYNO_ID', 'RENDER_INSTANCE_ID'],
},
},
{
envSource: process.env,
},
);
Some caveats:
If none of the specified environment variables are found, Figue will fall back to the default as expected when no env key is present.
If a variable is found but its value is nullish or falsy (like an empty string, or undefined), Figue will still consider it as set and use that value. If you want to ignore such values, you should handle that in your schema validation.
Ensure that the order of environment variables in the array reflects their priority, as Figue will use the first one it finds.
Get defaults
You can use the getDefaults key of the second argument of defineConfig to specify a function that will be called to get some defaults:
const { config } = defineConfig(
{
env: {
doc: 'Application current environment',
default: 'development',
schema: z.enum(['development', 'production', 'test']),
env: 'NODE_ENV',
},
port: {
doc: 'Application port to listen',
schema: z.coerce.number().int().positive(),
default: 3000,
env: 'PORT',
},
},
{
envSource: {
PORT: 3001,
},
// The config argument is build from the config definition defaults and the envSources
// Typically you will use it to override some defaults based the config
getDefaults: ({ config }) => ({
port: config.env === 'test' ? 4444 : config.port,
}),
},
);
You can also use the defaults property of the second argument of defineConfig to specify some static defaults (for example taken from a json file):
const { config } = defineConfig(
{
/* ... */
},
{
// Either an array of config partial...
defaults: [
{
port: 4444,
},
],
// ... or a single config partial
defaults: {
port: 4444,
},
},
);
Cross-field validation
Sometimes a single schema isn't enough — a field's validity may depend on another field's value. For these cases, add a validate function to any field. It runs after every per-field schema check has passed, and receives the fully-validated config:
import { defineConfig, validator } from 'figue';
import * as v from 'valibot';
type Config = {
adapters: { id: string; url: string }[];
defaultAdapterId: string;
};
const { config } = defineConfig({
adapters: {
doc: 'Available resource adapters',
schema: v.array(v.object({ id: v.string(), url: v.string() })),
default: [],
env: 'ADAPTERS',
},
defaultAdapterId: {
doc: 'Default adapter id (must reference one of `adapters[].id`)',
schema: v.string(),
default: '',
env: 'DEFAULT_ADAPTER_ID',
validate: validator<Config>(({ value, config }) => {
if (!config.adapters.some(a => a.id === value)) {
return `must match one of adapters[].id (got "${value}")`;
}
}),
},
});
Co-locating the rule with the field it belongs to keeps things readable, even when your config definition is split across modules.
Return contract — the function can either throw or return:
Return value
Meaning
void / undefined / true
passes
false
generic "Validation failed at <path>" issue
string
used as the issue message
{ message, path? }
a fully-formed issue (path defaults to the current field)
ValidateIssue[]
multiple issues from a single rule
throw new Error(msg)
caught; error.message becomes the issue
All issues from cross-field rules are aggregated into a single ConfigValidationError, just like schema errors.
Typing the config argument — by default config is loosely typed (any) so you don't have to declare anything. When you want full IntelliSense on the cross-referenced fields, wrap your function with the validator<Config>(...) helper as shown above. It's a no-op at runtime, purely a type hint.
Note — validate only runs once every field's schema has validated successfully, so you can trust that config is fully typed and coerced.
What's wrong with convict?
Convict is meant to be used in node based environnement, it needs to have access to global variables that may may not be present in some environnement (like process, global), and it also imports fs.
Figue?
Figue is the french for fig -> con-fig.
Development
Clone this repository
Install dependencies using pnpm install
Run interactive tests using pnpm dev
Credits
This project is crafted with ❤️ by Corentin Thomasset.
If you find this project helpful, please consider supporting my work.
No homepage URL was recorded for CorentinTh/figue in TopGit's last sync. The README tab above frequently contains screenshots and demo links, or check the repository description on GitHub.
How active is development on CorentinTh/figue?
The most recent commit recorded on CorentinTh/figue was 1 month ago, based on the GitHub push timestamp. The repository has 2 forks — one of the better signals of community interest.
Is CorentinTh/figue open source?
Yes — CorentinTh/figue ships under the MIT license, which makes its source code freely readable (and, depending on license terms, forkable and reusable). Source: github.com/CorentinTh/figue.
What license does CorentinTh/figue use?
CorentinTh/figue is released under the MIT license. Always verify the LICENSE file directly on GitHub for the authoritative terms — license strings can be edited out of sync with a project's actual stance.
What topics is CorentinTh/figue associated with?
GitHub's repository topics for CorentinTh/figue: "config", "configuration-management", "env", "environment-variables", "standard-schema", "valibot", "zod". TopGit's editorial category is open-source.
Where do I read more about CorentinTh/figue?
This TopGit page is a snapshot — the READ ME tab shows the project's own README content (links stripped, images preserved). The GitHub repository at github.com/CorentinTh/figue is the definitive source.