Skip to content
This repository was archived by the owner on May 21, 2021. It is now read-only.

Commit 58a14c0

Browse files
authored
test: relay global object identification
This PR is a continuation of the #3. It introduces tests for global object identification and also a few changes. I noticed that most people weren't actually *using* the boilerplate but copying-and-pasting files. That wasn't actually my initial idea and for that reason I decided to remove the product module from the source. It will allow anyone to use the boilerplate following the docs to create their own modules (domain based).
1 parent 5543374 commit 58a14c0

18 files changed

+287
-214
lines changed

src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import gqlLoader from './loaders/gql.loader'
1010
const { APPLICATION_NAME, SERVER_HOSTNAME, SERVER_PORT } = process.env
1111

1212
const bootstrap = async () => {
13-
await databaseLoader()
13+
const database = await databaseLoader()
1414

1515
const application = expressLoader()
1616

17-
gqlLoader(application)
17+
gqlLoader(application, database)
1818

1919
application.listen({ hostname: SERVER_HOSTNAME, port: SERVER_PORT }, () => {
2020
consola.info(APPLICATION_NAME)

src/interfaces/context.interface.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Connection, Repository } from 'typeorm'
2+
3+
import { Node } from '../relay/node.interface'
4+
5+
export interface Context {
6+
database: Connection
7+
repositories: Record<string, Repository<Node>>
8+
}

src/loaders/gql.loader.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ import { Express } from 'express'
33
import { GraphQLSchema } from 'graphql'
44
import { buildSchema, BuildSchemaOptions } from 'type-graphql'
55
import Container from 'typedi'
6+
import { Connection } from 'typeorm'
67

7-
import { NodeResolver } from '../relay/node.resolver'
8+
import { Context } from '../interfaces/context.interface'
89
import { isDevelopment } from '../utils'
910

1011
const { GRAPHQL_PATH } = process.env
1112

1213
export const createSchema = (
1314
options?: Partial<BuildSchemaOptions>
1415
): Promise<GraphQLSchema> => {
15-
const defined = {
16-
resolvers: [NodeResolver, `${__dirname}/../modules/**/*.resolver.{ts,js}`],
16+
const defined: BuildSchemaOptions = {
17+
resolvers: [`${__dirname}/../{modules,relay}/**/*.resolver.{ts,js}`],
1718
container: Container,
1819
emitSchemaFile: isDevelopment
1920
}
@@ -24,11 +25,17 @@ export const createSchema = (
2425
} as BuildSchemaOptions)
2526
}
2627

27-
export default async (app: Express): Promise<void> => {
28+
export default async (app: Express, database: Connection): Promise<void> => {
2829
const schema = await createSchema()
2930

31+
const context: Context = {
32+
database,
33+
repositories: {}
34+
}
35+
3036
const apolloServer = new ApolloServer({
31-
schema
37+
schema,
38+
context
3239
})
3340

3441
apolloServer.applyMiddleware({ app, path: GRAPHQL_PATH })

src/modules/.gitkeep

Whitespace-only changes.

src/modules/product/inputs/product.input.ts

-13
This file was deleted.

src/modules/product/payloads/add-product.payload.ts

-9
This file was deleted.

src/modules/product/product.connection.ts

-7
This file was deleted.

src/modules/product/product.edge.ts

-7
This file was deleted.

src/modules/product/product.entity.ts

-33
This file was deleted.

src/modules/product/product.resolver.ts

-32
This file was deleted.

src/modules/product/product.service.ts

-27
This file was deleted.

src/relay/node.resolver.ts

+29-26
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,20 @@ import { UserInputError } from 'apollo-server-express'
22
import { fromGlobalId, toGlobalId } from 'graphql-relay'
33
import {
44
Arg,
5+
Ctx,
56
FieldResolver,
67
ID,
78
Info,
89
Query,
910
Resolver,
1011
Root
1112
} from 'type-graphql'
12-
import { Inject } from 'typedi'
1313

14-
import { ProductService } from '../modules/product/product.service'
14+
import { Context } from '../interfaces/context.interface'
1515
import { Node } from './node.interface'
1616

1717
@Resolver(() => Node)
1818
export class NodeResolver {
19-
@Inject()
20-
private readonly productService!: ProductService
21-
2219
@FieldResolver()
2320
globalId(
2421
@Root() { id }: { id: string },
@@ -27,39 +24,45 @@ export class NodeResolver {
2724
return toGlobalId(name, id)
2825
}
2926

27+
private async fetcher(
28+
globalId: string,
29+
{ repositories }: Context
30+
): Promise<Node | undefined> {
31+
const { type, id } = fromGlobalId(globalId)
32+
33+
const repository = repositories[type]
34+
35+
if (!repository) {
36+
throw new UserInputError(
37+
`Could not resolve to a node with the global ID of '${globalId}'`
38+
)
39+
}
40+
41+
return repository.findOne(id)
42+
}
43+
3044
// TODO: use dataloader
31-
// TODO: find a better way to automate and avoid if conditions
3245
@Query(() => Node, {
3346
nullable: true,
3447
description: 'Fetches an object given its global ID.'
3548
})
3649
async node(
37-
@Arg('id', () => ID, {
38-
description: 'The global ID of the object.'
39-
})
40-
globalId: string
41-
): Promise<Node | undefined> {
42-
const { type, id } = fromGlobalId(globalId)
43-
44-
if (type == 'Product') {
45-
return this.productService.findById(id)
46-
}
47-
48-
throw new UserInputError(
49-
`Could not resolve to a node with the global ID of '${globalId}'`
50-
)
50+
@Arg('id', () => ID, { description: 'The global ID of the object.' })
51+
globalId: string,
52+
@Ctx() context: Context
53+
): ReturnType<NodeResolver['fetcher']> {
54+
return this.fetcher(globalId, context)
5155
}
5256

5357
@Query(() => [Node], {
5458
nullable: 'items',
5559
description: 'Fetches objects given their global IDs.'
5660
})
5761
async nodes(
58-
@Arg('ids', () => [ID], {
59-
description: 'The global IDs of the objects.'
60-
})
61-
globalIds: Array<string>
62-
): Promise<Array<ReturnType<NodeResolver['node']>>> {
63-
return globalIds.map(id => this.node(id))
62+
@Arg('ids', () => [ID], { description: 'The global IDs of the objects.' })
63+
globalIds: Array<string>,
64+
@Ctx() context: Context
65+
): Promise<Array<ReturnType<NodeResolver['fetcher']>>> {
66+
return globalIds.map(id => this.fetcher(id, context))
6467
}
6568
}

0 commit comments

Comments
 (0)