Generator Configuration
Configure Athena JS schema generation from PostgreSQL in direct or gateway mode.
This page documents how to generate typed schema contracts from PostgreSQL and where the generated files go.
For this to work end-to-end, three things must line up:
- discoverable config file
- valid provider mode
- output path/naming settings
CLI entrypoint
athena-js exposes a dedicated generator command:
athena-js generate
athena-js generate --dry-run
athena-js generate --config ./athena.config.tsOutput:
- in normal mode, writes files to disk using configured targets
- with
--dry-run, prints the file list only
Config discovery
loadGeneratorConfig() discovers the first file in this order when --config is not passed:
athena.config.tsathena.config.jsathena-js.config.tsathena-js.config.js.athena.config.ts.athena.config.js
When missing, CLI throws:
No generator config found in <cwd>. Expected one of: ...
Use --config with a relative or absolute path to avoid this in monorepos.
Config surface at a glance
export interface AthenaGeneratorConfig {
provider: GeneratorProviderConfig
output: GeneratorOutputConfig
naming?: Partial<GeneratorNamingConfig>
features?: Partial<GeneratorFeatureFlags>
experimental?: Partial<GeneratorExperimentalFlags>
}All nested sections are validated by normal TypeScript shape and then normalized with defaults.
defineGeneratorConfig helper
Use this helper to keep autocompletion and exactness in config files.
import { defineGeneratorConfig } from "@xylex-group/athena";
export default defineGeneratorConfig({
provider: {
kind: "postgres",
mode: "direct",
connectionString: process.env.DATABASE_URL!,
database: "app_db",
},
output: {
targets: {
model: "src/generated/{database_kebab}/{schema_kebab}/{model_kebab}.model.ts",
schema: "src/generated/{database_kebab}/{schema_kebab}/index.ts",
database: "src/generated/{database_kebab}/index.ts",
registry: "src/generated/index.ts",
},
},
});Provider modes
provider is one of two implemented PostgreSQL modes and one scaffolded future-mode contract.
Postgres direct mode
{
kind: "postgres",
mode: "direct",
connectionString: "postgres://user:pass@host:5432/db",
database: "app_db",
schemas: ["public"],
}Behavior:
- uses Node Postgres (
pg) catalog queries - includes primary keys, nullability, enums, and relations via direct SQL
- useful for local dev and CI jobs with direct DB access
Postgres gateway mode
{
kind: "postgres",
mode: "gateway",
gatewayUrl: "https://athena.example.com",
apiKey: process.env.ATHENA_API_KEY!,
database: "app_db",
schemas: ["public"],
backend: "athena",
}Behavior:
- executes catalog introspection over
POST /gateway/query - runs four SQL statements through Athena query path:
- columns
- enums
- primary keys
- foreign keys
- this mode is available without the experimental flag and can be used in restricted networks where DB socket access is blocked
Scylla mode (contract placeholder)
{
kind: "scylla",
mode: "direct",
contactPoints: ["127.0.0.1:9042"],
keyspace: "app",
datacenter: "eu-west-1",
}Current behavior:
- intentionally throws
Scylla introspection provider is not implemented yet - controlled by
experimental.scyllaProviderContracts(defaults totrue), so this config is accepted but generation will fail until implemented
Output contract
interface GeneratorOutputConfig {
targets: GeneratorOutputTargets;
placeholderMap: Record<string, string>;
}
interface GeneratorOutputTargets {
model: string;
schema: string;
database: string;
registry: string;
}Defaults
model:src/generated/{database_kebab}/{schema_kebab}/{model_kebab}.model.tsschema:src/generated/{database_kebab}/{schema_kebab}/index.tsdatabase:src/generated/{database_kebab}/index.tsregistry:src/generated/index.ts
Supported placeholders
The renderer resolves the following built-ins:
providerkinddatabase,database_camel,database_pascal,database_snake,database_kebabschema,schema_camel,schema_pascal,schema_snake,schema_kebabmodel,model_camel,model_pascal,model_snake,model_kebab
kind is always set to the artifact category in the generator runtime (model, schema, database, registry).
placeholderMap
You can add custom tokens that may reference built-ins and each other, up to an internal recursion depth of 8.
output: {
targets: {
model: "src/{namespace}/{schema_snake}/{model_snake}.ts",
},
placeholderMap: {
namespace: "{database_kebab}/{schema_kebab}",
},
}If a template references an unknown token, generation fails with:
Unknown placeholder token "<token>" in template "<template>"
Naming controls
interface GeneratorNamingConfig {
modelType: "preserve" | "camel" | "pascal" | "snake" | "kebab";
modelConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
schemaConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
databaseConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
registryConst: "preserve" | "camel" | "pascal" | "snake" | "kebab";
}Defaults:
modelType: "pascal"modelConst: "camel"schemaConst: "camel"databaseConst: "camel"registryConst: "camel"
Feature flags
interface GeneratorFeatureFlags {
emitRelations: boolean;
emitRegistry: boolean;
}Defaults:
emitRelations: trueemitRegistry: true
Disable registry emission in constrained workflows:
features: {
emitRegistry: false,
},Experimental flags
interface GeneratorExperimentalFlags {
postgresGatewayIntrospection: boolean;
scyllaProviderContracts: boolean;
}Defaults:
postgresGatewayIntrospection: falsescyllaProviderContracts: true
postgresGatewayIntrospection is currently retained for compatibility and does not gate supported behavior.
scyllaProviderContracts controls whether the Scylla config shape is allowed.
What generation emits
runSchemaGenerator() produces:
- normalized snapshot from the provider
- generated artifacts in memory
- written paths (when
--dry-runis not set)
Artifact types:
model: row/interfaces +defineModelschema:defineSchemaobjectdatabase:defineDatabaseobjectregistry:defineRegistryobject (features.emitRegistrymust be true)
The generator deduplicates output paths and throws if two artifacts collide.
Config examples by profile
Local development (direct DB)
import { defineGeneratorConfig } from "@xylex-group/athena";
export default defineGeneratorConfig({
provider: {
kind: "postgres",
mode: "direct",
connectionString: process.env.DATABASE_URL!,
database: "app_db",
schemas: ["public", "billing"],
},
output: {
targets: {
model: "src/generated/{database_kebab}/{schema_kebab}/{model_kebab}.model.ts",
schema: "src/generated/{database_kebab}/{schema_kebab}/index.ts",
database: "src/generated/{database_kebab}/index.ts",
registry: "src/generated/registry.ts",
},
placeholderMap: {
namespace: "{database_kebab}/{schema_kebab}",
},
},
naming: {
modelType: "pascal",
modelConst: "camel",
schemaConst: "snake",
},
});Gateway-only environment
export default {
provider: {
kind: "postgres",
mode: "gateway",
gatewayUrl: process.env.ATHENA_URL!,
apiKey: process.env.ATHENA_API_KEY!,
database: "app_db",
schemas: ["public", "billing"],
backend: "athena",
},
output: {
targets: {
model: "src/generated/{database_kebab}/{schema_kebab}/{model_kebab}.model.ts",
schema: "src/generated/{database_kebab}/{schema_kebab}/index.ts",
database: "src/generated/{database_kebab}/index.ts",
registry: "src/generated/index.ts",
},
placeholderMap: {},
},
};Troubleshooting
Config is not found
Symptom:
No generator config found in ... Expected one of: ...
Fix:
- move config to repo root
- run with
--config ./path/to/configfrom the expected cwd
Unknown placeholder token
Symptom:
Unknown placeholder token "<token>" in template "..."
Fix:
- check built-ins and custom entries in
placeholderMap - ensure no typos in token names
- avoid cyclic token references; resolution is bounded but should still fail fast if unresolved
Generated path collision
Symptom:
Generator output collision detected for path: ...
Fix:
- inspect
output.targetsandplaceholderMap - ensure each artifact maps to a unique path (example: avoid fixed names for all schemas)
Scylla config crashes
Symptom:
Scylla provider contracts are disabled...ornot implemented
Fix:
- enable placeholder contracts only if intended by setting
experimental.scyllaProviderContracts: true, and be ready to implement provider logic first
Gateway mode fails intermittently
Symptom:
- failed fetch calls to
/gateway/query
Fix:
- verify
gatewayUrlandapiKey - verify the gateway endpoint accepts
POST /gateway/query - run with verbose CLI logs around request payloads and inspect SQL text in
errorpayload
Pipeline integration tips
Use dry-run first in CI:
athena-js generate --config ./athena.config.ts --dry-runUseful checks:
- verify no
Unknown placeholder token - verify file count/paths are stable
- optionally diff generated output against repo baseline
A safe commit workflow:
- run
--dry-runfor PR check - run
generate - review generated files only
- keep generated artifacts deterministic across reruns
Programmatic API usage
The full config and pipeline APIs are also exposed from JS/TS:
loadGeneratorConfigfindGeneratorConfigPathnormalizeGeneratorConfigresolveGeneratorProvidergenerateArtifactsFromSnapshotrunSchemaGeneratorresolvePostgresColumnType
Use these to build custom scripts, schema verification steps, or local snapshot tests.