Building GraphQL API with Nodejs, TypeGraphQL , Typegoose and Troubleshooting common challenges.

Building GraphQL API with Nodejs, TypeGraphQL , Typegoose and Troubleshooting common challenges.

Β·

6 min read

I recently started out with this project where I am using Apollo GraphQL with TypeGraphQL , typegoose as mongoose wrapper and Express.js to create an API. I have been searching for different articles on using TypeGraphQL with Typegoose but seems that no has used it before πŸ€·β€β™‚οΈ also individually their documentation are top notch , but when trying to use them together I encountered a lot of bugs and challenges.

So It’s probably worth posting out this article so that it could be an help for other developers and to briefly explain some of the codes and concepts around GraphQL.

I have created an boilerplate GraphQL API with Node.js, Apollo, TypeGraphQL , TypeScript , Nx, MongoDB , typegoose. You may want to check that out, here's the repository github.com/DevUnderflow/nx-node-apollo-grahql-mongo

What is GraphQL?

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

Basically it is new API standard that enables a better way of implementing API's and a great alternative to REST. In REST we need maintain a bunch API endpoints, While in GraphQL there is just a single API endpoint created.

So GraphQL has two major components :

  • Schema : GraphQL server uses a schema to describe the shape of request / graphs. This schema basically defines a hierarchy of types with fields that are populated from back-end data stores.

    type User {
       username: String
       email: String 
    }
    
  • Resolvers : Server needs to know how to populate data for every field in your schema so that it can respond to requests for that data. To accomplish this, it uses resolvers. Resolvers can be of three types-

    • Queries

      {
        user {
            username
            email
        }
      }
      
    • Mutations

      mutation {
       createUser(username: "test", email: "test@test.com") {
           username
           email
       } 
      }
      
    • Subscriptions
      subscription {
        users
      }
      

Why choose GraphQL over REST

Challenges with REST

We have been using REST API's for a long time now. But there exists some problems with REST like fetching data from REST API comes with lot of unnecessary data being fetched. Also there is a need to remember a lot of API endpoints. With GraphQL we describe in the client which data we want to have instead of just asking all the data.

GraphQL Solves the issue

In the backend, we need to define our data types which will form our schemas. and the resolvers to resolve the request coming frontend. In GraphQL we only need to maintain a single endpoint where we would request only those data that we need on the frontend.

GraphQL basically reduces the network call by enabling us to fetch all the data that we need in a single query from a single endpoint. GraphQL also comes with a set of challenges which we would discuss later in this article.

Using Apollo with TypeGraphQL

While the most used library for GraphQL is Apollo. Apollo Server comes with a lot of features like caching , external data loader and many more. Screenshot 2020-12-27 212843.png The Apollo Client is a tool which helps you use GraphQL in the frontend. It provides different features like in-memory caching, state management etc. It can be integrated with multiple JavaScript frameworks like React, React Native, Vue, Angular and for iOS and Android, there are also possibilities to use the Apollo Client.

TypeGraphQL

TypeGraphQL is modern framework for creating GraphQL API's. While apollo is really great and solves many problems that we have. But developing a GraphQL API in Node.js with TypeScript is sometimes a bit of a pain. This makes it easy to use with typescript as your first language.

Creating a sample resolver would look like :

@Resolver()
class UserResolver {
  private userCollection: User[] = [];

  async users() {
    return await this.userCollection;
  }
}

Using TypeGraphql with Typegoose

Well Typegoose is basically a TypeScript wrapper around mongoose. It is easy to use in typescript environment and provides a lot features.

The first challenge I encountered while Integrating typegoose with typegraphql is that I had to define multiple interfaces one for typegoose, then one for typegraphql schema, So there was lot of redundancy happing around. So the solution I found was just to use typgraphql decorators on top of typegoose methods.

@ObjectType()
export class User {
  @Field()
  readonly _id: string;

  @Field()
  @prop()
  public username: string;

  @Field()
  @IsEmail()
  @prop()
  public email: string;
}

So here we define our mongoDB models with prop() decorator from typegoose and along with we simultaneously define our GraphQL schemas with ObjectType() and Field() decorators form typegraphql. It basically removes all the redundant interfaces we had earlier.

The second challenge I encountered is that during the initial phase I was writing all my core logic directly into resolver methods which eventually created lot of problems in maintaining the codebase So the solution was that I started refactoring all my codebase into different folder structure. I started using typescript decorator features for services and used dependency Injector to inject all my Database models and Services directly into Resolvers.

import { Service, Inject } from "typedi";
@Service()
export class UserService {
  constructor(@Inject("userModel") private readonly UserModel) {}
  async getAll() {
    return this.UserModel.find();
  }
}

So here we are creating UserService which injects userModel. now once we have our service running we can inject this directly into our Resolvers as :

@Resolver()
export class UserResolver {
  constructor(
    private readonly userService: UserService,
  ) { }
  @Query(() => [User])
  async users() {
    return this.userService.getAll();
  }
}

Most Common Challenge you'll face

When using GraphQL we have a lot advantages but it comes with its own set of challenges or cons you would say. The most common problem you would face is that server making a lot of multiple request to the database then expected. Suppose you have a list of posts, in which each of post has a user document to it. Now you may want to fetch all of these posts, with user data. When using REST this would be like having two database calls. One for posts and one for users corresponding to these posts.

But what in GraphQL? we now have an extra call to fetch each user data per resolver that is per. Now let's say we have 10 posts and each post also has 5 comments, each of which has an user document. So the number of calls we'll have is one for the list of posts, 10 for the post authors, 10 for each sub-list of 5 comments and 50 for the comment users which sums up to around 71 database calls to fetch a set of data!

Nobody would want this to happen, waiting 15 secs to load a set of posts. To solve this problem we have Dataloader library.

Dataloader basically lets will let you combine or batch multiple similar requests and cache database calls. Now the dataloader detects that posts having similar id's and batch them together and will reuse the user document which it already has in memory instead of making a new database call.

So These were the challenges I have came across till now while building a GraphQL based API.

The source code for the Nx-Node-Apollo Graphql API is here github.com/DevUnderflow/nx-node-apollo-grahql-mongo

Thanks for reading, stay awesome! ❀

I hope you have enjoyed this article and you may avoid above problems beforehand , I also hope that if it gave you some sort of inspiration for your work.

If you may want to checkout other articles its right here blogs.smithgajjar.dev. Feel free to follow me on LinkedIn.

Do checkout my website at smithgajjar.dev.

Did you find this article valuable?

Support Smith Gajjar by becoming a sponsor. Any amount is appreciated!