Constructive GraphQL Server is an Express-based server built on PostGraphile. It reads Constructive metadata to select API schemas, applies RLS-aware auth, and exposes a production-ready GraphQL API.
Install the package:
pnpm add @constructive-io/graphql-server @constructive-io/graphql-envStart a server:
import { getEnvOptions } from '@constructive-io/graphql-env';
import { GraphQLServer } from '@constructive-io/graphql-server';
GraphQLServer(
getEnvOptions({
pg: { database: 'constructive_db' },
server: { host: '0.0.0.0', port: 3000 },
})
);Tip: Set
PGHOST,PGPORT,PGUSER,PGPASSWORD,PGDATABASEto control DB connectivity. See Configuration for the full list of supported env vars and defaults.
pnpm install
cd graphql/server
pnpm devThis starts the server with env defaults from @constructive-io/graphql-env.
Tip: Set
PGHOST,PGPORT,PGUSER,PGPASSWORD,PGDATABASEto control DB connectivity. See Configuration for the full list of supported env vars and defaults.
Runs an Express server that wires CORS, uploads, domain parsing, auth, and PostGraphile into a single GraphQL endpoint. It serves /graphql and /graphiql, injects per-request pgSettings, and flushes cached schemas on demand or via database notifications. When meta API is enabled, it resolves API config (schemas, roles, modules) from the meta schema using the request host and enforces api.isPublic, with optional header overrides in private mode; when meta API is disabled, it serves the fixed schemas and roles from api.exposedSchemas, api.anonRole, and api.roleName.
- Automatic GraphQL API generation from PostgreSQL schemas
- RLS-aware authentication and per-request
pgSettings - Meta-schema routing by domain + subdomain
- File uploads via
graphql-upload - GraphiQL and health check endpoints
- Schema cache flush via
/flushor database notifications - Opt-in observability for memory, DB activity, and Graphile build debugging
@constructive-io/graphql-server includes an opt-in observability mode for local debugging.
- Master switch:
GRAPHQL_OBSERVABILITY_ENABLED=true - Debug routes:
GET /debug/memory,GET /debug/db - Background sampler: periodic NDJSON snapshots under
graphql/server/logs/ - CLI helpers:
pnpm debug:memory:analyzepnpm debug:heap:capture
Observability only activates when all of the following are true:
GRAPHQL_OBSERVABILITY_ENABLED=trueNODE_ENV=development- the server is bound to a loopback host such as
localhost,127.0.0.1, or::1
When those conditions are not met, the debug routes are not mounted and the sampler does not start. This keeps the default runtime surface minimal and prevents the observability layer from being exposed remotely.
For the operational workflow, sampler output, and heap snapshot usage, see docs/memory-debugging.md.
GET /healthz-> health checkGET /graphiql-> GraphiQL UIGET /graphql/POST /graphql-> GraphQL endpointPOST /graphql(multipart) -> file uploadsPOST /flush-> clears cached Graphile schema for the current API; mounted only whenAPI_FLUSH_TOKENis set, and requiresAuthorization: Bearer $API_FLUSH_TOKEN. Without the variable the route is not served (404) — operators deploying a schema-cache flush must set it on the server and on every caller. TheLISTEN/NOTIFYinvalidation path is unaffected.GET /debug/memory-> memory/process/Graphile debug snapshot when observability is enabledGET /debug/db-> PostgreSQL activity/locks/pool debug snapshot when observability is enabled
This is a production-only server: every request is resolved through the scoped-routing plane. There is no static single-tenant mode and no flag to disable routing. For single-database local development without route resolution or a database id, use @constructive-io/graphql-dev-server.
- The server resolves the request host with a single
resolve_route()call against the compiled route bindings in the scoped routing schema (API_ROUTING_SCHEMA, defaultrouting_public), mapping host → tenant/api/database/role. - Only APIs where
api.is_publicmatchesAPI_IS_PUBLICare served. - In private mode (
API_IS_PUBLIC=false), you can override with headers:X-Api-Name+X-Database-IdX-Schemata+X-Database-IdX-Meta-Schema+X-Database-Id
- A resolved database id is always required. There is no default database, so a request that resolves without a database id is rejected (
NO_DATABASE_ID→ HTTP 500).
Configuration is merged from defaults, config files, and env vars via @constructive-io/graphql-env. See graphql/env/README.md for the full list and examples.
| Env var | Purpose | Default |
|---|---|---|
PGHOST |
Postgres host | localhost |
PGPORT |
Postgres port | 5432 |
PGUSER |
Postgres user | postgres |
PGPASSWORD |
Postgres password | password |
PGDATABASE |
Postgres database | postgres |
GRAPHILE_SCHEMA |
Comma-separated schemas to expose | empty |
FEATURES_SIMPLE_INFLECTION |
Enable simple inflection | true |
FEATURES_OPPOSITE_BASE_NAMES |
Enable opposite base names | true |
FEATURES_POSTGIS |
Enable PostGIS support | true |
API_ROUTING_SCHEMA |
Schema containing resolve_route() |
routing_public |
API_IS_PUBLIC |
Serve public APIs only | true |
API_EXPOSED_SCHEMAS |
Additional schemas to expose | empty |
API_META_SCHEMAS |
Meta schemas to query | routing_public,metaschema_public,metaschema_modules_public |
API_ANON_ROLE |
Anonymous role name | administrator |
API_ROLE_NAME |
Authenticated role name | administrator |
API_FLUSH_TOKEN |
Bearer token required by POST /flush; route is not mounted when unset |
empty |
API_INTROSPECTION_ROLE |
Role PostGraphile introspects as; unset means the pool's connecting role. Naming a role with fewer grants removes fields from the schema | empty |
GRAPHQL_OBSERVABILITY_ENABLED |
Master switch for debug routes and sampler | false |
GRAPHQL_DEBUG_SAMPLER_ENABLED |
Enables periodic NDJSON sampling when observability is on | true |
GRAPHQL_DEBUG_SAMPLER_INTERVAL_MS |
Sampler interval in milliseconds | 10000 |
GRAPHQL_DEBUG_SAMPLER_DIR |
Override output directory for sampler logs | graphql/server/logs |
Use supertest or your HTTP client of choice against /graphql. For RLS-aware tests, provide a Bearer token and ensure the API's auth function is available.
@constructive-io/graphql-env- env parsing + defaults for GraphQL@constructive-io/graphql-types- shared types and defaultsgraphile-settings- PostGraphile configurationgraphile-meta-schema- meta schema support