@constructive-io/graphql-dev-server 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +149 -0
- package/esm/index.js +2 -0
- package/esm/preset.js +32 -0
- package/esm/server.js +61 -0
- package/esm/types.js +1 -0
- package/index.d.ts +4 -0
- package/index.js +7 -0
- package/package.json +66 -0
- package/preset.d.ts +21 -0
- package/preset.js +36 -0
- package/server.d.ts +11 -0
- package/server.js +68 -0
- package/types.d.ts +21 -0
- package/types.js +2 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dan Lynch <pyramation@gmail.com>
|
|
4
|
+
Copyright (c) 2025 Constructive <developers@constructive.io>
|
|
5
|
+
Copyright (c) 2020-present, Interweb, Inc.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# @constructive-io/graphql-dev-server
|
|
2
|
+
|
|
3
|
+
<p align="center" width="100%">
|
|
4
|
+
<img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center" width="100%">
|
|
8
|
+
<a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
|
|
9
|
+
<img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/constructive-io/constructive/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
**Constructive GraphQL Dev Server** is a pure-PostGraphile, single-tenant GraphQL server for local development and test harnesses. It points at one database, exposes the configured schemas, and runs every request as a single fixed Postgres role.
|
|
15
|
+
|
|
16
|
+
It is deliberately **not** the production server: there is no scoped-routing plane and no tenant `database_id`. Because of that, it only enables the Constructive plugins that do **not** require a `database_id`; plugins that resolve per-tenant config from a `database_id` are excluded.
|
|
17
|
+
|
|
18
|
+
> For production, use `@constructive-io/graphql-server`, which always resolves each request through the scoped routing plane (`resolve_route()`).
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { getEnvOptions } from '@constructive-io/graphql-env';
|
|
24
|
+
import { createDevServer } from '@constructive-io/graphql-dev-server';
|
|
25
|
+
|
|
26
|
+
const server = await createDevServer(
|
|
27
|
+
getEnvOptions({
|
|
28
|
+
pg: { database: 'my_db' },
|
|
29
|
+
api: { exposedSchemas: ['app_public'], roleName: 'administrator' }
|
|
30
|
+
}),
|
|
31
|
+
{ port: 5555 }
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
console.log(server.graphqlUrl); // http://127.0.0.1:5555/graphql
|
|
35
|
+
|
|
36
|
+
// later
|
|
37
|
+
await server.stop();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## What it does
|
|
41
|
+
|
|
42
|
+
- Builds a Constructive-style PostGraphile v5 preset via `createConstructivePreset()`, so all the standard presets (connection filters, search, PostGIS, ltree, direct uploads, meta-schema, inflection, type mappings, etc.) are baked in automatically.
|
|
43
|
+
- Serves the schemas in `api.exposedSchemas` from a single database.
|
|
44
|
+
- Runs every request as one fixed role (`api.roleName` → `api.anonRole` → `pg.user`). There is no per-request auth and no routing.
|
|
45
|
+
- Exposes `/healthz`, `/graphql`, and `/graphiql`.
|
|
46
|
+
|
|
47
|
+
## Plugins that are excluded
|
|
48
|
+
|
|
49
|
+
The following plugin groups resolve per-tenant configuration from a `database_id` and are therefore **disabled** in the dev server:
|
|
50
|
+
|
|
51
|
+
- **Presigned uploads / bucket provisioner** (`enablePresignedUploads: false`) — resolve per-tenant storage config keyed by `database_id`.
|
|
52
|
+
- **LLM** (`enableLlm: false`) — resolves per-tenant billing / inference-log config keyed by `database_id`.
|
|
53
|
+
|
|
54
|
+
Direct uploads stay enabled: they stream to a fixed, env-configured bucket and need no `database_id`.
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
Configuration is merged from defaults, config files, and env vars via `@constructive-io/graphql-env`. See `graphql/env/README.md` for the full list.
|
|
59
|
+
|
|
60
|
+
| Option | Source env var | Purpose |
|
|
61
|
+
| --------------------- | --------------------- | ------------------------------------------ |
|
|
62
|
+
| `pg.database` | `PGDATABASE` | Database to serve |
|
|
63
|
+
| `api.exposedSchemas` | `API_EXPOSED_SCHEMAS` | Schemas to expose |
|
|
64
|
+
| `api.roleName` | `API_ROLE_NAME` | Fixed Postgres role for every request |
|
|
65
|
+
| `api.anonRole` | `API_ANON_ROLE` | Fallback role if `roleName` is unset |
|
|
66
|
+
|
|
67
|
+
## API
|
|
68
|
+
|
|
69
|
+
- `createDevServer(opts?, serverOpts?)` → `Promise<DevServerInfo>` — starts an Express + PostGraphile server. `DevServerInfo` includes `url`, `graphqlUrl`, `port`, `host`, `httpServer`, `app`, and `stop()`.
|
|
70
|
+
- `buildDevPreset({ pool, schemas, role })` → `GraphileConfig.Preset` — the dev preset, if you want to embed it in your own server.
|
|
71
|
+
|
|
72
|
+
## Related Packages
|
|
73
|
+
|
|
74
|
+
- `@constructive-io/graphql-server` — production scoped-routing server
|
|
75
|
+
- `@constructive-io/graphql-env` — env parsing + defaults
|
|
76
|
+
- `graphile-settings` — PostGraphile configuration presets
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Education and Tutorials
|
|
81
|
+
|
|
82
|
+
1. 🚀 [Quickstart: Getting Up and Running](https://constructive.io/learn/quickstart)
|
|
83
|
+
Get started with modular databases in minutes. Install prerequisites and deploy your first module.
|
|
84
|
+
|
|
85
|
+
2. 📦 [Modular PostgreSQL Development with Database Packages](https://constructive.io/learn/modular-postgres)
|
|
86
|
+
Learn to organize PostgreSQL projects with pgpm workspaces and reusable database modules.
|
|
87
|
+
|
|
88
|
+
3. ✏️ [Authoring Database Changes](https://constructive.io/learn/authoring-database-changes)
|
|
89
|
+
Master the workflow for adding, organizing, and managing database changes with pgpm.
|
|
90
|
+
|
|
91
|
+
4. 🧪 [End-to-End PostgreSQL Testing with TypeScript](https://constructive.io/learn/e2e-postgres-testing)
|
|
92
|
+
Master end-to-end PostgreSQL testing with ephemeral databases, RLS testing, and CI/CD automation.
|
|
93
|
+
|
|
94
|
+
5. ⚡ [Supabase Testing](https://constructive.io/learn/supabase)
|
|
95
|
+
Use TypeScript-first tools to test Supabase projects with realistic RLS, policies, and auth contexts.
|
|
96
|
+
|
|
97
|
+
6. 💧 [Drizzle ORM Testing](https://constructive.io/learn/drizzle-testing)
|
|
98
|
+
Run full-stack tests with Drizzle ORM, including database setup, teardown, and RLS enforcement.
|
|
99
|
+
|
|
100
|
+
7. 🔧 [Troubleshooting](https://constructive.io/learn/troubleshooting)
|
|
101
|
+
Common issues and solutions for pgpm, PostgreSQL, and testing.
|
|
102
|
+
|
|
103
|
+
## Related Constructive Tooling
|
|
104
|
+
|
|
105
|
+
### 📦 Package Management
|
|
106
|
+
|
|
107
|
+
* [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
|
|
108
|
+
|
|
109
|
+
### 🧪 Testing
|
|
110
|
+
|
|
111
|
+
* [pgsql-test](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
|
|
112
|
+
* [pgsql-seed](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-seed): **🌱 PostgreSQL seeding utilities** for CSV, JSON, SQL data loading, and pgpm deployment.
|
|
113
|
+
* [supabase-test](https://github.com/constructive-io/constructive/tree/main/postgres/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
|
|
114
|
+
* [graphile-test](https://github.com/constructive-io/constructive/tree/main/graphile/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
|
|
115
|
+
* [pg-query-context](https://github.com/constructive-io/constructive/tree/main/postgres/pg-query-context): **🔒 Session context injection** to add session-local context (e.g., `SET LOCAL`) into queries—ideal for setting `role`, `jwt.claims`, and other session settings.
|
|
116
|
+
|
|
117
|
+
### 🧠 Parsing & AST
|
|
118
|
+
|
|
119
|
+
* [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): **🔄 SQL conversion engine** that interprets and converts PostgreSQL syntax.
|
|
120
|
+
* [libpg-query-node](https://www.npmjs.com/package/libpg-query): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
|
|
121
|
+
* [pg-proto-parser](https://www.npmjs.com/package/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
|
|
122
|
+
* [@pgsql/enums](https://www.npmjs.com/package/@pgsql/enums): **🏷️ TypeScript enums** for PostgreSQL AST for safe and ergonomic parsing logic.
|
|
123
|
+
* [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **📝 Type definitions** for PostgreSQL AST nodes in TypeScript.
|
|
124
|
+
* [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **🛠️ AST utilities** for constructing and transforming PostgreSQL syntax trees.
|
|
125
|
+
|
|
126
|
+
### 📚 Documentation & Skills
|
|
127
|
+
|
|
128
|
+
* [constructive-skills](https://github.com/constructive-io/constructive-skills): **📖 Platform documentation and AI agent skills** — feature catalog, blueprint reference, SDK guides (i18n, billing, limits, events, uploads, security, entities, search, AI), and deployment guides.
|
|
129
|
+
|
|
130
|
+
Install skills for AI coding agents:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# All platform skills (security, blueprints, codegen, billing, etc.)
|
|
134
|
+
npx skills add constructive-io/constructive-skills
|
|
135
|
+
|
|
136
|
+
# Individual repo skills (pgpm, testing, CLI, search, etc.)
|
|
137
|
+
npx skills add https://github.com/constructive-io/constructive --skill pgpm
|
|
138
|
+
npx skills add https://github.com/constructive-io/constructive --skill constructive-testing
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Credits
|
|
142
|
+
|
|
143
|
+
**🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**
|
|
144
|
+
|
|
145
|
+
## Disclaimer
|
|
146
|
+
|
|
147
|
+
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
|
|
148
|
+
|
|
149
|
+
No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
|
package/esm/index.js
ADDED
package/esm/preset.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createConstructivePreset, makePgService } from 'graphile-settings';
|
|
2
|
+
/**
|
|
3
|
+
* Build the dev-server PostGraphile v5 preset.
|
|
4
|
+
*
|
|
5
|
+
* This is a pure-PostGraphile surface over a single database: it exposes the
|
|
6
|
+
* configured schemas with a fixed role and no scoped-routing plane. Only
|
|
7
|
+
* plugins that do NOT depend on a tenant database id are enabled. The presigned
|
|
8
|
+
* upload / bucket-provisioner plugins resolve per-tenant storage config from a
|
|
9
|
+
* `database_id`, and the LLM plugin resolves per-tenant billing / inference-log
|
|
10
|
+
* config from a `database_id`, so all three are excluded here. Direct uploads
|
|
11
|
+
* stream to a fixed env-configured bucket and need no database id, so they stay.
|
|
12
|
+
* Nothing enabled here reads `jwt.claims.database_id`.
|
|
13
|
+
*/
|
|
14
|
+
export const buildDevPreset = ({ pool, schemas, role }) => ({
|
|
15
|
+
extends: [
|
|
16
|
+
createConstructivePreset({
|
|
17
|
+
enablePresignedUploads: false,
|
|
18
|
+
enableLlm: false
|
|
19
|
+
})
|
|
20
|
+
],
|
|
21
|
+
pgServices: [makePgService({ pool, schemas })],
|
|
22
|
+
grafserv: {
|
|
23
|
+
graphqlPath: '/graphql',
|
|
24
|
+
graphiqlPath: '/graphiql',
|
|
25
|
+
graphiql: true,
|
|
26
|
+
graphiqlOnGraphQLGET: false
|
|
27
|
+
},
|
|
28
|
+
grafast: {
|
|
29
|
+
explain: process.env.NODE_ENV === 'development',
|
|
30
|
+
context: () => ({ pgSettings: { role } })
|
|
31
|
+
}
|
|
32
|
+
});
|
package/esm/server.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getEnvOptions } from '@constructive-io/graphql-env';
|
|
2
|
+
import { Logger } from '@pgpmjs/logger';
|
|
3
|
+
import { cors, healthz, poweredBy } from '@pgpmjs/server-utils';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import { createGraphileInstance } from 'graphile-cache';
|
|
6
|
+
import { getPgPool } from 'pg-cache';
|
|
7
|
+
import { getPgEnvOptions } from 'pg-env';
|
|
8
|
+
import { buildDevPreset } from './preset';
|
|
9
|
+
const log = new Logger('dev-server');
|
|
10
|
+
/**
|
|
11
|
+
* Create a pure-PostGraphile single-tenant dev server.
|
|
12
|
+
*
|
|
13
|
+
* Unlike the production `@constructive-io/graphql-server`, this server has no
|
|
14
|
+
* scoped-routing plane and no database id: it points at one database, exposes
|
|
15
|
+
* the configured schemas, and runs every request as a single fixed role. It is
|
|
16
|
+
* intended for local development and test harnesses only — never production.
|
|
17
|
+
*/
|
|
18
|
+
export const createDevServer = async (rawOpts = {}, serverOpts = {}) => {
|
|
19
|
+
const opts = getEnvOptions(rawOpts);
|
|
20
|
+
// Bind to 127.0.0.1 by default to avoid IPv6/IPv4 mismatches with supertest.
|
|
21
|
+
const host = serverOpts.host ?? '127.0.0.1';
|
|
22
|
+
const port = serverOpts.port ?? 0;
|
|
23
|
+
const schemas = opts.api?.exposedSchemas ?? [];
|
|
24
|
+
const role = opts.api?.roleName ?? opts.api?.anonRole ?? opts.pg?.user ?? 'postgres';
|
|
25
|
+
log.info(`[dev-server] starting db=${opts.pg?.database} schemas=[${schemas.join(', ')}] role=${role}`);
|
|
26
|
+
const pool = getPgPool(getPgEnvOptions({ ...opts.pg }));
|
|
27
|
+
const preset = buildDevPreset({ pool, schemas, role });
|
|
28
|
+
const cacheKey = `dev:${opts.pg?.database ?? ''}:${schemas.join(',')}:${role}`;
|
|
29
|
+
const instance = await createGraphileInstance({
|
|
30
|
+
preset,
|
|
31
|
+
cacheKey
|
|
32
|
+
});
|
|
33
|
+
const app = express();
|
|
34
|
+
healthz(app);
|
|
35
|
+
cors(app, serverOpts.origin ?? opts.server?.origin);
|
|
36
|
+
app.use(poweredBy('constructive'));
|
|
37
|
+
app.use((req, res, next) => instance.handler(req, res, next));
|
|
38
|
+
const httpServer = await new Promise((resolve, reject) => {
|
|
39
|
+
const server = app.listen(port, host, () => resolve(server));
|
|
40
|
+
server.on('error', reject);
|
|
41
|
+
});
|
|
42
|
+
const actualPort = httpServer.address().port;
|
|
43
|
+
const stop = async () => {
|
|
44
|
+
await new Promise((resolve, reject) => {
|
|
45
|
+
httpServer.close((err) => (err ? reject(err) : resolve()));
|
|
46
|
+
});
|
|
47
|
+
if (instance.httpServer?.listening) {
|
|
48
|
+
await new Promise((resolve) => instance.httpServer.close(() => resolve()));
|
|
49
|
+
}
|
|
50
|
+
await instance.pgl?.release?.();
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
httpServer,
|
|
54
|
+
app,
|
|
55
|
+
url: `http://${host}:${actualPort}`,
|
|
56
|
+
graphqlUrl: `http://${host}:${actualPort}/graphql`,
|
|
57
|
+
port: actualPort,
|
|
58
|
+
host,
|
|
59
|
+
stop
|
|
60
|
+
};
|
|
61
|
+
};
|
package/esm/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildDevPreset = exports.createDevServer = void 0;
|
|
4
|
+
var server_1 = require("./server");
|
|
5
|
+
Object.defineProperty(exports, "createDevServer", { enumerable: true, get: function () { return server_1.createDevServer; } });
|
|
6
|
+
var preset_1 = require("./preset");
|
|
7
|
+
Object.defineProperty(exports, "buildDevPreset", { enumerable: true, get: function () { return preset_1.buildDevPreset; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@constructive-io/graphql-dev-server",
|
|
3
|
+
"version": "3.3.2",
|
|
4
|
+
"author": "Constructive <developers@constructive.io>",
|
|
5
|
+
"description": "Pure-PostGraphile single-tenant dev server (no scoped routing, no database-id plugins)",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"module": "esm/index.js",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"homepage": "https://github.com/constructive-io/constructive",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public",
|
|
13
|
+
"directory": "dist"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/constructive-io/constructive"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/constructive-io/constructive/issues"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "makage clean",
|
|
24
|
+
"prepack": "npm run build",
|
|
25
|
+
"build": "makage build",
|
|
26
|
+
"build:dev": "makage build --dev",
|
|
27
|
+
"lint": "eslint . --fix",
|
|
28
|
+
"test": "jest --passWithNoTests",
|
|
29
|
+
"test:watch": "jest --watch"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/express": "^5.0.6",
|
|
33
|
+
"@types/pg": "^8.20.0",
|
|
34
|
+
"makage": "^0.3.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@constructive-io/graphql-env": "^3.19.2",
|
|
38
|
+
"@constructive-io/graphql-types": "^3.18.2",
|
|
39
|
+
"@pgpmjs/logger": "^2.15.2",
|
|
40
|
+
"@pgpmjs/server-utils": "^3.16.2",
|
|
41
|
+
"express": "^5.2.1",
|
|
42
|
+
"grafast": "1.0.2",
|
|
43
|
+
"grafserv": "1.0.0",
|
|
44
|
+
"graphile-build": "5.0.2",
|
|
45
|
+
"graphile-build-pg": "5.0.2",
|
|
46
|
+
"graphile-cache": "^4.1.2",
|
|
47
|
+
"graphile-config": "1.0.1",
|
|
48
|
+
"graphile-settings": "^6.1.2",
|
|
49
|
+
"graphile-utils": "5.0.1",
|
|
50
|
+
"graphql": "16.13.0",
|
|
51
|
+
"pg": "^8.21.0",
|
|
52
|
+
"pg-cache": "^3.16.2",
|
|
53
|
+
"pg-env": "^1.19.2",
|
|
54
|
+
"pg-sql2": "5.0.1",
|
|
55
|
+
"postgraphile": "5.0.3"
|
|
56
|
+
},
|
|
57
|
+
"keywords": [
|
|
58
|
+
"graphql",
|
|
59
|
+
"graphile",
|
|
60
|
+
"postgraphile",
|
|
61
|
+
"constructive",
|
|
62
|
+
"dev-server",
|
|
63
|
+
"single-tenant"
|
|
64
|
+
],
|
|
65
|
+
"gitHead": "08741ce3a6897192c2dbc7d1da48b9f456ffb59f"
|
|
66
|
+
}
|
package/preset.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { GraphileConfig } from 'graphile-config';
|
|
2
|
+
import type { Pool } from 'pg';
|
|
3
|
+
export interface DevPresetInput {
|
|
4
|
+
pool: Pool;
|
|
5
|
+
schemas: string[];
|
|
6
|
+
/** Postgres role every request runs as (there is no per-request auth here). */
|
|
7
|
+
role: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Build the dev-server PostGraphile v5 preset.
|
|
11
|
+
*
|
|
12
|
+
* This is a pure-PostGraphile surface over a single database: it exposes the
|
|
13
|
+
* configured schemas with a fixed role and no scoped-routing plane. Only
|
|
14
|
+
* plugins that do NOT depend on a tenant database id are enabled. The presigned
|
|
15
|
+
* upload / bucket-provisioner plugins resolve per-tenant storage config from a
|
|
16
|
+
* `database_id`, and the LLM plugin resolves per-tenant billing / inference-log
|
|
17
|
+
* config from a `database_id`, so all three are excluded here. Direct uploads
|
|
18
|
+
* stream to a fixed env-configured bucket and need no database id, so they stay.
|
|
19
|
+
* Nothing enabled here reads `jwt.claims.database_id`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const buildDevPreset: ({ pool, schemas, role }: DevPresetInput) => GraphileConfig.Preset;
|
package/preset.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildDevPreset = void 0;
|
|
4
|
+
const graphile_settings_1 = require("graphile-settings");
|
|
5
|
+
/**
|
|
6
|
+
* Build the dev-server PostGraphile v5 preset.
|
|
7
|
+
*
|
|
8
|
+
* This is a pure-PostGraphile surface over a single database: it exposes the
|
|
9
|
+
* configured schemas with a fixed role and no scoped-routing plane. Only
|
|
10
|
+
* plugins that do NOT depend on a tenant database id are enabled. The presigned
|
|
11
|
+
* upload / bucket-provisioner plugins resolve per-tenant storage config from a
|
|
12
|
+
* `database_id`, and the LLM plugin resolves per-tenant billing / inference-log
|
|
13
|
+
* config from a `database_id`, so all three are excluded here. Direct uploads
|
|
14
|
+
* stream to a fixed env-configured bucket and need no database id, so they stay.
|
|
15
|
+
* Nothing enabled here reads `jwt.claims.database_id`.
|
|
16
|
+
*/
|
|
17
|
+
const buildDevPreset = ({ pool, schemas, role }) => ({
|
|
18
|
+
extends: [
|
|
19
|
+
(0, graphile_settings_1.createConstructivePreset)({
|
|
20
|
+
enablePresignedUploads: false,
|
|
21
|
+
enableLlm: false
|
|
22
|
+
})
|
|
23
|
+
],
|
|
24
|
+
pgServices: [(0, graphile_settings_1.makePgService)({ pool, schemas })],
|
|
25
|
+
grafserv: {
|
|
26
|
+
graphqlPath: '/graphql',
|
|
27
|
+
graphiqlPath: '/graphiql',
|
|
28
|
+
graphiql: true,
|
|
29
|
+
graphiqlOnGraphQLGET: false
|
|
30
|
+
},
|
|
31
|
+
grafast: {
|
|
32
|
+
explain: process.env.NODE_ENV === 'development',
|
|
33
|
+
context: () => ({ pgSettings: { role } })
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
exports.buildDevPreset = buildDevPreset;
|
package/server.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConstructiveOptions } from '@constructive-io/graphql-types';
|
|
2
|
+
import type { DevServerInfo, DevServerOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Create a pure-PostGraphile single-tenant dev server.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the production `@constructive-io/graphql-server`, this server has no
|
|
7
|
+
* scoped-routing plane and no database id: it points at one database, exposes
|
|
8
|
+
* the configured schemas, and runs every request as a single fixed role. It is
|
|
9
|
+
* intended for local development and test harnesses only — never production.
|
|
10
|
+
*/
|
|
11
|
+
export declare const createDevServer: (rawOpts?: ConstructiveOptions, serverOpts?: DevServerOptions) => Promise<DevServerInfo>;
|
package/server.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createDevServer = void 0;
|
|
7
|
+
const graphql_env_1 = require("@constructive-io/graphql-env");
|
|
8
|
+
const logger_1 = require("@pgpmjs/logger");
|
|
9
|
+
const server_utils_1 = require("@pgpmjs/server-utils");
|
|
10
|
+
const express_1 = __importDefault(require("express"));
|
|
11
|
+
const graphile_cache_1 = require("graphile-cache");
|
|
12
|
+
const pg_cache_1 = require("pg-cache");
|
|
13
|
+
const pg_env_1 = require("pg-env");
|
|
14
|
+
const preset_1 = require("./preset");
|
|
15
|
+
const log = new logger_1.Logger('dev-server');
|
|
16
|
+
/**
|
|
17
|
+
* Create a pure-PostGraphile single-tenant dev server.
|
|
18
|
+
*
|
|
19
|
+
* Unlike the production `@constructive-io/graphql-server`, this server has no
|
|
20
|
+
* scoped-routing plane and no database id: it points at one database, exposes
|
|
21
|
+
* the configured schemas, and runs every request as a single fixed role. It is
|
|
22
|
+
* intended for local development and test harnesses only — never production.
|
|
23
|
+
*/
|
|
24
|
+
const createDevServer = async (rawOpts = {}, serverOpts = {}) => {
|
|
25
|
+
const opts = (0, graphql_env_1.getEnvOptions)(rawOpts);
|
|
26
|
+
// Bind to 127.0.0.1 by default to avoid IPv6/IPv4 mismatches with supertest.
|
|
27
|
+
const host = serverOpts.host ?? '127.0.0.1';
|
|
28
|
+
const port = serverOpts.port ?? 0;
|
|
29
|
+
const schemas = opts.api?.exposedSchemas ?? [];
|
|
30
|
+
const role = opts.api?.roleName ?? opts.api?.anonRole ?? opts.pg?.user ?? 'postgres';
|
|
31
|
+
log.info(`[dev-server] starting db=${opts.pg?.database} schemas=[${schemas.join(', ')}] role=${role}`);
|
|
32
|
+
const pool = (0, pg_cache_1.getPgPool)((0, pg_env_1.getPgEnvOptions)({ ...opts.pg }));
|
|
33
|
+
const preset = (0, preset_1.buildDevPreset)({ pool, schemas, role });
|
|
34
|
+
const cacheKey = `dev:${opts.pg?.database ?? ''}:${schemas.join(',')}:${role}`;
|
|
35
|
+
const instance = await (0, graphile_cache_1.createGraphileInstance)({
|
|
36
|
+
preset,
|
|
37
|
+
cacheKey
|
|
38
|
+
});
|
|
39
|
+
const app = (0, express_1.default)();
|
|
40
|
+
(0, server_utils_1.healthz)(app);
|
|
41
|
+
(0, server_utils_1.cors)(app, serverOpts.origin ?? opts.server?.origin);
|
|
42
|
+
app.use((0, server_utils_1.poweredBy)('constructive'));
|
|
43
|
+
app.use((req, res, next) => instance.handler(req, res, next));
|
|
44
|
+
const httpServer = await new Promise((resolve, reject) => {
|
|
45
|
+
const server = app.listen(port, host, () => resolve(server));
|
|
46
|
+
server.on('error', reject);
|
|
47
|
+
});
|
|
48
|
+
const actualPort = httpServer.address().port;
|
|
49
|
+
const stop = async () => {
|
|
50
|
+
await new Promise((resolve, reject) => {
|
|
51
|
+
httpServer.close((err) => (err ? reject(err) : resolve()));
|
|
52
|
+
});
|
|
53
|
+
if (instance.httpServer?.listening) {
|
|
54
|
+
await new Promise((resolve) => instance.httpServer.close(() => resolve()));
|
|
55
|
+
}
|
|
56
|
+
await instance.pgl?.release?.();
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
httpServer,
|
|
60
|
+
app,
|
|
61
|
+
url: `http://${host}:${actualPort}`,
|
|
62
|
+
graphqlUrl: `http://${host}:${actualPort}/graphql`,
|
|
63
|
+
port: actualPort,
|
|
64
|
+
host,
|
|
65
|
+
stop
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
exports.createDevServer = createDevServer;
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Express } from 'express';
|
|
2
|
+
import type { Server as HttpServer } from 'http';
|
|
3
|
+
/** Options controlling how the dev server binds its HTTP listener. */
|
|
4
|
+
export interface DevServerOptions {
|
|
5
|
+
/** Host to bind. Defaults to 127.0.0.1. */
|
|
6
|
+
host?: string;
|
|
7
|
+
/** Port to bind. Defaults to 0 (OS-assigned). */
|
|
8
|
+
port?: number;
|
|
9
|
+
/** CORS origin. Defaults to permissive (reflect request origin). */
|
|
10
|
+
origin?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Handle returned by {@link createDevServer}. */
|
|
13
|
+
export interface DevServerInfo {
|
|
14
|
+
httpServer: HttpServer;
|
|
15
|
+
app: Express;
|
|
16
|
+
url: string;
|
|
17
|
+
graphqlUrl: string;
|
|
18
|
+
port: number;
|
|
19
|
+
host: string;
|
|
20
|
+
stop: () => Promise<void>;
|
|
21
|
+
}
|
package/types.js
ADDED