Skip to content

Commit 2f1f032

Browse files
committed
add generic type support and improve type safety in GraphQL resolvers
1 parent 1824943 commit 2f1f032

File tree

3 files changed

+46
-32
lines changed

3 files changed

+46
-32
lines changed

src/lib/mutation.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@ import { GraphQLID } from 'graphql';
33

44
type GraphQLTypeLike = unknown; // minimal due to local graphql shim
55

6-
const buildMutation = (
6+
const buildMutation = <T extends Record<string, unknown>>(
77
name: string,
88
type: GraphQLTypeLike,
99
input: GraphQLTypeLike,
10-
model: Entity,
10+
model: Entity<T>,
1111
) => {
12-
const resolver = new Resolver(model);
12+
const resolver = new Resolver<T>(model);
1313
return {
1414
[`create${name}`]: {
1515
type: type as unknown,
1616
args: {
1717
input: { type: input as unknown },
1818
},
19-
resolve: (_value: unknown, { input }: { input: Record<string, unknown> }) => {
19+
resolve: (_value: unknown, { input }: { input: Partial<T> }) => {
2020
return resolver.create(input);
2121
},
2222
},
@@ -26,10 +26,7 @@ const buildMutation = (
2626
id: { type: GraphQLID },
2727
input: { type: input as unknown },
2828
},
29-
resolve: (
30-
_value: unknown,
31-
{ id, input }: { id: string | number; input: Record<string, unknown> },
32-
) => {
29+
resolve: (_value: unknown, { id, input }: { id: string | number; input: Partial<T> }) => {
3330
return resolver.update(id, input);
3431
},
3532
},

src/lib/query.ts

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
11
import { GraphQLList, GraphQLInt, GraphQLBoolean, GraphQLString } from 'graphql';
2-
import Resolver, { Entity } from './resolver';
2+
import Resolver, { Entity, AllOptions } from './resolver';
33
import dotenv from 'dotenv';
44

55
dotenv.config();
66

77
type GraphQLCompositeType = unknown; // from local graphql shim, keep minimal
88

9-
type ResolveArgs = {
10-
id?: number;
9+
type ResolveByIdArgs = {
10+
id: number;
11+
page?: never;
12+
limit?: never;
13+
offset?: never;
14+
all?: never;
15+
order?: never;
16+
};
17+
18+
type ResolveListArgs = {
19+
id?: undefined;
1120
page?: number;
1221
limit?: number;
1322
offset?: number;
1423
all?: boolean;
1524
order?: string;
1625
};
1726

18-
const buildQuery = (name: string, type: GraphQLCompositeType, model: Entity) => {
19-
const resolver = new Resolver(model);
27+
type ResolveArgs = ResolveByIdArgs | ResolveListArgs;
28+
29+
const buildQuery = <T extends Record<string, unknown>>(
30+
name: string,
31+
type: GraphQLCompositeType,
32+
model: Entity<T>,
33+
) => {
34+
const resolver = new Resolver<T>(model);
2035
return {
2136
[name]: {
2237
type: new GraphQLList(type as unknown),
@@ -28,23 +43,23 @@ const buildQuery = (name: string, type: GraphQLCompositeType, model: Entity) =>
2843
all: { type: GraphQLBoolean },
2944
order: { type: GraphQLString },
3045
},
31-
resolve: (_value: unknown, { id, page, limit, offset, all, order }: ResolveArgs) => {
32-
return (() => {
33-
if (id) {
34-
return [resolver.find(id)];
35-
} else {
36-
const settings =
37-
limit || page || offset || order
38-
? {
39-
currentPage: page ? page : 0,
40-
limit: limit ? limit : Number(process.env.APP_PERPAGE),
41-
offset: offset ? offset : 0,
42-
order: order ? order : '',
43-
}
44-
: { all };
45-
return resolver.all(settings);
46-
}
47-
})();
46+
resolve: (
47+
_value: unknown,
48+
{ id, page, limit, offset, all, order }: ResolveArgs,
49+
): Promise<T[]> | Array<Promise<T | null>> => {
50+
if (id !== undefined) {
51+
return [resolver.find(id)];
52+
}
53+
const settings: AllOptions =
54+
limit || page || offset || order
55+
? {
56+
currentPage: page ? page : 0,
57+
limit: limit ? limit : Number(process.env.APP_PERPAGE),
58+
offset: offset ? offset : 0,
59+
order: order ? order : '',
60+
}
61+
: { all };
62+
return resolver.all(settings);
4863
},
4964
},
5065
} as Record<string, unknown>;

src/lib/resolver.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ export interface Entity<T = Record<string, unknown>> {
1515
update(item: Partial<T>, opts: UpdateOptions): Promise<unknown>;
1616
}
1717

18-
type AllOptions = {
18+
export type AllOptions = {
1919
currentPage?: number;
2020
limit?: number;
2121
offset?: number;
2222
order?: string;
2323
all?: boolean;
2424
};
2525

26+
export type QueryOptions = Pick<AllOptions, 'currentPage' | 'limit' | 'offset' | 'order'>;
27+
2628
class Resolver<T extends Record<string, unknown> = Record<string, unknown>> {
2729
private entity: Entity<T>;
2830

@@ -45,7 +47,7 @@ class Resolver<T extends Record<string, unknown> = Record<string, unknown>> {
4547
Object.assign(defaults, { order: hasNameField ? 'name ASC' : options?.order });
4648

4749
const settings = Object.assign({}, defaults, options || {});
48-
const queryOptions: Record<string, unknown> = {};
50+
const queryOptions: QueryOptions = {};
4951

5052
if (!settings.all) {
5153
Object.assign(queryOptions, {

0 commit comments

Comments
 (0)