# 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!]!
      }
      
      1
      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[];
      }
      
      1
      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[];
      }
      
      1
      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});
        }
      }
      
      1
      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);
        }
      }
      
      1
      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);
        }
      }
      
      1
      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}`);
        }
      }
      
      1
      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);
        }
      }
      
      1
      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"})
      
      1

      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
            }
          }
        }
      })
      
      1
      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: 4/26/2024, 5:13:34 PM

          Other topics