DEV Community

cbb-it-minds for IT Minds

Posted on • Originally published at insights.it-minds.dk

How to architecture your JavaScript API using NestJS with a GraphQL API example part 2/2.

This is a follow up article to the previous introduction to NestJS (read the prior article here). After the introduction and setup, we are now ready to start building our resolvers for our API.

Build APIs in NestJS

When we are building APIs in NestJS, we primarily work in 3 different types of files. The files .service, .resolvers and .types.graphql. all have their own responsibilities:

The Types

The types is where we define the Queries, Mutations InputTypes and other GraphQL specifications which we want in our Schema. Our setup in the previous article (take a look in app.module.ts) join all our .graphql-files in one Schema.

The Service

If we think of a service in Angular, we primarily use these to prevent the component itself against performing requests to resources. Hence we use the power of dependency injection to inject these services to the component and call the service when need be for resources.

Same goes for the service in NestJS! Due to this separation we can easily test the functionality of services and its "users", so to speak. We use these services to acquire resources. Usually I use the service for business specific logic as well. Say you want to make some recommendation logic for our products.

The Resolvers

Resolvers are just resolvers as we know them. Here we define the logic for our Queries and Mutations. Usually I keep these as clean as possible and force logic into the services. Typically this results in delegation of the arguments from the Mutation and Query to a service.

With these type of files in mind, I think it is time for us to start creating the API.

Product Types to our Schema

We start by creating our Product-type and a corresponding inputType. We want to be able to create a Product and hereafter find it again by its ID. Hence we create a Query and a Mutation as well:


"""
products.types.graphql
"""

type Query {
  productGetById(id: ID!) Product
}

type Mutation {
  productCreate(createProductInput: CreateProductInput!): Product!
}

type Product {
  id: ID!
  title: String!
  brand: String!
  currentPrice: Float!
}

input CreateProductInput {
  title: String!
  brand: String!
  currentPrice: Float!
}

As we save the file you might find that the compiler generated a new graphql.ts-file. In this file you will see generated interfaces from the GraphQL types we just defined.

// graphql.ts

export interface Product {
    _id: string;
    title: string;
    brand: string;
    currentPrice: number;
}

export interface IQuery {
    getProductById(id: string): Product | Promise<Product>;
}

export interface IMutation {
    createProduct(createProductInput: CreateProductInput): Product | Promise<Product>;
}

export interface CreateProductInput {
    title: string;
    brand: string;
    currentPrice: number;
}

Now we can finally create our resolvers in our product.resolvers.ts-file. Looking in the file we notice it looks a lot like a service class from Angular. With the power of decorators and dependency injection we notice how the framework helps us being consistent with our resolvers.

@Resolver('Product')
export class ProductsResolvers {
  constructor(
    private readonly productsService: ProductsService,
  ) {}

  // specifying what mutation to use
  @Mutation('createProduct')
  async create(@Args('createProductInput') args: CreateProductInput) {
    return await this.productsService.createOne(args);
  }

  // naming convention to use this query
  @Query()
  async getProductById(id: string) {
    return await this.productsService.findById(id);
  }

}

All we need now is to implement the service and we are ready to test the resolvers. The products.service.ts-file is where we fetch the data from a database or other sources.

@Injectable()
export class ProductsService {
  constructor(private readonly productRepository: ProductRepository) {}

  async findOneById(id: string) {
    return await this.productRepository.findOne({ _id: id });
  }

  async createOne(product: Product) {
    return await this.productRepository.createOne(product);
  }
}

Now run the application and see that it's working ! You can use either the playground or Postman/Insomnia.

You are now ready to go HAM and implement your new NodeJs server in a new strict way!

Top comments (0)