Simple example GraphQL(Schema First) with NestJS 9.x/10.x 😻
Tags: nestjs, typescript, graphql, postgres
Published: 2022-02-07 18:10:33

In this post I will show you how to implement NestJS😻 with GraphQL, in schema first mode. For those who would like to use the code first mode I made a post [here](https://dev.to/tony133/simple-example-graphql-with-nestjs-8-x-3l5i)

For those unfamiliar with or unfamiliar with [NestJS](https://docs.nestjs.com/), it's a Node.js TypeScript framework that helps you build efficient and scalable enterprise-grade Node.js applications.

While for those unfamiliar with [GraphQL](https://graphql.org/) it is a powerful query language for APIs and a runtime to satisfy those queries with existing data. It's an elegant approach that solves many typical REST API problems.

I attach here the link to see the difference between [GraphQL vs REST](https://www.apollographql.com/blog/graphql-vs-rest-5d425123e34b/).

The ways to use [GraphQL](https://graphql.org/) are two first the code or first the schema, the first from the code we create the schema for GraphQL while the second we create our schema that interfaces with GraphQL

In this example we are going to see the schema first mode with the use of [TypeORM](https://typeorm.io/#/), but you can use whatever you like.


Well now after this short intro let's get started!

### So let's get started by creating the NestJS app

Open Terminal and install CLI for NestJS, if you have already installed it, skip this step.

```bash
$ npm i -g @nestjs/cli
```
Then create a NestJS project.
```bash
$ nest new nestjs-graphql-schema-first
$ cd nestjs-graphql-schema-first
// start the application
$ npm run start:dev
```
Open the browser on `localhost:3000` to verify that hello world is displayed.

then we create a docker-compose.yml file for create service PostgreSQL

```yaml
version: "3"
services:
  db:
    image: postgres
    restart: always
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: pass123
      POSTGRES_DB: postgres

```

for those who do not know what docker is I leave you the link here for more information: https://www.docker.com/get-started

There are many different ways to integrate Nest with databases, and all of them depend on personal preferences or project needs.

### Install graphql dependencies and devDependencies

```bash
$ npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
```

Create a `GraphqlOptions` class for GraphQL settings, as follows:

```typescript
import { GqlOptionsFactory } from '@nestjs/graphql';
import { Injectable } from '@nestjs/common';
import { join } from 'path';
import { ApolloDriverConfig } from '@nestjs/apollo';

@Injectable()
export class GraphqlOptions implements GqlOptionsFactory {
  createGqlOptions(): Promise<ApolloDriverConfig> | ApolloDriverConfig {
    return {
      installSubscriptionHandlers: true,
      debug: true,
      playground: true,
      typePaths: ['./**/*.graphql'],
      definitions: {
        path: join(process.cwd(), 'src/graphql.schema.ts'),
        outputAs: 'class',
      },
    };
  }
}

```

An important thing about this configuration is to set where we want the `graphql.schema.ts` file to be generated, you can call it whatever you want, the important thing is to prefix it with` * .ts`.

Well now let's move on to registering `GraphQLModule` in `AppModule`:

```typescript
@Module({
    GraphQLModule.forRootAsync<ApolloDriverConfig>({
      driver: ApolloDriver,
      useClass: GraphqlOptions,
    }),
})
export class AppModule {}

```

Now let's create a file called `users.graphql`, where we are going to write our scheme that allows us to interact later with `Graphql Playground`:

```
type User {
  id: ID
  name: String!
  email: String!
  username: String!
  password: String!
}

input CreateUserInput {
  name: String!
  email: String!
  username: String!
  password: String!
}

input UpdateUserInput {
  name: String!
  email: String!
  username: String!
  password: String!
}

type Query {
  users: [User]!
  user(id: Int!): User
}

type Mutation {
  createUser(createUserInput: CreateUserInput!): User!
  updateUser(id: Int!, updateUserInput: UpdateUserInput!): User!
  removeUser(id: Int!): User
}

```

In this file we have set up a lot of things before, we have a User object type and two inputs which are like `DTO`, which in turn allows us to define how the request should look. To complete our `users.graphql` file we have created two other types called `Query` and `Mutation`.
In very simple words, the use of the `query` in `graphql` is used to retrieve the data while the `mutation` is used for operations such as inserting and updating data or to delete data.

Well, now let's move on to installing dependencies for Typeorm.


### Install TypeORM and PostgreSQL dependencies

```sh
$ npm install --save @nestjs/typeorm typeorm pg
```
We also install another package for configuration files such as `.env` files (environments)

```sh
$ npm install --save @nestjs/config
```

Set `TypeOrmModule` and `ConfigModule` in `AppModule`

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module ({
   imports: [
    ConfigModule.forRoot(),
    TypeOrmModule.forRootAsync({
      useFactory: () => ({
        type: 'postgres',
        host: process.env.DATABASE_HOST,
        port: +process.env.DATABASE_PORT,
        username: process.env.DATABASE_USER,
        password: process.env.DATABASE_PASSWORD,
        database: process.env.DATABASE_NAME,
        autoLoadEntities: true,
        synchronize: true,
      }),
    }),
   ],
})
export class AppModule {}
```

Create a .env file, as follows:

```
  DATABASE_HOST=localhost
  DATABASE_USER=postgres
  DATABASE_PASSWORD=pass123
  DATABASE_NAME=postgres
  DATABASE_PORT=5432
```

If you have trouble setting up `TypeOrmModule`, make sure Docker is running with `docker compose up`.
Also make sure the database name inside your `.env` matches the one you have in your `docker-compose` file.

Well now let's create our entity we call it `users.entity.ts`:

```typescript
import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
} from 'typeorm';
import { Field, ID, ObjectType } from '@nestjs/graphql';

@Entity()
@ObjectType()
export class Users {
  @Field(() => ID)
  @PrimaryGeneratedColumn()
  id: number;

  @Field()
  @Column()
  name: string;

  @Field()
  @Column()
  username: string;

  @Field()
  @Column()
  email: string;

  @Field()
  @Column({ length: 60 })
  password: string;
}
```

Create Data Transfer Objects (Dto) class to create the user, which we will call `CreateUserInput`:

```typescript
import { Field, InputType } from '@nestjs/graphql';
import { IsEmail, IsNotEmpty, IsString, MaxLength } from 'class-validator';

@InputType()
export class CreateUserInput {
  @Field()
  @IsString()
  readonly name: string;

  @Field()
  @IsEmail()
  readonly email: string;

  @Field()
  @IsString()
  @MaxLength(40)
  readonly username: string;

  @Field()
  @IsNotEmpty()
  @IsString()
  @MaxLength(60)
  password: string;
}

```

Remember to install this package before creating the dto class for the upgrade.

```sh
$ npm i @nestjs/mapped-types
```

Well, now to update the user data we extend the `CreateUserInput` class:

```typescript
import { InputType, PartialType } from '@nestjs/graphql';
import { CreateUserInput } from './create-user.input';

@InputType()
export class UpdateUserInput extends PartialType(CreateUserInput) {}
```

we call validation pipe in the `main.ts` file as follows:

```typescript
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidNonWhitelisted: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  );
  await app.listen(3000);
}
bootstrap();
```

Well, now we're going to create a simple service, resolver, and user module

```sh
$ nest g module users
$ nest g service users
$ nest g resolver users
```

You should now have a users folder with `UsersModule`, `UsersService`, and `UsersResolver` inside.

Now before we write our service, let's create a final dto file for the pagination, as follows:

```typescript
import { ArgsType, Field, Int } from '@nestjs/graphql';
import { Max, Min } from 'class-validator';

@ArgsType()
export class UsersArgs {
  @Field(() => Int)
  @Min(0)
  offset = 0;

  @Field(() => Int)
  @Min(1)
  @Max(50)
  limit = 25;
}
```

Now let's start writing our `UsersService`:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcrypt from 'bcrypt';
import { Repository } from 'typeorm';
import { CreateUserInput } from './dto/create-user.input';
import { UpdateUserInput } from './dto/update-user.input';
import { UsersArgs } from './dto/users.args';
import { Users } from './entities/users.entity';
import { UserInputError } from '@nestjs/apollo';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(Users)
    private readonly usersRepository: Repository<Users>,
  ) {}

  public async findAll(usersArgs: UsersArgs): Promise<Users[]> 
  {
    const { limit, offset } = usersArgs;
    return this.usersRepository.find({
      skip: offset,
      take: limit,
    });
  }

  public async findOneById(id: string): Promise<Users> {
    const user = await this.usersRepository.findOne(id);

    if (!user) {
      throw new UserInputError(`User #${id} not found`);
    }
    return user;
  }

  public async create(createUserInput: CreateUserInput): Promise<Users> {
    createUserInput.password = bcrypt.hashSync(createUserInput.password, 8);

    const user = this.usersRepository.create({ ...createUserInput});
    return this.usersRepository.save(user);
  }

  public async update(
    id: string,
    updateUserInput: UpdateUserInput,
  ): Promise<Users> {
    updateUserInput.password = bcrypt.hashSync(updateUserInput.password, 8);

    const user = await this.usersRepository.preload({
      id: +id,
      ...updateUserInput,
    });

    if (!user) {
      throw new UserInputError(`User #${id} not found`);
    }
    return this.usersRepository.save(user);
  }

  public async remove(id: string): Promise<any> {
    const user = await this.findOneById(id);
    return this.usersRepository.remove(user);
  }
}
```

UserResolver:

```typescript
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { CreateUserInput, UpdateUserInput, UsersArgs } from './dto';
import { UsersService } from './users.service';
import { Users } from './entities/users.entity';
import { UserInputError } from '@nestjs/apollo';

@Resolver()
export class UsersResolver {
  constructor(private readonly usersService: UsersService) {}

  @Query(() => [Users])
  public async users(@Args() usersArgs: UsersArgs): Promise<Users[]> {
    return this.usersService.findAll(usersArgs);
  }

  @Query(() => Users)
  public async user(@Args('id') id: string): Promise<Users> {
    const user = await this.usersService.findOneById(id);
    if (!user) {
      throw new UserInputError(id);
    }
    return user;
  }

  @Mutation(() => Users)
  public async createUser(
    @Args('createUserInput') createUserInput: CreateUserInput,
  ): Promise<Users> {
    return await this.usersService.create(createUserInput);
  }

  @Mutation(() => Users)
  public async updateUser(
    @Args('id') id: string,
    @Args('updateUserInput') updateUserInput: UpdateUserInput,
  ): Promise<Users> {
    return await this.usersService.update(id, updateUserInput);
  }

  @Mutation(() => Users)
  public async removeUser(@Args('id') id: string): Promise<any> {
    return this.usersService.remove(id);
  }
}
```

Now register in our `UsersModule` our service and the resolver, like this:

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UsersResolver } from './users.resolver';
import { Users } from './entities/users.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Users])],
  providers: [UsersService, UsersResolver],
})
export class UsersModule {}
```

Well now let's try to interact with `Graphql` to see if everything works, but before we have to verify that starting our application the `graphql.schema.ts` file is generated, once done, from the browser we go to the address `http://localhost:3000/graphql` thus starting our `GraphQL Playground`, a UI that allows us to interact with our database.
Remember, inside the generated file `graphql.schema.ts`, we have the compilation of everything we have set inside the `users.graphql` and all the `* .graphql` files we create inside the modules, such as `post.graphql` or `roles.graphql`, are added in cascade inside our `graphql.schema.ts` file


If we want to update our updated `graphql.schema.ts` file with the new changes I make, without going to delete the file, we create a new file and call it `generate-typings.ts`, now we create a factory class to set the generator as follows:

```typescript
import { GraphQLDefinitionsFactory } from '@nestjs/graphql';
import { join } from 'path';

const definitionsFactory = new GraphQLDefinitionsFactory();
definitionsFactory.generate({
  typePaths: ['./src/**/*.graphql'],
  path: join(process.cwd(), 'src/graphql.schema.ts'),
  outputAs: 'class',
});
```

now from the terminal we run the following command:

```sh
$ ts-node  generate-typings
```

I add below the Queries and Mutations to run in the GraphQL Playground:


`getUsers`

```
{
    users {
        id
        name
        email
        username
        password
    }
}

```

`getUserById`

```
{
    user(id: "1") {
        id
        name
        email
        username
        password
    }
}

```

`addUser`

```

mutation {
    createUser(createUserInput: {
        name: "tony"
        email:"tony_admin@nest.com"
        username: "tony_admin"
        password: "secret123"
    }) {
        name
        email
        username
        password
    }
}

```

`updateUser`

```
mutation {
    updateUser(
        updateUserInput: {
            name: "tony"
            email: "tony_admin@nest.com"
            username: "tony_admin"
            password: "secret123"
        }
        id: "1"
    ) {
        name
        email
        username
        password
    }
}
```

`removeUser`

```
mutation {
    removeUser(id: "3") {
        name
        email
        username
        password
    }
}
```

Now I leave it to you to create a post module and a role module for users.

That's all.

I hope it will be helpful for anything write me in the comments as well.😉