├── .env.sample ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── api │ ├── emojis.js │ └── index.js ├── app.js ├── index.js └── middlewares.js └── test ├── api.test.js └── app.test.js /.env.sample: -------------------------------------------------------------------------------- 1 | NODE_ENV=development -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | jest: true, 5 | }, 6 | extends: 'airbnb-base', 7 | rules: { 8 | 'no-underscore-dangle': 0, 9 | 'no-param-reassign': 0, 10 | 'no-return-assign': 0, 11 | camelcase: 0, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2020 CJ R. 2 | 3 | Permission is hereby granted, free 4 | of charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Express API Starter 2 | 3 | How to use this template: 4 | 5 | ```sh 6 | npx create-express-api --directory my-api-name 7 | ``` 8 | 9 | Includes API Server utilities: 10 | 11 | * [morgan](https://www.npmjs.com/package/morgan) 12 | * HTTP request logger middleware for node.js 13 | * [helmet](https://www.npmjs.com/package/helmet) 14 | * Helmet helps you secure your Express apps by setting various HTTP headers. It's not a silver bullet, but it can help! 15 | * [dotenv](https://www.npmjs.com/package/dotenv) 16 | * Dotenv is a zero-dependency module that loads environment variables from a `.env` file into `process.env` 17 | * [cors](https://www.npmjs.com/package/cors) 18 | * CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options. 19 | 20 | Development utilities: 21 | 22 | * [nodemon](https://www.npmjs.com/package/nodemon) 23 | * nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected. 24 | * [eslint](https://www.npmjs.com/package/eslint) 25 | * ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. 26 | * [jest](https://www.npmjs.com/package/jest) 27 | * Jest is a delightful JavaScript Testing Framework with a focus on simplicity. 28 | * [supertest](https://www.npmjs.com/package/supertest) 29 | * HTTP assertions made easy via superagent. 30 | 31 | ## Setup 32 | 33 | ``` 34 | npm install 35 | ``` 36 | 37 | ## Lint 38 | 39 | ``` 40 | npm run lint 41 | ``` 42 | 43 | ## Test 44 | 45 | ``` 46 | npm test 47 | ``` 48 | 49 | ## Development 50 | 51 | ``` 52 | npm run dev 53 | ``` 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-api-starter", 3 | "version": "1.2.0", 4 | "description": "A basic starter for an express.js API", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node src/index.js", 8 | "dev": "nodemon src/index.js", 9 | "lint": "eslint --fix src", 10 | "test": "jest" 11 | }, 12 | "keywords": [], 13 | "author": "CJ R. (https://w3cj.now.sh)", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/w3cj/express-api-starter.git" 17 | }, 18 | "license": "MIT", 19 | "dependencies": { 20 | "cors": "^2.8.5", 21 | "dotenv": "^16.4.5", 22 | "express": "^4.21.1", 23 | "helmet": "^8.0.0", 24 | "morgan": "^1.10.0" 25 | }, 26 | "devDependencies": { 27 | "eslint": "^8.57.0", 28 | "eslint-config-airbnb-base": "^15.0.0", 29 | "eslint-plugin-import": "^2.31.0", 30 | "jest": "^29.7.0", 31 | "nodemon": "^3.1.7", 32 | "supertest": "^7.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/api/emojis.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const router = express.Router(); 4 | 5 | router.get('/', (req, res) => { 6 | res.json(['😀', '😳', '🙄']); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const emojis = require('./emojis'); 4 | 5 | const router = express.Router(); 6 | 7 | router.get('/', (req, res) => { 8 | res.json({ 9 | message: 'API - 👋🌎🌍🌏', 10 | }); 11 | }); 12 | 13 | router.use('/emojis', emojis); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const morgan = require('morgan'); 3 | const helmet = require('helmet'); 4 | const cors = require('cors'); 5 | 6 | require('dotenv').config(); 7 | 8 | const middlewares = require('./middlewares'); 9 | const api = require('./api'); 10 | 11 | const app = express(); 12 | 13 | app.use(morgan('dev')); 14 | app.use(helmet()); 15 | app.use(cors()); 16 | app.use(express.json()); 17 | 18 | app.get('/', (req, res) => { 19 | res.json({ 20 | message: '🦄🌈✨👋🌎🌍🌏✨🌈🦄', 21 | }); 22 | }); 23 | 24 | app.use('/api/v1', api); 25 | 26 | app.use(middlewares.notFound); 27 | app.use(middlewares.errorHandler); 28 | 29 | module.exports = app; 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app'); 2 | 3 | const port = process.env.PORT || 5000; 4 | app.listen(port, () => { 5 | /* eslint-disable no-console */ 6 | console.log(`Listening: http://localhost:${port}`); 7 | /* eslint-enable no-console */ 8 | }); 9 | -------------------------------------------------------------------------------- /src/middlewares.js: -------------------------------------------------------------------------------- 1 | function notFound(req, res, next) { 2 | res.status(404); 3 | const error = new Error(`🔍 - Not Found - ${req.originalUrl}`); 4 | next(error); 5 | } 6 | 7 | /* eslint-disable no-unused-vars */ 8 | function errorHandler(err, req, res, next) { 9 | /* eslint-enable no-unused-vars */ 10 | const statusCode = res.statusCode !== 200 ? res.statusCode : 500; 11 | res.status(statusCode); 12 | res.json({ 13 | message: err.message, 14 | stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack, 15 | }); 16 | } 17 | 18 | module.exports = { 19 | notFound, 20 | errorHandler, 21 | }; 22 | -------------------------------------------------------------------------------- /test/api.test.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | 3 | const app = require('../src/app'); 4 | 5 | describe('GET /api/v1', () => { 6 | it('responds with a json message', (done) => { 7 | request(app) 8 | .get('/api/v1') 9 | .set('Accept', 'application/json') 10 | .expect('Content-Type', /json/) 11 | .expect(200, { 12 | message: 'API - 👋🌎🌍🌏' 13 | }, done); 14 | }); 15 | }); 16 | 17 | describe('GET /api/v1/emojis', () => { 18 | it('responds with a json message', (done) => { 19 | request(app) 20 | .get('/api/v1/emojis') 21 | .set('Accept', 'application/json') 22 | .expect('Content-Type', /json/) 23 | .expect(200, ['😀', '😳', '🙄'], done); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | 3 | const app = require('../src/app'); 4 | 5 | describe('app', () => { 6 | it('responds with a not found message', (done) => { 7 | request(app) 8 | .get('/what-is-this-even') 9 | .set('Accept', 'application/json') 10 | .expect('Content-Type', /json/) 11 | .expect(404, done); 12 | }); 13 | }); 14 | 15 | describe('GET /', () => { 16 | it('responds with a json message', (done) => { 17 | request(app) 18 | .get('/') 19 | .set('Accept', 'application/json') 20 | .expect('Content-Type', /json/) 21 | .expect(200, { 22 | message: '🦄🌈✨👋🌎🌍🌏✨🌈🦄' 23 | }, done); 24 | }); 25 | }); 26 | --------------------------------------------------------------------------------