本文翻譯自Node.js Rest APIs example with Express, Sequelize & MySQL
最新修改:2020年9月29號 bezkoder Node.js
Express是Node.js最受歡迎的Web框架之一脉幢,支持路由视粮,中間件帆喇,視圖系統(tǒng)...Sequelize是基于Promise的Node.js ORM杠愧,它支持Postgres,MySQL收班,SQL Server的方言棚赔。在本教程中,我將 向您展示如何使用Express贬芥,Sequelize和MySQL數(shù)據(jù)庫來構(gòu)建Node.js Restful CRUD API。
您應(yīng)該首先在機器上安裝MySQL宣决。 可以在以下位置找到安裝說明:
Official MySQL installation manual
相關(guān)文章:
- Build Node.js Rest APIs with Express & MySQL (without Sequelize)
- Node.js: Upload/Import Excel file data into MySQL Database
- Node.js: Upload CSV file data into MySQL Database
全棧:
- Vue.js + Node.js + Express + MySQL example
- Vue.js + Node.js + Express + MongoDB example
- Angular + Node.js Express + MySQL example
- React + Node.js + Express + MySQL example
安全性:Node.js – JWT Authentication & Authorization example
部署:Deploying/Hosting Node.js app on Heroku with MySQL database
Node.js Rest CRUD API概述
我們將構(gòu)建Rest Apis蘸劈,它可以創(chuàng)建,檢索尊沸,更新威沫,刪除和按標(biāo)題查找教程。
首先洼专,我們從Express Web服務(wù)器開始棒掠。 接下來,我們?yōu)镸ySQL數(shù)據(jù)庫添加配置屁商,使用Sequelize創(chuàng)建Tutorial
模型烟很,編寫控制器。 然后棒假,我們定義用于處理所有CRUD操作(包括自定義查找程序)的路由溯职。
下表概述了將要導(dǎo)出的Rest API:
Methods | Urls | Actions |
---|---|---|
GET | api/tutorials | get all Tutorials |
GET | api/tutorials/:id | get Tutorial by id |
POST | api/tutorials | add new Tutorial |
PUT | api/tutorials/:id | update Tutorial by id |
DELETE | api/tutorials/:id | remove Tutorial by id |
DELETE | api/tutorials | remove all Tutorials |
GET | api/tutorials/published | find all published Tutorials |
GET | api/tutorials?title=[kw] | find all Tutorials which title contains 'kw' |
最后,我們將使用Postman測試Rest Apis帽哑。
這是我們的項目結(jié)構(gòu):
[圖片上傳失敗...(image-5f8879-1604969151628)]
示例視頻
這是我們的與MySQL數(shù)據(jù)庫一起運行的Node.js Express Sequelize應(yīng)用程序演示,并通過Postman測試Rest Apis叹俏。
創(chuàng)建Node.js應(yīng)用
首先妻枕,我們創(chuàng)建目錄:
$ mkdir nodejs-express-sequelize-mysql
$ cd nodejs-express-sequelize-mysql
接下來,我們使用package.json文件初始化Node.js App:
npm init
name: (nodejs-express-sequelize-mysql)
version: (1.0.0)
description: Node.js Rest Apis with Express, Sequelize & MySQL.
entry point: (index.js) server.js
test command:
git repository:
keywords: nodejs, express, sequelize, mysql, rest, api
author: bezkoder
license: (ISC)
Is this ok? (yes) yes
我們需要安裝必要的模塊:express
粘驰,sequelize
屡谐,mysql2
和body-parser
。
運行命令:
npm install express sequelize mysql2 body-parser cors --save
package.json文件應(yīng)如下所示:
{
"name": "nodejs-express-sequelize-mysql",
"version": "1.0.0",
"description": "Node.js Rest Apis with Express, Sequelize & MySQL",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"nodejs",
"express",
"rest",
"api",
"sequelize",
"mysql"
],
"author": "bezkoder",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"mysql2": "^2.0.2",
"sequelize": "^5.21.2"
}
}
建立Express Web服務(wù)器
在根目錄下蝌数,創(chuàng)建一個新的server.js
文件:
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(bodyParser.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome to bezkoder application." });
});
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
我們要做的是:
-
導(dǎo)入
express
愕掏,body-parser
和cors
模塊:- Express用于構(gòu)建Rest API
-
body-parser
有助于解析請求并創(chuàng)建req.body
對象 -
cors
提供了Express中間件,以使CORS具有多種選擇顶伞。
創(chuàng)建一個Express應(yīng)用饵撑,然后使用
app.use()
方法添加body-parser
和cors
中間件剑梳。 請注意,我們設(shè)置了origin:http:// localhost:8081
滑潘。定義一個易于測試的GET路由垢乙。
在端口8080上偵聽傳入請求。
現(xiàn)在语卤,使用以下命令運行該應(yīng)用:node server.js
追逮。
使用URL http:// localhost:8080/
打開瀏覽器,您將看到:
是的粹舵,第一步已經(jīng)完成钮孵。 在下一節(jié)中,我們將與Sequelize一起協(xié)作眼滤。
配置MySQL數(shù)據(jù)庫并進(jìn)行序列化
在app文件夾中油猫,我們創(chuàng)建一個單獨的config文件夾,然后使用db.config.js文件進(jìn)行配置柠偶,如下所示:
module.exports = {
HOST: "localhost",
USER: "root",
PASSWORD: "123456",
DB: "testdb",
dialect: "mysql",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};
前面5個參數(shù)用于MySQL連接情妖。
pool
是可選的,它將用于Sequelize連接池配置:
- max:池中的最大連接數(shù)
- min:池中的最小連接數(shù)
- idle:連接釋放之前可以空閑的最長時間(以毫秒為單位)
- acquire:該池將在拋出錯誤之前嘗試獲取連接的最長時間(以毫秒為單位)
有關(guān)更多詳細(xì)信息诱担,請訪問API Reference for the Sequelize constructor毡证。
初始化Sequelize
我們將在下一步包含模型的app/models
文件夾中初始化Sequelize。
現(xiàn)在蔫仙,使用以下代碼創(chuàng)建app/models/index.js
:
const dbConfig = require("../config/db.config.js");
const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false,
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle
}
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.tutorials = require("./tutorial.model.js")(sequelize, Sequelize);
module.exports = db;
不要忘記在server.js
中調(diào)用sync()
方法
...
const app = express();
app.use(...);
const db = require("./app/models");
db.sequelize.sync();
...
在開發(fā)中料睛,您可能需要刪除現(xiàn)有表并重新同步數(shù)據(jù)庫。 只需使用force:true即可摇邦,如下代碼:
db.sequelize.sync({ force: true }).then(() => {
console.log("Drop and re-sync db.");
});
定義Sequelize模型
在models文件夾中恤煞,像這樣創(chuàng)建tutorial.model.js文件:
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("tutorial", {
title: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
published: {
type: Sequelize.BOOLEAN
}
});
return Tutorial;
};
該Sequelize模型表示MySQL數(shù)據(jù)庫中的tutorials
表。 這些列將自動生成:id, title, description, published, createdAt, updatedAt.
初始化Sequelize之后施籍,我們無需編寫CRUD函數(shù)居扒,Sequelize支持所有這些功能:
- 創(chuàng)建一個新的教程:create(object)
- 通過id查找教程:findByPk(id)
- 獲取所有教程:findAll()
- 通過id:update(data, where: { id: id })
- 刪除教程:destroy(where: { id: id })
- 刪除所有教程:destroy(where:{})
- 按標(biāo)題查找所有教程:findAll({ where: { title: ... } })
這些功能將在我們的控制器中使用。
我們可以通過為每個教程添加評論來改進(jìn)示例丑慎。 這是一對多關(guān)系喜喂,我為此編寫了一個教程:
Sequelize Associations: One-to-Many example – Node.js, MySQL
或者,您可以為每個教程添加標(biāo)簽竿裂,并將教程添加到標(biāo)簽(多對多關(guān)系):
Sequelize Many-to-Many Association example with Node.js & MySQL
創(chuàng)建控制器
在app/controllers
文件夾中玉吁,讓我們使用以下CRUD函數(shù)創(chuàng)建tutorial.controller.js:
- create
- findAll
- findOne
- update
- delete
- deleteAll
- findAllPublised
const db = require("../models");
const Tutorial = db.tutorials;
const Op = db.Sequelize.Op;
// Create and Save a new Tutorial
exports.create = (req, res) => {
};
// Retrieve all Tutorials from the database.
exports.findAll = (req, res) => {
};
// Find a single Tutorial with an id
exports.findOne = (req, res) => {
};
// Update a Tutorial by the id in the request
exports.update = (req, res) => {
};
// Delete a Tutorial with the specified id in the request
exports.delete = (req, res) => {
};
// Delete all Tutorials from the database.
exports.deleteAll = (req, res) => {
};
// Find all published Tutorials
exports.findAllPublished = (req, res) => {
};
讓我們實現(xiàn)這些功能。
創(chuàng)建一個新對象
創(chuàng)建并保存一個新教程:
exports.create = (req, res) => {
// Validate request
if (!req.body.title) {
res.status(400).send({
message: "Content can not be empty!"
});
return;
}
// Create a Tutorial
const tutorial = {
title: req.body.title,
description: req.body.description,
published: req.body.published ? req.body.published : false
};
// Save Tutorial in the database
Tutorial.create(tutorial)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Tutorial."
});
});
};
檢索對象(有條件)
從數(shù)據(jù)庫中檢索所有教程/按標(biāo)題查找:
exports.findAll = (req, res) => {
const title = req.query.title;
var condition = title ? { title: { [Op.like]: `%${title}%` } } : null;
Tutorial.findAll({ where: condition })
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving tutorials."
});
});
};
我們使用req.query.title
從Request中獲取查詢字符串腻异,并將其作為findAll()
方法的條件进副。
檢索單個對象
查找具有ID的單個教程:
exports.findOne = (req, res) => {
const id = req.params.id;
Tutorial.findByPk(id)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message: "Error retrieving Tutorial with id=" + id
});
});
};
更新對象
更新由請求中的id
標(biāo)識的教程:
exports.update = (req, res) => {
const id = req.params.id;
Tutorial.update(req.body, {
where: { id: id }
})
.then(num => {
if (num == 1) {
res.send({
message: "Tutorial was updated successfully."
});
} else {
res.send({
message: `Cannot update Tutorial with id=${id}. Maybe Tutorial was not found or req.body is empty!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Error updating Tutorial with id=" + id
});
});
};
刪除對象
刪除具有指定id
的教程:
exports.delete = (req, res) => {
const id = req.params.id;
Tutorial.destroy({
where: { id: id }
})
.then(num => {
if (num == 1) {
res.send({
message: "Tutorial was deleted successfully!"
});
} else {
res.send({
message: `Cannot delete Tutorial with id=${id}. Maybe Tutorial was not found!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Could not delete Tutorial with id=" + id
});
});
};
刪除所有對象
從數(shù)據(jù)庫中刪除所有教程:
exports.deleteAll = (req, res) => {
Tutorial.destroy({
where: {},
truncate: false
})
.then(nums => {
res.send({ message: `${nums} Tutorials were deleted successfully!` });
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while removing all tutorials."
});
});
};
按條件查找所有對象
查找所有published = true
的教程:
exports.findAllPublished = (req, res) => {
Tutorial.findAll({ where: { published: true } })
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving tutorials."
});
});
};
可以對該控制器進(jìn)行一些修改以返回分頁響應(yīng):
{
"totalItems": 8,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
您可以在以下位置找到更多詳細(xì)信息:
Server side Pagination in Node.js with Sequelize and MySQL
定義路由
當(dāng)客戶端使用HTTP請求(GET,POST悔常,PUT影斑,DELETE)發(fā)送對端點的請求時给赞,我們需要通過設(shè)置路由來確定服務(wù)器的響應(yīng)方式。
這些是我們的路由:
-
/api/tutorials
:GET鸥昏,POST塞俱,DELETE -
/api/tutorials/:id
:GET,PUT吏垮,DELETE -
/api/tutorials/published
:GET
在app/routes
文件夾中創(chuàng)建一個turorial.routes.js
障涯,其內(nèi)容如下:
module.exports = app => {
const tutorials = require("../controllers/tutorial.controller.js");
var router = require("express").Router();
// Create a new Tutorial
router.post("/", tutorials.create);
// Retrieve all Tutorials
router.get("/", tutorials.findAll);
// Retrieve all published Tutorials
router.get("/published", tutorials.findAllPublished);
// Retrieve a single Tutorial with id
router.get("/:id", tutorials.findOne);
// Update a Tutorial with id
router.put("/:id", tutorials.update);
// Delete a Tutorial with id
router.delete("/:id", tutorials.delete);
// Delete all Tutorials
router.delete("/", tutorials.deleteAll);
app.use('/api/tutorials', router);
};
您可以看到我們使用了/controllers/tutorial.controller.js
中的控制器。
我們還需要在server.js
中包含路由(在app.listen()
之前):
...
require("./app/routes/turorial.routes")(app);
// set port, listen for requests
const PORT = ...;
app.listen(...);
測試API
使用以下命令運行我們的Node.js應(yīng)用程序:node server.js
膳汪。
控制臺顯示:
Server is running on port 8080.
Executing (default): DROP TABLE IF EXISTS `tutorials`;
Executing (default): CREATE TABLE IF NOT EXISTS `tutorials` (`id` INTEGER NOT NULL auto_increment , `title` VARCHAR(255), `description` VARCHAR(255), `published` TINYINT(1), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `tutorials`
Drop and re-sync db.
使用Postman唯蝶,我們將測試以上所有的Apis。
1遗嗽、使用POST/tutorials
Api創(chuàng)建一個新教程
創(chuàng)建一些新教程后粘我,您可以檢查MySQL表:
mysql> select * from tutorials;
+----+-------------------+-------------------+-----------+---------------------+---------------------+
| id | title | description | published | createdAt | updatedAt |
+----+-------------------+-------------------+-----------+---------------------+---------------------+
| 1 | JS: Node Tut #1 | Tut#1 Description | 0 | 2019-12-13 01:13:57 | 2019-12-13 01:13:57 |
| 2 | JS: Node Tut #2 | Tut#2 Description | 0 | 2019-12-13 01:16:08 | 2019-12-13 01:16:08 |
| 3 | JS: Vue Tut #3 | Tut#3 Description | 0 | 2019-12-13 01:16:24 | 2019-12-13 01:16:24 |
| 4 | Vue Tut #4 | Tut#4 Description | 0 | 2019-12-13 01:16:48 | 2019-12-13 01:16:48 |
| 5 | Node & Vue Tut #5 | Tut#5 Description | 0 | 2019-12-13 01:16:58 | 2019-12-13 01:16:58 |
+----+-------------------+-------------------+-----------+---------------------+---------------------+
2、使用GET /tutorials
Api檢索所有的教程
3痹换、使用
GET /tutorials/:id
Api根據(jù)id查詢單個教程4征字、使用`PUT /tutorials/:id' Api更新教程
在更新某些行后,請查看tutorials
表:
mysql> select * from tutorials;
+----+-------------------+-------------------+-----------+---------------------+---------------------+
| id | title | description | published | createdAt | updatedAt |
+----+-------------------+-------------------+-----------+---------------------+---------------------+
| 1 | JS: Node Tut #1 | Tut#1 Description | 0 | 2019-12-13 01:13:57 | 2019-12-13 01:13:57 |
| 2 | JS: Node Tut #2 | Tut#2 Description | 0 | 2019-12-13 01:16:08 | 2019-12-13 01:16:08 |
| 3 | JS: Vue Tut #3 | Tut#3 Description | 1 | 2019-12-13 01:16:24 | 2019-12-13 01:22:51 |
| 4 | Vue Tut #4 | Tut#4 Description | 1 | 2019-12-13 01:16:48 | 2019-12-13 01:25:28 |
| 5 | Node & Vue Tut #5 | Tut#5 Description | 1 | 2019-12-13 01:16:58 | 2019-12-13 01:25:30 |
+----+-------------------+-------------------+-----------+---------------------+---------------------+
5娇豫、使用GET /tutorials?title=node
查找標(biāo)題包含'node'的所有教程
6匙姜、使用
GET /tutorials/published
Api查詢所有已發(fā)布的教程7、使用'DELETE /tutorials/:id
Api刪除一個教程 ![Delete a Tutorial](https://upload-images.jianshu.io/upload_images/15949040-921d6d926ec1ec72?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) id = 2的教程已從
tutorials`表中刪除:
mysql> select * from tutorials;
+----+-------------------+-------------------+-----------+---------------------+---------------------+
| id | title | description | published | createdAt | updatedAt |
+----+-------------------+-------------------+-----------+---------------------+---------------------+
| 1 | JS: Node Tut #1 | Tut#1 Description | 0 | 2019-12-13 01:13:57 | 2019-12-13 01:13:57 |
| 3 | JS: Vue Tut #3 | Tut#3 Description | 1 | 2019-12-13 01:16:24 | 2019-12-13 01:22:51 |
| 4 | Vue Tut #4 | Tut#4 Description | 1 | 2019-12-13 01:16:48 | 2019-12-13 01:25:28 |
| 5 | Node & Vue Tut #5 | Tut#5 Description | 1 | 2019-12-13 01:16:58 | 2019-12-13 01:25:30 |
+----+-------------------+-------------------+-----------+---------------------+---------------------+
8冯痢、使用'DELETE /tutorialsApi刪除所有的教程 ![Delete all Tutorials](https://upload-images.jianshu.io/upload_images/15949040-e3fc6ce85574b9bc?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 現(xiàn)在在
tutorials`表中沒有一行數(shù)據(jù)了:
mysql> SELECT * FROM tutorials;
Empty set (0.00 sec)
結(jié)論
今天氮昧,我們已經(jīng)學(xué)習(xí)了如何使用Express Web服務(wù)器創(chuàng)建Node.js Rest Apis。 我們還知道添加MySQL數(shù)據(jù)庫和Sequelize配置浦楣,創(chuàng)建Sequelize模型袖肥,編寫控制器以及定義用于處理所有CRUD操作的路由的方法。
您可以在下一個教程中找到更多有趣的東西:
-
Server side Pagination in Node.js with Sequelize and MySQL
返回分頁數(shù)據(jù)作為響應(yīng):
{
"totalItems": 8,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
-
Deploying/Hosting Node.js app on Heroku with MySQL database
或者您可以將圖像保存到MySQL數(shù)據(jù)庫: - Upload/store images in MySQL using Node.js, Express & Multer
學(xué)習(xí)愉快振劳! 再見椎组。
進(jìn)一步閱讀
- Express.js Routing
- https://www.npmjs.com/package/express
- https://www.npmjs.com/package/body-parser
- https://www.npmjs.com/package/mysql2
- Tutorials and Guides for Sequelize v5
將教程數(shù)據(jù)從文件上傳到MySQL數(shù)據(jù)庫表:
- Node.js: Upload Excel file data into MySQL Database
- Node.js: Upload CSV file data into MySQL Database
源代碼
您可以在Github上找到此示例的完整源代碼。
如果要為每個教程添加評論澎迎。 它是一對多關(guān)聯(lián)庐杨,有一個有關(guān)該關(guān)系的教程:Sequelize Associations: One-to-Many example – Node.js, MySQL
或者,您可以為每個教程添加標(biāo)簽夹供,并將教程添加到標(biāo)簽(多對多關(guān)系):
Sequelize Many-to-Many Association example with Node.js & MySQL