Let's develop a full stack application with NextJS / NestJS / PostgreSQL / Docker! Hands-on ① (Server-side edition)

Introduction

By the way, I will post the first blog in my life w First, I will explain the reason why I tried to write this article.

Actually, I've been developing a matching app that allows you to find nearby drinking friends in your personal project. Originally, I was developing using React, GraphQL, PostgreSQL, but I decided to improve it with NextJS, TYPEORM, GraphQL, PostgreSQL using Typescript.

However, I didn't know how difficult this was and where to start, and honestly, my heart was about to break. Tohohoho In particular, I couldn't find an architecture with good redundancy and readability on the server side, but there must be an absolutely good way! (No company should have written server-side code in freestyle) As a result of investigating without giving up, I arrived at this article.

Yahoo Tech Blog

NestJS! ?? A framework I've never heard of !? Moreover, it's almost the same as NextJS, lol

As a result of using it, it's really good!

So, I thought it would be nice to have a full-stack hands-on article using Typescript, so I decided to write this article. I also use Yahoo and it should be the latest technology stack lol

By the way, this time I will use Express instead of GraphQL. From the next time, I will write articles on GraphQL version and Kubernetes version.

I hope this article helps someone.

If you want to see only the code, here

Prerequisites

Target audience

Goal I will make an app called ** I Theater **. Invented for this blog. ezgif.com-video-to-gif.gif I don't think everyone can go out because of Corona, so it's an app that allows you to list the titles of movies you want to watch later.

Main story

Let's make it now! We will develop the application with the following configuration.

App configuration diagram

Architecture.png

Let's write from the server side (NestJS)!

Now let's write the code using the NestJS CLI! Please refer to here for the document.

Project creation

First, let's create a base project by hitting the following CLI command!

npm i -g @nestjs/cli
nest new server 

Then, the project will be created with the following folder structure. スクリーンショット 2021-01-04 19.37.12.png

People from AngularJS may be familiar with the configuration.

Let's create the necessary code for MovieList

This time, you need to create MovieList related code to save the movie names and get the list of movie names.

There are a total of 5 files to create.

Create movielist.entity.ts

NestJS has a built-in TypeORM as an ORM, so let's benefit from it!

The official document is here!

First, create a movielist folder under src and create a `` `movielist.entity.ts```.

movielist.entity.ts


import { Entity, Column, PrimaryGeneratedColumn, Unique } from 'typeorm';
import { IsNotEmpty } from 'class-validator';

@Entity()
@Unique(['movieName'])
export class MovieList {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ nullable: false })
  @IsNotEmpty({ message: 'Movie Name must not be empty' })
  movieName: string;
}

The columns should be the auto-generated id and the `` `movieNameto store the movie name.moviename```Let's prevent empty strings by adding null check and unique function to the column.

Create movielist.interface.ts

This time, let's specify the data type in advance by using Typescript! Basically, match it to movielist.entity.ts.

nest g interface movielist/movielist 

By hitting the above command, a file will be created in the movie list. スクリーンショット 2021-01-04 19.37.12.png

The interface should be enough

movielist.interface.ts


export interface Movielist {
  id: number;
  movieName: string;
}

Create movielist.controller.ts

Next, let's create a controller file! This file is like a control tower, located between the request from the nginx server and the class of service.

nest g controller movielist

movielist.controller.ts


import { Controller, Get, Post, Req } from '@nestjs/common';
import { MovielistService } from './movielist.service';

import { Request } from 'express';
import { Movielist } from './movielist.interface';

@Controller('movielist')
export class MovielistController {
  constructor(
    private movieListService: MovielistService,
  ) {}

  @Get()
  fetchAll(): Promise<Movielist[]> {
    return this.movieListService.fetchAll();
  }

  @Post()
  insertOne(@Req() request: Request): void {
    this.movieListService.insertOne(request.body.movieName);
  }
}

Like TYPEORM, Express is built into NestJS. Unlike normal Express, the advantage is that you can specify the root path simply by prefixing the method with Get and Post annotations.

Prepend the Controller annotation to the class name to tell NestJS that it is a controller class. This time, `movielist``` is specified in the parameter, so the path to call the fetchAll method is domain name/movielist ``.

Also, inject the dependency by specifying the service to be created later in the parameter of the constructor.

movielist.service.ts Let's create a service class! The service class is a file that describes database processing.

nest g service movielist

This time we will implement `fetchAll``` to fetch the entire list of movies and ``` insertOne``` to add the name of the movie. Use the `Repository``` function of the typeorm package to link with the database.

movielist.service.ts


import { Injectable, Param } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { MovieList } from './movielist.entity';
import { Movielist } from './movielist.interface';

@Injectable()
export class MovielistService {
  constructor(
    @InjectRepository(MovieList)
    private movieListRepository: Repository<MovieList>,
  ) {}

  async fetchAll(): Promise<Movielist[]> {
    return await this.movieListRepository.find();
  }

  async insertOne(@Param() movieName): Promise<void> {
    await this.movieListRepository.insert({
      movieName: movieName,
    });
  }
}

DI (Dependency Injection) is possible on the controller class side by adding the Injectable annotation.

Create movielist.module.ts

Finally, create a module to configure the MovieList configuration. Hit the following command.

nest g module movielist

movielist.module.ts


import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MovielistController } from './movielist.controller';
import { MovieList } from './movielist.entity';
import { MovielistService } from './movielist.service';

@Module({
  imports: [TypeOrmModule.forFeature([MovieList])],
  controllers: [MovielistController],
  providers: [MovielistService],
})
export class MovielistModule {}

This code also uses TYPE ORM, so let's import the entity we just created!

Import the MovieList module into your App module!

The NestJS project always requires a module that is one root. Build from that module as a starting point, including child modules.

Before that, let's install the necessary modules!

$ yarn add @nestjs/typeorm typeorm pg

app.Let's import the MovieList module and ormconfig file created earlier into module.




#### **` app.module.ts`**

import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Connection } from 'typeorm'; import * as ormconfig from '../ormconfig';

import { MovielistModule } from './movielist/movielist.module';

@Module({ imports: [TypeOrmModule.forRoot(ormconfig), MovielistModule], controllers: [AppController], providers: [AppService], }) export class AppModule { constructor(private connection: Connection) {} }


 TypeOrmModule.forRoot () corresponds to the properties required to configure TYPEORM. Specify ormconfig as a parameter.

 Let's specify 5000 as the port on which NestJS starts.


#### **` main.ts`**
```ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(5000);
}
bootstrap();

Create ormconfig.ts

Finally, let's create it in the root folder of ormconfig.

ormconfig.ts


import { ConnectionOptions } from 'typeorm';

// Check typeORM documentation for more information.
const config: ConnectionOptions = {
  type: 'postgres',
  host: process.env.PGHOST,
  port: parseInt(process.env.PGPORT) | 5432,
  username: process.env.PGUSER,
  password: process.env.PGPASSWORD,
  database: process.env.PGDATABASE,
  entities: [__dirname + '/**/*.entity{.ts,.js}'],
  // We are using migrations, synchronize should be set to false.
  synchronize: true,
  // Run migrations automatically,
  // You can disable this if you prefer running migration manually.
  migrationsRun: true,
  logging: true,
  logger: 'file',
  // Allow both start:prod and start:dev to use migrations
  // __dirname is either dist or src folder, meaning either
  // the compiled js in prod or the ts in dev
  migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
  cli: {
    migrationsDir: 'src/migrations',
  },
};

export = config;

Set the synchronize property to true, but let's set it to false during production! Basically, it is recommended to manage with a migration file.

At the end

Are you surprised at the sudden end? I'm sorry (; ^ ω ^) It seems to be longer than expected, so I decided to divide it into the first part and the second part. w

As mentioned above, this time it was the server side edition.

Proceed to the next article Front End!

Recommended Posts

Let's develop a full stack application with NextJS / NestJS / PostgreSQL / Docker! Hands-on ① (Server-side edition)
Let's develop a full stack application with NextJS / NestJS / PostgreSQL / Docker! Hands-on ② (Front end, Docker edition)
Deploy a Docker application with Greengrass