From a9ba61baaba142c1cf77abc3ecdeef1d20e1418d Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:11:31 +0500 Subject: [PATCH 01/11] added rest-api-demo with server.js - more files incoming --- Backend Development/rest-api-demo/server.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Backend Development/rest-api-demo/server.js diff --git a/Backend Development/rest-api-demo/server.js b/Backend Development/rest-api-demo/server.js new file mode 100644 index 0000000..5604c8d --- /dev/null +++ b/Backend Development/rest-api-demo/server.js @@ -0,0 +1,11 @@ +import { app } from "./app"; // import app + +// run app by starting to listen +const server = app.listen(app.get("port"), () => { + console.log( + `App is running at http://localhost:${app.get("port")} in ${app.get( + "env" + )} mode` + ); + console.log(" Press CTRL-C to stop\n"); +}); From 4d8746706852ad69e29ceebcf215eeee5188a448 Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:13:20 +0500 Subject: [PATCH 02/11] added connection.js --- Backend Development/rest-api-demo/app.js | 22 ++++++++++++++++ .../config/database/connection.js | 26 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 Backend Development/rest-api-demo/app.js create mode 100644 Backend Development/rest-api-demo/config/database/connection.js diff --git a/Backend Development/rest-api-demo/app.js b/Backend Development/rest-api-demo/app.js new file mode 100644 index 0000000..6320c70 --- /dev/null +++ b/Backend Development/rest-api-demo/app.js @@ -0,0 +1,22 @@ +import express from "express"; // import express + +import { DBConnect } from "./config/database/connection"; // import database connection function + +import { MONGODB_URI, PORT } from "./utils/secrets"; // import secrets +import { defaultErrorHandler, cors } from "./utils/apiHelpers"; // import helper functions + +import { userRouter } from "./routers/user/user"; // import router + +export const app = express(); // create app/server, store it in app - export +export default app; // default export + +DBConnect(MONGODB_URI); // connect database ( call DBConnect and pass mongo uri) + +app.use(cors); // enable cors + +app.set("port", PORT || 3000); // set server port +app.use(express.json()); // parse json + +app.use("/api/v1/user", userRouter.Router()); // initialize router here + +app.use(defaultErrorHandler); // handle errors at one place diff --git a/Backend Development/rest-api-demo/config/database/connection.js b/Backend Development/rest-api-demo/config/database/connection.js new file mode 100644 index 0000000..54f65cc --- /dev/null +++ b/Backend Development/rest-api-demo/config/database/connection.js @@ -0,0 +1,26 @@ +import mongoose from "mongoose"; +import bluebird from "bluebird"; + +export const DBConnect = async (mongoUrl) => { + mongoose.Promise = bluebird; + + const connectionP = new Promise((res, rej) => { + mongoose + .connect(mongoUrl) + .then((ins) => { + console.log("Mongo connected!!!"); + console.log( + `Using mongo host '${mongoose.connection.host}' and port '${mongoose.connection.port}'` + ); + return res(ins); + }) + .catch((err) => { + console.log( + `MongoDB connection error. Please make sure MongoDB is running. ${err}` + ); + return rej(err); + }); + }); + + return connectionP; +}; From 01f185a330e9ac450286f906dce2745a74f0f50b Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:14:05 +0500 Subject: [PATCH 03/11] added apiHelpers.js --- .../rest-api-demo/utils/apiHelpers.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Backend Development/rest-api-demo/utils/apiHelpers.js diff --git a/Backend Development/rest-api-demo/utils/apiHelpers.js b/Backend Development/rest-api-demo/utils/apiHelpers.js new file mode 100644 index 0000000..c454bd9 --- /dev/null +++ b/Backend Development/rest-api-demo/utils/apiHelpers.js @@ -0,0 +1,20 @@ +export const defaultErrorHandler = (err, req, res, next) => { + const { code, message, data } = err; + res.status(code || 500).json({ + message: message || "Internal server error", + result: {}, + errors: data || [], + }); +}; + +export const cors = (req, res, next) => { + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader( + "Access-Control-Allow-Methods", + "GET, POST, PUT, PATCH, DELETE" + ); + res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); + if (req.method === "OPTIONS") return res.sendStatus(200); + next(); +}; +export default cors; From 672067d68699059a37b31c0330ecd459532113d2 Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:15:01 +0500 Subject: [PATCH 04/11] added secrets.js for all the .env vars --- .../rest-api-demo/utils/secrets.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Backend Development/rest-api-demo/utils/secrets.js diff --git a/Backend Development/rest-api-demo/utils/secrets.js b/Backend Development/rest-api-demo/utils/secrets.js new file mode 100644 index 0000000..e49b42d --- /dev/null +++ b/Backend Development/rest-api-demo/utils/secrets.js @@ -0,0 +1,22 @@ +import dotenv from "dotenv"; +dotenv.config(); + +const ENVIRONMENT = process.env.NODE_ENV; +const prod = ENVIRONMENT === "production"; +export const MONGODB_URI = prod + ? process.env["MONGODB_URI"] + : process.env["MONGODB_LOCAL_URI"]; + +export const PORT = process.env.PORT; + +if (!MONGODB_URI) { + if (prod) { + throw new Error( + "No mongo connection string. Set MONGODB_URI environment variable." + ); + } else { + throw new Error( + "No mongo connection string. Set MONGODB_URI_LOCAL environment variable." + ); + } +} From 3cd5f0708cb3e3f304379178d5d3cd18041e9a6d Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:17:56 +0500 Subject: [PATCH 05/11] added .env file --- Backend Development/rest-api-demo/.env | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Backend Development/rest-api-demo/.env diff --git a/Backend Development/rest-api-demo/.env b/Backend Development/rest-api-demo/.env new file mode 100644 index 0000000..2bf4bf8 --- /dev/null +++ b/Backend Development/rest-api-demo/.env @@ -0,0 +1,4 @@ +MONGODB_LOCAL_URI = mongodb://localhost:27017/boilerplate +MONGODB_URI = "your production mongo url" + +NODE_ENV = "not production" \ No newline at end of file From 73129dacfda954a65a1158e36c32e50b08dd752d Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:27:26 +0500 Subject: [PATCH 06/11] added user router " --- .../rest-api-demo/routers/user.js | 11 ++++++++ .../rest-api-demo/utils/apiHelpers.js | 27 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 Backend Development/rest-api-demo/routers/user.js diff --git a/Backend Development/rest-api-demo/routers/user.js b/Backend Development/rest-api-demo/routers/user.js new file mode 100644 index 0000000..4abf1c0 --- /dev/null +++ b/Backend Development/rest-api-demo/routers/user.js @@ -0,0 +1,11 @@ +import { Router } from "express"; +import { response, restApiValidation } from "../utils/apiHelpers"; + +export const userRouter = Router(); +export default userRouter; + +userRouter.get("/", [], async (req, res, next) => { + restApiValidation(req, res, next); + const result = await userService.list(); + response(res, result); +}); diff --git a/Backend Development/rest-api-demo/utils/apiHelpers.js b/Backend Development/rest-api-demo/utils/apiHelpers.js index c454bd9..fe29625 100644 --- a/Backend Development/rest-api-demo/utils/apiHelpers.js +++ b/Backend Development/rest-api-demo/utils/apiHelpers.js @@ -18,3 +18,30 @@ export const cors = (req, res, next) => { next(); }; export default cors; + +export const restApiValidation = (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + const error = new Error(`Bad Request`); + error.data = errors.array().map((err) => { + return { + message: err.msg || "", + param: err.param || "", + location: err.location || "", + value: err.value || "", + }; + }); + error.code = 400; + throw error; + } + return true; +}; + +export const response = (res, result) => { + const response = { + message: "Success", + result: result, + errors: [], + }; + res.status(200).json(response); +}; From 683348c29c5ba55ff4900fe86cb2589473bce66e Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:30:49 +0500 Subject: [PATCH 07/11] added user service --- .../rest-api-demo/services/user.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Backend Development/rest-api-demo/services/user.js diff --git a/Backend Development/rest-api-demo/services/user.js b/Backend Development/rest-api-demo/services/user.js new file mode 100644 index 0000000..b559611 --- /dev/null +++ b/Backend Development/rest-api-demo/services/user.js @@ -0,0 +1,17 @@ +class UserService { + transformUser(user) { + return { + email: user.email, + name: user.name, + age: user.age, + }; + } + + async list() { + // const user = await this.create({ ...data, password: hashedPassword }); + return this.transformUser(user); + } +} + +export const userService = new UserService(userDAO); +export default userService; From 3ddd7ebfd8eae86cb98c5301fea4d9a5bf36edb4 Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:31:51 +0500 Subject: [PATCH 08/11] added user model --- Backend Development/rest-api-demo/models/user.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Backend Development/rest-api-demo/models/user.js diff --git a/Backend Development/rest-api-demo/models/user.js b/Backend Development/rest-api-demo/models/user.js new file mode 100644 index 0000000..e5620e1 --- /dev/null +++ b/Backend Development/rest-api-demo/models/user.js @@ -0,0 +1,13 @@ +import mongoose from "mongoose"; + +const { Schema, model } = mongoose; + +const schemaFields = { + email: { type: String, required: true, unique: true }, + name: { type: String, required: false }, + age: { type: Number, required: false }, +}; + +const schema = new Schema(schemaFields, { timestamps: true }); + +export const UserModel = model("User", schema); From 92b8265fe3bee021b7957dc8267fe7ec0337ec6e Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:43:41 +0500 Subject: [PATCH 09/11] added package.json --- Backend Development/rest-api-demo/app.js | 2 +- .../rest-api-demo/package.json | 25 +++++++++++++++++++ .../rest-api-demo/routers/user.js | 4 ++- .../rest-api-demo/services/user.js | 9 +++++-- 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 Backend Development/rest-api-demo/package.json diff --git a/Backend Development/rest-api-demo/app.js b/Backend Development/rest-api-demo/app.js index 6320c70..002bad2 100644 --- a/Backend Development/rest-api-demo/app.js +++ b/Backend Development/rest-api-demo/app.js @@ -17,6 +17,6 @@ app.use(cors); // enable cors app.set("port", PORT || 3000); // set server port app.use(express.json()); // parse json -app.use("/api/v1/user", userRouter.Router()); // initialize router here +app.use("/api/v1/user", userRouter); // initialize router here app.use(defaultErrorHandler); // handle errors at one place diff --git a/Backend Development/rest-api-demo/package.json b/Backend Development/rest-api-demo/package.json new file mode 100644 index 0000000..c40e672 --- /dev/null +++ b/Backend Development/rest-api-demo/package.json @@ -0,0 +1,25 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "rest-api-boilerplate-basic", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node index.js", + "nodemon": "nodemon --experimental-modules --es-module-specifier-resolution=node index.js" + }, + "author": "NizTheDev", + "license": "MIT", + "devDependencies": { + "nodemon": "^2.0.6" + }, + "dependencies": { + "axios": "^0.21.1", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "express-validator": "^6.7.0", + "mongoose": "^5.13.7", + "validator": "^13.5.2" + }, + "type": "module" +} diff --git a/Backend Development/rest-api-demo/routers/user.js b/Backend Development/rest-api-demo/routers/user.js index 4abf1c0..ea727ff 100644 --- a/Backend Development/rest-api-demo/routers/user.js +++ b/Backend Development/rest-api-demo/routers/user.js @@ -6,6 +6,8 @@ export default userRouter; userRouter.get("/", [], async (req, res, next) => { restApiValidation(req, res, next); - const result = await userService.list(); + const page = parseInt(req.params.page) || 1; + const perPage = parseInt(req.params.perPage) || 10; + const result = await userService.list(page, perPage); response(res, result); }); diff --git a/Backend Development/rest-api-demo/services/user.js b/Backend Development/rest-api-demo/services/user.js index b559611..3fc0d31 100644 --- a/Backend Development/rest-api-demo/services/user.js +++ b/Backend Development/rest-api-demo/services/user.js @@ -1,3 +1,5 @@ +import { UserModel } from "../models/user"; + class UserService { transformUser(user) { return { @@ -7,8 +9,11 @@ class UserService { }; } - async list() { - // const user = await this.create({ ...data, password: hashedPassword }); + async list(page, perPage) { + let skip = (page - 1) * perPage; + skip = page > 1 ? skip - 1 : skip; + const limit = page > 1 ? perPage + 2 : perPage + 1; + const user = await UserModel.find({}).skip(skip).limit(limit).sort(sort); return this.transformUser(user); } } From 7e25e9fc06e4d0f7ed78d7f9058072d19862544e Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 13:49:17 +0500 Subject: [PATCH 10/11] added README.md --- Backend Development/rest-api-demo/README.MD | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Backend Development/rest-api-demo/README.MD diff --git a/Backend Development/rest-api-demo/README.MD b/Backend Development/rest-api-demo/README.MD new file mode 100644 index 0000000..e7932c0 --- /dev/null +++ b/Backend Development/rest-api-demo/README.MD @@ -0,0 +1,20 @@ +This project is only a sample to look and recreate your own rest-api with concepts from this project. +It is not the super simple version, but it's not the most complex either. +It has clean structure for better understanding. + +Starting point for this project is server.js which imports app.js containing all the logic + +follow the following steps to create your own services + +1 create model +2 create service +3 create router +4 import and use the router in app.js + +NOTE: This project can be massively improved. I have kept it simple for the ease of understanding for beginners. + +Full version of this boiler-plate: https://github.com/officialabdulrehman/node-express-restapi-boilerplate + +Auther: https://github.com/officialabdulrehman + +Happy Coding!!! From c63491c16dad4a9e0f7be090c1b203481e680f41 Mon Sep 17 00:00:00 2001 From: Abdul Rehman Nizamani Date: Sat, 23 Oct 2021 14:10:36 +0500 Subject: [PATCH 11/11] added missing import in user router --- Backend Development/rest-api-demo/routers/user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Backend Development/rest-api-demo/routers/user.js b/Backend Development/rest-api-demo/routers/user.js index ea727ff..e4e00e9 100644 --- a/Backend Development/rest-api-demo/routers/user.js +++ b/Backend Development/rest-api-demo/routers/user.js @@ -1,4 +1,5 @@ import { Router } from "express"; +import userService from "../services/user"; import { response, restApiValidation } from "../utils/apiHelpers"; export const userRouter = Router();