├── API_Response_Format.md ├── CONTRIBUTING.md ├── Clean_Repo_Structure.md ├── Commit_Guidelines.md ├── Docker.md ├── LICENSE ├── Microservices.md ├── README.md ├── VALIDATE_REQUEST.md └── microservices ├── microservices-overview-ci-cd.png └── microservices-overview.png /API_Response_Format.md: -------------------------------------------------------------------------------- 1 | # Format for sending API response 2 | 3 | In almost all the API repository I see, the following code block is very common there: 4 | 5 | ```javascript 6 | res.status(200).send(data); 7 | res.status(404).send({ message: 'Not found' }); 8 | res.status(500).send({ error: 'Internal server error happened' }); 9 | ``` 10 | 11 | So what is the problem with the above code or the way of sending response? I found the following: 12 | 13 | - `.status()` is repeated 14 | - `send()` do not have a standard format for sending response which makes it really hard for domain developers to parse the response. 15 | 16 | ## My Solution 17 | 18 | I made a utility function to send response like this: 19 | 20 | ```javascript 21 | /** 22 | * @function 23 | * 24 | * This function is a response sending wrapper 25 | * Instead of writing extra repetitive lines 26 | * use this wrapper 27 | * 28 | * Made with DRY in mind 29 | * 30 | * @author Ashok Dey 31 | * 32 | * @param {object} res the response object 33 | * @param {number} statusCode the http status code 34 | * @param {array | object } data the data you want to send with the response 35 | * @param {string} message the message you want to send for success/failure 36 | */ 37 | 38 | export default function sendResponse(res, statusCode, data = {}, message) { 39 | if (typeof statusCode !== 'number') { 40 | throw new Error('statusCode should be a number'); 41 | } 42 | 43 | // status variable to store the status of the response either success or failed 44 | let status = null; 45 | 46 | // regex pattern to validate that the status code is always 3 digits in length 47 | const lengthPattern = /^[0-9]{3}$/; 48 | 49 | // check for the length of the status code, if its 3 then set default value for status as 50 | // failed 51 | // else throw an error 52 | if (!lengthPattern.test(statusCode)) { 53 | throw new Error('Invalid Status Code'); 54 | } 55 | 56 | // regex to test that status code start with 2 or 3 and should me 3 digits in length 57 | const pattern = /^(2|3)\d{2}$/; 58 | 59 | // if the status code starts with 2, set satus variable as success 60 | pattern.test(statusCode) ? (status = 'success') : (status = 'failed'); 61 | 62 | return res.status(statusCode).json({ 63 | status, 64 | data, 65 | message 66 | }); 67 | } 68 | ``` 69 | 70 | ### Usage 71 | 72 | ```javascript 73 | // inside src/controllers/users.controllers.js 74 | import log4js from 'log4js'; 75 | const logger = log4js.getLogger('UserModule'); 76 | 77 | import { isActionAllowed } from '../../utils/isAllowedToAccess'; 78 | import { sendResponse, handleCustomThrow } from '../../utils'; 79 | import { getAllUsersService } from '../../services/users/users.services'; 80 | 81 | export async function getUsers(req, res) { 82 | try { 83 | // check the user got permission to access user list 84 | const checkAction = await isActionAllowed(req.user.id, 'users'); 85 | if (!checkAction || !checkAction.read) { 86 | return sendResponse(res, 403, {}, 'Not allowed'); 87 | } 88 | 89 | const data = await getAllUsersService(); 90 | return sendResponse(res, 200, { users: data }, 'Fetched data successfully'); 91 | } catch (error) { 92 | logger.error('error in get all users: ', error); 93 | return handleCustomThrow(res, error); 94 | } 95 | } 96 | ``` 97 | 98 | I have good memories with the above code block and it's now 2 years old. This highly impressed [Bikash Dash](https://github.com/beeeku). 99 | 100 | Credits: 101 | 102 | - Erlier data was sent as an empty array which created a barrier to add extra keys in the data. So data should be returned as an object and this was coined by [Sahil Chitkara](https://github.com/sahilchitkara) 103 | 104 | ### Bonus :blush: 105 | 106 | Not exactly a bonus, in case you want to see whats inside `handleCustomThrow` 107 | 108 | ```javascript 109 | // inside /src/utils/handleCustomThrow.js 110 | import sendResponse from './sendResponse'; 111 | export { sendResponse, customValidators, jwtUtils }; 112 | 113 | export function handleCustomThrow(res, error) { 114 | if (error.code === 400) { 115 | return sendResponse(res, error.code, {}, error.msg); 116 | } 117 | if (error.code === 401) { 118 | return sendResponse(res, error.code, {}, error.msg); 119 | } 120 | if (error.code === 403) { 121 | return sendResponse(res, error.code, {}, error.msg); 122 | } 123 | if (error.code === 404) { 124 | return sendResponse(res, error.code, {}, error.msg); 125 | } 126 | return sendResponse(res, 500, {}, 'Something went wrong'); 127 | } 128 | ``` 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | // Will be updated 2 | -------------------------------------------------------------------------------- /Clean_Repo_Structure.md: -------------------------------------------------------------------------------- 1 | # Clean Folder Structure 2 | 3 | Based on my observations and learing from working on more than 5+ REST APIs projects in production I found the following structure of folder pretty neat both by code and files (Right now specific to **Node.js** applications) 4 | 5 | ``` 6 | src 7 | ├── config 8 | │ ├── db.config.js 9 | │ ├── logger.config.js 10 | ├── controllers 11 | │ ├── Todos.controller.js 12 | │ ├── Users.controller.js 13 | │ ├── index.js 14 | ├── db 15 | │ ├── redis.js 16 | │ ├── mysql.js 17 | │ ├── mongoose.js 18 | │ ├── index.js 19 | ├── models 20 | │ ├── Todo.model.js 21 | │ ├── User.model.js 22 | │ ├── index.js 23 | ├── routes 24 | │ ├── App.routes.js 25 | │ ├── Users.routes.js 26 | │ ├── Todos.routes.js 27 | │ ├── index.js 28 | ├── services 29 | │ ├── Todos.service.js 30 | │ ├── Users.service.js 31 | │ ├── index.js 32 | ├── tests 33 | │ ├── Todos.tests.js 34 | │ ├── Users.tests.js 35 | ├── utils 36 | │ └── Logger.js 37 | │ ├── index.js 38 | └── ExtraFolders/if you need 39 | └── index.js 40 | └── .env 41 | ``` 42 | 43 | ## Description 44 | 45 | - `src` contains all the code files 46 | - `src/index.js` is the enrty point for the application 47 | - `config` contains the different configurations that you may need to boot up your application 48 | - `controllers` contains the business logic of the routes 49 | - `db` contains the connection files to different databases/data persistance you choosen 50 | - `models` contains your data models for `mongoose/sequelize` 51 | - `routes` contains the api endpoints and use `controllers` for the logic 52 | - `services` contains the code that interacts with your databases/persistance store and are used inside `controllers` 53 | - `tests` contains your unit tests for the logic. Polite reminder: Unit tests are important 54 | - `utils` contains all the code blocks as functions which are used more than once in your whole codebase. 55 | 56 | ## Example Repos 57 | 58 | - **[Gaurav](https://github.com/igauravsehrawat)** has done a good job on implementing the above conventions in his [payroll system](https://github.com/igauravsehrawat/payroll-system) 59 | - **RecastAI** also got something similar. Hava a look at their [bot-connector](https://github.com/RecastAI/bot-connector/) and also they got a [Wiki](https://github.com/RecastAI/bot-connector/wiki/00-Architecture) 60 | 61 | ## Credits 62 | 63 | - The idea of having services was coined to me by **[Sudheer Signh Paliwal](https://github.com/justsudheerpaliwal)**, fantastic engineer and singer, all rounder in short. :blush: 64 | 65 | ### Todos 66 | 67 | - Addition of code examples for each file in different folders 68 | -------------------------------------------------------------------------------- /Commit_Guidelines.md: -------------------------------------------------------------------------------- 1 | # Commit Guidelines 2 | 3 | So why you need commit guidelines at all? If you browse the log of any random Git repository, you will probably find its commit messages are more or less a mess. 4 | 5 | So I started follwoing a pattern that try to clear the mess a little less messy. My commit message has the following pattern: 6 | `--[verb]:[changes performed]` 7 | 8 | Verb can be any of the following: 9 | 10 | - `init` 11 | - `update` 12 | - `fix` 13 | - `refactor` 14 | - `log` 15 | - `removed` 16 | 17 | ## Sample commit messages 18 | 19 | - First commit after setting up a repo: `--init: initial setup` 20 | - After adding new changes: `--update: added db connections` 21 | - After making any fix: `--fix: DB pool connection instetad of normal connection` 22 | - After removing anything: `--removed: redis was no longer required for todo app` 23 | - When you moved a repeting code-block as util function: `--refactor: new util to validate array payload for todos` 24 | - After adding logs for debug: `--log: added debudding logs inside update todo controller` 25 | 26 | ## Example repos: 27 | 28 | - Almost all my recent repo follow this commit convention. Checkout [my repos](https://github.com/ashokdey?tab=repositories) 29 | - Recently **[Gaurav](https://github.com/igauravsehrawat)** adopted this convention. Checkout his [payroll system demo app](https://github.com/igauravsehrawat/payroll-system) 30 | -------------------------------------------------------------------------------- /Docker.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashokdey/guidelines/0056188d9d9e71ceaadc23ef5a2d232df74a774e/Docker.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ashok Dey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Microservices.md: -------------------------------------------------------------------------------- 1 | # Microservices 2 | 3 | This articles is taken from [Michael Herman's](https://mherman.org/) [testdrivenio](https://testdriven.io/) 4 | 5 | ### Bottom Line 6 | 7 | Ideally... 8 | 9 | 1. All services are configured programmatically by an API. 10 | 1. Events are the driving force ([event-driven](https://martinfowler.com/articles/201701-event-driven.html), [pub/sub model](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern), trigger/action) 11 | 1. Documentation is a first-class citizen. 12 | 1. Live and die with [12-factor](https://12factor.net/) 13 | 1. Services should have a single purpose (do one thing and do it well), be loosely coupled (know little about each other), and have high cohesion (encapsulates all related functionality and data together). 14 | 15 | _APIs, events, and docs!_ 16 | 17 | ### Big pieces 18 | 19 | 1. Application 20 | 1. Pipelines 21 | 1. Platform 22 | 23 | ![microservices-overview](microservices/microservices-overview.png) 24 | 25 | ## Application 26 | 27 | Where the services themselves live... 28 | 29 | ### Big pieces 30 | 31 | 1. APIs 32 | 1. Storage 33 | 1. Docs 34 | 35 | ### APIs 36 | 37 | 1. Powered by web frameworks (Java/Spring Boot, Python/Flask, Node/Express) 38 | 1. Languages and frameworks don't matter as much as solid API contracts 39 | 1. Single Responsibility (think `ls`, do one thing and do it well) 40 | 1. [12 Factor](https://12factor.net/) 41 | 1. Immutability!!! 42 | 1. Documentation is key - Swagger / OpenAPI spec 43 | 1. How do you handle auth? Do service talk on each other or to a single API Gateway? 44 | 45 | ### Storage 46 | 47 | Tools: 48 | 49 | 1. Databases (Mongo, Postgres, Redis) 50 | 1. Task Queues 51 | 1. Message Brokers (RabbitMQ, Kafka) 52 | 53 | Issues: 54 | 55 | 1. Storage is a _hard_ problem. 56 | 1. Containers are ephemeral, data is not. 57 | 1. Do you have a database per service or a shared database? 58 | 1. Disaster recovery? Testing? 59 | 60 | ## Platform 61 | 62 | Infrastructure management... 63 | 64 | ### Big Pieces 65 | 66 | 1. Software defined networks 67 | 1. Service discovery 68 | 1. Health checks (side cars approach) 69 | 1. Logging 70 | 1. Monitoring 71 | 1. Load balancing 72 | 1. Path-based routing 73 | 1. Circuit breaking 74 | 75 | ### Tools 76 | 77 | 1. Kubernetes 78 | 1. Docker Swarm 79 | 1. Mesos 80 | 1. Nomad 81 | 1. Fully-managed: EKS, GKE, ECS 82 | 1. [Ubuntus MAAS](https://www.ubuntu.com/server/maas) (metal as a service) 83 | 1. [OpenStack](https://www.openstack.org/) 84 | 85 | ## Pipelines 86 | 87 | ### Continuos Integration 88 | 89 | Triggers: 90 | 91 | 1. Commit to source repo, build and test project 92 | 1. What else? 93 | 94 | ### Tools 95 | 96 | 1. Github, GitLab, Git 97 | 1. Jenkins, Travis, Circle 98 | 1. AWS, GCP, Azure 99 | 1. Docker Repos 100 | 1. Linters, static code analysis 101 | 102 | ### Continuous Delivery 103 | 104 | Triggers: 105 | 106 | 1. On successful build, deploy 107 | 1. What else? 108 | 109 | ### Tools 110 | 111 | 1. Shell scripts (yuk!) 112 | 1. Fabric (shell scripts on steroids) 113 | 1. Terraform templates 114 | 1. AWS (CodeDeploy, CodePipeline, CloudFormation), GCP, Azure 115 | 1. Concourse 116 | 1. [Spinnaker](https://www.spinnaker.io/) 117 | 1. Old school (Ansible, Chef, Puppet) 118 | 119 | ![microservices-overview-ci-cd](microservices/microservices-overview-ci-cd.png) 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :books: Guidelines 2 | 3 | I have learnt a lot reading from articles, code of others, meeting people and working with few of the superstars. Apart from this I try to mentor people in my spare time especially on Sunday and I see people are doing things wrong way just because of lack of proper guidence. They follow beginner tutorials and they tend to follow the same in the enterprise application. This guidence repo is a step towards solving this problem. 4 | 5 | ## Guides 6 | 7 | - [Clean REST API folder structure (For Node.js)](Clean_Repo_Structure.md) 8 | - [Commit Guidelines for respository](Commit_Guidelines.md) 9 | - [Having a standard API response format](API_Response_Format.md) 10 | - [Better approach of Request Validation](VALIDATE_REQUEST.md) 11 | - [Microservices](Microservices.md) [from [Michael Herman's](https://mherman.org/) [testdrivenio](https://testdriven.io/)] 12 | 13 | ### Contribution 14 | 15 | If you are an experienced developer reading this I request you to come foward and give you valuable inputs by opening a pull request containing your corrections or any new guide from you. 16 | 17 | ### Contributors 18 | 19 | - [Gaurav Sehrawat](https://github.com/igauravsehrawat) 20 | - [Bikash Dash](https://github.com/beeeku) 21 | 22 | ### TODOs 23 | 24 | - Setup `CONTRIBUTION.md` 25 | -------------------------------------------------------------------------------- /VALIDATE_REQUEST.md: -------------------------------------------------------------------------------- 1 | # Better way to validate client REQUEST 2 | 3 | While making a REST API it is always mandatory to validate what you are getting from the client and this thing is so common. I spoke to a lot of people and I have seend production code that uses `if-else` ladder to validate requests. 4 | Something like this: 5 | 6 | ```javascript 7 | if (!req.body.todoName) { 8 | res 9 | .status(400) 10 | .json({ status: false, message: 'TodoName name is mandatory.' }); 11 | return; 12 | } 13 | if (!req.body.todoDescription) { 14 | res 15 | .status(400) 16 | .json({ status: false, message: 'Todo description is mandatory.' }); 17 | return; 18 | } 19 | ``` 20 | 21 | ## Using Request Vaidators 22 | 23 | Let's have a look at a better way to validate. Here is have used `express-validator` and you can have a look at `Joi` as well. 24 | 25 | ```javascript 26 | // inside controllers/random.controller.js 27 | const deleteSlot = Router(); 28 | 29 | function validateRequest(req) { 30 | req 31 | .checkBody('vmId', 'vmId should be integer') 32 | .isInt() 33 | .exists(); 34 | 35 | req 36 | .checkBody('mvId', 'mvId should be integer') 37 | .isInt() 38 | .exists(); 39 | 40 | req 41 | .checkBody('slotIdentifier', 'slotIdentifier should be integer') 42 | .isInt() 43 | .exists(); 44 | 45 | return req.validationErrors(); 46 | } 47 | 48 | deleteSlot.route('/slots').delete(async (req, res) => { 49 | try { 50 | const errors = validateRequest(req); 51 | if (errors) { 52 | return sendResponse(res, 422, {}, errors[0].msg); 53 | } 54 | const { vmId, mvId, slotIdentifier } = req.body; 55 | // call delete service here 56 | return sendResponse(res, 200, {}, 'Successfully deleted slot'); 57 | } catch (err) { 58 | return sendResponse(res, 500, {}, err.message); 59 | } 60 | }); 61 | ``` 62 | -------------------------------------------------------------------------------- /microservices/microservices-overview-ci-cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashokdey/guidelines/0056188d9d9e71ceaadc23ef5a2d232df74a774e/microservices/microservices-overview-ci-cd.png -------------------------------------------------------------------------------- /microservices/microservices-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashokdey/guidelines/0056188d9d9e71ceaadc23ef5a2d232df74a774e/microservices/microservices-overview.png --------------------------------------------------------------------------------