DEV Community 👩‍💻👨‍💻

Cover image for Stand Up a Node.js Server with NestJS including TypeScript and GraphQL
Aryan J
Aryan J

Posted on • Updated on • Originally published at thewebdevcoach.com

Stand Up a Node.js Server with NestJS including TypeScript and GraphQL

Recently, I discovered NestJS and instantly fell in love. It is everything that I love about Angular (which includes TypeScript, the opinionated file structure, the modules, the decorators, and the dependency injection) in a Node framework. Additionally, it supports GraphQL.

Before We Get Started

This tutorial is not meant for beginners. It will not cover Node basics. If you're looking to learn how Node and Express work, I made a series of videos where I create and deploy a basic timestamp microservice. Additionally, it will not cover GraphQL basics.

I made a video tutorial based off of this blog post. I suggest reading this blog post and watching the video as they complement each other well.

This tutorial uses nest-cli version 6.6.4 and was written on September 18th, 2019. If anything is out of date or to report any errors/blockers, please feel free to send me a tweet.

If at any point you feel lost, you can take a look at the final form of this code.

With all of that out of the way, let's do this!

Getting Started with NestJS

NestJS is a Node.js framework that is compatible with both TypeScript and pure JavaScript. It comes with guard, pipe and interceptor support out-of-the-box. This makes it easy-to-use yet extremely powerful.

To get started, install the NestJS CLI. This allows you to easily create a new NestJS project.

npm i -g @nestjs/cli
nest new my-node-project

where my-node-project is the name of your Node project. If you have NPM and yarn installed, the NestJS will ask for your preference. If the project was created correctly, you should have a newly created project with the following structure:

.
├── README.md
├── nest-cli.json
├── package.json
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
├── tsconfig.build.json
├── tsconfig.json
├── tslint.json
└── yarn.lock

Now go into that directory and run your newly created Node server:

cd my-node-project
npm run start:dev

Go to http://localhost:3000 (by default) to hit your "Hello World!" endpoint.

Sprinkle in GraphQL

GraphQL is a query language for APIs. NestJS uses their own GraphQLModule (imported from @nestj/graphql) which is a wrapper around the Apollo GraphQL server.

Before we get started, let's remove the soon-to-be unused files (that were used for the "Hello World!" endpoint). More specifically, please delete src/app.controller.ts, src/app.service.ts, and their corresponding test files.

To get started with GraphQL and NestJS, install the necessary dependencies:

npm i --save @nestjs/graphql apollo-server-express graphql-tools graphql

With these packages installed, register the GraphQLModule in /src/app.module.ts:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { join } from 'path';

@Module({
  imports: [
    GraphQLModule.forRoot({
      definitions: {
        path: join(process.cwd(), '/src/graphql.schema.d.ts'),
        outputAs: 'class',
      },
      typePaths: ['./**/*.graphql'],
      resolverValidationOptions: {
        requireResolversForResolveType: false,
      },
    }),
  ],
})
export class AppModule {}

I know, I know. There are tons of changes in here that I threw at you all. The NestJS GraphQL documentation does a fantastic job at explaining these changes. Here's my take.

GraphQLModule.forRoot()

This registers the GraphQLModule with the server. The .forRoot() method takes an options object as an argument.

definitions

The @nestjs/graphql package automatically generates TypeScript defintions from the GraphQL schemas (see typePaths). We use the definitions object to configure the path where TypeScript definitions should be saved. By default, the GraphQL types are transformed to interfaces. I personally prefer classes which is what is seen in definitions.outputAs.

typePaths

typePaths tells the GraphQLModule where in the project to look for GraphQL files.

resolverValidationOptions

When running the server without resolverValidationOptions.requireResolversForResolveType equal to false, I get a warning similar to this one. Therefore, it's false (for now).

Alright, back to GraphQL. Add src/schema.graphql to your project as follows:

type Message {
  id: Int!
  description: String!
}

type Query {
  messages: [Message]!
}

type Mutation {
  createMessage(description: String!): Message!
}

Restart your Node server, go to http://localhost:3000/graphql and you'll see a GraphQL playground. Of course, any query or mutation you try running will end in an error as we're yet to write our resolvers.

Writing a GraphQL Resolver with NestJS

Let's write our first GraphQL resolver. Firstly, create a new NestJS module:

nest generate module messages

This will import the MessagesModule into AppModule and create a new src/messages directory where the business logic for your Messages resolver will live (see what I was saying about NestJS' modularity?).

Now, let's create that resolver. We'll create a dummy variable named messagesThatReallyShouldBeInADb that will function as our database and store all the messages and a GraphQL query that returns all the messages. In src/messages/messages.resolver.ts:

import { Resolver, Query } from '@nestjs/graphql';

@Resolver()
export class MessagesResolver {
  // this is just for demonstration purposes
  // do NOT do this in real-life
  // this is meant as a substitute for a database
  messagesThatReallyShouldBeInADb = [
    { id: 0, description: 'The seed message' },
  ];

  @Query()
  messages() {
    return this.messagesThatReallyShouldBeInADb;
  }
}

Note the decorators NestJS provides us (Resolver and Query). This automagically maps to the messages query that we declared in src/schema.graphql. We must now provide this resolver to the MessagesModule. In src/messages.module.ts:

import { Module } from '@nestjs/common';
import { MessagesResolver } from './messages.resolver';

@Module({
  providers: [MessagesResolver],
  exports: [MessagesResolver],
})
export class MessagesModule {}

Go to http://localhost:3000/graphql, refresh the page, and run the messages query:

{
  messages {
    description
  }
}

If everything was done correctly, you should see the seed message:
GraphQL playground with the messages query and the returned messages array with the seed message

Now let's add the createMessage mutation to src/messages/messages.resolver.ts. Remember the resolver type signature takes four arguments (parent, args, context, info). NestJS provides decorators for each argument. For this specific mutation, we use the @Args() decorator and pass it the name of the argument we want to access (description):

import { Mutation, Resolver, Query, Args } from '@nestjs/graphql';

@Resolver()
export class MessagesResolver {
  // this is just for demonstration purposes
  // do NOT do this in real-life
  // this is meant as a substitute for a databse
  messagesThatReallyShouldBeInADb = [
    { id: 0, description: 'The seed message' },
  ];

  @Query()
  messages() {
    return this.messagesThatReallyShouldBeInADb;
  }

  @Mutation()
  createMessage(@Args('description') description: string) {
    const id = this.messagesThatReallyShouldBeInADb.length;
    const newMessage = { id, description };
    this.messagesThatReallyShouldBeInADb.push(newMessage);
    return newMessage;
  }
}

With the mutation added to the resolver, let's return to our GraphQL Playground at http://localhost:3000/graphql and create some messages:

mutation {
  createMessage(description: "This is a witty description") {
    description
  }
}

which should successfully return:

GraphQL playground with the messages mutation and the returned message

Feel free to create a few messages using our new mutation and query for all messages.

Conclusion

With that, you now have a NestJS server complete with GraphQL, a simple GraphQL schema and a simple resolver for that schema (complete with a query and mutation). If you did everything correctly, the messages query and createMessage mutation should work as it does in this demo server. Again, if at any time you got lost and want to see the entire demo project, check out my GitHub repository.

The next step is to add a database to this stack. Prisma is an amazing solution that provides us with additional GraphQL and database tooling. In the next installment of this series, we will dive into using Prisma to save our messages.

If you liked this post, please support me by following me on Twitter, YouTube and GitHub.

Top comments (6)

Collapse
checkmatez profile image
Max

I wish there was React-style Node.js framework...
Nest.js is great, but I more of React person rather than Angular.

Collapse
heindauven profile image
Hein Dauven

If you mean barebones, then you already have that with Nodejs. Or Koa.

I really like Nestjs. It really forces you to work in an architecturally sound way.

There are a few things I dislike though, namely:

  • Too much boilerplate.
  • Docs don't help if you stray off the common path. (social providers auth with sessions for instance.)
Collapse
checkmatez profile image
Max • Edited on

No, not barebones, but with some actual architecture. Just without abstract classes, decorators and fabrics. Something more functional and declarative.

Koa isn't full blown framework, it is just web-server. Would be nice to have something on top.

Thread Thread
mucorolle profile image
Muco Rolle Tresor

This is what I wish too

Collapse
aryanjnyc profile image
Aryan J Author

Hey Hein:

You're right. The docs don't have recipes for many things (as you said, social providers auth with sessions). They do feature an authentication section that describes using passport.js to create an AuthenticationModule. Have you taken a look at that?

I'm looking for new material to write. Do you think a blog post / set of videos that cover social provider authentication would help?

Thread Thread
heindauven profile image
Hein Dauven • Edited on

Hey Aryan, yes I've followed the docs but didn't find it too helpful for the issues I faced. (backend & frontend on different ports, causing same site cookies to not be attached, lack of API docs for some strategies. Not knowing which parameters are available and what they do. )

A lot can be written about social providers and authentication, like:

  • sessions VS jwt, when one is preferred over the other.
  • Sessions and JWT in the same app. (sessions for users, jwt for API requests server to server)
  • Retrieving specific data from social providers
  • Implementing social providers the nestjs way.

Many of the examples I found online for Nestjs + social providers contained way too much code and didn't feel like a solid Nestjs way of doing things. A curated approach would be very welcome, be it in article and/or video form.

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.