NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Posted November 7, 2023
NestJS GraphQL API with TypeORM, Apollo Server and Postgres

GraphQL is a query language. It perfectly integrates with NestJS TypeScript APIs when harnessing the power of Types check. On top of that, TypeORM is perfect for simplifying database operations pragmatically with PostgreSQL, MongoDB, MSSQL, etc. You need an Apollo server to create a federated supergraph and modernize your APIs.

In this tutorial, you will create a Nest.js GraphQL example API powered by Apollo Server, PostgreSQL, and TypeORM.

What you will Learn

As I walk you through the journey of getting Nest.js and GraphQL running with Apollo server and TypeORM, you will learn:

  • How to model a GraphQL NestJS entity utilizing TypeORM.
  • The perfect way to run Apollo Server to power NestJS GraphQL APIs.
  • How to add database Relationships for GraphQL using TypeORM and PostgreSQL, featuring ManyToOne and OneToMany relations.
  • How to build NestJS GraphQL Resolvers to handle API Queries and Mutations.
  • The right way to use GraphQLModule and ApolloDriver to streamline the NestJS API graph.
  • How to use GraphQL Code first approach.

Now, Dive in and modernize your NestJS GraphQL APIs with Apollo Server, TypeORM, and PostgreSQL to plug APIs with a scalable architecture that gives clients the power to exactly what they need.

Prerequisites

As you follow along, ensure you have the following:

  • Basic knowledge working with Nest.js
  • Have Node.js installed on your computer.
  • For the database, make sure you have PostgreSQL ready.

What you will Create

You will create a simple Blog app on this NestJS GraphQL API with TypeORM and Apollo server tutorial. You will use two entities:

  • Author with OneToMany Relationships and
  • Post with ManyToOne Relationship

Each module will have its own Resolver while demonstrating how to add GraphQL TypeORM database relations.

Creating your First NestJS TypeORM Apollo-Inspired GraphQL API

In your project working directory, use the following command to create an out-of-the-box Nest.js App:

npm i -g @nestjs/cli
nest new nest-graphql

Head over to the new app you have created:

cd nest-nest-graphql

Install the following TypeORM and GraphQL packages:

npm install pg graphql @nestjs/graphql @nestjs/apollo apollo-server-express @nestjs/typeorm typeorm 

Because we are creating Nest.js GraphQL Apollo server:

  • You will need a module for your API. This will create the forFeature module as the starting point Nest uses to build your API graph.
  • You need a controller to listen to requests that TypeORM will use and communicate with your database to perform CRUD operations.
  • Entities are your application blueprint and type safety.
  • Finally a provider to abstract complexity and logic of NestJS GraphQL operations within your databases.

Creating NestJS GraphQL App: A GraphQL Code First

Along this guide, you will use the GraphQL Code First approach to create your API. In the Code-First approach, you define your GraphQL types and schema directly within your code using TypeScript decorators with key decorators such as @ObjectType() and @Field().

So, why the Code First approach? The focus is on writing TypeScript code. In this case, the schema is derived from the code. This way, the GraphQL schema is generated dynamically at runtime based on your code’s decorators and TypeScript types.

The Advantages?

  • Direct integration with TypeScript and type checking.
  • Less boilerplate code.

Go ahead and create the following two resources:

nest g resource posts --no-spec

Now, it’s here you must select the GraphQL approach you want in your NestJS as follows:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

When prompted Would you like to generate CRUD entry points? chose yes with y:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

NestJS will create the boilerplate code you need to use. At the same time and using the same approach, create a resource for authors as follows:

nest g resource authors --no-spec

This is the easiest approach to creating full-fledged modules with its service, resolver, entities, and dto:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Creating NestJS GraphQL Schema and Entities with GraphQL and TypeORM

This step describes how you integrate GraphQL with Nestjs and TypeORM. It describes the decorators and TypeScript types your code will use to build GraphQL.

GraphQL uses ObjectTypes to represent a domain object in GraphQL API. It represents the structure of the data GraphQL will query or mutate. In this case, the author and post are the ObjectTypes. They define the query objects, mutations, and schema the GraphQL API needs.

On the other hand, entities represent your database structure. In it, you will use TypeORM to define how your database will look and the relationship you want to add.

You have the following files:

  • author.entity.ts inside the src/authors/entities folder.
  • post.entity.ts inside the src/posts/entities folder.

In your author.entity.ts, you will represent Author ObjectType and its entity as follows:

import { Field, ID, ObjectType } from '@nestjs/graphql';
import { Post } from 'src/posts/entities/post.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
@ObjectType()
export class Author {
  @PrimaryGeneratedColumn('uuid')
  @Field(() => ID)
  id: string;

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

  @OneToMany(() => Post, (post) => post.author)
  @Field(() => [Post])
  posts: Post[];
}
  • The decorator ObjectType from nestjs/graphql will let NestJS and Apollo identify NestJS Author, an object class the GraphQL will use to generate Author schema.
  • The @Field() decorator will add the specified class property to its parent schema type. It adds scalar types out of the box, such as Int, String, and ID.
  • To define the structure of your database table, TypeORM will add Columns and all the attributes your table needs. For example, TypeORM will allow Nest.js to create id (as Primary key) and name fields on your database.
  • At the same time, TypeORM will generate the table for you within the database while still adding the OneToMany Relationship that the Author has with a Post.

Now, you can go ahead and create Post Object and its TypeORM Entity as follows (post.entity.ts):

import { Field, ID, ObjectType } from '@nestjs/graphql';
import { Author } from 'src/authors/entities/author.entity';
import {Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn} from 'typeorm';

@Entity()
@ObjectType()
export class Post {
  @PrimaryGeneratedColumn('uuid')
  @Field(() => ID)
  id: string;

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

  @ManyToOne(() => Author)
  @JoinColumn()
  @Field(() => Author)
  author: Author;
}

Adding GraphQL Code First Schema using NestJS GraphQLModule and ApolloDriver

Because you are using the Code-First approach, you need to make sure you have NestJS GraphQLModule at the application entry point.

Adding NestJS GraphQLModule using Code First

What you need is to import the GraphQLModule in src/app.module.ts and tell NestJS we’re using code first and the ApolloDriver as follows:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PostsModule } from './posts/posts.module';
import { AuthorsModule } from './authors/authors.module';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { join } from 'path';

@Module({
  imports: [
    PostsModule,
    AuthorsModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Integrating GraphQL and Nestjs Modules and TypeOrmModule

Again, you want to have a database connection to execute database operations. Here, we are utilizing TypeORM to assist us in mapping the entities to the responsive table while creating the attributes and the relations.

To use TypeORM, you must tell NestJS to utilize TypeOrmModule at the entry point. This way, NestJS will ensure all your tables are in sync with your code and runtime.

But first, your Modules must also inherit TypeOrmModule as forFeature to allow NestJS and these modules to use the TypeORM Entities you created.

Go to src/authors/authors.module.ts and update this module as follows:

import { Module } from '@nestjs/common';
import { AuthorsService } from './authors.service';
import { AuthorsResolver } from './authors.resolver';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Author } from './entities/author.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Author])],
  providers: [AuthorsResolver, AuthorsService],
})
export class AuthorsModule {}

Apply the same changes to the src/posts/posts.module.ts file as follows:

import { Module } from '@nestjs/common';
import { PostsService } from './posts.service';
import { PostsResolver } from './posts.resolver';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Post } from './entities/post.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Post])],
  providers: [PostsResolver, PostsService],
})
export class PostsModule {}

Now you can go to the src/app.module.ts file and use TypeOrmModule as forRoot as follows:

//... Other imports
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    PostsModule,
    AuthorsModule,
    TypeOrmModule.forRoot({
      type: 'postgres', // Use 'mysql' for MySQL
      host: 'localhost',
      port: 5432, // Default port
      username: 'postgres',
      password: 'pass',
      database: 'blog',
      entities: ['dist/**/*.entity.{ts,js}'],
      synchronize: true, // Set to false in production
    }),
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

What you need to note:

  • TypeORM will use the PostgreSQL database to sync your entities. Therefore, you have the right connection parameters for your PostgreSQL. Ensure you update them (database, password, and username)
  • At the same time, TypeORM won’t create your database. It only creates a table. So, ensure you have a database named blog within PostgreSQL.

Auto Generating Schema File and Database Tables

At this point, NestJS can use GraphQL and TypeORM to create your schema and Tables, respectively.

To test if this works, run npm run start:dev. If everything goes well, your app should be running tables created, and a schema file should be generated:

Go to the src/schema.gql file, and GraphQL will have a schema file generated in SDL as follows:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

As you follow along and update resolvers, this file will be updated automatically.

Related: NestJS Resolvers FT GraphQL Code-First and Schema-First 🚀

Also, go to PostgreSQL and confirm your tables:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Creating NestJS GraphQL Providers (Services)

A service abstract application logic (like fetching data from a database). Here, you use the @Injectable() decorator and create a repository layer to manage what you want GraphQL to do within NestJS.

In your PostsService, you will need the following:

  • The @nestjs/common decorators for Injectable.
  • Use the @nestjs/typeorm decorator for dependency injection of the Repository from TypeORM.
  • Inject the Post entity’s repository into the service.
  • Constructor that takes injected Repository<Post> as a parameter.

You finally need the Methods to handle GraphQL requests as follows:

  • createPost(title: string, authorId: string) creates a new post with the given title and author ID. It then saves the post using the posts repository.
  • findAllByAuthor(authorId: string) finds all posts related to a specific author using a provided author ID.
  • postAuthor(postId: string) finds the author of a specific post using the post ID.

Now update PostsService (src/posts/posts.service.ts) to add all these characteristics as follows:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Post } from './entities/post.entity';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postsRepository: Repository<Post>,
  ) {}

  createPost(title: string, authorId: string) {
    const post = this.postsRepository.create({
      title,
      author: {
        id: authorId,
      },
    });

    return this.postsRepository.save(post);
  }

  findAllByAuthor(authorId: string): Promise<Post[]> {
    return this.postsRepository.find({
      where: {
        author: {
          id: authorId,
        },
      },
    });
  }

  async findAll(): Promise<Post[]> {
    return await this.postsRepository.find();
  }

  async postAuthor(postId: string) {
    const post = await this.postsRepository.findOne({
      where: {
        id: postId,
      },
      relations: {
        author: true,
      },
    });

    return post.author;
  }
}

Similarly, the AuthorsService should have these Methods:

  • create(name: string) to create a new author with the given name and an empty array of posts.
  • findAll() fetches all authors in the database.
  • authorPosts(authorId: string) for finding all posts related to a specific author using a specific author ID.

Your src/authors/authors.service.ts file will be updated as follows:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Author } from './entities/author.entity';

@Injectable()
export class AuthorsService {
  constructor(
    @InjectRepository(Author)
    private readonly authorsRepository: Repository<Author>,
  ) {}

  create(name: string) {
    const author = this.authorsRepository.create({ name, posts: [] });
    return this.authorsRepository.save(author);
  }

  findAll() { 
    return this.authorsRepository.find();
  }

  async authorPosts(authorId: string) {
    const author = await this.authorsRepository.findOne({
      where: { id: authorId },
      relations: { posts: true },
    });

    return author.posts;
  }
}

Creating NestJS GraphQL Resolvers (Controllers)

GraphQL interacts with your API with the http://localhost:3000/graphql endpoint with a lovely GraphQL IDE. Here, you need Resolvers to let IDE know what your Queries and Mutations look like.

This step is equivalent to Controllers in Rest APIs. But for GraphQL, the Query and mutation declarations are done using resolver files. They construct our GraphQL schema.

Related: NestJS Resolvers FT GraphQL Code-First and Schema-First 🚀

Resolvers will also handle the methods you created in your Providers (Services).

Go to PostsResolver (src/posts/posts.resolver.ts) and create:

  • Queries methods
  • Mutations for createPost mutation to create a new post.
  • ResolveField for the author field resolver that resolves the author for a given Post.
import {Args,Mutation,Parent,Query,ResolveField, Resolver} from '@nestjs/graphql';
import { Author } from '../authors/entities/author.entity';
import { Post } from './entities/post.entity';
import { PostsService } from './posts.service';

@Resolver(() => Post)
export class PostsResolver {
  constructor(private readonly postsService: PostsService) {}

  @Query(() => Post)
  async post() {
    // let's fetch the first post available in the database
    const posts = await this.postsService.findAll();
    return posts.length > 0 ? posts[0] : null;
  }

  @Mutation(() => Post)
  createPost(@Args('title') title: string, @Args('authorId') authorId: string) {
    return this.postsService.createPost(title, authorId);
  }

  @ResolveField(() => [Author])
  author(@Parent() post: Post) {
    return this.postsService.postAuthor(post.id);
  }
}

Here you have:

  • The @Resolver decorator defines a resolver class.
  • The decorators @Query, @Mutation, and @ResolveField are to define GraphQL queries, mutations, and field resolvers, respectively.
  • The @Args decorator will extract arguments from the GraphQL schema.

Likewise, the AuthorsResolver (src/authors/authors.resolver.ts) will have the Query Mutation and ResolveField methods as follows:

import {Args,Mutation,Parent,Query,ResolveField, Resolver} from '@nestjs/graphql';
import { Post } from '../posts/entities/post.entity';
import { Author } from './entities/author.entity';
import { AuthorsService } from './authors.service';

@Resolver(() => Author)
export class AuthorsResolver {
  constructor(private readonly authorsService: AuthorsService) {}

  @Query(() => [Author])
  authors() {
    return this.authorsService.findAll();
  }

  @Mutation(() => Author)
  createAuthor(@Args('name') name: string) {
    return this.authorsService.create(name);
  }

  @ResolveField(() => [Post])
  posts(@Parent() author: Author) {
    return this.authorsService.authorPosts(author.id);
  }
}

Starting NestJS GraphQL API

At this point, ensure your application is running:

npm run start:dev

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

NestJS will expose GraphQL Playground on http://localhost:3000/graphql as follows:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Testing NestJS GraphQL API using the GraphQL Playground

Let’s start with mutations createPost and createAuthor to add data.

First, you need to create an Author using the following Mutation:

mutation {
  createAuthor(name: "John Doe") {
    id
    name
  }
}

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Now you can create a Post and assign it to this author:

mutation {
  createPost(title: "Sample Post", 
    authorId: "cff60b9f-9189-4beb-8023-1694e30f340e") {
    id
    title
    author {
      id
      name
    }
  }
}

But note cff60b9f-9189-4beb-8023-1694e30f340e is the author ID associated with this Post:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Because you have the data, use Queries to fetch it as follows:

Fetch a post with the author’s details:

query {
  post {
    id
    title
    author {
      id
      name
    }
  }
}

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Fetch a list of authors with their post details

query {
  authors {
    id
    name
    posts {
      id
      title
    }
  }
}

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Because you have the Field Resolvers:

query {
  post {
    id
    title
    author {
      id
      name
      posts {
        id
        title
      }
    }
  }
}

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Note the changes you are making are in your database:

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Conclusion

This was an easy yet more structured way of building your NestJS server-side apps with GraphQL Apollo server and TypeORM PostgreSQL relational database. I hope you found this tutorial helpful. Get the code ued on this guide on GitHub.

NestJS GraphQL API with TypeORM, Apollo Server and Postgres

Written By:

Joseph Chege