Skip to main content

Getting Started

Let's be honest - most configuration tools make you write a novel before you can generate a single file. Not here. You can start with just a few lines and grow from there.

The Absolute Minimum

Create an axogen.config.ts file:

import {defineConfig, env} from "@axonotes/axogen";

export default defineConfig({
targets: {
app: env({
path: "app/.env",
variables: {
NODE_ENV: "development",
PORT: 3000,
},
}),
},
});

Run it:

axogen generate

Boom. You've got an app/.env file. Not earth-shattering, but you're up and running.

Axogen Generate Output

Adding Environment Variables

Of course, hardcoded values aren't very useful. Let's make it read from your actual environment:

import {defineConfig, env, loadEnv} from "@axonotes/axogen";
import * as z from "zod";

const envVars = loadEnv(
z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string(),
})
);

export default defineConfig({
targets: {
app: env({
path: "app/.env",
variables: {
PORT: envVars.PORT,
DATABASE_URL: envVars.DATABASE_URL,
},
}),
},
});

Create a .env.axogen file with your values:

PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
Don't commit your secrets!

Add .env.axogen to your .gitignore file! It's just like any other .env file - you don't want to push your secrets to git.

Now when you run axogen generate, it validates your environment variables and generates the config. If DATABASE_URL is missing, it'll yell at you. No more silent failures.

Validation Error

Multiple Formats

Here's where it gets interesting. Need the same data in different formats? Easy:

import {defineConfig, env, json, loadEnv} from "@axonotes/axogen";
import * as z from "zod";

const envVars = loadEnv(
z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string(),
})
);

export default defineConfig({
targets: {
app: env({
path: "app/.env",
variables: {
PORT: envVars.PORT,
DATABASE_URL: envVars.DATABASE_URL,
},
}),
config: json({
path: "config.json",
variables: {
database: {
url: envVars.DATABASE_URL,
},
server: {
port: envVars.PORT,
},
},
}),
},
});

One source of truth, multiple outputs. Your backend reads the .env, your frontend build process reads the JSON. They're always in sync.

Adding Commands

Sometimes you want to run things after generating configs:

export default defineConfig({
targets: {
// ... your targets
},
commands: {
start: "npm start",
dev: "npm run dev",
},
});
axogen run start

CLI Reference

# Generate all targets
axogen generate

# Generate specific target
axogen generate --target app

# See what would be generated (without writing files)
axogen generate --dry-run

# Run commands
axogen run start

# Get help on any command
axogen run --help
axogen generate --help

What's Next?

This is the foundation. Start small, grow as needed. Check out:

Configuration management doesn't have to suck. Your environment variables can have types. Your scripts can be intelligent. Your deployments can be consistent.

That's the way.