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.