├── .gitignore ├── README.md ├── app ├── config │ └── db.config.js ├── controllers │ └── tutorial.controller.js ├── models │ ├── db.js │ └── tutorial.model.js └── routes │ └── tutorial.routes.js ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js Rest APIs with Express & MySQL example 2 | 3 | For instruction, please visit: 4 | > [Build Node.js Rest APIs with Express & MySQL](https://www.bezkoder.com/node-js-rest-api-express-mysql/) 5 | 6 | Front-end that works well with this Back-end 7 | > [Axios Client](https://www.bezkoder.com/axios-request/) 8 | 9 | > [Angular 8](https://www.bezkoder.com/angular-crud-app/) / [Angular 10](https://www.bezkoder.com/angular-10-crud-app/) / [Angular 11](https://www.bezkoder.com/angular-11-crud-app/) / [Angular 12](https://www.bezkoder.com/angular-12-crud-app/) / [Angular 13](https://www.bezkoder.com/angular-13-crud-example/) / [Angular 14](https://www.bezkoder.com/angular-14-crud-example/) / [Angular 15](https://www.bezkoder.com/angular-15-crud-example/) / [Angular 16 Client](https://www.bezkoder.com/angular-16-crud-example/) / [Angular 17 Client](https://www.bezkoder.com/angular-17-crud-example/) 10 | 11 | > [Vue 2 Client](https://www.bezkoder.com/vue-js-crud-app/) / [Vue 3 Client](https://www.bezkoder.com/vue-3-crud/) / [Vuetify Client](https://www.bezkoder.com/vuetify-data-table-example/) 12 | 13 | > [React Client](https://www.bezkoder.com/react-crud-web-api/) / [React Redux Client](https://www.bezkoder.com/react-redux-crud-example/) 14 | 15 | More Practice 16 | > [Build Node.js Rest APIs with Express, Sequelize & MySQL](https://www.bezkoder.com/node-js-express-sequelize-mysql/) 17 | 18 | > [Server side Pagination in Node.js with Sequelize and MySQL](https://www.bezkoder.com/node-js-sequelize-pagination-mysql/) 19 | 20 | > [Node.js Express File Upload Rest API example](https://www.bezkoder.com/node-js-express-file-upload/) 21 | 22 | > [Node.js Express File Upload with Google Cloud Storage example](https://www.bezkoder.com/google-cloud-storage-nodejs-upload-file/) 23 | 24 | > [Node.js: Upload CSV file data into Database with Express](https://www.bezkoder.com/node-js-upload-csv-file-database/) 25 | 26 | > [Node.js: Upload Excel file data into Database with Express](https://www.bezkoder.com/node-js-upload-excel-file-database/) 27 | 28 | > [Deploying/Hosting Node.js app on Heroku with MySQL database](https://www.bezkoder.com/deploy-node-js-app-heroku-cleardb-mysql/) 29 | 30 | Security: 31 | > [Node.js Express: JWT example | Token Based Authentication & Authorization](https://www.bezkoder.com/node-js-jwt-authentication-mysql/) 32 | 33 | Associations: 34 | > [Sequelize Associations: One-to-Many Relationship example](https://www.bezkoder.com/sequelize-associate-one-to-many/) 35 | 36 | > [Sequelize Associations: Many-to-Many Relationship example](https://www.bezkoder.com/sequelize-associate-many-to-many/) 37 | 38 | Fullstack: 39 | > [Vue.js + Node.js + Express + MySQL example](https://www.bezkoder.com/vue-js-node-js-express-mysql-crud-example/) 40 | 41 | > [Vue.js + Node.js + Express + MongoDB example](https://www.bezkoder.com/vue-node-express-mongodb-mevn-crud/) 42 | 43 | > [Angular 8 + Node.js + Express + MySQL example](https://www.bezkoder.com/angular-node-express-mysql/) 44 | 45 | > [Angular 10 + Node.js + Express + MySQL example](https://www.bezkoder.com/angular-10-node-js-express-mysql/) 46 | 47 | > [Angular 11 + Node.js Express + MySQL example](https://www.bezkoder.com/angular-11-node-js-express-mysql/) 48 | 49 | > [Angular 12 + Node.js Express + MySQL example](https://www.bezkoder.com/angular-12-node-js-express-mysql/) 50 | 51 | > [Angular 13 + Node.js Express + MySQL example](https://www.bezkoder.com/angular-13-node-js-express-mysql/) 52 | 53 | > [Angular 14 + Node.js + Express + MySQL example](https://www.bezkoder.com/angular-14-node-js-express-mysql/) 54 | 55 | > [Angular 15 + Node.js + Express + MySQL example](https://www.bezkoder.com/angular-15-node-js-express-mysql/) 56 | 57 | > [Angular 16 + Node.js + Express + MySQL example](https://www.bezkoder.com/angular-16-node-js-express-mysql/) 58 | 59 | > [Angular 17 + Node.js + Express + MySQL example](https://www.bezkoder.com/angular-17-node-js-express-mysql/) 60 | 61 | > [React + Node.js + Express + MySQL example](https://www.bezkoder.com/react-node-express-mysql/) 62 | 63 | > [React + Redux + Node.js Express + MySQL](https://www.bezkoder.com/react-redux-mysql-crud/) 64 | 65 | ## Project setup 66 | ``` 67 | npm install 68 | ``` 69 | 70 | ### Run 71 | ``` 72 | node server.js 73 | ``` 74 | -------------------------------------------------------------------------------- /app/config/db.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | HOST: "localhost", 3 | USER: "root", 4 | PASSWORD: "123456", 5 | DB: "testdb" 6 | }; -------------------------------------------------------------------------------- /app/controllers/tutorial.controller.js: -------------------------------------------------------------------------------- 1 | const Tutorial = require("../models/tutorial.model.js"); 2 | 3 | // Create and Save a new Tutorial 4 | exports.create = (req, res) => { 5 | // Validate request 6 | if (!req.body) { 7 | res.status(400).send({ 8 | message: "Content can not be empty!" 9 | }); 10 | } 11 | 12 | // Create a Tutorial 13 | const tutorial = new Tutorial({ 14 | title: req.body.title, 15 | description: req.body.description, 16 | published: req.body.published || false 17 | }); 18 | 19 | // Save Tutorial in the database 20 | Tutorial.create(tutorial, (err, data) => { 21 | if (err) 22 | res.status(500).send({ 23 | message: 24 | err.message || "Some error occurred while creating the Tutorial." 25 | }); 26 | else res.send(data); 27 | }); 28 | }; 29 | 30 | // Retrieve all Tutorials from the database (with condition). 31 | exports.findAll = (req, res) => { 32 | const title = req.query.title; 33 | 34 | Tutorial.getAll(title, (err, data) => { 35 | if (err) 36 | res.status(500).send({ 37 | message: 38 | err.message || "Some error occurred while retrieving tutorials." 39 | }); 40 | else res.send(data); 41 | }); 42 | }; 43 | 44 | // Find a single Tutorial by Id 45 | exports.findOne = (req, res) => { 46 | Tutorial.findById(req.params.id, (err, data) => { 47 | if (err) { 48 | if (err.kind === "not_found") { 49 | res.status(404).send({ 50 | message: `Not found Tutorial with id ${req.params.id}.` 51 | }); 52 | } else { 53 | res.status(500).send({ 54 | message: "Error retrieving Tutorial with id " + req.params.id 55 | }); 56 | } 57 | } else res.send(data); 58 | }); 59 | }; 60 | 61 | // find all published Tutorials 62 | exports.findAllPublished = (req, res) => { 63 | Tutorial.getAllPublished((err, data) => { 64 | if (err) 65 | res.status(500).send({ 66 | message: 67 | err.message || "Some error occurred while retrieving tutorials." 68 | }); 69 | else res.send(data); 70 | }); 71 | }; 72 | 73 | // Update a Tutorial identified by the id in the request 74 | exports.update = (req, res) => { 75 | // Validate Request 76 | if (!req.body) { 77 | res.status(400).send({ 78 | message: "Content can not be empty!" 79 | }); 80 | } 81 | 82 | console.log(req.body); 83 | 84 | Tutorial.updateById( 85 | req.params.id, 86 | new Tutorial(req.body), 87 | (err, data) => { 88 | if (err) { 89 | if (err.kind === "not_found") { 90 | res.status(404).send({ 91 | message: `Not found Tutorial with id ${req.params.id}.` 92 | }); 93 | } else { 94 | res.status(500).send({ 95 | message: "Error updating Tutorial with id " + req.params.id 96 | }); 97 | } 98 | } else res.send(data); 99 | } 100 | ); 101 | }; 102 | 103 | // Delete a Tutorial with the specified id in the request 104 | exports.delete = (req, res) => { 105 | Tutorial.remove(req.params.id, (err, data) => { 106 | if (err) { 107 | if (err.kind === "not_found") { 108 | res.status(404).send({ 109 | message: `Not found Tutorial with id ${req.params.id}.` 110 | }); 111 | } else { 112 | res.status(500).send({ 113 | message: "Could not delete Tutorial with id " + req.params.id 114 | }); 115 | } 116 | } else res.send({ message: `Tutorial was deleted successfully!` }); 117 | }); 118 | }; 119 | 120 | // Delete all Tutorials from the database. 121 | exports.deleteAll = (req, res) => { 122 | Tutorial.removeAll((err, data) => { 123 | if (err) 124 | res.status(500).send({ 125 | message: 126 | err.message || "Some error occurred while removing all tutorials." 127 | }); 128 | else res.send({ message: `All Tutorials were deleted successfully!` }); 129 | }); 130 | }; 131 | -------------------------------------------------------------------------------- /app/models/db.js: -------------------------------------------------------------------------------- 1 | const mysql = require("mysql"); 2 | const dbConfig = require("../config/db.config.js"); 3 | 4 | var connection = mysql.createPool({ 5 | host: dbConfig.HOST, 6 | user: dbConfig.USER, 7 | password: dbConfig.PASSWORD, 8 | database: dbConfig.DB 9 | }); 10 | 11 | module.exports = connection; 12 | -------------------------------------------------------------------------------- /app/models/tutorial.model.js: -------------------------------------------------------------------------------- 1 | const sql = require("./db.js"); 2 | 3 | // constructor 4 | const Tutorial = function(tutorial) { 5 | this.title = tutorial.title; 6 | this.description = tutorial.description; 7 | this.published = tutorial.published; 8 | }; 9 | 10 | Tutorial.create = (newTutorial, result) => { 11 | sql.query("INSERT INTO tutorials SET ?", newTutorial, (err, res) => { 12 | if (err) { 13 | console.log("error: ", err); 14 | result(err, null); 15 | return; 16 | } 17 | 18 | console.log("created tutorial: ", { id: res.insertId, ...newTutorial }); 19 | result(null, { id: res.insertId, ...newTutorial }); 20 | }); 21 | }; 22 | 23 | Tutorial.findById = (id, result) => { 24 | sql.query(`SELECT * FROM tutorials WHERE id = ${id}`, (err, res) => { 25 | if (err) { 26 | console.log("error: ", err); 27 | result(err, null); 28 | return; 29 | } 30 | 31 | if (res.length) { 32 | console.log("found tutorial: ", res[0]); 33 | result(null, res[0]); 34 | return; 35 | } 36 | 37 | // not found Tutorial with the id 38 | result({ kind: "not_found" }, null); 39 | }); 40 | }; 41 | 42 | Tutorial.getAll = (title, result) => { 43 | let query = "SELECT * FROM tutorials"; 44 | 45 | if (title) { 46 | query += ` WHERE title LIKE '%${title}%'`; 47 | } 48 | 49 | sql.query(query, (err, res) => { 50 | if (err) { 51 | console.log("error: ", err); 52 | result(null, err); 53 | return; 54 | } 55 | 56 | console.log("tutorials: ", res); 57 | result(null, res); 58 | }); 59 | }; 60 | 61 | Tutorial.getAllPublished = result => { 62 | sql.query("SELECT * FROM tutorials WHERE published=true", (err, res) => { 63 | if (err) { 64 | console.log("error: ", err); 65 | result(null, err); 66 | return; 67 | } 68 | 69 | console.log("tutorials: ", res); 70 | result(null, res); 71 | }); 72 | }; 73 | 74 | Tutorial.updateById = (id, tutorial, result) => { 75 | sql.query( 76 | "UPDATE tutorials SET title = ?, description = ?, published = ? WHERE id = ?", 77 | [tutorial.title, tutorial.description, tutorial.published, id], 78 | (err, res) => { 79 | if (err) { 80 | console.log("error: ", err); 81 | result(null, err); 82 | return; 83 | } 84 | 85 | if (res.affectedRows == 0) { 86 | // not found Tutorial with the id 87 | result({ kind: "not_found" }, null); 88 | return; 89 | } 90 | 91 | console.log("updated tutorial: ", { id: id, ...tutorial }); 92 | result(null, { id: id, ...tutorial }); 93 | } 94 | ); 95 | }; 96 | 97 | Tutorial.remove = (id, result) => { 98 | sql.query("DELETE FROM tutorials WHERE id = ?", id, (err, res) => { 99 | if (err) { 100 | console.log("error: ", err); 101 | result(null, err); 102 | return; 103 | } 104 | 105 | if (res.affectedRows == 0) { 106 | // not found Tutorial with the id 107 | result({ kind: "not_found" }, null); 108 | return; 109 | } 110 | 111 | console.log("deleted tutorial with id: ", id); 112 | result(null, res); 113 | }); 114 | }; 115 | 116 | Tutorial.removeAll = result => { 117 | sql.query("DELETE FROM tutorials", (err, res) => { 118 | if (err) { 119 | console.log("error: ", err); 120 | result(null, err); 121 | return; 122 | } 123 | 124 | console.log(`deleted ${res.affectedRows} tutorials`); 125 | result(null, res); 126 | }); 127 | }; 128 | 129 | module.exports = Tutorial; 130 | -------------------------------------------------------------------------------- /app/routes/tutorial.routes.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | const tutorials = require("../controllers/tutorial.controller.js"); 3 | 4 | var router = require("express").Router(); 5 | 6 | // Create a new Tutorial 7 | router.post("/", tutorials.create); 8 | 9 | // Retrieve all Tutorials 10 | router.get("/", tutorials.findAll); 11 | 12 | // Retrieve all published Tutorials 13 | router.get("/published", tutorials.findAllPublished); 14 | 15 | // Retrieve a single Tutorial with id 16 | router.get("/:id", tutorials.findOne); 17 | 18 | // Update a Tutorial with id 19 | router.put("/:id", tutorials.update); 20 | 21 | // Delete a Tutorial with id 22 | router.delete("/:id", tutorials.delete); 23 | 24 | // Delete all Tutorials 25 | router.delete("/", tutorials.deleteAll); 26 | 27 | app.use('/api/tutorials', router); 28 | }; 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-express-mysql", 3 | "version": "1.0.0", 4 | "description": "Node.js Restful CRUD API with Node.js, Express and MySQL", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "nodejs", 11 | "express", 12 | "mysql", 13 | "restapi" 14 | ], 15 | "author": "bezkoder", 16 | "license": "ISC", 17 | "dependencies": { 18 | "cors": "^2.8.5", 19 | "express": "^4.18.2", 20 | "mysql": "^2.18.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | // const bodyParser = require("body-parser"); /* deprecated */ 3 | const cors = require("cors"); 4 | 5 | const app = express(); 6 | 7 | var corsOptions = { 8 | origin: "http://localhost:8081" 9 | }; 10 | 11 | app.use(cors(corsOptions)); 12 | 13 | // parse requests of content-type - application/json 14 | app.use(express.json()); /* bodyParser.json() is deprecated */ 15 | 16 | // parse requests of content-type - application/x-www-form-urlencoded 17 | app.use(express.urlencoded({ extended: true })); /* bodyParser.urlencoded() is deprecated */ 18 | 19 | // simple route 20 | app.get("/", (req, res) => { 21 | res.json({ message: "Welcome to bezkoder application." }); 22 | }); 23 | 24 | require("./app/routes/tutorial.routes.js")(app); 25 | 26 | // set port, listen for requests 27 | const PORT = process.env.PORT || 8080; 28 | app.listen(PORT, () => { 29 | console.log(`Server is running on port ${PORT}.`); 30 | }); 31 | --------------------------------------------------------------------------------