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.
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
Goal I will make an app called ** I Theater **. Invented for this blog. 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.
Let's make it now! We will develop the application with the following configuration.
Now let's write the code using the NestJS CLI! Please refer to here for the document.
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.
People from AngularJS may be familiar with the configuration.
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.
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.
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.
The interface should be enough
movielist.interface.ts
export interface Movielist {
id: number;
movieName: string;
}
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.
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!
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();
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.
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!