Good day to you. This series will share with you how to build a web application with server, client side, and you will learn many useful things on this project.
As the title you will learn how to
- Use NestJs build a server connect with a MongoDB (use JWT for authentication, how to query on server, create schema, entity, controller, service)
- Use NextJS build a client side with TailwindCss fast build component, Zustand state management (easy manage state), Axios Request API.
After check all this full series, I believe you will have a mindset how to build a website app from backend to frontend. You will understand how to become a full stack developer through this todo web app
Full Tutorial TodoList Apps with NextJS Zustand Axios TailwindCSS React NestJS Mongoose JWT (Day 2)
On day 2: We will focus on how to work with CRUD (Created, Update, Read, Delete with table User) on NestJS Framework
Step 1: Create structure like this
Step 2: Type command: nest g resource users will auto generate above folder
Step 3: Implement below code on each below file
users/dto/users.dto.ts
users/interface/users.interface.ts
users/schemas/users.schema.ts
users/users.controller.ts
// huuvi168@gmail.com
// Zidane - Webzine Tech Tips, all thing about web development
import {
Controller,
Get,
Post,
Body,
UseGuards,
Request,
Delete,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersDto } from './dto/users.dto';
import { JwtAuthGuard } from 'src/auth/guard/jwt-auth.guard';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@UseGuards(JwtAuthGuard)
@Get('getProfile')
public async getProfile(@Request() req: any) {
return await this.usersService.getProfile(req.user.params._id);
}
@Get()
public async getUsers(): Promise<any> {
return await this.usersService.getUsers();
}
@Post('login')
public async login(@Body() user: UsersDto) {
return await this.usersService.login(user);
}
@Post('register')
public async register(@Body() newUser: UsersDto) {
return await this.usersService.register(newUser);
}
@Get(':id')
public async getUserById(id: string) {
return await this.usersService.getUserById(id);
}
@Delete()
public async deleteUserById(id: string) {
return await this.usersService.deleteUserById(id);
}
public async putUserById(
id: string,
propertyName: string,
propertyValue: string,
) {
return await this.usersService.putUserById(id, propertyName, propertyValue);
}
}
users/users.service.ts
import { ApiErrorResponse } from './../util/api-error-response.util';
import { ApiSucceedResponse } from 'src/util/api-success-response.util';
import { IUser } from './interface/users.interface';
import { Injectable, HttpException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import mongoose, { Model, Types } from 'mongoose';
import { UsersDto } from './dto/users.dto';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class UsersService {
constructor(
@InjectModel('User') private readonly userModel: Model<IUser>,
private readonly jwtService: JwtService,
) {}
// https://localhost:8000/users => ok
public async getUsers() {
const users = await this.userModel.find().exec();
if (!users || !users[0]) {
throw new HttpException('User Not Found', 404);
}
return new ApiSucceedResponse("Retrieved all users", users);
}
public async login(user: UsersDto) {
const result = await this.userModel.findOne({
username: user.username,
});
if (!result) {
throw new HttpException('User not found', 404);
}
const isMatch = await bcrypt.compare(user.password, result.password);
if (!isMatch) {
throw new HttpException('Password is not match', 404);
}
const access_token = await this.jwtService.sign({ id: result._id });
return new ApiSucceedResponse('Login succeed', access_token);
}
public async register(newUser: UsersDto) {
// check exist
const checkExist = await this.userModel.findOne({
username: newUser.username
}).exec()
if (checkExist) {
throw new HttpException(`username ${newUser.username} is Exist`, 404)
}
const salt = await bcrypt.genSalt();
const hashPassword = await bcrypt.hash(newUser.password, salt);
newUser.password = hashPassword;
const user = await new this.userModel(newUser);
user.save();
if (user) {
return new ApiSucceedResponse('Registered user successfully', user);
}
return new ApiErrorResponse('Registered user failed', []);
}
async validate(id: string) {
const user = await this.getUserById(id);
return user ? user : null;
}
public async getUserById(id: string) {
const user = await this.userModel
.findById({ _id: new mongoose.Types.ObjectId(id) }) // use this way for get mongo objectID
.exec();
if (!user) {
throw new HttpException('User Not Found!', 404);
}
return new ApiSucceedResponse('Retrieved data successfully', user);
}
public async deleteUserById(id: string) {
const user = await this.userModel
.deleteOne({ _id: new mongoose.Types.ObjectId(id) })
.exec();
if (user.deletedCount === 0) {
throw new HttpException('User Not Found', 404);
}
return new ApiSucceedResponse('User was removed', []);
}
public async putUserById(
id: string,
propertyName: string,
propertyValue: string,
) {
const user = await this.userModel
.findOneAndUpdate(
{ _id: new Types.ObjectId(id) },
{
[propertyName]: [propertyValue],
},
)
.exec();
if (!user) {
throw new HttpException('Not Found', 404);
}
return new ApiSucceedResponse('User was update succeed', user);
}
public async getProfile(id: number) {
let user = await this.userModel
.findById({
_id: id,
})
.exec();
if (!user) {
throw new HttpException('User Not Found', 404);
}
return new ApiSucceedResponse('Retrieved data successfully', user);
}
}
On this point you will learn how to query all users, update user, delete user query
Find all user
this.userModel.find().exec()
Find one user with any field
this.userModel.findOne({})
Find by Id need import moogoose from 'moogoose' package
this.userModel.findById({ _id: new mongoose.Types.ObjectId(id) }).exec()
Here is the delete code
this.userModel.deleteOne({conditions})
Here is the save code
this.userModel(newUser).save()
Remember put Users on Module file
src/users/users.module.ts
ultis/ApiSucceedResponse.ts
export class ApiSucceedResponse {
public statusCode: number;
public message: string;
public params: [];
constructor(message: string, params: any) {
this.statusCode = 200;
this.message = message;
this.params = params;
}
}