Skip to content

Commit 446622a

Browse files
committedApr 21, 2022
Complete code
0 parents  commit 446622a

19 files changed

+5626
-0
lines changed
 

‎.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

‎.eslintrc.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
}

‎.gitignore

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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

‎.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

‎.prettierrc

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
}

‎README.md

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<div id="top"></div>
2+
3+
<h1 align="center">Node.js Express REST API MySQL JS Example</h1>
4+
5+
<div align="center">
6+
<p align="center">
7+
This REST API example is a basic backend application to test basic API functions with MySQL database.
8+
</p>
9+
<a href="https://www.postman.com/workspace/node-js-express-mysql-rest-api-example/overview">View Postman Files</a>
10+
</div>
11+
12+
<!-- TABLE OF CONTENTS -->
13+
<details>
14+
<summary>Table of Contents</summary>
15+
<ol>
16+
<li>
17+
<a href="#about-the-application">About The Application</a>
18+
<ul>
19+
<li><a href="#built-with">Built With</a></li>
20+
</ul>
21+
</li>
22+
<li><a href="#how-to-install">How To Install</a></li>
23+
<li><a href="#available-scripts">Available Scripts</a></li>
24+
<li><a href="#postman">Postman</a></li>
25+
</ol>
26+
</details>
27+
28+
<!-- ABOUT THE APPLICATION -->
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+
<p align="right">(<a href="#top">back to top</a>)</p>
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+
<p align="right">(<a href="#top">back to top</a>)</p>
50+
51+
<!-- INSTALLATION INSTRUCTIONS -->
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+
<p align="right">(<a href="#top">back to top</a>)</p>
68+
69+
<!-- AVAILABLE SCRIPTS -->
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+
<p align="right">(<a href="#top">back to top</a>)</p>
88+
89+
<!-- POSTMAN -->
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+
<div align="center">
96+
<img src="./assets/postman/postman-global-env-variables.png" alt="Postman global env variables."/>
97+
<img src="./assets/postman/postman-jobs-env-variables.png" alt="Postman admin env variables."/>
98+
</div>
99+
100+
<p align="right">(<a href="#top">back to top</a>)</p>
Loading
165 KB
Loading

‎config/default.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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+
};

‎package-lock.json

+5,057
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
}

‎postman/User.postman_collection.json

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
}

‎sql/init-db.sql

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
);

‎src/app.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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;

‎src/controllers/api.controller.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
};

‎src/databases/mysql.db.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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;

‎src/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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}`));

‎src/models/user.model.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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;

‎src/routers/api.router.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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;

0 commit comments

Comments
 (0)
Please sign in to comment.