Tạo một web RESTful API bằng Node.js đơn giản (Phần 2)

20 May 2019 — Written by Chính Phạm
#node.js#restful api#express.js

Hế lô mọi người, lại là một đêm nóng dã man của tháng 5, mình cùng nhau ngồi xuống để tiếp tục đàm đạo phần tiếp theo của sê ri xây dựng web RESTful API bằng Node.js

🚀 Link tàu nhanh phần trước đây, các bạn có thể lướt qua rồi mình có thể tiếp tục nhá 🙈

Ở phần trước chắc code của các bạn đã hoạt động trơn tru rồi nhỉ 😂...đùa thôi, code có xí xi mà không chạy được thì nhục lắm 😚

Mở thư mục chứa project lên, tạo giúp mình một số thư mục:

  • routes
  • models
  • controllers

Cấu trúc của thư mục project của bạn bây giờ sẽ như thế này:

- simple-restful-api
|
| - controllers
|	|
|	` - api
|	
| - models
| - node_modules
| - routes
| - app.js
` - package.json 

Xí xí, giải thích một tẹo chổ này nè:

Chắc sẽ có một số bạn thắc mắc là sao không gộp chung controllersroutes thành một, tại vì hầu hết các bài hướng dẫn, người ta đều gộp chung vào mà. Ông lại tách riêng ra chi vậy, rảnh háng hả ?? 🧐

Mình tách riêng ra là có lý do đó, mình nghĩ như thế này, việc tách ra sẽ giúp ta dễ quản lý source hơn, routes nó chỉ đảm nhiệm duy nhất một việc là điều hướng các method phía người dùng cho controller phân loại theo từng function để xử lý cụ thể từng hành động mà phía người dùng muốn 🙄

Tiếp theo các bạn (mở/tạo mới) lần lượt các file bên dưới và thêm code vào nhé 👨‍💻:

app.js

const express = require('express')
const app = express()
const path = require('path')
const router = express.Router()
const port = process.env.PORT || 3000
const bodyParser = require('body-parser')

app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())

// global properties
global.appRoot = path.resolve(__dirname)
global.appControllers = appRoot + '/controllers'
global.appModels = appRoot + '/models'
global.appRoutes = appRoot + '/routes'

// Body parse json
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())

// import routes
const apiRouter = require(appRoutes + '/api-router')(app)

app.listen(port)
console.log('Server listening on port: ' + port)

Ở đây mình có thêm một số biến:

  • global.appRoot: Thư mục root của project.
  • global.appControllers: Thư mục chứa các controller.
  • global.appModels: Thư mục chứa model của từng đối tượng.
  • global.appRoutes: Thư mục chưa các router.

Lý do mình define thêm các biến global giúp các bạn chỉ cần khai báo một lần và dùng cho nhiều file con, nằm sâu trong các thư mục.

Ví dụ muốn gọi thư mục controller ở một tệp bất kì trong project thì chỉ cần gỏ: appControllers là được. Dễ mà 😚

import routes: Chổ này để mình truyền express được định danh là app sang bên file routes/api-router.js để xử lý nhé, các bạn có nhớ ở phần 1 mình có đoạn này không?

app.use('/', (req, res) => {
	res.status(200).send({
		message: 'Xin chào Việt Nam!'
	})
})

Nếu để đoạn này trong app.js nhưng vẫn xài router thì sao 🤨?

Tất nhiên, mọi điều hướng của bạn khi truy cập đều hiển thị đoạn json:

{
  "message": "Xin chào Việt Nam!"
}

Lý do là khi khai báo dòng trên, nó sẽ hiển nhiên lấy đó làm mặc định, mặc cho bạn nhập url kiểu: http://localhost:3000/abc/xyz

Chưa hình dung ra thì thử ngay sẽ ra thôi 😂, mình kinh nghiệm chưa uyên thâm gì nên hiểu thế nào thì nói lại như vậy, bạn nào khác thì để lại nhận xét bên dưới cho mọi người cùng tham khảo nhé 👇

Có thể bạn đã biết? Bình luận có thể ẩn danh và không cần đăng ký!

routes/api-router.js

module.exports = (app) => {
	const baseURL = '/api/v1'

	// API for student
	const studentController = require(appControllers + '/api/student-controller')
	app.route(baseURL + '/student').get(studentController.listStudent)
	app.route(baseURL + '/student/:id').get(studentController.getStudentId)
}

Với đoạn này thì bạn đã thông báo cho bên app.js biết là router của mày nè, khứa student tao muốn bên controller phải trả cho tao một list student (Array) theo phương thức get nha, còn khứa student/:id nó muốn lấy chính xác student theo id. Nhớ đó, làm sai là tao crash không trượt phát nào thì ráng chịu hà 😝

Sau một hồi đấu đá thì vẫn chưa chạy được đâu =))), phải làm tiếp các bước bên dưới đã 😂

models/student.js

function Student(id, firstname, lastname, age) {
	this.id = id
	this.firstname = firstname
	this.lastname = lastname
	this.age = age
}

// id
Student.prototype.getId = () => {
	return this.id
}
Student.prototype.setId = (id) => {
	this.id = id
}

// first name
Student.prototype.getFirstName = () => {
	return this.firstname
}
Student.prototype.setFirstName = (firstname) => {
	this.firstname = firstname
}


// last name
Student.prototype.getLastName = () => {
	return this.lastname
}
Student.prototype.setLastName = (lastname) => {
	this.lastname = lastname
}


// age
Student.prototype.getAge = () => {
	return this.age
}
Student.prototype.setAge = (age) => {
	this.age = age
}

module.exports = Student

Mấy chế làm qua hướng đối tượng bên Java rồi nhìn có quen không? 😂 đáng ra mình viết model bằng Mongoose Schema rồi dùng Mongodb để quản lý nhập, sửa, xóa luôn, nhưng xét lại thì mình nghĩ nên viết đơn giản vậy cho dễ tiếp cận đã, vì mình đang muốn xây dựng web RESTful API đơn giản mà 🌚, thôi để dành giới thiệu sau vậy 😋

Nhìn vào code chắc bạn cũng đoán được rồi nhỉ, chổ này để getset cho từng đối tượng. Ai quên rồi thì xem lại tính đóng gói trong lập trình hướng đối tượng nha, sẵn ôn lại kiến thức luôn 😬

controllers/api/student-controller.js

const student = require(appModels + '/student')

// list students
const listStudents = [
	new student(1, 'Chinh', 'Pham', 22),
	new student(2, 'Nguyen Van', 'A', 18)
]

module.exports = (() => {
	const router = {}

	router.listStudent = (req, res) => {
		res.status(200).json(listStudents)
	}

	router.getStudentId = (req, res) => {
		let object = listStudents.find((student) => {
			return student.id === parseInt(req.params.id)
		})

		if (object) {
			res.status(200).json(object)
		} else {
			res.status(404).json({
				message: 'This student does not exist.'
			})
		}
	}

	return router
})()

Quay lại câu chuyện:

  • apiRouter: Ê ku, có người muốn xem list student của mài kìa!!!
  • controller.listStudent: Đã xem ✓

Nhanh như cắt, hắn lấy luôn listStudents ném thẳng ra một cách lạnh lùng và tỏ ra cool ngầu vãi chưởng và... 😝

  • controller.listStudent: 200 -> success (đệt!!! 😓 nó ra hiệu là tao ổn, mài cứ tự nhiên đê)

  • apiRouter: Ơ! người dùng lại muốn xem kìa, nhưng họ chỉ xem mỗi thằng đầu tiên thôi!!!

  • apiRouter: Typing...

  • apiRouter: http://localhost:3000/api/v1/student/2 -> request id = 2

  • controller.getStudentId: Đã xem ✓

Hú hú, khẹc khẹc, đứa nào có id2 ra có người tìm kìa!

  • controller.getStudentId: 200 -> success (nó lại ra hiệu như bình thường)
// http://localhost:3000/api/v1/student/2

{
  "id": 2,
  "firstname": "Nguyen Van",
  "lastname": "A",
  "age": 18
}
  • apiRouter: Thinking... Á thằng này gắt thật, để thử troll nó xem phản ứng thế nào 🙂

  • apiRouter: http://localhost:3000/api/v1/student/9999999

  • controller.getStudentId: Đã xem ✓

    Hắn gào thét một hồi mà không có đứa nào id là 999 hết, hắn liền trả lời luôn

  • controller.getStudentId: 404 -> not found (kèm message là: không có đứa nào id là 999 hết nhá!!!!! 🙂🙂🙂🙂🙂)


Qua câu chuyện mình muốn mô phỏng lại quá trình làm việc giữa routercontroller cho dễ hình dung á 😅

Ở phạm vi bài viết này thì bạn đã có một RESTful API đơn giản rồi, bạn có thể dùng thử thêm method .post, .put, .delete.

Ở mỗi function trong controller đều có param là req, res, tùy vào mục đích mà sử dụng, ví dụ mình request một method post và trong body có trường là name, ở controller nếu muốn lấy name này ra thì chỉ cần gọi: req.body.name

Kết quả

Kiểm tra phiên bản Node.js


Đây là repo final của mình nè, các bạn có thể tải về tham khảo thử 😛

🚀 Github Simple RESTful API Final

Xin cảm ơn các bạn đã theo dõi bài viết này 💚 Mọi góp ý, thắc mắc xin để lại bình luận bên dưới rồi chúng ta cùng đàm đạo nhé 😂