Skip to content

Commit 09454c2

Browse files
author
hirsch
committed
Merge branch 'release/3.2.0'
2 parents 428f0e9 + 2ac7994 commit 09454c2

38 files changed

+358
-654
lines changed

.env.example

+1-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ CONTROLLERS=src/api/controllers/**/*Controller.ts
5151
MIDDLEWARES=src/api/middlewares/**/*Middleware.ts
5252
INTERCEPTORS=src/api/interceptors/**/*Interceptor.ts
5353
SUBSCRIBERS=src/api/subscribers/**/*Subscriber.ts
54-
QUERIES=src/api/queries/**/*Query.ts
55-
MUTATIONS=src/api/mutations/**/*Mutation.ts
54+
RESOLVERS=src/api/resolvers/**/*Resolver.ts
5655

5756
#
5857
# GraphQL

.env.test

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ CONTROLLERS=src/api/controllers/**/*Controller.ts
3333
MIDDLEWARES=src/api/middlewares/**/*Middleware.ts
3434
INTERCEPTORS=src/api/interceptors/**/*Interceptor.ts
3535
SUBSCRIBERS=src/api/subscribers/**/*Subscriber.ts
36-
QUERIES=src/api/queries/**/*Query.ts
37-
MUTATIONS=src/api/mutations/**/*Mutation.ts
36+
RESOLVERS=src/api/resolvers/**/*Resolver.ts
3837

3938
#
4039
# GraphQL

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ yarn-error.log
1515
.DS_Store
1616
Thumbs.db
1717
.tmp/
18+
src/api/schema.gql
1819

1920
# Typing #
2021
typings/

README.md

+56-4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Try it!! We are happy to hear your feedback or any kind of new features.
5555
- **Fast Database Building** with simple migration from [TypeORM](https://github.com/typeorm/typeorm).
5656
- **Easy Data Seeding** with our own factories.
5757
- **GraphQL** provides as a awesome query language for our api [GraphQL](http://graphql.org/).
58+
- **TypeGraphQL** thanks to [TypeGraphQL](https://19majkel94.github.io/type-graphql/) we have a some cool decorators to simplify the usage of GraphQL.
5859
- **DataLoaders** helps with performance thanks to caching and batching [DataLoaders](https://github.com/facebook/dataloader).
5960

6061
![divider](./w3tec-divider.png)
@@ -69,6 +70,8 @@ Try it!! We are happy to hear your feedback or any kind of new features.
6970
- [Logging](#-logging)
7071
- [Event Dispatching](#-event-dispatching)
7172
- [Seeding](#-seeding)
73+
- [GraphQL](#-graph-q-l)
74+
- [Docker](#-docker)
7275
- [Further Documentations](#-further-documentation)
7376
- [Related Projects](#-related-projects)
7477
- [License](#-license)
@@ -209,9 +212,9 @@ The swagger and the monitor route can be altered in the `.env` file.
209212
| **src/api/services/** | Service layer |
210213
| **src/api/subscribers/** | Event subscribers |
211214
| **src/api/validators/** | Custom validators, which can be used in the request classes |
212-
| **src/api/queries/** | GraphQL queries |
213-
| **src/api/mutations/** | GraphQL mutations |
214-
| **src/api/types/** | GraphQL types |
215+
| **src/api/resolvers/** | GraphQL resolvers (query, mutation & field-resolver) |
216+
| **src/api/types/** | GraphQL types ,input-types and scalar types |
217+
| **src/api/** schema.gql | Generated GraphQL schema |
215218
| **src/api/** swagger.json | Swagger documentation |
216219
| **src/auth/** | Authentication checkers and services |
217220
| **src/core/** | The core features like logger and env variables |
@@ -392,7 +395,56 @@ yarn start db.seed
392395
393396
![divider](./w3tec-divider.png)
394397
395-
## ❯ Run in Docker container
398+
## ❯ GraphQL
399+
400+
For the GraphQL part we used the library [TypeGraphQL](https://19majkel94.github.io/type-graphql/) to build awesome GraphQL API's.
401+
402+
The context(shown below) of the GraphQL is builded in the **graphqlLoader.ts** file. Inside of this loader we create a scoped container for each incoming request.
403+
404+
```typescript
405+
export interface Context {
406+
requestId: number;
407+
request: express.Request;
408+
response: express.Response;
409+
container: ContainerInstance;
410+
}
411+
```
412+
413+
### DataLoader
414+
415+
For the usage of the DataLoaders we created a annotation, which automatically creates and registers a new DataLoader to the scoped container.
416+
417+
Here is an example of the **PetResolver**.
418+
419+
```typescript
420+
import DataLoader from 'dataloader';
421+
import { DLoader } from '../../decorators/DLoader';
422+
...
423+
constructor(
424+
private petService: PetService,
425+
@Logger(__filename) private log: LoggerInterface,
426+
@DLoader(UserModel) private userLoader: DataLoader<string, UserModel>
427+
) { }
428+
...
429+
```
430+
431+
Or you could use the repository too.
432+
433+
```typescript
434+
@DLoader(UserRepository) private userLoader: DataLoader<string, UserModel>
435+
```
436+
437+
Or even use a custom method of your given repository.
438+
439+
```typescript
440+
@DLoader(PetRepository, {
441+
method: 'findByUserIds',
442+
key: 'userId',
443+
multiple: true,
444+
}) private petLoader: DataLoader<string, PetModel>
445+
```
446+
447+
## ❯ Docker
396448
397449
### Install Docker
398450

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "express-typescript-boilerplate",
3-
"version": "3.1.0",
3+
"version": "3.2.0",
44
"description": "A delightful way to building a Node.js RESTful API Services with beautiful code written in TypeScript",
55
"main": "src/app.ts",
66
"scripts": {
@@ -40,7 +40,7 @@
4040
"dependencies": {
4141
"bcrypt": "3.0.1",
4242
"chalk": "^2.4.1",
43-
"class-validator": "0.9.1",
43+
"class-validator": "^0.9.1",
4444
"commander": "^2.19.0",
4545
"compression": "^1.7.1",
4646
"copyfiles": "^2.1.0",
@@ -71,6 +71,7 @@
7171
"swagger-ui-express": "4.0.1",
7272
"ts-node": "7.0.1",
7373
"tslint": "^5.8.0",
74+
"type-graphql": "^0.15.0",
7475
"typedi": "0.8.0",
7576
"typeorm": "^0.2.5",
7677
"typeorm-seeding": "^1.0.0-beta.6",

src/api/Context.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import express from 'express';
2+
import { ContainerInstance } from 'typedi';
3+
4+
export interface Context {
5+
requestId: number;
6+
request: express.Request;
7+
response: express.Response;
8+
container: ContainerInstance;
9+
}

src/api/models/Pet.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class Pet {
2121
name: 'user_id',
2222
nullable: true,
2323
})
24-
public userId: number;
24+
public userId: string;
2525

2626
@ManyToOne(type => User, user => user.pets)
2727
@JoinColumn({ name: 'user_id' })

src/api/mutations/CreatePetMutation.ts

-36
This file was deleted.

src/api/queries/GetPetsQuery.ts

-28
This file was deleted.

src/api/queries/GetUsersQuery.ts

-28
This file was deleted.

src/api/resolvers/PetResolver.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import DataLoader from 'dataloader';
2+
import { Arg, Ctx, FieldResolver, Mutation, Query, Resolver, Root } from 'type-graphql';
3+
import { Service } from 'typedi';
4+
5+
import { DLoader } from '../../decorators/DLoader';
6+
import { Logger, LoggerInterface } from '../../decorators/Logger';
7+
import { Context } from '../Context';
8+
import { Pet as PetModel } from '../models/Pet';
9+
import { User as UserModel } from '../models/User';
10+
import { PetService } from '../services/PetService';
11+
import { PetInput } from '../types/input/PetInput';
12+
import { Pet } from '../types/Pet';
13+
14+
@Service()
15+
@Resolver(of => Pet)
16+
export class PetResolver {
17+
18+
constructor(
19+
private petService: PetService,
20+
@Logger(__filename) private log: LoggerInterface,
21+
@DLoader(UserModel) private userLoader: DataLoader<string, UserModel>
22+
) { }
23+
24+
@Query(returns => [Pet])
25+
public pets(@Ctx() { requestId }: Context): Promise<PetModel[]> {
26+
this.log.info(`{${requestId}} Find all users`);
27+
return this.petService.find();
28+
}
29+
30+
@Mutation(returns => Pet)
31+
public async addPet(@Arg('pet') pet: PetInput): Promise<PetModel> {
32+
const newPet = new PetModel();
33+
newPet.name = pet.name;
34+
newPet.age = pet.age;
35+
return this.petService.create(newPet);
36+
}
37+
38+
@FieldResolver()
39+
public async owner(@Root() pet: PetModel): Promise<any> {
40+
if (pet.userId) {
41+
return this.userLoader.load(pet.userId);
42+
}
43+
// return this.userService.findOne(`${pet.userId}`);
44+
}
45+
46+
// user: createDataLoader(UserRepository),
47+
48+
// petsByUserIds: createDataLoader(PetRepository, {
49+
// method: 'findByUserIds',
50+
// key: 'userId',
51+
// multiple: true,
52+
// }),
53+
54+
}

src/api/resolvers/UserResolver.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { FieldResolver, Query, Resolver, Root } from 'type-graphql';
2+
import { Service } from 'typedi';
3+
4+
import { User as UserModel } from '../models/User';
5+
import { PetService } from '../services/PetService';
6+
import { UserService } from '../services/UserService';
7+
import { User } from '../types/User';
8+
9+
@Service()
10+
@Resolver(of => User)
11+
export class UserResolver {
12+
13+
constructor(
14+
private userService: UserService,
15+
private petService: PetService
16+
) {}
17+
18+
@Query(returns => [User])
19+
public users(): Promise<any> {
20+
return this.userService.find();
21+
}
22+
23+
@FieldResolver()
24+
public async pets(@Root() user: UserModel): Promise<any> {
25+
return this.petService.findByUser(user);
26+
}
27+
28+
}

src/api/services/PetService.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Service } from 'typedi';
22
import { OrmRepository } from 'typeorm-typedi-extensions';
3+
import uuid from 'uuid';
34

45
import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher';
56
import { Logger, LoggerInterface } from '../../decorators/Logger';
@@ -38,6 +39,7 @@ export class PetService {
3839

3940
public async create(pet: Pet): Promise<Pet> {
4041
this.log.info('Create a new pet => ', pet.toString());
42+
pet.id = uuid.v1();
4143
const newPet = await this.petRepository.save(pet);
4244
this.eventDispatcher.dispatch(events.pet.created, newPet);
4345
return newPet;

src/api/services/UserService.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Service } from 'typedi';
22
import { OrmRepository } from 'typeorm-typedi-extensions';
3+
import uuid from 'uuid';
34

45
import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher';
56
import { Logger, LoggerInterface } from '../../decorators/Logger';
@@ -22,12 +23,13 @@ export class UserService {
2223
}
2324

2425
public findOne(id: string): Promise<User | undefined> {
25-
this.log.info('Find all users');
26+
this.log.info('Find one user');
2627
return this.userRepository.findOne({ id });
2728
}
2829

2930
public async create(user: User): Promise<User> {
3031
this.log.info('Create a new user => ', user.toString());
32+
user.id = uuid.v1();
3133
const newUser = await this.userRepository.save(user);
3234
this.eventDispatcher.dispatch(events.user.created, newUser);
3335
return newUser;

0 commit comments

Comments
 (0)