Node.js + MySQL CRUD API 구현하기
Node.js, Express, Sequelize를 이용하여 MySQL CRUD RESTful API를 구성하는 방법에 대해 알아보겠습니다.
Sequelize는 MySQL, Postgres, SQLite, MS-SQL Server 등의 데이터베이스를 지원하는 promise 기반의 Node.js ORM(Object Relational Mapping)입니다. transaction, eager & lazy loading, relation, read replication 등의 다양한 기능을 지원합니다.
구현 환경은 다음의 버전을 기준으로 구성하였습니다.
- Node.js v12.13.1
- MySQL v5.7.3
- express v4.17.1
- sequelize v6.6.2
- mysql2 v2.2.5
- typescript v4.1.5
1. 모듈 설치 & 디렉터리 구조 설정
1.1. Node.js App 생성
디렉터리 생성 후에 npm을 이용하여 Node.js App을 생성하고 필요한 모듈을 설치해줍니다.
$ mkdir mysql-server
$ cd mysql-server
$ npm init --yes
$ npm install express body-parser cors mysql2 sequelize
React 또는 Angular Application 처럼 디렉터리 내부에 package.json 정보가 포함되어 있는 경우에는 npm init 없이 디렉터리 구조만 설정해주어도 됩니다.
package.json은 다음과 같이 설정됩니다.
{
"name": "mysql-server",
"version": "0.0.0",
"scripts": {
...
},
"dependencies": {
...
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"mysql2": "^2.2.5",
"sequelize": "^6.6.2",
...
},
...
}
1.2. 디렉터리 구조 설정
모듈을 설치한 이후엔 다음과 같이 디렉터리 구조를 설정해줍니다. 파일은 TypeScript(.ts)를 베이스로 하였으나 JavaScript(.js)로 설정해주어도 됩니다.
src
├── app
│ └── mysql
│ └──config
│ │ └── config.ts
│ └── controller
│ │ └── controller.ts
│ ├── model
│ │ ├── index.ts
│ │ └── model.ts
│ └── route
│ └── route.ts
└── mysql-server.ts
2. Web Server 설정
2.1. Express Web Server 설정
Express를 사용하여 API 사용이 가능한 Web Server를 설정해줍니다. src 디렉터리 하위에 생성한 mysql-server.ts 파일을 다음과 같이 작성해줍니다.
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 8080;
// Set CORS option
app.use(cors());
// 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 }));
// RESTful API route for DB
app.use('/', require('./app/mysql/route/route.ts'));
// DB Connection
const db = require('./app/mysql/model/index.ts');
db.sequelizeConfig.sync();
// Default route for server status
app.get('/', (req, res) => {
res.json({ message: `Server is running on port ${PORT}` });
});
// Set listen port for request
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
mysql-server.ts에서 사용한 모듈의 역할은 다음과 같습니다.
- express
RESTful API 환경을 구축하기 위해 사용하는 Node.js 웹 프레임워크입니다. - body-parser
request 구문과 req.body를 파싱하는 Node.js 미들웨어입니다. - cors
CORS 설정에 사용되는 Express 미들웨어입니다.
3. MySQL 설정
3.1. MySQL Connection 설정
MySQL의 Connection을 위해 app/mysql/config/config.ts 파일을 다음과 같이 작성해줍니다.
module.exports = {
host: '[host]',
username: '[username]',
password: '[password]',
db: '[dbname]',
dialect: '[dbtype]',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};
url에 작성할 항목은 다음과 같습니다.
- host: MySQL IP (동일한 환경일 경우 localhost)
- username: MySQL 접속 유저명
- password: MySQL 접속 비밀번호
- db: MySQL 접속 DB명
- dialect: DB Connector 설정 (mysql, mariadb, postgres, mssql, ...)
- pool: DB Connection Pool 설정
3.2. Sequelize 설정
Sequelize를 이용하여 schema와 model을 설정하기 위해 app/mysql/model 디렉터리 하위의 파일들을 설정해줍니다.
Sequelize 설정을 위한 index.ts 파일은 다음과 같이 작성해줍니다.
const dbConfig = require('../config/config.ts');
const Sequelize = require('sequelize');
const sequelizeConfig = new Sequelize(
dbConfig.db,
dbConfig.username,
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.sequelizeConfig = sequelizeConfig;
db.tutorial = require('./model.ts')(sequelizeConfig, Sequelize);
module.exports = db;
Sequelize의 model 설정을 위한 model.ts 파일은 다음과 같이 작성해줍니다. schema명은 'tutorial'로 하였습니다.
module.exports = (sequelizeConfig, Sequelize) => {
// Set Model
const Tutorial = sequelizeConfig.define(
'tutorial',
{
title: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
published: {
type: Sequelize.BOOLEAN
}
}
);
return Tutorial;
};
예제에서 schema명은 'tutorial'로 작성하였지만 MySQL과의 connection 이후에 생성되는 table name은 뒤에 's'가 붙어서 'tutorials'로 생성됩니다.
4. CRUD API Router & Controller 설정
4.1. Router 설정
Router에서 사용할 CRUD RESTful API의 HTTP Request는 다음과 같습니다.
Method | Route URL | Action |
GET | /api/tutorial | 모든 tutorial 조회 |
GET | /api/tutorial/:id | id로 tutorial 조회 |
POST | /api/tutorial | tutorial 생성 |
PUT | /api/tutorial/:id | id로 tutorial 수정 |
DELETE | /api/tutorial/:id | id로 tutorial 삭제 |
Router 설정을 위해서 app/mysql/route/route.ts 파일을 다음과 같이 작성해줍니다.
const router = require('express').Router();
const tutorial = require('../controller/controller.ts');
// Create tutorial
router.post('/api/tutorial', tutorial.create);
// Retrieve all tutorials
router.get('/api/tutorial', tutorial.findAll);
// Retrieve tutorial by id
router.get('/api/tutorial/:id', tutorial.findOne);
// Update tutorial by id
router.put('/api/tutorial/:id', tutorial.update);
// Delete tutorial by id
router.delete('/api/tutorial/:id', tutorial.delete);
module.exports = router;
4.2. Controller 설정
Controller 설정을 위해서 app/mysql/controller/controller.ts 파일을 다음과 같이 작성해줍니다. 앞서 작성한 route.ts 파일에서 API에 따라 호출되는 메서드들을 구현해줍니다.
const db = require('../model/index.ts');
const Tutorial = db.tutorial;
const Op = db.sequelize.Op;
// Create tutorial
exports.create = (req, res) => {
// Validate request
if (!req.body.title) {
res.status(400).send({
message: 'Title is empty!'
});
return;
}
// Set tutorial
const tutorial = {
title: req.body.title,
description: req.body.description,
published: req.body.published ? req.body.published : false
};
// Save tutorial
Tutorial
.create(tutorial)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message: err.message || 'Create tutorial failure.'
});
});
};
// Retrieve all tutorials
exports.findAll = (req, res) => {
const title = req.query.title;
let condition = { where: {} };
if (keyword) {
condition = {
where : {
[Op.or]: [
{
title: {
[Op.like]: `%${keyword}%`
}
},
{
description: {
[Op.like]: `%${keyword}%`
}
}
]
}
}
};
Tutorial
.findAll(condition)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message: err.message || 'Retrieve all tutorials failure.'
});
});
};
// Retrieve tutorial by 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: err.message || 'Retrieve tutorial failure. (id: ' + id + ')'
});
});
};
// Update tutorial by id
exports.update = (req, res) => {
const id = req.params.id;
const condition = id ? { where: { id: id } } : null;
Tutorial
.update(
req.body,
condition
)
.then(resultCount => {
if (resultCount == 1) {
res.send({
message: 'Tutorial updated.'
});
} else {
res.send({
message: 'Cannot update tutorial. (id: ' + id + ')'
});
}
})
.catch(err => {
res.status(500).send({
message: err.message || 'Update tutorial failure. (id: ' + id + ')'
});
});
};
// Delete tutorial by id
exports.delete = (req, res) => {
const id = req.params.id;
const condition = id ? { where: { id: id } } : null;
Tutorial
.destroy(condition)
.then(resultCount => {
if (resultCount == 1) {
res.send({
message: 'Tutorial deleted.'
});
} else {
res.send({
message: 'Cannot delete tutorial. (id: ' + id + ')'
});
}
})
.catch(err => {
res.status(500).send({
message: err.message || 'Delete tutorial failure. (id: ' + id + ')'
});
});
};
5. 서버 실행 & API 테스트
5.1. MySQL 서버 실행
src 디렉터리 하위에 생성한 mysql-server.ts 파일을 Node.js로 실행해줍니다.
$ node mysql-server.ts
서버가 정상적으로 실행되면 설정한 포트로 서버가 실행중이라는 메시지와 함께 테이블이 없는 경우에 생성해주는 쿼리문이 출력됩니다.
브라우저에서 http://localhost:8080 url로 접속하면 다음과 같이 메시지가 출력되는 것을 확인할 수 있습니다.
5.2. CRUD API 테스트
Postman을 이용하여 다음과 같이 API 테스트를 해줍니다.
5.2.1. Create (POST)
5.2.2. Retrieve (GET)
5.2.3. Update (PUT)
5.2.4. Delete (DELETE)
이상으로 Node.js와 MySQL을 이용하여 CRUD RESTful API를 구성하는 방법에 대해 알아봤습니다.
※ Reference
- bezkoder.com, , bezkoder.com/node-js-express-sequelize-mysql/
- sequelize.org, sequelize, sequelize.org/
- velog.io/@yejinh, express 미들웨어 body-parser 모듈, velog.io/@yejinh/express-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-bodyParser-%EB%AA%A8%EB%93%88
- guswnsxodlf.github.io, node.js express에서 CORS 허용하기, guswnsxodlf.github.io/enable-CORS-on-express