@ttoss/graphql-api 0.1.0
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 +674 -0
- package/README.md +160 -0
- package/bin/cli.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +51 -0
- package/dist/esm/chunk-SAO2ACBY.js +8 -0
- package/dist/esm/cli.js +26 -0
- package/dist/esm/index.js +156 -0
- package/dist/esm/server.js +46 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +207 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +90 -0
- package/package.json +76 -0
- package/src/cli.ts +35 -0
- package/src/composeWithRelay/composeWithRelay.ts +67 -0
- package/src/composeWithRelay/globalId.ts +32 -0
- package/src/composeWithRelay/index.ts +7 -0
- package/src/composeWithRelay/nodeFieldConfig.ts +82 -0
- package/src/composeWithRelay/nodeInterface.ts +31 -0
- package/src/index.ts +3 -0
- package/src/server.ts +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# @ttoss/graphql-api
|
|
2
|
+
|
|
3
|
+
This package provides an opinionated way to create an GraphQL API using ttoss ecosystem modules. The main goal of this package is to provide a resilient way to create a complex GraphQL API to meet the following goals:
|
|
4
|
+
|
|
5
|
+
1. **Modular**: you can create your GraphQL API using modules, so you can reduce the complexity of a big GraphQL API.
|
|
6
|
+
1. **Relay**: ttoss uses Relay as the main GraphQL client, so this package implements the [Relay Server Specification](https://relay.dev/docs/guides/graphql-server-specification/).
|
|
7
|
+
1. **Build Schema**: as Relay needs an introspection query to work, this package provides a way to build the GraphQL schema by running `ttoss-graphl-api build-schema`.
|
|
8
|
+
1. **AppSync Support**: this package provides a way to create a GraphQL API that works with AWS AppSync, besides you can also create a local GraphQL API server.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @ttoss/graphql-api graphql
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Getting Started
|
|
17
|
+
|
|
18
|
+
This library uses [`graphql-compose`](https://graphql-compose.github.io/) to create the GraphQL schema. It re-export all the [`graphql-compose`](https://graphql-compose.github.io/) types and methods, so you can use it directly from this package.
|
|
19
|
+
|
|
20
|
+
### Type Creation
|
|
21
|
+
|
|
22
|
+
For more examples about how to create types, check the [`graphql-compose`](https://graphql-compose.github.io/docs/basics/understanding-types.html) documentation.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { schemaComposer } from '@ttoss/graphql-api';
|
|
26
|
+
|
|
27
|
+
const UserTC = schemaComposer.createObjectTC({
|
|
28
|
+
name: 'User',
|
|
29
|
+
fields: {
|
|
30
|
+
id: 'ID!',
|
|
31
|
+
name: 'String!',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Resolvers
|
|
37
|
+
|
|
38
|
+
### Integrate All Modules
|
|
39
|
+
|
|
40
|
+
Once you've created all your types and resolvers, you can integrate all the modules to create the GraphQL schema.
|
|
41
|
+
|
|
42
|
+
```ts title="src/schemaComposer.ts"
|
|
43
|
+
// scr/schemaComposer.ts
|
|
44
|
+
import { schemaComposer } from '@ttoss/graphql-api';
|
|
45
|
+
import './modules/Module1/composer';
|
|
46
|
+
import './modules/Module3/composer';
|
|
47
|
+
import './modules/User/composer';
|
|
48
|
+
|
|
49
|
+
export { schemaComposer };
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Relay Server Specification
|
|
53
|
+
|
|
54
|
+
As ttoss uses Relay as the main GraphQL client, this library implements the [Relay Server Specification](https://relay.dev/docs/guides/graphql-server-specification/).
|
|
55
|
+
|
|
56
|
+
### Object Identification
|
|
57
|
+
|
|
58
|
+
Method `composeWithRelay` will handle the object identification for your `ObjectTypeComposer`, it will return a globally unique ID among all types in the following format `base64(TypeName + ':' + recordId)`.
|
|
59
|
+
|
|
60
|
+
Method `composeWithRelay` only works if `ObjectTypeComposer` meets the following requirements:
|
|
61
|
+
|
|
62
|
+
1. Has defined `recordIdFn`: returns the id for the globalId construction. For example, if you use DynamoDB, you could create id from hash and range keys:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
UserTC.setRecordIdFn((source) => {
|
|
66
|
+
return `${source.hashKey}:${source.rangeKey}`;
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
2. Have `findById` resolver: this resolver will be used by `RootQuery.node` to resolve the object by globalId. For example:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
UserTC.addResolver({
|
|
74
|
+
name: 'findById',
|
|
75
|
+
type: UserTC,
|
|
76
|
+
args: {
|
|
77
|
+
id: 'String!',
|
|
78
|
+
},
|
|
79
|
+
resolve: ({ args }) => {
|
|
80
|
+
const { type, recordId } = fromGlobalId(args.id);
|
|
81
|
+
// find object
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Example
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import {
|
|
90
|
+
composeWithRelay,
|
|
91
|
+
schemaComposer,
|
|
92
|
+
fromGlobalId,
|
|
93
|
+
} from '@ttoss/graphql-api';
|
|
94
|
+
|
|
95
|
+
const UserTC = schemaComposer.createObjectTC({
|
|
96
|
+
name: 'User',
|
|
97
|
+
fields: {
|
|
98
|
+
id: 'ID!',
|
|
99
|
+
name: 'String!',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 1. Returns you id for the globalId construction.
|
|
105
|
+
*/
|
|
106
|
+
UserTC.setRecordIdFn((source) => {
|
|
107
|
+
/**
|
|
108
|
+
* If you use DynamoDB, you could create id from hash and range keys:
|
|
109
|
+
* return `${source.hashKey}:${source.rangeKey}`;
|
|
110
|
+
*/
|
|
111
|
+
return source.id;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 2. Define `findById` resolver (that will be used by `RootQuery.node`).
|
|
116
|
+
*/
|
|
117
|
+
UserTC.addResolver({
|
|
118
|
+
name: 'findById',
|
|
119
|
+
type: UserTC,
|
|
120
|
+
args: {
|
|
121
|
+
id: 'String!',
|
|
122
|
+
},
|
|
123
|
+
resolve: ({ args }) => {
|
|
124
|
+
const { type, recordId } = fromGlobalId(args.id);
|
|
125
|
+
// find object
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 3. This will add the `id` field and the `node` query.
|
|
131
|
+
*/
|
|
132
|
+
composeWithRelay(UserTC);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
_We inspired ourselves on [graphql-compose-relay](https://graphql-compose.github.io/docs/plugins/plugin-relay.html) to create `composeWithRelay`._
|
|
136
|
+
|
|
137
|
+
## Building Schema
|
|
138
|
+
|
|
139
|
+
As Relay needs an introspection query to work, this package provides a way to build the GraphQL schema by running `ttoss-graphl-api build-schema`.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
ttoss-graphl-api build-schema
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Server
|
|
146
|
+
|
|
147
|
+
This package provides a Koa server to run your GraphQL API. You can use the `createServer` method to create the server.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { createServer } from '@ttoss/graphql-api/server';
|
|
151
|
+
|
|
152
|
+
const server = createServer({
|
|
153
|
+
schemaComposer,
|
|
154
|
+
graphiql: true,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
server.listen(3000, () => {
|
|
158
|
+
console.log('Server listening on port 3000');
|
|
159
|
+
});
|
|
160
|
+
```
|
package/bin/cli.js
ADDED
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: () => from[key],
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
|
|
29
|
+
// src/cli.ts
|
|
30
|
+
var fs = __toESM(require("fs"));
|
|
31
|
+
var path = __toESM(require("path"));
|
|
32
|
+
var import_ts_node = require("ts-node");
|
|
33
|
+
var import_npmlog = __toESM(require("npmlog"));
|
|
34
|
+
var import_yargs = __toESM(require("yargs"));
|
|
35
|
+
var logPrefix = "graphql-api";
|
|
36
|
+
(0, import_ts_node.register)({
|
|
37
|
+
transpileOnly: true
|
|
38
|
+
});
|
|
39
|
+
var argv = (0, import_yargs.default)(process.argv.slice(2)).argv;
|
|
40
|
+
if (argv._.includes("build-schema")) {
|
|
41
|
+
import_npmlog.default.info(logPrefix, "Building schema...");
|
|
42
|
+
const {
|
|
43
|
+
schemaComposer
|
|
44
|
+
} = require(path.resolve(process.cwd(), "src", "schemaComposer.ts"));
|
|
45
|
+
const sdl = schemaComposer.toSDL();
|
|
46
|
+
fs.mkdirSync("schema", {
|
|
47
|
+
recursive: true
|
|
48
|
+
});
|
|
49
|
+
fs.writeFileSync("schema/schema.graphql", sdl);
|
|
50
|
+
import_npmlog.default.info(logPrefix, "Schema built.");
|
|
51
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
var __require = /* @__PURE__ */(x => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function (x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw new Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
export { __require };
|
package/dist/esm/cli.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { __require } from "./chunk-SAO2ACBY.js";
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { register } from "ts-node";
|
|
8
|
+
import log from "npmlog";
|
|
9
|
+
import yargs from "yargs";
|
|
10
|
+
var logPrefix = "graphql-api";
|
|
11
|
+
register({
|
|
12
|
+
transpileOnly: true
|
|
13
|
+
});
|
|
14
|
+
var argv = yargs(process.argv.slice(2)).argv;
|
|
15
|
+
if (argv._.includes("build-schema")) {
|
|
16
|
+
log.info(logPrefix, "Building schema...");
|
|
17
|
+
const {
|
|
18
|
+
schemaComposer
|
|
19
|
+
} = __require(path.resolve(process.cwd(), "src", "schemaComposer.ts"));
|
|
20
|
+
const sdl = schemaComposer.toSDL();
|
|
21
|
+
fs.mkdirSync("schema", {
|
|
22
|
+
recursive: true
|
|
23
|
+
});
|
|
24
|
+
fs.writeFileSync("schema/schema.graphql", sdl);
|
|
25
|
+
log.info(logPrefix, "Schema built.");
|
|
26
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import "./chunk-SAO2ACBY.js";
|
|
3
|
+
|
|
4
|
+
// src/composeWithRelay/composeWithRelay.ts
|
|
5
|
+
import { ObjectTypeComposer } from "graphql-compose";
|
|
6
|
+
|
|
7
|
+
// src/composeWithRelay/nodeFieldConfig.ts
|
|
8
|
+
import { getProjectionFromAST } from "graphql-compose";
|
|
9
|
+
|
|
10
|
+
// src/composeWithRelay/globalId.ts
|
|
11
|
+
var base64 = i => {
|
|
12
|
+
return Buffer.from(i, "ascii").toString("base64");
|
|
13
|
+
};
|
|
14
|
+
var unbase64 = i => {
|
|
15
|
+
return Buffer.from(i, "base64").toString("ascii");
|
|
16
|
+
};
|
|
17
|
+
var toGlobalId = (type, recordId) => {
|
|
18
|
+
return base64([type, recordId].join(":"));
|
|
19
|
+
};
|
|
20
|
+
var fromGlobalId = globalId => {
|
|
21
|
+
const unbasedGlobalId = unbase64(globalId);
|
|
22
|
+
const [type, recordId] = unbasedGlobalId.split(":");
|
|
23
|
+
return {
|
|
24
|
+
type,
|
|
25
|
+
recordId
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/composeWithRelay/nodeFieldConfig.ts
|
|
30
|
+
var getNodeFieldConfig = (typeMapForRelayNode, nodeInterface) => {
|
|
31
|
+
return {
|
|
32
|
+
description: "Fetches an object that has globally unique ID among all types",
|
|
33
|
+
type: nodeInterface,
|
|
34
|
+
args: {
|
|
35
|
+
id: {
|
|
36
|
+
type: "ID!",
|
|
37
|
+
description: "The globally unique ID among all types"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
resolve: (source, args, context, info) => {
|
|
41
|
+
if (!args.id || !(typeof args.id === "string")) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const {
|
|
45
|
+
type
|
|
46
|
+
} = fromGlobalId(args.id);
|
|
47
|
+
if (!typeMapForRelayNode[type]) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const {
|
|
51
|
+
tc,
|
|
52
|
+
resolver: findById
|
|
53
|
+
} = typeMapForRelayNode[type];
|
|
54
|
+
if (findById && findById.resolve && tc) {
|
|
55
|
+
const graphqlType = tc.getType();
|
|
56
|
+
let projection;
|
|
57
|
+
if (info) {
|
|
58
|
+
projection = getProjectionFromAST({
|
|
59
|
+
...info,
|
|
60
|
+
returnType: graphqlType
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
projection = {};
|
|
64
|
+
}
|
|
65
|
+
const idArgName = Object.keys(findById.args)[0];
|
|
66
|
+
return findById.resolve({
|
|
67
|
+
source,
|
|
68
|
+
args: {
|
|
69
|
+
[idArgName]: args.id
|
|
70
|
+
},
|
|
71
|
+
// eg. mongoose has _id fieldname, so should map
|
|
72
|
+
context,
|
|
73
|
+
info,
|
|
74
|
+
projection
|
|
75
|
+
}).then(res => {
|
|
76
|
+
if (!res) return res;
|
|
77
|
+
res.__nodeType = graphqlType;
|
|
78
|
+
return res;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/composeWithRelay/nodeInterface.ts
|
|
87
|
+
import { InterfaceTypeComposer } from "graphql-compose";
|
|
88
|
+
var NodeTC = InterfaceTypeComposer.createTemp({
|
|
89
|
+
name: "Node",
|
|
90
|
+
description: "An object, that can be fetched by the globally unique ID among all types.",
|
|
91
|
+
fields: {
|
|
92
|
+
id: {
|
|
93
|
+
type: "ID!",
|
|
94
|
+
description: "The globally unique ID among all types."
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
resolveType: payload => {
|
|
98
|
+
return payload.__nodeType.name ? payload.__nodeType.name : null;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
var NodeInterface = NodeTC.getType();
|
|
102
|
+
var getNodeInterface = sc => {
|
|
103
|
+
if (sc.hasInstance("Node", InterfaceTypeComposer)) {
|
|
104
|
+
return sc.get("Node");
|
|
105
|
+
}
|
|
106
|
+
sc.set("Node", NodeTC);
|
|
107
|
+
return NodeTC;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/composeWithRelay/composeWithRelay.ts
|
|
111
|
+
var TypeMapForRelayNode = {};
|
|
112
|
+
var composeWithRelay = tc => {
|
|
113
|
+
if (!(tc instanceof ObjectTypeComposer)) {
|
|
114
|
+
throw new Error("You should provide ObjectTypeComposer instance to composeWithRelay method");
|
|
115
|
+
}
|
|
116
|
+
const nodeInterface = getNodeInterface(tc.schemaComposer);
|
|
117
|
+
const nodeFieldConfig = getNodeFieldConfig(TypeMapForRelayNode, nodeInterface);
|
|
118
|
+
if (tc.getTypeName() === "Query" || tc.getTypeName() === "RootQuery") {
|
|
119
|
+
tc.setField("node", nodeFieldConfig);
|
|
120
|
+
return tc;
|
|
121
|
+
}
|
|
122
|
+
if (tc.getTypeName() === "Mutation" || tc.getTypeName() === "RootMutation") {
|
|
123
|
+
return tc;
|
|
124
|
+
}
|
|
125
|
+
if (!tc.hasRecordIdFn()) {
|
|
126
|
+
throw new Error(`ObjectTypeComposer(${tc.getTypeName()}) should have recordIdFn. This function returns ID from provided object.`);
|
|
127
|
+
}
|
|
128
|
+
const findById = tc.getResolver("findById");
|
|
129
|
+
if (!findById) {
|
|
130
|
+
throw new Error(`ObjectTypeComposer(${tc.getTypeName()}) provided to composeWithRelay should have findById resolver.`);
|
|
131
|
+
}
|
|
132
|
+
TypeMapForRelayNode[tc.getTypeName()] = {
|
|
133
|
+
resolver: findById,
|
|
134
|
+
tc
|
|
135
|
+
};
|
|
136
|
+
tc.addFields({
|
|
137
|
+
id: {
|
|
138
|
+
type: "ID!",
|
|
139
|
+
description: "The globally unique ID among all types",
|
|
140
|
+
resolve: source => {
|
|
141
|
+
return toGlobalId(tc.getTypeName(), tc.getRecordId(source));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
tc.addInterface(nodeInterface);
|
|
146
|
+
return tc;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/composeWithRelay/index.ts
|
|
150
|
+
import { schemaComposer } from "graphql-compose";
|
|
151
|
+
composeWithRelay(schemaComposer.Query);
|
|
152
|
+
|
|
153
|
+
// src/index.ts
|
|
154
|
+
import { default as default2 } from "graphql-compose-connection";
|
|
155
|
+
export * from "graphql-compose";
|
|
156
|
+
export { default2 as composeWithConnection, composeWithRelay, fromGlobalId, toGlobalId };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import "./chunk-SAO2ACBY.js";
|
|
3
|
+
|
|
4
|
+
// src/server.ts
|
|
5
|
+
import { getGraphQLParameters, processRequest, renderGraphiQL, sendResult, shouldRenderGraphiQL } from "graphql-helix";
|
|
6
|
+
import Koa from "koa";
|
|
7
|
+
import Router from "@koa/router";
|
|
8
|
+
import bodyParser from "koa-bodyparser";
|
|
9
|
+
var createServer = ({
|
|
10
|
+
schemaComposer,
|
|
11
|
+
graphiql = false
|
|
12
|
+
}) => {
|
|
13
|
+
const server = new Koa();
|
|
14
|
+
const router = new Router();
|
|
15
|
+
server.use(bodyParser());
|
|
16
|
+
router.all("/graphql", async ctx => {
|
|
17
|
+
const request = {
|
|
18
|
+
body: ctx.request.body,
|
|
19
|
+
headers: ctx.headers,
|
|
20
|
+
method: ctx.method,
|
|
21
|
+
query: ctx.request.query
|
|
22
|
+
};
|
|
23
|
+
if (shouldRenderGraphiQL(request)) {
|
|
24
|
+
if (graphiql) {
|
|
25
|
+
ctx.body = renderGraphiQL({});
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const {
|
|
30
|
+
operationName,
|
|
31
|
+
query,
|
|
32
|
+
variables
|
|
33
|
+
} = getGraphQLParameters(request);
|
|
34
|
+
const result = await processRequest({
|
|
35
|
+
operationName,
|
|
36
|
+
query,
|
|
37
|
+
variables,
|
|
38
|
+
request,
|
|
39
|
+
schema: schemaComposer.buildSchema()
|
|
40
|
+
});
|
|
41
|
+
sendResult(result, ctx.res);
|
|
42
|
+
});
|
|
43
|
+
server.use(router.routes()).use(router.allowedMethods());
|
|
44
|
+
return server;
|
|
45
|
+
};
|
|
46
|
+
export { Router, createServer };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ObjectTypeComposer } from 'graphql-compose';
|
|
2
|
+
export * from 'graphql-compose';
|
|
3
|
+
export { default as composeWithConnection } from 'graphql-compose-connection';
|
|
4
|
+
|
|
5
|
+
declare const composeWithRelay: <TContext>(tc: ObjectTypeComposer<any, TContext>) => ObjectTypeComposer<any, TContext>;
|
|
6
|
+
|
|
7
|
+
type ResolvedGlobalId = {
|
|
8
|
+
type: string;
|
|
9
|
+
recordId: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Takes a type name and an ID specific to that type name, and returns a
|
|
13
|
+
* "global ID" that is unique among all types.
|
|
14
|
+
*/
|
|
15
|
+
declare const toGlobalId: (type: string, recordId: string | number) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Takes the "global ID" created by toGlobalID, and returns the type name and ID
|
|
18
|
+
* used to create it.
|
|
19
|
+
*/
|
|
20
|
+
declare const fromGlobalId: (globalId: string) => ResolvedGlobalId;
|
|
21
|
+
|
|
22
|
+
export { composeWithRelay, fromGlobalId, toGlobalId };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all) __defProp(target, name, {
|
|
12
|
+
get: all[name],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
19
|
+
get: () => from[key],
|
|
20
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
32
|
+
value: mod,
|
|
33
|
+
enumerable: true
|
|
34
|
+
}) : target, mod));
|
|
35
|
+
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
36
|
+
value: true
|
|
37
|
+
}), mod);
|
|
38
|
+
|
|
39
|
+
// src/index.ts
|
|
40
|
+
var src_exports = {};
|
|
41
|
+
__export(src_exports, {
|
|
42
|
+
composeWithConnection: () => import_graphql_compose_connection.default,
|
|
43
|
+
composeWithRelay: () => composeWithRelay,
|
|
44
|
+
fromGlobalId: () => fromGlobalId,
|
|
45
|
+
toGlobalId: () => toGlobalId
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(src_exports);
|
|
48
|
+
|
|
49
|
+
// src/composeWithRelay/composeWithRelay.ts
|
|
50
|
+
var import_graphql_compose3 = require("graphql-compose");
|
|
51
|
+
|
|
52
|
+
// src/composeWithRelay/nodeFieldConfig.ts
|
|
53
|
+
var import_graphql_compose = require("graphql-compose");
|
|
54
|
+
|
|
55
|
+
// src/composeWithRelay/globalId.ts
|
|
56
|
+
var base64 = i => {
|
|
57
|
+
return Buffer.from(i, "ascii").toString("base64");
|
|
58
|
+
};
|
|
59
|
+
var unbase64 = i => {
|
|
60
|
+
return Buffer.from(i, "base64").toString("ascii");
|
|
61
|
+
};
|
|
62
|
+
var toGlobalId = (type, recordId) => {
|
|
63
|
+
return base64([type, recordId].join(":"));
|
|
64
|
+
};
|
|
65
|
+
var fromGlobalId = globalId => {
|
|
66
|
+
const unbasedGlobalId = unbase64(globalId);
|
|
67
|
+
const [type, recordId] = unbasedGlobalId.split(":");
|
|
68
|
+
return {
|
|
69
|
+
type,
|
|
70
|
+
recordId
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/composeWithRelay/nodeFieldConfig.ts
|
|
75
|
+
var getNodeFieldConfig = (typeMapForRelayNode, nodeInterface) => {
|
|
76
|
+
return {
|
|
77
|
+
description: "Fetches an object that has globally unique ID among all types",
|
|
78
|
+
type: nodeInterface,
|
|
79
|
+
args: {
|
|
80
|
+
id: {
|
|
81
|
+
type: "ID!",
|
|
82
|
+
description: "The globally unique ID among all types"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
resolve: (source, args, context, info) => {
|
|
86
|
+
if (!args.id || !(typeof args.id === "string")) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const {
|
|
90
|
+
type
|
|
91
|
+
} = fromGlobalId(args.id);
|
|
92
|
+
if (!typeMapForRelayNode[type]) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const {
|
|
96
|
+
tc,
|
|
97
|
+
resolver: findById
|
|
98
|
+
} = typeMapForRelayNode[type];
|
|
99
|
+
if (findById && findById.resolve && tc) {
|
|
100
|
+
const graphqlType = tc.getType();
|
|
101
|
+
let projection;
|
|
102
|
+
if (info) {
|
|
103
|
+
projection = (0, import_graphql_compose.getProjectionFromAST)({
|
|
104
|
+
...info,
|
|
105
|
+
returnType: graphqlType
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
projection = {};
|
|
109
|
+
}
|
|
110
|
+
const idArgName = Object.keys(findById.args)[0];
|
|
111
|
+
return findById.resolve({
|
|
112
|
+
source,
|
|
113
|
+
args: {
|
|
114
|
+
[idArgName]: args.id
|
|
115
|
+
},
|
|
116
|
+
// eg. mongoose has _id fieldname, so should map
|
|
117
|
+
context,
|
|
118
|
+
info,
|
|
119
|
+
projection
|
|
120
|
+
}).then(res => {
|
|
121
|
+
if (!res) return res;
|
|
122
|
+
res.__nodeType = graphqlType;
|
|
123
|
+
return res;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/composeWithRelay/nodeInterface.ts
|
|
132
|
+
var import_graphql_compose2 = require("graphql-compose");
|
|
133
|
+
var NodeTC = import_graphql_compose2.InterfaceTypeComposer.createTemp({
|
|
134
|
+
name: "Node",
|
|
135
|
+
description: "An object, that can be fetched by the globally unique ID among all types.",
|
|
136
|
+
fields: {
|
|
137
|
+
id: {
|
|
138
|
+
type: "ID!",
|
|
139
|
+
description: "The globally unique ID among all types."
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
resolveType: payload => {
|
|
143
|
+
return payload.__nodeType.name ? payload.__nodeType.name : null;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
var NodeInterface = NodeTC.getType();
|
|
147
|
+
var getNodeInterface = sc => {
|
|
148
|
+
if (sc.hasInstance("Node", import_graphql_compose2.InterfaceTypeComposer)) {
|
|
149
|
+
return sc.get("Node");
|
|
150
|
+
}
|
|
151
|
+
sc.set("Node", NodeTC);
|
|
152
|
+
return NodeTC;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/composeWithRelay/composeWithRelay.ts
|
|
156
|
+
var TypeMapForRelayNode = {};
|
|
157
|
+
var composeWithRelay = tc => {
|
|
158
|
+
if (!(tc instanceof import_graphql_compose3.ObjectTypeComposer)) {
|
|
159
|
+
throw new Error("You should provide ObjectTypeComposer instance to composeWithRelay method");
|
|
160
|
+
}
|
|
161
|
+
const nodeInterface = getNodeInterface(tc.schemaComposer);
|
|
162
|
+
const nodeFieldConfig = getNodeFieldConfig(TypeMapForRelayNode, nodeInterface);
|
|
163
|
+
if (tc.getTypeName() === "Query" || tc.getTypeName() === "RootQuery") {
|
|
164
|
+
tc.setField("node", nodeFieldConfig);
|
|
165
|
+
return tc;
|
|
166
|
+
}
|
|
167
|
+
if (tc.getTypeName() === "Mutation" || tc.getTypeName() === "RootMutation") {
|
|
168
|
+
return tc;
|
|
169
|
+
}
|
|
170
|
+
if (!tc.hasRecordIdFn()) {
|
|
171
|
+
throw new Error(`ObjectTypeComposer(${tc.getTypeName()}) should have recordIdFn. This function returns ID from provided object.`);
|
|
172
|
+
}
|
|
173
|
+
const findById = tc.getResolver("findById");
|
|
174
|
+
if (!findById) {
|
|
175
|
+
throw new Error(`ObjectTypeComposer(${tc.getTypeName()}) provided to composeWithRelay should have findById resolver.`);
|
|
176
|
+
}
|
|
177
|
+
TypeMapForRelayNode[tc.getTypeName()] = {
|
|
178
|
+
resolver: findById,
|
|
179
|
+
tc
|
|
180
|
+
};
|
|
181
|
+
tc.addFields({
|
|
182
|
+
id: {
|
|
183
|
+
type: "ID!",
|
|
184
|
+
description: "The globally unique ID among all types",
|
|
185
|
+
resolve: source => {
|
|
186
|
+
return toGlobalId(tc.getTypeName(), tc.getRecordId(source));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
tc.addInterface(nodeInterface);
|
|
191
|
+
return tc;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/composeWithRelay/index.ts
|
|
195
|
+
var import_graphql_compose4 = require("graphql-compose");
|
|
196
|
+
composeWithRelay(import_graphql_compose4.schemaComposer.Query);
|
|
197
|
+
|
|
198
|
+
// src/index.ts
|
|
199
|
+
var import_graphql_compose_connection = __toESM(require("graphql-compose-connection"));
|
|
200
|
+
__reExport(src_exports, require("graphql-compose"), module.exports);
|
|
201
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
202
|
+
0 && (module.exports = {
|
|
203
|
+
composeWithConnection,
|
|
204
|
+
composeWithRelay,
|
|
205
|
+
fromGlobalId,
|
|
206
|
+
toGlobalId
|
|
207
|
+
});
|