Typed, environment-aware configuration loading for TypeScript and Go.
Comfig loads a configuration file, resolves references to environment variables, files, and cloud
secret managers, then returns a typed value in your application's native types. TypeScript
validates with Zod; Go supports validation through WithValidator.
- Typed results — inferred from a Zod schema in TypeScript, from type parameters in Go.
- Environment-aware — reads
config/<environment>.json, where the environment comes from theenvorENVenvironment variable, falling back tolocal. - Committable — configuration files live in the repo, so onboarding doesn't require hand-copying
.envfiles, and secrets are never exposed in environment variables or the container environment. - References —
env://andfile://resolvers included,aws://andgcp://via adapters. - Pluggable — swap the source, the parser, or add custom resolvers.
- Expandable fields — inline values in development, secret references in production, with no code changes.
- Lightweight — the Go core has no third-party dependencies; the TypeScript core only depends on Zod.
TypeScript:
npm install @comfig/core zodGo (Go 1.26+):
go get github.com/philipjohanszon/comfig/go/comfigTypeScript:
import { Comfig, EnvResolver } from "@comfig/core"
import { z } from "zod"
const config = await new Comfig(z.object({
token: z.string(),
port: z.number(),
debug: z.boolean(),
}))
.useResolver(() => EnvResolver())
.load()Go:
package main
import (
"context"
"github.com/philipjohanszon/comfig/go/comfig"
)
type Config struct {
Token string `json:"token"`
Port int `json:"port"`
Debug bool `json:"debug"`
}
func loadConfig(ctx context.Context) (Config, error) {
return comfig.New[Config](
comfig.WithResolvers(func(context.Context, Config) ([]comfig.Resolver, error) {
return []comfig.Resolver{comfig.NewEnvResolver()}, nil
}),
).Load(ctx)
}Both read the same config/<environment>.json:
{
"token": "env://LOCAL_TOKEN",
"port": 3000,
"debug": true
}env://LOCAL_TOKEN is resolved from the LOCAL_TOKEN environment variable by the EnvResolver.
The environment defaults to local, but can be set with the env environment variable, falling
back to ENV. Setting it to prod, for example, loads config/prod.json.
For the full API, expandable values, and custom sources and resolvers, see the TypeScript guide and the Go guide.
TypeScript:
@comfig/core— configuration loading, validation, and built-in resolvers@comfig/aws-secrets-manager— AWS Secrets Manager resolver@comfig/gcp-secret-manager— Google Cloud Secret Manager resolver
Go:
github.com/philipjohanszon/comfig/go/comfig— configuration loading and built-in resolversgithub.com/philipjohanszon/comfig/go/comfig-aws— AWS Secrets Manager resolvergithub.com/philipjohanszon/comfig/go/comfig-gcp— Google Cloud Secret Manager resolver
env://NAMEreads the environment variableNAME.file://pathreads the contents ofpath.aws://secret,aws://secret@stage, andaws://secret#version-idread AWS Secrets Manager.gcp://secretandgcp://secret@versionread Google Cloud Secret Manager; the default version islatest.
A reference with an unregistered prefix is left unchanged, so you only install the resolvers you
use. Missing environment variables and duplicate resolver prefixes return errors. For aws://
references, encode a literal @ in the secret ID as %40 when selecting a version.
The adapters use their SDK's default credential chain unless you provide an existing client. Each adapter has a self-contained guide:
@comfig/aws-secrets-managerandcomfig-awsforaws://references@comfig/gcp-secret-managerandcomfig-gcpforgcp://references
The TypeScript packages use pnpm:
cd ts && pnpm install && pnpm build && pnpm testEach Go module has its own test suite:
cd go/comfig && go test ./...
cd ../comfig-aws && go test ./...
cd ../comfig-gcp && go test ./...MIT