The author | week sauce
This article was written on May 13, 2021. First in zhouzhoujiang personal blog, reproduced please indicate the source.
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, has built-in and full TypeScript support, and combines ELEMENTS of OOP (object-oriented programming), FP (functional programming), and FRP (functional response programming). Similar in style to Java Spring, it provides an out-of-the-box application architecture on the server side, enabling front-end personnel to quickly create extensible, loosely coupled, and easy to maintain applications.
For more information about Nest concepts, visit the official Nest documentation website
Create an
npm i -g @nestjs/cli
nest new nest-blog-api
Copy the code
Initial project
Database model
Database storage: mysql ORM Framework: Typeorm Nest Access Typeorm Typrorm documents
Set up the mysql
First, set up mysql service, create database and obtain database connection and user name and password
Project profile
Nest connects to the database. I created the. Env file in the project using environment variables, and put the database connection configuration and subsequent (oss upload related configuration) in this file
Nest Connection database
Add @nestjs/typeorm, @nestjs/config (for reading project configuration), modify app.module.ts
My blog site is divided into front desk and back office management system
Main functions of background management system
- User authentication login (currently only a single administrator)
- Article management: list, edit, create, detail
- Project management: list, edit, create, detail
- Tag management: add tags to articles and items, edit and create tags
- Categories: Add categories for articles and items, edit and create categories
Foreground page function
- List of articles, detailed display
- List of projects, detailed display
- Comment feature (not done yet)
Database design
As well as some statistics for auxiliary tables
Define the model
Next, use TypeOrM to define the Entity Article model as follows, which involves the creation of an association, see TypeOrM Association
import {
BeforeUpdate,
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn
} from 'typeorm';
import { CategoryEntity } from './category.entity';
import { CommentEntity } from './comment.entity';
import { TagEntity } from './tag.entity';
import { UserEntity } from './user.entity';
@Entity('article')
export class ArticleEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
slug: string;
@Column()
title: string;
@Column({ default: ' ' })
image: string;
@Column('text')
description: string;
@Column('text')
content: string;
@Column('text', { nullable: true })
config: string;
/** * 2 */
@Column({ default: 2 })
state: number;
@Column({ default: false })
isDeleted: boolean;
@Column({
type: 'timestamp'.default: () = > 'CURRENT_TIMESTAMP'
})
createdAt: Date;
@Column({
type: 'timestamp'.default: () = > 'CURRENT_TIMESTAMP'
})
updatedAt: Date;
@BeforeUpdate()
updateTimestamp() {
this.updatedAt = new Date(a); } @ManyToOne(type= > UserEntity,
user= > user.articles
)
author: UserEntity;
@ManyToOne(
type= > CategoryEntity,
category= > category.articles
)
category: CategoryEntity;
@ManyToMany(
type= > TagEntity,
tag= > tag.articles
)
@JoinTable({
name: 'article_tag'
})
tags: TagEntity[];
@OneToMany(
type= > CommentEntity,
comment= > comment.article
)
comments: CommentEntity[];
}
Copy the code
Do the same for other entities
After the service is started, the entity is automatically mapped, which synchronizes the data model to the database.
Now you can have fun typing business code.
Function module
Next, the application structure is organized by differentiating functional modules by business objects.Generally, a function module contains three files: Controller, Service, and Module.
service
Complex tasks should be delegated to providers. In this case, a service is a provider that stores and retrieves data and is called in the Controller. The Service class declaration is preceded by the @Injectable() decorator. Dependencies can be injected in controller via Constructor. The following code is a relatively typical add, delete, or change operation
//category.service.ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { PaginationData } from 'core/models/common';
import { CategoryEntity } from 'entity/category.entity';
import { In, Repository } from 'typeorm';
@Injectable()
export class CategoryService {
constructor(@InjectRepository(CategoryEntity) private readonly categoryRepository: Repository
) {}
/** * get the category list ** /
async findAll(
index: number,
size: number,
module? : string ):Promise<PaginationData<CategoryEntity>> {
let res = null;
if (module) {
res = await this.categoryRepository.findAndCount({
where: { module: In([module.'common']), isDeleted: false },
take: size,
skip: (index - 1) * size
});
} else {
res = await this.categoryRepository.findAndCount({
where: { isDeleted: false },
take: size,
skip: (index - 1) * size
});
}
return { index, size, list: res[0].total: res[1]}; }/** * get the classification details ** /
async findOne(id: number): Promise<CategoryEntity> {
const res = await this.categoryRepository.findOne({ id });
return res;
}
/** * create ** /
async create(
title: string,
description: string,
module: string
): Promise<number> {
const category = new CategoryEntity();
category.title = title;
category.description = description;
category.module = module;
const newCategory = await this.categoryRepository.save(category);
return newCategory.id;
}
/** * modify ** /
async update(
id: number,
title: string,
description: string,
module: string
): Promise<number> {
const category = await this.categoryRepository.findOne({ id });
if(! category) {throw new HttpException('This category does not exist', HttpStatus.BAD_REQUEST);
}
category.title = title;
category.description = description;
category.module = module;
await this.categoryRepository.save(category);
return id;
}
/** * delete ** /
async delete(id: number): Promise<void> {
const category = await this.categoryRepository.findOne({ id });
if(! category) {throw new HttpException('This category does not exist', HttpStatus.BAD_REQUEST);
}
category.isDeleted = true;
await this.categoryRepository.save(category); }}Copy the code
controller
Controller The controller is responsible for processing incoming requests and returning responses to the client. The routing mechanism controls which controller receives which requests. Typically, each controller has multiple routes, and different routes can perform different operations. The Controller uses the @Controller decorator, and the categoryService defined above is injected through the class constructor, which means we have created and initialized the categoryService member in the Controller.
//category.controller.ts
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Query
} from '@nestjs/common';
import { CategoryService } from './category.service';
@Controller('category')
export class CategoryController {
constructor(private readonly categoryService: CategoryService) {}
/** * get the category list *@param index
* @param size
* @param module* /
@Get('all')
async findAll(
@Query('index') index: number,
@Query('size') size: number,
@Query('module') module? : string) {
return this.categoryService.findAll(
Number(index) || 0.Number(size),
module
);
}
/** * get the classification details *@param id* /
@Get(':id')
async findOne(@Param('id') id: number) {
return this.categoryService.findOne(id);
}
/** * Create category *@param title
* @param description
* @param module* /
@Post()
async create(
@Body('title') title: string,
@Body('description') description: string,
@Body('module') module: string
) {
return this.categoryService.create(title, description, module);
}
/** * edit category *@param id
* @param title
* @param description
* @param module* /
@Put(':id')
async update(
@Param('id') id: number,
@Body('title') title: string,
@Body('description') description: string,
@Body('module') module: string
) {
return this.categoryService.update(id, title, description, module);
}
/** * delete *@param id* /
@Delete(':id')
async delete(@Param('id') id: number) {
return this.categoryService.delete(id); }}Copy the code
module
Modules are classes with @Module() decorators, and every Nest app must have a root Module, app.module. Then, multiple function modules are divided according to the actual functions of the application. For example, category is a relatively independent function module, and category.module.ts can be said to be an outlet of this function module.
//category.module.ts
import { AuthMiddleware } from '@/common/middleware/auth';
import { UserModule } from '@/user/user.module';
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod
} from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CategoryEntity } from 'core/entity/category.entity';
import { CategoryController } from './category.controller';
import { CategoryService } from './category.service';
@Module({
imports: [TypeOrmModule.forFeature([CategoryEntity]), UserModule],
providers: [CategoryService],
controllers: [CategoryController]
})
export class CategoryModule implements NestModule {
public configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes(
{
path: 'category/all'.method: RequestMethod.GET
},
{
path: 'category/:id'.method: RequestMethod.GET
},
{
path: 'category'.method: RequestMethod.POST
},
{
path: 'category/:id'.method: RequestMethod.PUT
},
{
path: 'category/:id'.method: RequestMethod.DELETE } ); }}Copy the code
The @module() decorator takes an object that describes module properties:
providers | Providers instantiated by the Nest injector and can be shared at least across the module |
---|---|
controllers | A set of controllers that must be created |
imports | A list of imported modules that export the required providers in this module |
exports | Subset of providers that are provided by this module and should be available in other modules. Import userService from userModule and add userModule to the imports array of other modules because all modules rely on the user module’s public authentication method. This allows other modules to access userService. |
You also need to register all function modules in app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ScheduleModule } from '@nestjs/schedule';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ArticleModule } from './article/article.module';
import { CategoryModule } from './category/category.module';
import { DashboardModule } from './dashboard/dashboard.module';
import { LogModule } from './log/log.module';
import { ProjectModule } from './project/project.module';
import { ShareModule } from './share/share.module';
import { StatisModule } from './statis/statis.module';
import { TagModule } from './tag/tag.module';
import { TaskModule } from './task/task.module';
import { UserModule } from './user/user.module';
@Module({
imports: [
TypeOrmModule.forRoot(),
ConfigModule.forRoot({
envFilePath: '.env'.isGlobal: true
}),
ScheduleModule.forRoot(),
ArticleModule,
ProjectModule,
TagModule,
CategoryModule,
UserModule,
ShareModule,
DashboardModule,
LogModule,
StatisModule,
TaskModule
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}
Copy the code
This is the most basic functional module running, then according to the business functions to achieve business code, while improving the infrastructure, such as adding user unified authentication controller, global error filter, global interface return result processor, improve swagger definition, etc.
For the complete code, visit zzj-admin-API