├── .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 |

Node.js Express REST API MySQL JS Example

4 | 5 |
6 |

7 | This REST API example is a basic backend application to test basic API functions with MySQL database. 8 |

9 | View Postman Files 10 |
11 | 12 | 13 |
14 | Table of Contents 15 |
    16 |
  1. 17 | About The Application 18 | 21 |
  2. 22 |
  3. How To Install
  4. 23 |
  5. Available Scripts
  6. 24 |
  7. Postman
  8. 25 |
26 |
27 | 28 | 29 | 30 | ## About The Application 31 | 32 | This REST API example is a basic backend application to test basic API functions with MySQL database. 33 | 34 | It is built with Node.js and Express Framework with Javascript. In addition, the applications database is MySQL, with the use of mysql2 library. 35 | 36 | In the applicaiton we can manage user data, such as create/edit/delete a user. In addition, we can get all the users in the database. 37 | 38 | The point of this backend application is to test CRUD operations with MySQL database. 39 | 40 |

(back to top)

41 | 42 | ### Built With 43 | 44 | - [Node.js](https://nodejs.org/en/) 45 | - [Express](https://expressjs.com/) 46 | - [Cors](https://www.npmjs.com/package/cors) 47 | - [MySQL2](https://www.npmjs.com/package/mysql2) 48 | 49 |

(back to top)

50 | 51 | 52 | 53 | ## How To Install 54 | 55 | **Git clone** 56 | 57 | ``` 58 | git clone https://github.com/almoggutin/Node-Express-REST-API-MySQL-JS-Example 59 | ``` 60 | 61 | **Instructions** 62 | 63 | - After cloning the the repository run `npm i` in order to install all the dependencies. 64 | - Create an env file in the root of the project named .env and fill in the follwing variables: PORT, DB_HOST, DB_PORT, DB_USERNAME, DB_USERNAME_PASSWORD, DB_NAME. 65 | - In the sql directory, there are sql files that you will need to execute in order to initialize the database. 66 | 67 |

(back to top)

68 | 69 | 70 | 71 | ## Available Scripts 72 | 73 | In the project directory, you can run: 74 | 75 | ### `npm start` 76 | 77 | Runs the app in the production mode.\ 78 | However, this script is only meant to be run when deploying the application. The application is built, where you need to setup the env variables on the machine that you will be hosting it on or on a web hosting service, unlike in development mode. 79 | 80 | ### `npm run dev` 81 | 82 | Runs the app in the development mode.\ 83 | Open localhost on the port you decided on in the env variables to view it in the browser. 84 | 85 | The API will reload if you make edits with the use of nodemon. 86 | 87 |

(back to top)

88 | 89 | 90 | 91 | ## Postman 92 | 93 | If you would like to run the files locally on your machine in the postman desktop application, included in the repository, in the `postman` directory all the files so you can import them. In addition you will have to configure env variables in postman so that you will be able to test properly everything. 94 | 95 |
96 | Postman global env variables. 97 | Postman admin env variables. 98 |
99 | 100 |

(back to top)

101 | --------------------------------------------------------------------------------