├── .eslintignore ├── .prettierignore ├── assets └── postman │ ├── postman-jobs-env-variables.png │ └── postman-global-env-variables.png ├── sql └── init-db.sql ├── .gitignore ├── config └── default.js ├── src ├── index.js ├── routers │ └── api.router.js ├── app.js ├── databases │ └── mysql.db.js ├── models │ └── user.model.js └── controllers │ └── api.controller.js ├── .prettierrc ├── .eslintrc.json ├── package.json ├── postman └── User.postman_collection.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /assets/postman/postman-jobs-env-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almoggutin/node-express-rest-api-mysql-js-example/HEAD/assets/postman/postman-jobs-env-variables.png -------------------------------------------------------------------------------- /assets/postman/postman-global-env-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almoggutin/node-express-rest-api-mysql-js-example/HEAD/assets/postman/postman-global-env-variables.png -------------------------------------------------------------------------------- /sql/init-db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE api_example; 2 | 3 | USE api_example; 4 | 5 | CREATE TABLE users ( 6 | id NVARCHAR(255) PRIMARY KEY, 7 | first_name NVARCHAR(100) NOT NULL, 8 | last_name NVARCHAR(100) NOT NULL, 9 | age int NOT NULL 10 | ); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Output 2 | /node_modules 3 | 4 | # OS 5 | .DS_Store 6 | 7 | # ENV 8 | .env* 9 | *.env 10 | 11 | # IDE - VSCode 12 | .vscode/* 13 | !.vscode/settings.json 14 | !.vscode/tasks.json 15 | !.vscode/launch.json 16 | !.vscode/extensions.json -------------------------------------------------------------------------------- /config/default.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PORT: process.env.PORT || 3000, 3 | DB_HOST: process.env.DB_HOST || '', 4 | DB_PORT: process.env.DB_PORT || '', 5 | DB_USERNAME: process.env.DB_USERNAME || '', 6 | DB_USERNAME_PASSWORD: process.env.DB_USERNAME_PASSWORD || '', 7 | DB_NAME: process.env.DB_NAME || '', 8 | }; 9 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const dotenv = require('dotenv'); 2 | 3 | const NODE_ENV = process.env.NODE_ENV || 'development'; 4 | if (NODE_ENV === 'development') dotenv.config(); 5 | 6 | const config = require('config'); 7 | const PORT = config.get('PORT'); 8 | 9 | const app = require('./app'); 10 | app.listen(PORT, () => console.log(`Server is running in ${NODE_ENV} mode on port: ${PORT}`)); 11 | -------------------------------------------------------------------------------- /src/routers/api.router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const apiController = require('../controllers/api.controller'); 4 | 5 | const router = express.Router(); 6 | 7 | // Endpoint for getting all the records 8 | router.get('/', apiController.getUsers); 9 | 10 | // Endpoint for creating a new record 11 | router.post('/new', apiController.addUser); 12 | 13 | // Endpoints for updating/deleting a record 14 | router.route('/:id').put(apiController.updateUser).delete(apiController.deleteUser); 15 | 16 | module.exports = router; 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "endOfLine": "lf", 5 | "htmlWhitespaceSensitivity": "css", 6 | "insertPragma": false, 7 | "jsxBracketSameLine": false, 8 | "jsxSingleQuote": false, 9 | "printWidth": 120, 10 | "proseWrap": "preserve", 11 | "quoteProps": "preserve", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleQuote": true, 15 | "tabWidth": 4, 16 | "trailingComma": "es5", 17 | "useTabs": false, 18 | "vueIndentScriptAndStyle": false 19 | } 20 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cors = require('cors'); 3 | 4 | const apiRouter = require('./routers/api.router'); 5 | 6 | require('./databases/mysql.db'); 7 | 8 | const app = express(); 9 | 10 | app.use(express.json()); 11 | 12 | const NODE_ENV = process.env.NODE_ENV || 'development'; 13 | const whitelist = []; 14 | const corsOptions = { 15 | origin: function (origin = '', callback) { 16 | if (whitelist.indexOf(origin) !== -1) callback(null, true); 17 | else callback(new Error('Not allowed by CORS')); 18 | }, 19 | methods: ['GET, POST'], 20 | allowedHeaders: ['Content-Type'], 21 | }; 22 | app.use(NODE_ENV === 'development' ? cors() : cors(corsOptions)); 23 | 24 | app.get('/', (req, res) => res.send()); 25 | 26 | app.use('/api', apiRouter); 27 | 28 | module.exports = app; 29 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es2021": true 6 | }, 7 | "extends": ["eslint:recommended", "prettier", "plugin:node/recommended"], 8 | "parserOptions": { 9 | "ecmaVersion": "latest", 10 | "sourceType": "module" 11 | }, 12 | "plugins": ["prettier"], 13 | "settings": { 14 | "node": { 15 | "tryExtensions": [".js", ".json", ".node"] 16 | } 17 | }, 18 | "rules": { 19 | "prettier/prettier": "error", 20 | "no-unused-vars": "warn", 21 | "no-console": "off", 22 | "func-names": "off", 23 | "no-process-exit": "off", 24 | "class-methods-use-this": "off", 25 | "node/no-unsupported-features/es-syntax": ["error", { "ignores": ["modules"] }] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/databases/mysql.db.js: -------------------------------------------------------------------------------- 1 | const mysql = require('mysql2/promise'); 2 | const config = require('config'); 3 | 4 | const DB_HOST = config.get('DB_HOST'); 5 | const DB_PORT = config.get('DB_PORT'); 6 | const DB_NAME = config.get('DB_NAME'); 7 | const DB_USERNAME = config.get('DB_USERNAME'); 8 | const DB_USERNAME_PASSWORD = config.get('DB_USERNAME_PASSWORD'); 9 | 10 | const connectionOptions = { 11 | host: DB_HOST, 12 | port: DB_PORT, 13 | database: DB_NAME, 14 | user: DB_USERNAME, 15 | password: DB_USERNAME_PASSWORD, 16 | }; 17 | 18 | const pool = mysql.createPool(connectionOptions); 19 | 20 | const connectToMySQL = async () => { 21 | try { 22 | await pool.getConnection(); 23 | 24 | console.log('MySQL database connected!'); 25 | } catch (err) { 26 | console.log('MySQL database connection error!'); 27 | 28 | process.exit(1); 29 | } 30 | }; 31 | 32 | connectToMySQL().then(); 33 | 34 | module.exports = pool; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-express-rest-api-mysql-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "cross-env NODE_ENV=production node src/index.js", 9 | "dev": "cross-env NODE_ENV=development nodemon src/index.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "eslint": "^8.10.0", 16 | "eslint-config-prettier": "^8.4.0", 17 | "eslint-plugin-node": "^11.1.0", 18 | "eslint-plugin-prettier": "^4.0.0", 19 | "nodemon": "^2.0.15", 20 | "prettier": "^2.5.1" 21 | }, 22 | "dependencies": { 23 | "config": "^3.3.7", 24 | "cors": "^2.8.5", 25 | "cross-env": "^7.0.3", 26 | "dotenv": "^16.0.0", 27 | "express": "^4.17.3", 28 | "mysql2": "^2.3.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/models/user.model.js: -------------------------------------------------------------------------------- 1 | const pool = require('../databases/mysql.db'); 2 | 3 | class User { 4 | constructor(firstName, lastName, age = 0) { 5 | this._firstName = firstName; 6 | this._lastName = lastName; 7 | this._age = age; 8 | } 9 | 10 | get firstName() { 11 | return this._firstName; 12 | } 13 | 14 | set firstName(firstName) { 15 | if (!firstName) throw new Error('Invalid first name value.'); 16 | 17 | firstName = firstName.trim(); 18 | if (firstName === '') throw new Error('Invalid first name value.'); 19 | 20 | this._firstName = firstName; 21 | } 22 | 23 | get lastName() { 24 | return this._lastName; 25 | } 26 | 27 | set lastName(lastName) { 28 | if (!lastName) throw new Error('Invalid last name value.'); 29 | 30 | lastName = lastName.trim(); 31 | if (lastName === '') throw new Error('Invalid last name value.'); 32 | 33 | this._lastName = lastName; 34 | } 35 | 36 | get age() { 37 | return this._age; 38 | } 39 | 40 | set age(age) { 41 | if (age < 0) throw new Error('Invalid age value.'); 42 | 43 | this._age = age; 44 | } 45 | 46 | async save() { 47 | const sql = `INSERT INTO users (id, first_name, last_name, age) VALUES (UUID(), "${this.firstName}", "${this.lastName}", ${this.age})`; 48 | await pool.execute(sql); 49 | } 50 | 51 | static async find() { 52 | const sql = 'SELECT * FROM users'; 53 | const [rows, fields] = await pool.execute(sql); 54 | 55 | return rows; 56 | } 57 | 58 | static async findByIdAndUpdate(id, options) { 59 | const sql = `UPDATE users SET first_name = "${options.firstName}", last_name = "${options.lastName}", age = ${options.age} WHERE id = "${id}"`; 60 | await pool.execute(sql); 61 | } 62 | 63 | static async findByIdAndDelete(id) { 64 | const sql = `DELETE FROM users WHERE id = "${id}"`; 65 | await pool.execute(sql); 66 | } 67 | } 68 | 69 | module.exports = User; 70 | -------------------------------------------------------------------------------- /postman/User.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "aadc18b2-d744-437e-9ad6-c88ded6e5267", 4 | "name": "User", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "User: Get All Users", 10 | "request": { 11 | "method": "GET", 12 | "header": [], 13 | "url": { 14 | "raw": "{{HOST}}:{{PORT}}/api", 15 | "host": [ 16 | "{{HOST}}" 17 | ], 18 | "port": "{{PORT}}", 19 | "path": [ 20 | "api" 21 | ] 22 | } 23 | }, 24 | "response": [] 25 | }, 26 | { 27 | "name": "User: New User", 28 | "request": { 29 | "method": "POST", 30 | "header": [ 31 | { 32 | "key": "Content-Type", 33 | "value": "application/json", 34 | "type": "text" 35 | } 36 | ], 37 | "body": { 38 | "mode": "raw", 39 | "raw": "{\n \"firstName\": \"\",\n \"lastName\": \"\",\n \"age\": 0\n}" 40 | }, 41 | "url": { 42 | "raw": "{{HOST}}:{{PORT}}/api/new", 43 | "host": [ 44 | "{{HOST}}" 45 | ], 46 | "port": "{{PORT}}", 47 | "path": [ 48 | "api", 49 | "new" 50 | ] 51 | } 52 | }, 53 | "response": [] 54 | }, 55 | { 56 | "name": "User: Edit User", 57 | "request": { 58 | "method": "PUT", 59 | "header": [ 60 | { 61 | "key": "Content-Type", 62 | "value": "application/json", 63 | "type": "text" 64 | } 65 | ], 66 | "body": { 67 | "mode": "raw", 68 | "raw": "{\n \"firstName\": \"\",\n \"lastName\": \"\",\n \"age\": 0\n}" 69 | }, 70 | "url": { 71 | "raw": "{{HOST}}:{{PORT}}/api/{{userId}}", 72 | "host": [ 73 | "{{HOST}}" 74 | ], 75 | "port": "{{PORT}}", 76 | "path": [ 77 | "api", 78 | "{{userId}}" 79 | ] 80 | } 81 | }, 82 | "response": [] 83 | }, 84 | { 85 | "name": "User: Delete User", 86 | "request": { 87 | "method": "DELETE", 88 | "header": [], 89 | "url": { 90 | "raw": "{{HOST}}:{{PORT}}/api/{{userId}}", 91 | "host": [ 92 | "{{HOST}}" 93 | ], 94 | "port": "{{PORT}}", 95 | "path": [ 96 | "api", 97 | "{{userId}}" 98 | ] 99 | } 100 | }, 101 | "response": [] 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /src/controllers/api.controller.js: -------------------------------------------------------------------------------- 1 | const User = require('../models/user.model'); 2 | 3 | const getUsers = async (req, res) => { 4 | try { 5 | const users = await User.find(); 6 | 7 | res.send({ 8 | statusCode: 200, 9 | statusMessage: 'Ok', 10 | message: 'Successfully retrieved all the users.', 11 | data: users, 12 | }); 13 | } catch (err) { 14 | res.status(500).send({ statusCode: 500, statusMessage: 'Internal Server Error', message: null, data: null }); 15 | } 16 | }; 17 | 18 | const addUser = async (req, res) => { 19 | const { firstName, lastName, age } = req.body; 20 | if (!firstName || !firstName.trim() || !lastName || !lastName.trim() || age == null || age < 0) 21 | return res.status(400).send({ statusCode: 400, statusMessage: 'Bad Request', message: null, data: null }); 22 | 23 | try { 24 | const user = new User(firstName, lastName, age); 25 | await user.save(); 26 | 27 | res.status(201).send({ 28 | statusCode: 201, 29 | statusMessage: 'Created', 30 | message: 'Successfully created a user.', 31 | data: null, 32 | }); 33 | } catch (err) { 34 | res.status(500).send({ 35 | statusCode: 500, 36 | statusMessage: 'Internal Server Error', 37 | message: null, 38 | data: null, 39 | }); 40 | } 41 | }; 42 | 43 | const updateUser = async (req, res) => { 44 | const id = req.params.id; 45 | const { firstName, lastName, age } = req.body; 46 | if (!firstName || !firstName.trim() || !lastName || !lastName.trim() || age == null || age < 0) 47 | return res.status(400).send({ statusCode: 400, statusMessage: 'Bad Request', message: null, data: null }); 48 | 49 | try { 50 | await User.findByIdAndUpdate(id, req.body); 51 | 52 | return res.status(202).send({ 53 | statusCode: 202, 54 | statusMessage: 'Accepted', 55 | message: 'Successfully updated a user.', 56 | data: null, 57 | }); 58 | } catch (err) { 59 | console.log(err); 60 | res.status(500).send({ 61 | statusCode: 500, 62 | statusMessage: 'Internal Server Error', 63 | message: null, 64 | data: null, 65 | }); 66 | } 67 | }; 68 | 69 | const deleteUser = async (req, res) => { 70 | const id = req.params.id; 71 | 72 | try { 73 | await User.findByIdAndDelete(id); 74 | 75 | res.send({ 76 | statusCode: 200, 77 | statusMessage: 'Ok', 78 | message: 'Successfully deleted a user.', 79 | data: null, 80 | }); 81 | } catch (err) { 82 | res.status(500).send({ 83 | statusCode: 500, 84 | statusMessage: 'Internal Server Error', 85 | message: null, 86 | data: null, 87 | }); 88 | } 89 | }; 90 | 91 | module.exports = { 92 | getUsers, 93 | addUser, 94 | updateUser, 95 | deleteUser, 96 | }; 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |7 | This REST API example is a basic backend application to test basic API functions with MySQL database. 8 |
9 | View Postman Files 10 |
97 |
98 |