|
| 1 | +--- |
| 2 | +name: constructive-env |
| 3 | +description: Unified environment configuration for Constructive and PGPM projects — typed defaults, env var parsing, config file discovery, and hierarchical option merging. Use when asked to "configure environment", "set env vars", "manage defaults", "use getEnvOptions", "configure database connection", "set up server config", "configure SMTP", "configure CDN", "manage configuration hierarchy", or when working with @pgpmjs/env or @constructive-io/graphql-env. |
| 4 | +compatibility: Node.js 18+, @pgpmjs/env, @constructive-io/graphql-env |
| 5 | +metadata: |
| 6 | + author: constructive-io |
| 7 | + version: "1.0.0" |
| 8 | +--- |
| 9 | + |
| 10 | +# Constructive Environment Configuration |
| 11 | + |
| 12 | +Unified, type-safe environment configuration for all Constructive and PGPM projects. Two packages work together to provide a clean, hierarchical configuration system with sensible defaults. |
| 13 | + |
| 14 | +## When to Apply |
| 15 | + |
| 16 | +Use this skill when: |
| 17 | +- Configuring database connections, server settings, CDN, SMTP, or jobs programmatically |
| 18 | +- Understanding how environment variables map to typed configuration objects |
| 19 | +- Writing code that needs to read environment-aware configuration |
| 20 | +- Choosing between `@pgpmjs/env` and `@constructive-io/graphql-env` |
| 21 | +- Understanding the merge hierarchy (defaults → env vars → runtime overrides) |
| 22 | + |
| 23 | +## Architecture Overview |
| 24 | + |
| 25 | +Two packages form a layered configuration system: |
| 26 | + |
| 27 | +``` |
| 28 | +@pgpmjs/types → PgpmOptions interface + pgpmDefaults |
| 29 | + ↓ |
| 30 | +@pgpmjs/env → getEnvVars() + loadConfigSync() + getEnvOptions() |
| 31 | + ↓ (extends) |
| 32 | +@constructive-io/graphql-types → ConstructiveOptions interface + constructiveDefaults |
| 33 | + ↓ |
| 34 | +@constructive-io/graphql-env → getGraphQLEnvVars() + getEnvOptions() |
| 35 | +``` |
| 36 | + |
| 37 | +### Which Package to Import |
| 38 | + |
| 39 | +| You're working in... | Import from | |
| 40 | +|----------------------|-------------| |
| 41 | +| PGPM packages (`pgpm/*`) | `@pgpmjs/env` | |
| 42 | +| PostgreSQL tools (`postgres/*`) | `@pgpmjs/env` or `pg-env` | |
| 43 | +| GraphQL server, explorer, codegen | `@constructive-io/graphql-env` | |
| 44 | +| Constructive CLI commands | `@constructive-io/graphql-env` | |
| 45 | +| Job functions, SMTP | `@pgpmjs/env` | |
| 46 | +| Tests (pgsql-test based) | `@pgpmjs/env` (via `getConnEnvOptions`) | |
| 47 | + |
| 48 | +**Rule of thumb:** If your code needs GraphQL/Graphile/API options, use `@constructive-io/graphql-env`. Otherwise, use `@pgpmjs/env`. |
| 49 | + |
| 50 | +## Merge Hierarchy |
| 51 | + |
| 52 | +Options are merged in this order (later overrides earlier): |
| 53 | + |
| 54 | +1. **Defaults** — `pgpmDefaults` (+ `constructiveGraphqlDefaults` for GraphQL layer) |
| 55 | +2. **Config file** — `pgpm.json` or `pgpm.config.js` (rarely needed — see [references/config-file.md](references/config-file.md)) |
| 56 | +3. **Environment variables** — parsed from `process.env` |
| 57 | +4. **Runtime overrides** — passed programmatically to `getEnvOptions()` |
| 58 | + |
| 59 | +In practice, most projects only use layers 1, 3, and 4. Config files are a last resort for shared workspace settings. |
| 60 | + |
| 61 | +Arrays are **replaced**, not concatenated — the later source wins completely. |
| 62 | + |
| 63 | +## Quick Start |
| 64 | + |
| 65 | +### Core PGPM Options |
| 66 | + |
| 67 | +```typescript |
| 68 | +import { getEnvOptions } from '@pgpmjs/env'; |
| 69 | + |
| 70 | +// Get fully merged options (defaults + config + env + overrides) |
| 71 | +const opts = getEnvOptions(); |
| 72 | + |
| 73 | +// With runtime overrides |
| 74 | +const opts = getEnvOptions({ |
| 75 | + pg: { database: 'mydb' }, |
| 76 | + deployment: { fast: true } |
| 77 | +}); |
| 78 | + |
| 79 | +// With custom working directory and env object |
| 80 | +const opts = getEnvOptions({}, '/path/to/project', process.env); |
| 81 | +``` |
| 82 | + |
| 83 | +### Constructive Options (includes GraphQL) |
| 84 | + |
| 85 | +```typescript |
| 86 | +import { getEnvOptions } from '@constructive-io/graphql-env'; |
| 87 | + |
| 88 | +// Gets everything: PGPM defaults + GraphQL defaults + config + env + overrides |
| 89 | +const opts = getEnvOptions({ |
| 90 | + pg: { database: 'constructive' }, |
| 91 | + api: { isPublic: true } |
| 92 | +}); |
| 93 | +``` |
| 94 | + |
| 95 | +### Specialized Accessors |
| 96 | + |
| 97 | +```typescript |
| 98 | +import { getConnEnvOptions, getDeploymentEnvOptions } from '@pgpmjs/env'; |
| 99 | + |
| 100 | +// Database connection options with roles resolved |
| 101 | +const connOpts = getConnEnvOptions({ prefix: 'test-' }); |
| 102 | + |
| 103 | +// Deployment options only |
| 104 | +const deployOpts = getDeploymentEnvOptions({ fast: true }); |
| 105 | +``` |
| 106 | + |
| 107 | +## Type System |
| 108 | + |
| 109 | +### PgpmOptions (core) |
| 110 | + |
| 111 | +```typescript |
| 112 | +interface PgpmOptions { |
| 113 | + db?: Partial<PgTestConnectionOptions>; // DB config, roles, connections |
| 114 | + pg?: Partial<PgConfig>; // PostgreSQL connection (host, port, user, etc.) |
| 115 | + server?: ServerOptions; // HTTP server (host, port, trustProxy, origin) |
| 116 | + cdn?: CDNOptions; // S3/MinIO file storage |
| 117 | + deployment?: DeploymentOptions; // Migration deployment settings |
| 118 | + migrations?: MigrationOptions; // Code generation settings |
| 119 | + jobs?: JobsConfig; // Job worker/scheduler config |
| 120 | + errorOutput?: ErrorOutputOptions; // Error formatting |
| 121 | + smtp?: SmtpOptions; // Email configuration |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +### ConstructiveOptions (extends PgpmOptions) |
| 126 | + |
| 127 | +```typescript |
| 128 | +interface ConstructiveOptions extends PgpmOptions { |
| 129 | + graphile?: GraphileOptions; // PostGraphile schema config |
| 130 | + features?: GraphileFeatureOptions; // Feature flags (inflection, PostGIS) |
| 131 | + api?: ApiOptions; // API routing (public/admin, roles, schemas) |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +## Utility Functions |
| 136 | + |
| 137 | +### Env Var Parsers |
| 138 | + |
| 139 | +```typescript |
| 140 | +import { parseEnvBoolean, parseEnvNumber } from '@pgpmjs/env'; |
| 141 | + |
| 142 | +parseEnvBoolean('true'); // true |
| 143 | +parseEnvBoolean('1'); // true |
| 144 | +parseEnvBoolean('yes'); // true |
| 145 | +parseEnvBoolean('false'); // false |
| 146 | +parseEnvBoolean(undefined); // undefined |
| 147 | + |
| 148 | +parseEnvNumber('5432'); // 5432 |
| 149 | +parseEnvNumber('invalid'); // undefined |
| 150 | +``` |
| 151 | + |
| 152 | +### Node Environment |
| 153 | + |
| 154 | +```typescript |
| 155 | +import { getNodeEnv } from '@pgpmjs/env'; |
| 156 | + |
| 157 | +const env = getNodeEnv(); // 'development' | 'production' | 'test' |
| 158 | +``` |
| 159 | + |
| 160 | +## Anti-Patterns |
| 161 | + |
| 162 | +**Never do this:** |
| 163 | + |
| 164 | +```typescript |
| 165 | +// BAD: Manual env var access scattered throughout code |
| 166 | +const host = process.env.PGHOST || 'localhost'; |
| 167 | +const port = parseInt(process.env.PGPORT || '5432'); |
| 168 | +const usePublic = process.env.API_IS_PUBLIC === 'true'; |
| 169 | +``` |
| 170 | + |
| 171 | +**Do this instead:** |
| 172 | + |
| 173 | +```typescript |
| 174 | +// GOOD: Use the unified configuration system |
| 175 | +import { getEnvOptions } from '@pgpmjs/env'; |
| 176 | +const opts = getEnvOptions(); |
| 177 | +const { host, port } = opts.pg; |
| 178 | +``` |
| 179 | + |
| 180 | +```typescript |
| 181 | +// GOOD: For Constructive/GraphQL packages |
| 182 | +import { getEnvOptions } from '@constructive-io/graphql-env'; |
| 183 | +const opts = getEnvOptions(); |
| 184 | +const { isPublic } = opts.api; |
| 185 | +``` |
| 186 | + |
| 187 | +## Reference Guide |
| 188 | + |
| 189 | +| Reference | Topic | Consult When | |
| 190 | +|-----------|-------|--------------| |
| 191 | +| [references/env-vars.md](references/env-vars.md) | Source file locations for env vars and types | Finding which source file defines a specific env var or type | |
| 192 | +| [references/defaults.md](references/defaults.md) | Default values for all configuration | Understanding what values are used when nothing is overridden | |
| 193 | +| [references/config-file.md](references/config-file.md) | Config file reference (last resort) | Only when you specifically need a `pgpm.json` — this is rarely needed | |
| 194 | + |
| 195 | +## Cross-References |
| 196 | + |
| 197 | +- `pgpm` skill (`references/env.md`) — The `pgpm env` CLI command for shell-level env var management |
| 198 | +- `constructive-server-config` skill — Server-specific env vars and startup configuration |
| 199 | +- `constructive-deployment` skill — Deployment-specific configuration |
| 200 | +- `constructive-functions` skill — Using `parseEnvBoolean` in Knative functions |
0 commit comments