# TypeGraphQL
Although TypeGraphQL is data-layer library agnostic, it integrates well with other decorator-based libraries, like TypeORM, sequelize-typescript or Typegoose.
# Installation
To begin, install the @tsed/typegraphql
package:
Now, we can configure the Ts.ED server by importing @tsed/typegraphql
in your Server:
# Types
We want to get the equivalent of this type described in SDL:
type Recipe {
id: ID!
title: String!
description: String
creationDate: Date!
ingredients: [String!]!
}
2
3
4
5
6
7
So we create the Recipe class with all properties and types:
class Recipe {
id: string;
title: string;
description?: string;
creationDate: Date;
ingredients: string[];
}
2
3
4
5
6
7
Then we decorate the class and its properties with decorators:
import {Field, ID, ObjectType} from "type-graphql";
@ObjectType()
export class Recipe {
@Field((type) => ID)
id: string;
@Field()
title: string;
@Field({nullable: true})
description?: string;
@Field()
creationDate: Date;
@Field((type) => [String])
ingredients: string[];
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The detailed rules for when to use nullable, array and others are described in fields and types docs (opens new window).
# Resolvers
After that we want to create typical crud queries and mutation. To do that we create the resolver (controller) class that will have injected RecipeService in the constructor:
import {Inject} from "@tsed/di";
import {ResolverController} from "@tsed/typegraphql";
import {Arg, Args, Query} from "type-graphql";
import {RecipeNotFoundError} from "../errors/RecipeNotFoundError";
import {RecipesService} from "../services/RecipesService";
import {Recipe} from "../types/Recipe";
import {RecipesArgs} from "../types/RecipesArgs";
@ResolverController(Recipe)
export class RecipeResolver {
@Inject()
private recipesService: RecipesService;
@Query((returns) => Recipe)
async recipe(@Arg("id") id: string) {
const recipe = await this.recipesService.findById(id);
if (recipe === undefined) {
throw new RecipeNotFoundError(id);
}
return recipe;
}
@Query((returns) => [Recipe])
recipes(@Args() {skip, take}: RecipesArgs) {
return this.recipesService.findAll({skip, take});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Inject Ts.ED Context
There are two ways to inject the Ts.ED context in your resolver.
The first one is to use the @InjectContext()
decorator:
import {Inject, InjectContext} from "@tsed/di";
import {PLatformContext} from "@tsed/common";
import {ResolverController} from "@tsed/typegraphql";
import {Arg, Args, Query} from "type-graphql";
import {Recipe} from "../types/Recipe";
@ResolverController(Recipe)
export class RecipeResolver {
@InjectContext()
private $ctx: PLatformContext;
@Query((returns) => Recipe)
async recipe(@Arg("id") id: string) {
this.$ctx.logger.info("Hello world");
console.log(this.$ctx.request.headers);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
The second one is to use the @Ctx()
decorator provided by TypeGraphQL:
import {ResolverController} from "@tsed/typegraphql";
import {Arg, Ctx, Query} from "type-graphql";
import {Recipe} from "../types/Recipe";
@ResolverController(Recipe)
export class RecipeResolver {
@InjectContext()
private $ctx: Context;
@Query((returns) => Recipe)
async recipe(@Arg("id") id: string, @Ctx("req.$ctx") $ctx: PlatformContext) {
$ctx.logger.info("Hello world");
console.log($ctx.request.headers);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Data Source
Data source is one of the Apollo server features which can be used as option for your Resolver or Query. Ts.ED provides a DataSourceService decorator to declare a DataSource which will be injected to the Apollo server context.
import {DataSource} from "@tsed/typegraphql";
import {RESTDataSource} from "apollo-datasource-rest";
import {User} from "../models/User";
@DataSource()
export class UserDataSource extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://myapi.com/api/users";
}
getUserById(id: string): Promise<User> {
return this.get(`/${id}`);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Then you can retrieve your data source through the context in your resolver like that:
import {ResolverController} from "@tsed/typegraphql";
import {Arg, Authorized, Ctx, Query} from "type-graphql";
import {UserDataSource} from "../datasources/UserDataSource";
import {User} from "../models/User";
@ResolverController(User)
export class UserResolver {
@Authorized()
@Query(() => User)
public async user(@Arg("userId") userId: string, @Ctx("dataSources") dataSources: any): Promise<User> {
const userDataSource: UserDataSource = dataSources.userDataSource;
return userDataSource.getUserById(userId);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Multiple GraphQL server
If you register multiple GraphQL servers, you must specify the server id in the @ResolverController
decorator.
@ResolverController(Recipe, {id: "server1"})
Another solution is to not use @ResolverController
(use @Resolver
from TypeGraphQL), and declare explicitly the resolver in the server configuration:
@Configuration({
graphql: {
server1: {
resolvers: {
RecipeResolver
}
},
server2: {
resolvers: {
OtherResolver
}
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
# Subscriptions
Ts.ED provides a @tsed/graphql-ws
package to support the subscription
feature of GraphQL. See here for more details.
# Author
# Maintainers
Last Updated: 10/24/2024, 6:33:40 AM
Other topics
- Session & cookies
- Passport.js
- Keycloak
- Prisma
- TypeORM
- MikroORM
- Mongoose
- GraphQL
- GraphQL WS
- Apollo
- TypeGraphQL
- GraphQL Nexus
- Socket.io
- Swagger
- AJV
- Multer
- Serve static files
- Templating
- Serverless HTTP
- Seq
- OIDC
- Stripe
- Agenda
- Terminus
- Serverless
- Server-sent events
- IORedis
- Vike
- Jest
- Vitest
- Controllers
- Providers
- Model
- JsonMapper
- Middlewares
- Pipes
- Interceptors
- Authentication
- Hooks
- Exceptions
- Throw HTTP Exceptions
- Cache
- Command
- Response Filter
- Injection scopes
- Custom providers
- Lazy-loading provider
- Custom endpoint decorator
- Testing
- Customize 404