@constructive-io/graphql-server 5.1.1 → 5.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/README.md +4 -10
- package/esm/middleware/api.js +18 -38
- package/esm/server.js +0 -4
- package/middleware/api.js +18 -38
- package/package.json +22 -22
- package/server.js +0 -4
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ This starts the server with env defaults from `@constructive-io/graphql-env`.
|
|
|
56
56
|
|
|
57
57
|
## What it does
|
|
58
58
|
|
|
59
|
-
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`,
|
|
59
|
+
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`.
|
|
60
60
|
|
|
61
61
|
## Key Features
|
|
62
62
|
|
|
@@ -101,7 +101,7 @@ For the operational workflow, sampler output, and heap snapshot usage, see [docs
|
|
|
101
101
|
|
|
102
102
|
## Scoped routing
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
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`](../dev-server/README.md).
|
|
105
105
|
|
|
106
106
|
- The server resolves the request host with a single `resolve_route()` call against the compiled route bindings in the scoped routing schema (`API_SCOPED_ROUTING_SCHEMA`, default `constructive_routing_public`), mapping host → tenant/api/database/role.
|
|
107
107
|
- Only APIs where `api.is_public` matches `API_IS_PUBLIC` are served.
|
|
@@ -109,11 +109,7 @@ When `API_ENABLE_SCOPED_ROUTING=true` (default):
|
|
|
109
109
|
- `X-Api-Name` + `X-Database-Id`
|
|
110
110
|
- `X-Schemata` + `X-Database-Id`
|
|
111
111
|
- `X-Meta-Schema` + `X-Database-Id`
|
|
112
|
-
|
|
113
|
-
When `API_ENABLE_SCOPED_ROUTING=false` (static single-tenant mode):
|
|
114
|
-
|
|
115
|
-
- The server skips route resolution and serves the fixed schemas in `API_EXPOSED_SCHEMAS`.
|
|
116
|
-
- Roles and database IDs come from `API_ANON_ROLE`, `API_ROLE_NAME`, and `API_DEFAULT_DATABASE_ID`.
|
|
112
|
+
- 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).
|
|
117
113
|
|
|
118
114
|
## Configuration
|
|
119
115
|
|
|
@@ -130,14 +126,12 @@ Configuration is merged from defaults, config files, and env vars via `@construc
|
|
|
130
126
|
| `FEATURES_SIMPLE_INFLECTION` | Enable simple inflection | `true` |
|
|
131
127
|
| `FEATURES_OPPOSITE_BASE_NAMES` | Enable opposite base names | `true` |
|
|
132
128
|
| `FEATURES_POSTGIS` | Enable PostGIS support | `true` |
|
|
133
|
-
| `API_ENABLE_SCOPED_ROUTING` | Enable scoped routing | `true` |
|
|
134
129
|
| `API_SCOPED_ROUTING_SCHEMA` | Schema containing `resolve_route()` | `constructive_routing_public` |
|
|
135
130
|
| `API_IS_PUBLIC` | Serve public APIs only | `true` |
|
|
136
|
-
| `API_EXPOSED_SCHEMAS` |
|
|
131
|
+
| `API_EXPOSED_SCHEMAS` | Additional schemas to expose | empty |
|
|
137
132
|
| `API_META_SCHEMAS` | Meta schemas to query | `constructive_routing_public,metaschema_public,metaschema_modules_public` |
|
|
138
133
|
| `API_ANON_ROLE` | Anonymous role name | `administrator` |
|
|
139
134
|
| `API_ROLE_NAME` | Authenticated role name | `administrator` |
|
|
140
|
-
| `API_DEFAULT_DATABASE_ID` | Default database ID | unset |
|
|
141
135
|
| `GRAPHQL_OBSERVABILITY_ENABLED` | Master switch for debug routes and sampler | `false` |
|
|
142
136
|
| `GRAPHQL_DEBUG_SAMPLER_ENABLED` | Enables periodic NDJSON sampling when observability is on | `true` |
|
|
143
137
|
| `GRAPHQL_DEBUG_SAMPLER_INTERVAL_MS` | Sampler interval in milliseconds | `10000` |
|
package/esm/middleware/api.js
CHANGED
|
@@ -72,6 +72,18 @@ const resolveModuleSettings = async (registry, ctx) => {
|
|
|
72
72
|
// Helpers
|
|
73
73
|
// =============================================================================
|
|
74
74
|
const isApiError = (result) => !!result && typeof result.errorHtml === 'string';
|
|
75
|
+
/**
|
|
76
|
+
* Every resolved API surface must carry a database id — there is no default
|
|
77
|
+
* database. A resolved structure without one is a misconfiguration; fail loud
|
|
78
|
+
* rather than silently proceeding with an undefined tenant.
|
|
79
|
+
*/
|
|
80
|
+
const assertDatabaseId = (result) => {
|
|
81
|
+
if (!result.databaseId) {
|
|
82
|
+
const error = new Error('No database id resolved for this request. A database id is required; there is no default database.');
|
|
83
|
+
error.code = 'NO_DATABASE_ID';
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
75
87
|
const parseCommaSeparatedHeader = (value) => value.split(',').map((s) => s.trim()).filter(Boolean);
|
|
76
88
|
const getPrivateHeaderMode = (headers) => {
|
|
77
89
|
if (headers.apiName)
|
|
@@ -161,28 +173,11 @@ const queryByApiName = async (pool, databaseId, name, isPublic) => {
|
|
|
161
173
|
// =============================================================================
|
|
162
174
|
const determineMode = (ctx) => {
|
|
163
175
|
const { opts, headers } = ctx;
|
|
164
|
-
// Static single-tenant mode: scoped routing off — expose configured schemas
|
|
165
|
-
// directly with no route resolution.
|
|
166
|
-
if (!opts.api?.enableScopedRouting)
|
|
167
|
-
return 'static';
|
|
168
176
|
if (opts.api?.isPublic === false) {
|
|
169
177
|
return getPrivateHeaderMode(headers) ?? 'scoped-route';
|
|
170
178
|
}
|
|
171
179
|
return 'scoped-route';
|
|
172
180
|
};
|
|
173
|
-
const resolveStatic = (ctx) => {
|
|
174
|
-
const { opts } = ctx;
|
|
175
|
-
return {
|
|
176
|
-
dbname: opts.pg?.database ?? '',
|
|
177
|
-
anonRole: opts.api?.anonRole ?? '',
|
|
178
|
-
roleName: opts.api?.roleName ?? '',
|
|
179
|
-
schema: opts.api?.exposedSchemas ?? [],
|
|
180
|
-
apiModules: [],
|
|
181
|
-
domains: [],
|
|
182
|
-
databaseId: opts.api?.defaultDatabaseId,
|
|
183
|
-
isPublic: false
|
|
184
|
-
};
|
|
185
|
-
};
|
|
186
181
|
const resolveSchemataHeader = async (ctx, validatedSchemas) => {
|
|
187
182
|
const { opts, headers } = ctx;
|
|
188
183
|
const headerSchemas = parseCommaSeparatedHeader(headers.schemata);
|
|
@@ -221,8 +216,6 @@ const resolveMetaSchemaHeader = (ctx, validatedSchemas) => {
|
|
|
221
216
|
*/
|
|
222
217
|
const resolveScopedRoute = async (ctx) => {
|
|
223
218
|
const { opts, pool, host } = ctx;
|
|
224
|
-
if (!opts.api?.enableScopedRouting)
|
|
225
|
-
return null;
|
|
226
219
|
const schema = opts.api?.scopedRoutingSchema || 'constructive_routing_public';
|
|
227
220
|
const route = await resolveRoute(pool, schema, host);
|
|
228
221
|
if (!route)
|
|
@@ -295,9 +288,6 @@ export const getApiConfig = async (opts, req) => {
|
|
|
295
288
|
const mode = determineMode(ctx);
|
|
296
289
|
let result;
|
|
297
290
|
switch (mode) {
|
|
298
|
-
case 'static':
|
|
299
|
-
result = resolveStatic(ctx);
|
|
300
|
-
break;
|
|
301
291
|
case 'schemata-header':
|
|
302
292
|
result = await resolveSchemataHeader(ctx, validatedSchemas);
|
|
303
293
|
break;
|
|
@@ -313,6 +303,7 @@ export const getApiConfig = async (opts, req) => {
|
|
|
313
303
|
}
|
|
314
304
|
// Cache successful results
|
|
315
305
|
if (result && !isApiError(result)) {
|
|
306
|
+
assertDatabaseId(result);
|
|
316
307
|
svcCache.set(cacheKey, result);
|
|
317
308
|
}
|
|
318
309
|
return result;
|
|
@@ -323,22 +314,6 @@ export const getApiConfig = async (opts, req) => {
|
|
|
323
314
|
export const createApiMiddleware = (opts) => {
|
|
324
315
|
return async (req, res, next) => {
|
|
325
316
|
log.debug(`[api-middleware] ${req.method} ${req.path}`);
|
|
326
|
-
// Fast path: static single-tenant mode (scoped routing disabled) — no
|
|
327
|
-
// route resolution, expose the configured schemas directly.
|
|
328
|
-
if (!opts.api?.enableScopedRouting) {
|
|
329
|
-
req.api = resolveStatic({
|
|
330
|
-
opts,
|
|
331
|
-
pool: null,
|
|
332
|
-
domain: '',
|
|
333
|
-
subdomain: null,
|
|
334
|
-
cacheKey: 'static',
|
|
335
|
-
headers: {},
|
|
336
|
-
host: ''
|
|
337
|
-
});
|
|
338
|
-
req.databaseId = req.api.databaseId;
|
|
339
|
-
req.svc_key = 'static';
|
|
340
|
-
return next();
|
|
341
|
-
}
|
|
342
317
|
try {
|
|
343
318
|
const apiConfig = await getApiConfig(opts, req);
|
|
344
319
|
if (isApiError(apiConfig)) {
|
|
@@ -360,6 +335,11 @@ export const createApiMiddleware = (opts) => {
|
|
|
360
335
|
res.status(404).send(errorPage404Message(err.message));
|
|
361
336
|
return;
|
|
362
337
|
}
|
|
338
|
+
if (err.code === 'NO_DATABASE_ID') {
|
|
339
|
+
log.error('[api-middleware] no database id resolved:', err.message);
|
|
340
|
+
res.status(500).send(errorPage50x);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
363
343
|
if (err.message?.includes('does not exist')) {
|
|
364
344
|
res.status(404).send(errorPage404Message("The resource you're looking for does not exist."));
|
|
365
345
|
return;
|
package/esm/server.js
CHANGED
|
@@ -85,12 +85,8 @@ class Server {
|
|
|
85
85
|
serverHost: effectiveOpts.server?.host,
|
|
86
86
|
serverPort: effectiveOpts.server?.port,
|
|
87
87
|
apiIsPublic: apiOpts.isPublic,
|
|
88
|
-
enableScopedRouting: apiOpts.enableScopedRouting,
|
|
89
88
|
scopedRoutingSchema: apiOpts.scopedRoutingSchema,
|
|
90
89
|
metaSchemas: apiOpts.metaSchemas?.join(',') || 'default',
|
|
91
|
-
exposedSchemas: apiOpts.exposedSchemas?.join(',') || 'none',
|
|
92
|
-
anonRole: apiOpts.anonRole,
|
|
93
|
-
roleName: apiOpts.roleName,
|
|
94
90
|
observabilityEnabled
|
|
95
91
|
});
|
|
96
92
|
if (observabilityRequested && !observabilityEnabled) {
|
package/middleware/api.js
CHANGED
|
@@ -78,6 +78,18 @@ const resolveModuleSettings = async (registry, ctx) => {
|
|
|
78
78
|
// Helpers
|
|
79
79
|
// =============================================================================
|
|
80
80
|
const isApiError = (result) => !!result && typeof result.errorHtml === 'string';
|
|
81
|
+
/**
|
|
82
|
+
* Every resolved API surface must carry a database id — there is no default
|
|
83
|
+
* database. A resolved structure without one is a misconfiguration; fail loud
|
|
84
|
+
* rather than silently proceeding with an undefined tenant.
|
|
85
|
+
*/
|
|
86
|
+
const assertDatabaseId = (result) => {
|
|
87
|
+
if (!result.databaseId) {
|
|
88
|
+
const error = new Error('No database id resolved for this request. A database id is required; there is no default database.');
|
|
89
|
+
error.code = 'NO_DATABASE_ID';
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
81
93
|
const parseCommaSeparatedHeader = (value) => value.split(',').map((s) => s.trim()).filter(Boolean);
|
|
82
94
|
const getPrivateHeaderMode = (headers) => {
|
|
83
95
|
if (headers.apiName)
|
|
@@ -169,28 +181,11 @@ const queryByApiName = async (pool, databaseId, name, isPublic) => {
|
|
|
169
181
|
// =============================================================================
|
|
170
182
|
const determineMode = (ctx) => {
|
|
171
183
|
const { opts, headers } = ctx;
|
|
172
|
-
// Static single-tenant mode: scoped routing off — expose configured schemas
|
|
173
|
-
// directly with no route resolution.
|
|
174
|
-
if (!opts.api?.enableScopedRouting)
|
|
175
|
-
return 'static';
|
|
176
184
|
if (opts.api?.isPublic === false) {
|
|
177
185
|
return getPrivateHeaderMode(headers) ?? 'scoped-route';
|
|
178
186
|
}
|
|
179
187
|
return 'scoped-route';
|
|
180
188
|
};
|
|
181
|
-
const resolveStatic = (ctx) => {
|
|
182
|
-
const { opts } = ctx;
|
|
183
|
-
return {
|
|
184
|
-
dbname: opts.pg?.database ?? '',
|
|
185
|
-
anonRole: opts.api?.anonRole ?? '',
|
|
186
|
-
roleName: opts.api?.roleName ?? '',
|
|
187
|
-
schema: opts.api?.exposedSchemas ?? [],
|
|
188
|
-
apiModules: [],
|
|
189
|
-
domains: [],
|
|
190
|
-
databaseId: opts.api?.defaultDatabaseId,
|
|
191
|
-
isPublic: false
|
|
192
|
-
};
|
|
193
|
-
};
|
|
194
189
|
const resolveSchemataHeader = async (ctx, validatedSchemas) => {
|
|
195
190
|
const { opts, headers } = ctx;
|
|
196
191
|
const headerSchemas = parseCommaSeparatedHeader(headers.schemata);
|
|
@@ -229,8 +224,6 @@ const resolveMetaSchemaHeader = (ctx, validatedSchemas) => {
|
|
|
229
224
|
*/
|
|
230
225
|
const resolveScopedRoute = async (ctx) => {
|
|
231
226
|
const { opts, pool, host } = ctx;
|
|
232
|
-
if (!opts.api?.enableScopedRouting)
|
|
233
|
-
return null;
|
|
234
227
|
const schema = opts.api?.scopedRoutingSchema || 'constructive_routing_public';
|
|
235
228
|
const route = await (0, routing_1.resolveRoute)(pool, schema, host);
|
|
236
229
|
if (!route)
|
|
@@ -303,9 +296,6 @@ const getApiConfig = async (opts, req) => {
|
|
|
303
296
|
const mode = determineMode(ctx);
|
|
304
297
|
let result;
|
|
305
298
|
switch (mode) {
|
|
306
|
-
case 'static':
|
|
307
|
-
result = resolveStatic(ctx);
|
|
308
|
-
break;
|
|
309
299
|
case 'schemata-header':
|
|
310
300
|
result = await resolveSchemataHeader(ctx, validatedSchemas);
|
|
311
301
|
break;
|
|
@@ -321,6 +311,7 @@ const getApiConfig = async (opts, req) => {
|
|
|
321
311
|
}
|
|
322
312
|
// Cache successful results
|
|
323
313
|
if (result && !isApiError(result)) {
|
|
314
|
+
assertDatabaseId(result);
|
|
324
315
|
server_utils_1.svcCache.set(cacheKey, result);
|
|
325
316
|
}
|
|
326
317
|
return result;
|
|
@@ -332,22 +323,6 @@ exports.getApiConfig = getApiConfig;
|
|
|
332
323
|
const createApiMiddleware = (opts) => {
|
|
333
324
|
return async (req, res, next) => {
|
|
334
325
|
log.debug(`[api-middleware] ${req.method} ${req.path}`);
|
|
335
|
-
// Fast path: static single-tenant mode (scoped routing disabled) — no
|
|
336
|
-
// route resolution, expose the configured schemas directly.
|
|
337
|
-
if (!opts.api?.enableScopedRouting) {
|
|
338
|
-
req.api = resolveStatic({
|
|
339
|
-
opts,
|
|
340
|
-
pool: null,
|
|
341
|
-
domain: '',
|
|
342
|
-
subdomain: null,
|
|
343
|
-
cacheKey: 'static',
|
|
344
|
-
headers: {},
|
|
345
|
-
host: ''
|
|
346
|
-
});
|
|
347
|
-
req.databaseId = req.api.databaseId;
|
|
348
|
-
req.svc_key = 'static';
|
|
349
|
-
return next();
|
|
350
|
-
}
|
|
351
326
|
try {
|
|
352
327
|
const apiConfig = await (0, exports.getApiConfig)(opts, req);
|
|
353
328
|
if (isApiError(apiConfig)) {
|
|
@@ -369,6 +344,11 @@ const createApiMiddleware = (opts) => {
|
|
|
369
344
|
res.status(404).send((0, _404_message_1.default)(err.message));
|
|
370
345
|
return;
|
|
371
346
|
}
|
|
347
|
+
if (err.code === 'NO_DATABASE_ID') {
|
|
348
|
+
log.error('[api-middleware] no database id resolved:', err.message);
|
|
349
|
+
res.status(500).send(_50x_1.default);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
372
352
|
if (err.message?.includes('does not exist')) {
|
|
373
353
|
res.status(404).send((0, _404_message_1.default)("The resource you're looking for does not exist."));
|
|
374
354
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.2",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "Constructive GraphQL Server",
|
|
6
6
|
"main": "index.js",
|
|
@@ -41,39 +41,39 @@
|
|
|
41
41
|
"backend"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@constructive-io/csrf": "^0.
|
|
45
|
-
"@constructive-io/express-context": "^0.
|
|
46
|
-
"@constructive-io/graphql-env": "^3.
|
|
47
|
-
"@constructive-io/graphql-types": "^3.
|
|
48
|
-
"@constructive-io/query-builder": "^3.
|
|
49
|
-
"@constructive-io/s3-utils": "^2.
|
|
50
|
-
"@constructive-io/url-domains": "^2.
|
|
44
|
+
"@constructive-io/csrf": "^0.18.2",
|
|
45
|
+
"@constructive-io/express-context": "^0.14.2",
|
|
46
|
+
"@constructive-io/graphql-env": "^3.19.2",
|
|
47
|
+
"@constructive-io/graphql-types": "^3.18.2",
|
|
48
|
+
"@constructive-io/query-builder": "^3.1.2",
|
|
49
|
+
"@constructive-io/s3-utils": "^2.21.2",
|
|
50
|
+
"@constructive-io/url-domains": "^2.20.2",
|
|
51
51
|
"@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
|
|
52
|
-
"@pgpmjs/env": "^2.
|
|
53
|
-
"@pgpmjs/logger": "^2.
|
|
54
|
-
"@pgpmjs/server-utils": "^3.
|
|
55
|
-
"@pgpmjs/types": "^2.
|
|
56
|
-
"agentic-server": "0.
|
|
52
|
+
"@pgpmjs/env": "^2.30.2",
|
|
53
|
+
"@pgpmjs/logger": "^2.15.2",
|
|
54
|
+
"@pgpmjs/server-utils": "^3.16.2",
|
|
55
|
+
"@pgpmjs/types": "^2.37.2",
|
|
56
|
+
"agentic-server": "0.12.2",
|
|
57
57
|
"cors": "^2.8.6",
|
|
58
58
|
"deepmerge": "^4.3.1",
|
|
59
59
|
"express": "^5.2.1",
|
|
60
|
-
"gql-ast": "^3.
|
|
60
|
+
"gql-ast": "^3.14.2",
|
|
61
61
|
"grafast": "1.0.2",
|
|
62
62
|
"grafserv": "1.0.0",
|
|
63
63
|
"graphile-build": "5.0.2",
|
|
64
64
|
"graphile-build-pg": "5.0.2",
|
|
65
|
-
"graphile-cache": "^4.
|
|
65
|
+
"graphile-cache": "^4.1.2",
|
|
66
66
|
"graphile-config": "1.0.1",
|
|
67
|
-
"graphile-function-bindings": "^1.
|
|
68
|
-
"graphile-settings": "^6.
|
|
67
|
+
"graphile-function-bindings": "^1.1.2",
|
|
68
|
+
"graphile-settings": "^6.1.2",
|
|
69
69
|
"graphile-utils": "5.0.1",
|
|
70
70
|
"graphql": "16.13.0",
|
|
71
71
|
"graphql-upload": "^13.0.0",
|
|
72
72
|
"lru-cache": "^11.2.7",
|
|
73
73
|
"pg": "^8.21.0",
|
|
74
|
-
"pg-cache": "^3.
|
|
75
|
-
"pg-env": "^1.
|
|
76
|
-
"pg-query-context": "^2.
|
|
74
|
+
"pg-cache": "^3.16.2",
|
|
75
|
+
"pg-env": "^1.19.2",
|
|
76
|
+
"pg-query-context": "^2.20.2",
|
|
77
77
|
"pg-sql2": "5.0.1",
|
|
78
78
|
"postgraphile": "5.0.3",
|
|
79
79
|
"request-ip": "^3.3.0"
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"@types/pg": "^8.20.0",
|
|
88
88
|
"@types/request-ip": "^0.0.41",
|
|
89
89
|
"cookie-parser": "^1.4.7",
|
|
90
|
-
"graphile-test": "5.
|
|
90
|
+
"graphile-test": "5.1.2",
|
|
91
91
|
"makage": "^0.3.0",
|
|
92
92
|
"nodemon": "^3.1.14",
|
|
93
93
|
"ts-node": "^10.9.2"
|
|
94
94
|
},
|
|
95
|
-
"gitHead": "
|
|
95
|
+
"gitHead": "08741ce3a6897192c2dbc7d1da48b9f456ffb59f"
|
|
96
96
|
}
|
package/server.js
CHANGED
|
@@ -92,12 +92,8 @@ class Server {
|
|
|
92
92
|
serverHost: effectiveOpts.server?.host,
|
|
93
93
|
serverPort: effectiveOpts.server?.port,
|
|
94
94
|
apiIsPublic: apiOpts.isPublic,
|
|
95
|
-
enableScopedRouting: apiOpts.enableScopedRouting,
|
|
96
95
|
scopedRoutingSchema: apiOpts.scopedRoutingSchema,
|
|
97
96
|
metaSchemas: apiOpts.metaSchemas?.join(',') || 'default',
|
|
98
|
-
exposedSchemas: apiOpts.exposedSchemas?.join(',') || 'none',
|
|
99
|
-
anonRole: apiOpts.anonRole,
|
|
100
|
-
roleName: apiOpts.roleName,
|
|
101
97
|
observabilityEnabled
|
|
102
98
|
});
|
|
103
99
|
if (observabilityRequested && !observabilityEnabled) {
|