- vừa được xem lúc

Xây dựng một REST API Skeleton với Node.js

0 0 35

Người đăng: Pham Tuan Viet

Theo Viblo Asia

Node.js đang dần trở nên phổ biến với những ứng dụng dạng microservice hay REST Api bới hiệu năng cực nhanh và tính bất đồng bộ của chúng. Đặc biệt là rất gọn nhẹ và xử lý đồng thời nhiều request với thời gian phản hồi siêu nhanh.

Nhưng dù bất kể ngôn ngữ nào chúng ta cũng cần xây dựng một khung sườn (skeleton) vững chắc để có thể bám theo nó để dễ dàng maintain và phát triển nó. Vậy nên hôm nay chúng ta cùng nhau chia sẻ và xây dựng một skeleton riêng cho mình nhé.

Construct

Cũng giống như các ngôn ngữ REST API khác chúng ta cũng có thể xây dựng skeleton theo dạng MVC pattern. Nhưng ở bài viết này chúng ta chỉ xây dựng đến API chỉ cần sử dụng đến controller và models kết hợp với middleware để kiếm soát các request và response. Các bạn có thể tham khảo dưới đây:

Cấu trúc thư mục gồm:

  • common: các functions, contants chung cho toàn dự án.
  • controllers: các action điều hướng xử lý các request và response.
  • database: chứa các file config kết nối mongosee, mysql, vv...
  • middleware: chứa các rules để kiếm soát các request và response .
  • models: chứa các model và function xử lý dữ liệu.
  • routes: chứa các function thiếu lập điều hướng với các route.

Init skeleton

Trước khi xây dựng server REST API với Express, chúng ta cần cài đặt một số thư viện cơ bản như sau:

"dependencies": { "bcrypt": "^5.0.0", "body-parser": "^1.18.2", "express": "^4.16.2", "jquery": "^3.5.1", "jsonwebtoken": "^8.1.0", "mongoose": "^5.9.25", "nodemon": "^2.0.4", "request": "^2.83.0", "request-promise": "^4.2.2", "validator": "^9.1.1" }, "devDependencies": { "env-cmd": "^10.1.0" }

Vì database chúng ta sẽ sử dụng là mongo nên sẽ cần sử dụng mongoose để truy xuất database và sẽ cần env-cmd để load file .evn (các biến môi trường).

Database

mongoose.js chứa thông tin cấu hình kết nối mongodb.

let mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect(process.env.MONGODB_URL, { useNewUrlParser: true, useCreateIndex: true,
}); module.exports = mongoose;

Models

use.model.js chứa thông tin khởi tạo Schema kèm theo đó là các functions. Chúng ta hoàn toàn có thể tách schema ra thành một thư mục riêng để code có thể clear và rõ ràng hơn.

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken'); const UserSchema = new mongoose.Schema ( { username: { type: String, lowercase: true, required: [true, 'can\'t be blank'], match: [/^[a-zA-Z0-9]+$/, 'is invalid'], index: true }, email: { type: String, lowercase: true, required: [true, 'can\'t be blank'], match: [/\_@.com\S+\.\S+/, 'is invalid'], index: true }, password: { type: String, required: true, minLength: 7 }, tokens: [{ token: { type: String, required: true } }] }, {timestamps: true}
); UserSchema.pre('save', async function(next) { const user = this; if (user.isModified('password')) { user.password = await bcrypt.hash(user.password, 8); } next();
}); UserSchema.methods.toAuthJSON = function() { return { username: this.username, email: this.email, token: this.generateAuthToken() };
}; UserSchema.methods.generateAuthToken = function() { const token = jwt.sign( { _id: this._id, username: this.username }, process.env.JWT_KEY ); this.tokens = this.tokens.concat({token}); this.save(); return token;
}; UserSchema.statics.findByCredentials = async(email, password) => { const user = await User.findOne({email}); if (!user) { throw new Error({error: 'Invalid login credentials'}); } const isPasswordMatch = await bcrypt.compare(password, user.password); if (!isPasswordMatch) { throw new Error({error: 'Invalid login credentials'}); } return user;
}; const User = mongoose.model('User', UserSchema); module.exports = User;

Middleware

auth.middleware.js để bảo mật chúng ta cần verify lại request trước khi cho phép cấp quyền tiếp tục tương tác bằng cách kiểm tra token với header Authorization.

const User = require('../models/user.model');
const jwt = require('jsonwebtoken'); const auth = async(req, res, next) => { const token = req.header('Authorization').replace('Bearer ', ''); const data = jwt.verify(token, process.env.JWT_KEY); try { const user = await User.findOne({_id: data._id, 'tokens.token': token}); if (!user) { throw new Error(); } req.user = user; req.token = token; next(); } catch (error) { res.status(401).send({error: 'Not authorized to access this resource'}); } }; module.exports = auth;

Routes

index.js sẽ đóng vài trò load toàn bộ cấu hình route

const express = require('express');
const authRoute = require('./auth.routes');
const router = express.Router(); router.use('/', authRoute); module.exports = router;

auth.route.js sẽ thiết lập các phương thức xử lý và điều hướng chúng tới các controller - action. Chúng ta sẽ chia ra làm 2 loại route là public và private bằng cách thiết lập middleware cho chúng.

let router = require('express').Router();
const AuthController = require('../controllers/auth.controller');
const auth = require('../middleware/auth.middleware'); //public
router.post('/login', AuthController.login);
router.post('/signup', UserController.signup); //private
router.post('/logout', auth, AuthController.logout);
router.post('/logout-all', auth, AuthController.logoutAll); module.exports = router;

Controllers

Ở đây, chúng ta cần tạo ra các function tương ứng với các action như đã khai báo tại auth.route.js. Các action đóng vài trò điều hướng và xử lý dữ liệu trả về trước khi response trở lại.

auth.controller.js

const User = require('../models/user.model'); function AuthController()
{ this.signup = (req, res) => { try { let user = new User(); user.username = req.body.username; user.email = req.body.email; user.password = req.body.password; user.save().then(function(){ return res.json({user: user.toAuthJSON()}); }).catch(function (error) { return res.status(400).json(error); }); } catch (error) { return res.status(400).json(error); } }; this.login = async(req, res) => { try { const {email, password} = req.body; const user = await User.findByCredentials(email, password); if (!user) { return res.status(401).send({error: 'Login failed! Check authentication credentials'}); } res.send({user: user.toAuthJSON()}); } catch (error) { res.status(400).send({error: 'Login failed'}); } }; this.logout = async(req, res) => { try { req.user.tokens = req.user.tokens.filter((token) => { return token.token != req.token; }); await req.user.save(); res.send({'message': 'Logout Done!'}); } catch (error) { res.status(500).send(error); } }; this.logoutAll = async(req, res) => { try { req.user.tokens.splice(0, req.user.tokens.length); await req.user.save(); res.send(); } catch (error) { res.status(500).send(error); } }; return this;
} module.exports = AuthController();

Create server run app

Tạo app.js với nội dung như sau:

const express = require('express');
const port = process.env.PORT || 3000;
const app = express();
const route = require('./routes/index');
const bodyParser = require('body-parser');
require('./database/mongoose'); app.use( bodyParser.json() );
app.use( express.json() );
app.use('/', route); app.listen(port, () => { console.log('Server listening on ' + port);
});

Vì chúng ta cần xử lý dữ liệu dưới dạng json vậy nên sẽ cần parser json với bodyParser. Tiếp đó load toàn bộ route với require('./routes/index');

Trước khi chạy thử chúng ta cần config lại package.json để ứng dụng có thể load được các biến môi trường với:

"scripts": { "start": "env-cmd -f ./.env node src/app.js", "dev": "env-cmd -f ./.env nodemon --exec \"node src/app.js\"" }

nodemon sẽ được sử dụng như một công cụ tự động reload lại app sau khi save thành công.

Tiếp theo, chúng ta thử chạy thử skeleton nhé. http://localhost:3000/

Signup API

Login API

Kết luận

Như vậy, chúng ta đã có thể tạo ra một bộ skeleton đơn giản với đầy đủ những tính năng cơ bản như login, signup, logout. ngoài ra chúng ta có thể tự bổ sung cho mình một bộ skeleton đầy đủ hơn với nhiều những tính năng hay ho khác. Hy vọng bài viết sẽ hữu ích với các ae Newbie ngôn ngữ này. Mọi đóng góp sẽ giúp tác giả hoàn thiện hơn trong những bài viết tiếp theo.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Áp dụng kiến trúc 3 Layer Architecture vào project NodeJS

The problem encountered. Các framework nodejs phổ biết như Express cho phép chúng ta dễ dàng tạo ra Resful API xử lí các request từ phía client một cách nhanh chóng và linh hoạt.

0 0 64

- vừa được xem lúc

Tìm hiểu về NestJS (Phần 2)

Trong bài viết trước, mình đã giới thiệu về NestJS và các thành phần cơ bản của framework này cũng như xây dựng demo một api bằng NestJS. Như mình đã giới thiệu, NestJS có một hệ sinh thái hỗ trợ cho chúng ta trong quá trình phát triển mà các framework khác như Express, Fastify,... phải tự build hoặ

0 0 154

- vừa được xem lúc

Xây dựng Restful Api bằng Nodejs

Mở đầu. Thực ra là không có mở đầu gì đâu mà hay làm ngay bước tiếp theo ! .

0 0 34

- vừa được xem lúc

CORS là gì? CORS với Nodejs

CORS (hay Cross-origin resource sharing) là gì . . Lỗi cors trên trình duyệt:. .

0 0 56

- vừa được xem lúc

[K8S] Phần 15 - Triển khai ứng dụng NodeJS lên K8S

Giới thiệu. Trong các phần trước mình đã giới thiệu về cách dựng một hệ thống Kubernetes Cluster với khá đầy đủ các thành phần cần thiết như:.

0 0 29

- vừa được xem lúc

Nhắc các cuộc họp trên Google Calendar của bạn bởi Bot Telegram

Hôm nay là ngày 25/09/2022, khoảng 8h sáng đang mơ mơ màng màng trên chiếc giường ngủ, và có những tiếng chuông điện thoại reo lên từ đám bạn rủ rê đi bơi, đi cà phê:. "Sao chủ nhật nào cũng phải gọi

0 0 34