@ttoss/graphql-api 0.4.6 → 0.5.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/README.md +19 -5
- package/dist/cli.js +53 -41
- package/dist/esm/cli.js +53 -41
- package/package.json +3 -3
- package/src/cli.ts +72 -54
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ _We inspired ourselves on [graphql-compose-relay](https://graphql-compose.github
|
|
|
140
140
|
This packages provides the method `composeWithConnection` to create a connection type and queries for a given type, based on [graphql-compose-connection](https://graphql-compose.github.io/docs/plugins/plugin-connection.html) plugin and following the [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm).
|
|
141
141
|
|
|
142
142
|
```typescript
|
|
143
|
-
import { composeWithConnection } from '@ttoss/
|
|
143
|
+
import { composeWithConnection } from '@ttoss/graphql-api';
|
|
144
144
|
|
|
145
145
|
AuthorTC.addResolver({
|
|
146
146
|
name: 'findMany',
|
|
@@ -190,7 +190,7 @@ schemaComposer.Query.addFields({
|
|
|
190
190
|
});
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
When you `composeWithConnection` a type, it will add the resolver `connection` to the type, so you can add to `Query` or any other type:
|
|
193
|
+
When you `composeWithConnection` a type composer, it will add the resolver `connection` to the type composer, so you can add to `Query` or any other type composer. For example:
|
|
194
194
|
|
|
195
195
|
```ts
|
|
196
196
|
schemaComposer.Query.addFields({
|
|
@@ -204,6 +204,8 @@ The resolver `connection` has the following arguments based on the [Relay Connec
|
|
|
204
204
|
- `after`: the cursor to start the query.
|
|
205
205
|
- `last`: the number of nodes to return.
|
|
206
206
|
- `before`: the cursor to start the query.
|
|
207
|
+
- `limit`: the limit of nodes to return. It's the `first` or `last` argument plus one. It's used to know if there are more nodes to return to set `hasNextPage` or `hasPreviousPage` [PageInfo](https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo) fields. For example, if `first` is `10`, `limit` will be `11`. If the resolver returns `11` nodes, the resolver will return `10` but it knows there are more nodes to return, so `hasNextPage` will be `true`.
|
|
208
|
+
- `skip`: it's the `count` minus `last`. It only works on [backward pagination](https://relay.dev/graphql/connections.htm#sec-Backward-pagination-arguments).
|
|
207
209
|
- `sort`: the sort option to use. It's the keys of the `sort` object. In our example, it's `ASC` and `DESC`.
|
|
208
210
|
- `filter`: the filter to use. It'll exist if you add the `filter` to `findManyResolver` for example, the implementation below will add the `filter` argument with the `name` and `book` fields:
|
|
209
211
|
|
|
@@ -249,10 +251,14 @@ The resolver that will be used to find the nodes. It receives the following argu
|
|
|
249
251
|
after?: string;
|
|
250
252
|
last?: number;
|
|
251
253
|
before?: string;
|
|
254
|
+
/**
|
|
255
|
+
* It's the `first` or `last` argument plus one.
|
|
256
|
+
*/
|
|
257
|
+
limit: number;
|
|
252
258
|
/**
|
|
253
259
|
* The `filter` argument, if provided on the query.
|
|
254
260
|
*/
|
|
255
|
-
filter
|
|
261
|
+
filter?: {
|
|
256
262
|
name: string;
|
|
257
263
|
book: string;
|
|
258
264
|
};
|
|
@@ -278,6 +284,8 @@ The resolver that will be used to find the nodes. It receives the following argu
|
|
|
278
284
|
|
|
279
285
|
#### `countResolver`
|
|
280
286
|
|
|
287
|
+
The resolver that will be used to count the nodes.
|
|
288
|
+
|
|
281
289
|
#### `sort`
|
|
282
290
|
|
|
283
291
|
It's an object that defines the sort options. Each key is the sort name and the value is an object with the following properties:
|
|
@@ -383,16 +391,22 @@ As Relay needs an introspection query to work, this package provides a way to bu
|
|
|
383
391
|
ttoss-graphl-api build-schema
|
|
384
392
|
```
|
|
385
393
|
|
|
386
|
-
You can add the `build` script to your `package.json`:
|
|
394
|
+
You can add the `build-schema` script to your `package.json`:
|
|
387
395
|
|
|
388
396
|
```json
|
|
389
397
|
{
|
|
390
398
|
"scripts": {
|
|
391
|
-
"build": "ttoss-graphl-api build-schema"
|
|
399
|
+
"build-schema": "ttoss-graphl-api build-schema"
|
|
392
400
|
}
|
|
393
401
|
}
|
|
394
402
|
```
|
|
395
403
|
|
|
404
|
+
If your `schemaComposer` is in a different directory, you can pass the `--directory`/`-d` option to `ttoss-graphl-api build-schema` command:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
ttoss-graphl-api build-schema -d tests
|
|
408
|
+
```
|
|
409
|
+
|
|
396
410
|
## How to Create Tests
|
|
397
411
|
|
|
398
412
|
We recommend testing the whole GraphQL API using the `graphql` object and the schema composer to provide the schema. For example:
|
package/dist/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ var fs = __toESM(require("fs"));
|
|
|
31
31
|
var path = __toESM(require("path"));
|
|
32
32
|
var typescriptPlugin = __toESM(require("@graphql-codegen/typescript"));
|
|
33
33
|
var import_core = require("@graphql-codegen/core");
|
|
34
|
+
var import_helpers = require("yargs/helpers");
|
|
34
35
|
var import_graphql = require("graphql");
|
|
35
36
|
var import_ts_node = require("ts-node");
|
|
36
37
|
var import_npmlog = __toESM(require("npmlog"));
|
|
@@ -43,46 +44,57 @@ var logPrefix = "graphql-api";
|
|
|
43
44
|
moduleResolution: "NodeNext"
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
namingConvention: "keep"
|
|
47
|
+
var buildSchema = async ({
|
|
48
|
+
directory
|
|
49
|
+
}) => {
|
|
50
|
+
import_npmlog.default.info(logPrefix, "Building schema...");
|
|
51
|
+
await fs.promises.mkdir("schema", {
|
|
52
|
+
recursive: true
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
await fs.promises.access("schema/types.ts");
|
|
56
|
+
} catch {
|
|
57
|
+
await fs.promises.writeFile("schema/types.ts", "");
|
|
58
|
+
}
|
|
59
|
+
const {
|
|
60
|
+
schemaComposer
|
|
61
|
+
} = require(path.resolve(process.cwd(), directory, "schemaComposer.ts"));
|
|
62
|
+
const sdl = schemaComposer.toSDL();
|
|
63
|
+
const codegenConfig = {
|
|
64
|
+
documents: [],
|
|
65
|
+
config: {
|
|
66
|
+
declarationKind: {
|
|
67
|
+
type: "interface",
|
|
68
|
+
interface: "interface"
|
|
70
69
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
namingConvention: "keep"
|
|
71
|
+
},
|
|
72
|
+
filename: "schema/types.ts",
|
|
73
|
+
schema: (0, import_graphql.parse)(sdl),
|
|
74
|
+
plugins: [{
|
|
75
|
+
typescript: {}
|
|
76
|
+
}],
|
|
77
|
+
pluginMap: {
|
|
78
|
+
typescript: typescriptPlugin
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
await fs.promises.writeFile("schema/schema.graphql", sdl);
|
|
82
|
+
import_npmlog.default.info(logPrefix, "Generating types...");
|
|
83
|
+
const typesOutput = await (0, import_core.codegen)(codegenConfig);
|
|
84
|
+
const typesOutputIgnore = ["/* eslint-disable */"].join("\n");
|
|
85
|
+
await fs.promises.writeFile("schema/types.ts", `${typesOutputIgnore}
|
|
85
86
|
${typesOutput}`);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
import_npmlog.default.info(logPrefix, "Schema and types generated!");
|
|
88
|
+
};
|
|
89
|
+
(0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).command("build-schema", "fetch the contents of the URL", yargs2 => {
|
|
90
|
+
return yargs2.options({
|
|
91
|
+
directory: {
|
|
92
|
+
alias: ["d"],
|
|
93
|
+
type: "string",
|
|
94
|
+
describe: "Schema composer directory relative to the project root",
|
|
95
|
+
default: "src"
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}, argv => {
|
|
99
|
+
return buildSchema(argv);
|
|
100
|
+
}).demandCommand(1).strictOptions().parse();
|
package/dist/esm/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as fs from "node:fs";
|
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import * as typescriptPlugin from "@graphql-codegen/typescript";
|
|
8
8
|
import { codegen } from "@graphql-codegen/core";
|
|
9
|
+
import { hideBin } from "yargs/helpers";
|
|
9
10
|
import { parse } from "graphql";
|
|
10
11
|
import { register } from "ts-node";
|
|
11
12
|
import log from "npmlog";
|
|
@@ -18,46 +19,57 @@ register({
|
|
|
18
19
|
moduleResolution: "NodeNext"
|
|
19
20
|
}
|
|
20
21
|
});
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
},
|
|
44
|
-
namingConvention: "keep"
|
|
22
|
+
var buildSchema = async ({
|
|
23
|
+
directory
|
|
24
|
+
}) => {
|
|
25
|
+
log.info(logPrefix, "Building schema...");
|
|
26
|
+
await fs.promises.mkdir("schema", {
|
|
27
|
+
recursive: true
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
await fs.promises.access("schema/types.ts");
|
|
31
|
+
} catch {
|
|
32
|
+
await fs.promises.writeFile("schema/types.ts", "");
|
|
33
|
+
}
|
|
34
|
+
const {
|
|
35
|
+
schemaComposer
|
|
36
|
+
} = __require(path.resolve(process.cwd(), directory, "schemaComposer.ts"));
|
|
37
|
+
const sdl = schemaComposer.toSDL();
|
|
38
|
+
const codegenConfig = {
|
|
39
|
+
documents: [],
|
|
40
|
+
config: {
|
|
41
|
+
declarationKind: {
|
|
42
|
+
type: "interface",
|
|
43
|
+
interface: "interface"
|
|
45
44
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
namingConvention: "keep"
|
|
46
|
+
},
|
|
47
|
+
filename: "schema/types.ts",
|
|
48
|
+
schema: parse(sdl),
|
|
49
|
+
plugins: [{
|
|
50
|
+
typescript: {}
|
|
51
|
+
}],
|
|
52
|
+
pluginMap: {
|
|
53
|
+
typescript: typescriptPlugin
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
await fs.promises.writeFile("schema/schema.graphql", sdl);
|
|
57
|
+
log.info(logPrefix, "Generating types...");
|
|
58
|
+
const typesOutput = await codegen(codegenConfig);
|
|
59
|
+
const typesOutputIgnore = ["/* eslint-disable */"].join("\n");
|
|
60
|
+
await fs.promises.writeFile("schema/types.ts", `${typesOutputIgnore}
|
|
60
61
|
${typesOutput}`);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
log.info(logPrefix, "Schema and types generated!");
|
|
63
|
+
};
|
|
64
|
+
yargs(hideBin(process.argv)).command("build-schema", "fetch the contents of the URL", yargs2 => {
|
|
65
|
+
return yargs2.options({
|
|
66
|
+
directory: {
|
|
67
|
+
alias: ["d"],
|
|
68
|
+
type: "string",
|
|
69
|
+
describe: "Schema composer directory relative to the project root",
|
|
70
|
+
default: "src"
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}, argv => {
|
|
74
|
+
return buildSchema(argv);
|
|
75
|
+
}).demandCommand(1).strictOptions().parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttoss/graphql-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A library for building GraphQL APIs using ttoss ecosystem.",
|
|
5
5
|
"author": "ttoss",
|
|
6
6
|
"contributors": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"graphql-middleware": "^6.1.35",
|
|
40
40
|
"graphql-shield": "^7.6.5",
|
|
41
41
|
"npmlog": "^7.0.1",
|
|
42
|
-
"ts-node": "^10.9.
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
43
|
"yargs": "^17.7.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"graphql": "^16.8.1",
|
|
51
51
|
"jest": "^29.7.0",
|
|
52
52
|
"tsup": "^8.0.1",
|
|
53
|
-
"@ttoss/config": "^1.31.
|
|
53
|
+
"@ttoss/config": "^1.31.4"
|
|
54
54
|
},
|
|
55
55
|
"keywords": [
|
|
56
56
|
"api",
|
package/src/cli.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as typescriptPlugin from '@graphql-codegen/typescript';
|
|
4
4
|
import { codegen } from '@graphql-codegen/core';
|
|
5
|
+
import { hideBin } from 'yargs/helpers';
|
|
5
6
|
import { parse } from 'graphql';
|
|
6
7
|
import { register } from 'ts-node';
|
|
7
8
|
import log from 'npmlog';
|
|
@@ -17,70 +18,87 @@ register({
|
|
|
17
18
|
},
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const buildSchema = async ({ directory }: { directory: string }) => {
|
|
22
|
+
log.info(logPrefix, 'Building schema...');
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
if (argv._.includes('build-schema')) {
|
|
25
|
-
log.info(logPrefix, 'Building schema...');
|
|
24
|
+
await fs.promises.mkdir('schema', { recursive: true });
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
await fs.promises.writeFile('schema/types.ts', '');
|
|
38
|
-
}
|
|
26
|
+
/**
|
|
27
|
+
* Create schema/types.ts if it doesn't exist. We need to do this because
|
|
28
|
+
* graphql-codegen will throw an error if the file doesn't exist and the
|
|
29
|
+
* code import the types from that file.
|
|
30
|
+
*/
|
|
31
|
+
try {
|
|
32
|
+
await fs.promises.access('schema/types.ts');
|
|
33
|
+
} catch {
|
|
34
|
+
await fs.promises.writeFile('schema/types.ts', '');
|
|
35
|
+
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
38
|
+
const { schemaComposer } = require(
|
|
39
|
+
path.resolve(process.cwd(), directory, 'schemaComposer.ts')
|
|
40
|
+
);
|
|
44
41
|
|
|
45
|
-
|
|
42
|
+
const sdl = schemaComposer.toSDL();
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
namingConvention: 'keep',
|
|
44
|
+
/**
|
|
45
|
+
* https://the-guild.dev/graphql/codegen/docs/advanced/programmatic-usage
|
|
46
|
+
*/
|
|
47
|
+
const codegenConfig = {
|
|
48
|
+
documents: [],
|
|
49
|
+
config: {
|
|
50
|
+
declarationKind: {
|
|
51
|
+
type: 'interface',
|
|
52
|
+
interface: 'interface',
|
|
58
53
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
pluginMap: {
|
|
67
|
-
typescript: typescriptPlugin,
|
|
54
|
+
namingConvention: 'keep',
|
|
55
|
+
},
|
|
56
|
+
filename: 'schema/types.ts',
|
|
57
|
+
schema: parse(sdl),
|
|
58
|
+
plugins: [
|
|
59
|
+
{
|
|
60
|
+
typescript: {},
|
|
68
61
|
},
|
|
69
|
-
|
|
62
|
+
],
|
|
63
|
+
pluginMap: {
|
|
64
|
+
typescript: typescriptPlugin,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
70
67
|
|
|
71
|
-
|
|
68
|
+
await fs.promises.writeFile('schema/schema.graphql', sdl);
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
log.info(logPrefix, 'Generating types...');
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
const typesOutput = await codegen(codegenConfig);
|
|
76
73
|
|
|
77
|
-
|
|
74
|
+
const typesOutputIgnore = ['/* eslint-disable */'].join('\n');
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
await fs.promises.writeFile(
|
|
77
|
+
'schema/types.ts',
|
|
78
|
+
`${typesOutputIgnore}\n${typesOutput}`
|
|
79
|
+
);
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
log.info(logPrefix, 'Schema and types generated!');
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
yargs(hideBin(process.argv))
|
|
85
|
+
.command(
|
|
86
|
+
'build-schema',
|
|
87
|
+
'fetch the contents of the URL',
|
|
88
|
+
(yargs) => {
|
|
89
|
+
return yargs.options({
|
|
90
|
+
directory: {
|
|
91
|
+
alias: ['d'],
|
|
92
|
+
type: 'string',
|
|
93
|
+
describe: 'Schema composer directory relative to the project root',
|
|
94
|
+
default: 'src',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
(argv) => {
|
|
99
|
+
return buildSchema(argv);
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
.demandCommand(1)
|
|
103
|
+
.strictOptions()
|
|
104
|
+
.parse();
|