├── .gitattributes ├── gateway ├── .eslintignore ├── .npmrc ├── .prettierrc ├── src │ ├── index.js │ ├── config.js │ ├── heroes │ │ ├── heroes.router.js │ │ └── heroes.service.js │ ├── movies │ │ ├── movies.router.js │ │ └── movies.service.js │ ├── error.js │ └── app.js ├── .eslintrc.json ├── Dockerfile └── package.json ├── heroes ├── .eslintignore ├── .npmrc ├── .prettierrc ├── src │ ├── hero │ │ ├── hero.model.js │ │ ├── hero.router.js │ │ └── hero.controller.js │ ├── config.js │ ├── app.js │ └── index.js ├── .eslintrc.json ├── Dockerfile └── package.json ├── movies ├── .eslintignore ├── .npmrc ├── CHANGELOG.md ├── .prettierrc ├── src │ ├── movie │ │ ├── movie.model.js │ │ ├── movie.router.js │ │ └── movie.controller.js │ ├── config.js │ ├── error.js │ ├── app.js │ └── index.js ├── .eslintrc.json ├── Dockerfile └── package.json ├── .editorconfig ├── docker-compose.yml ├── LICENSE ├── .gitignore ├── README.md └── postman └── docker-express-gateway.postman.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /gateway/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /heroes/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /movies/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /gateway/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | progress = false 3 | save-exact = true 4 | -------------------------------------------------------------------------------- /heroes/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | progress = false 3 | save-exact = true 4 | -------------------------------------------------------------------------------- /movies/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | progress = false 3 | save-exact = true 4 | -------------------------------------------------------------------------------- /movies/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.0 4 | 5 | * Initial release 6 | -------------------------------------------------------------------------------- /gateway/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /heroes/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /movies/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /heroes/src/hero/hero.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const HeroSchema = mongoose.Schema({ 4 | name: String 5 | }); 6 | 7 | const Hero = mongoose.model('Hero', HeroSchema); 8 | 9 | module.exports = Hero; 10 | -------------------------------------------------------------------------------- /movies/src/movie/movie.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const MovieSchema = mongoose.Schema({ 4 | name: String 5 | }); 6 | 7 | const Movie = mongoose.model('Movie', MovieSchema); 8 | 9 | module.exports = Movie; 10 | -------------------------------------------------------------------------------- /gateway/src/index.js: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | 3 | const app = require('./app'); 4 | const config = require('./config'); 5 | 6 | app.listen(config.PORT, () => { 7 | Object.keys(config).forEach((key) => winston.info(`${key}: ${config[key]}`)); 8 | }); 9 | -------------------------------------------------------------------------------- /heroes/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "plugin:prettier/recommended"], 3 | "env": { 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 8 8 | }, 9 | "rules": { 10 | "no-process-env": "error" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /movies/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "plugin:prettier/recommended"], 3 | "env": { 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 8 8 | }, 9 | "rules": { 10 | "no-process-env": "error" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "plugin:prettier/recommended"], 3 | "env": { 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 8 8 | }, 9 | "rules": { 10 | "no-process-env": "error" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /heroes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.11.1-alpine 2 | 3 | LABEL maintainer="robertoachar@gmail.com" 4 | 5 | WORKDIR /usr/src/app 6 | 7 | VOLUME [ "/usr/src/app" ] 8 | 9 | RUN npm i -g nodemon 10 | 11 | ENV NODE_ENV=development 12 | ENV DATABASE=mongodb://mongodb:27017/gateway-heroes 13 | ENV PORT=3000 14 | 15 | EXPOSE 3000 16 | 17 | CMD [ "nodemon", "-L", "src/index.js"] 18 | -------------------------------------------------------------------------------- /movies/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.11.1-alpine 2 | 3 | LABEL maintainer="robertoachar@gmail.com" 4 | 5 | WORKDIR /usr/src/app 6 | 7 | VOLUME [ "/usr/src/app" ] 8 | 9 | RUN npm i -g nodemon 10 | 11 | ENV NODE_ENV=development 12 | ENV DATABASE=mongodb://mongodb:27017/gateway-movies 13 | ENV PORT=4000 14 | 15 | EXPOSE 4000 16 | 17 | CMD [ "nodemon", "-L", "src/index.js"] 18 | -------------------------------------------------------------------------------- /gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.11.1-alpine 2 | 3 | LABEL maintainer="robertoachar@gmail.com" 4 | 5 | WORKDIR /usr/src/app 6 | 7 | VOLUME [ "/usr/src/app" ] 8 | 9 | RUN npm i -g nodemon 10 | 11 | ENV NODE_ENV=development 12 | ENV PORT=5000 13 | ENV HEROES=http://heroes:3000/api/heroes 14 | ENV MOVIES=http://movies:4000/api/movies 15 | 16 | EXPOSE 5000 17 | 18 | CMD [ "nodemon", "-L", "src/index.js"] 19 | -------------------------------------------------------------------------------- /heroes/src/config.js: -------------------------------------------------------------------------------- 1 | /* eslint no-process-env: 0 */ 2 | 3 | require('dotenv').config(); 4 | 5 | const environment = ['NODE_ENV', 'DATABASE', 'PORT']; 6 | 7 | environment.forEach((name) => { 8 | if (!process.env[name]) { 9 | throw new Error(`${name}: ${process.env[name]}`); 10 | } 11 | }); 12 | 13 | module.exports = { 14 | NODE_ENV: process.env.NODE_ENV, 15 | DATABASE: process.env.DATABASE, 16 | PORT: process.env.PORT 17 | }; 18 | -------------------------------------------------------------------------------- /movies/src/config.js: -------------------------------------------------------------------------------- 1 | /* eslint no-process-env: 0 */ 2 | 3 | require('dotenv').config(); 4 | 5 | const environment = ['NODE_ENV', 'DATABASE', 'PORT']; 6 | 7 | environment.forEach((name) => { 8 | if (!process.env[name]) { 9 | throw new Error(`${name}: ${process.env[name]}`); 10 | } 11 | }); 12 | 13 | module.exports = { 14 | NODE_ENV: process.env.NODE_ENV, 15 | DATABASE: process.env.DATABASE, 16 | PORT: process.env.PORT 17 | }; 18 | -------------------------------------------------------------------------------- /heroes/src/app.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const express = require('express'); 3 | 4 | const app = express(); 5 | const heroRouter = require('./hero/hero.router'); 6 | 7 | app.use(bodyParser.urlencoded({ extended: false })); 8 | app.use(bodyParser.json()); 9 | 10 | app.get('/', (req, res) => { 11 | res.json({ message: 'It works!!!' }); 12 | }); 13 | 14 | app.use('/api/heroes', heroRouter); 15 | 16 | module.exports = app; 17 | -------------------------------------------------------------------------------- /gateway/src/config.js: -------------------------------------------------------------------------------- 1 | /* eslint no-process-env: 0 */ 2 | 3 | require('dotenv').config(); 4 | 5 | const environments = ['NODE_ENV', 'PORT', 'HEROES', 'MOVIES']; 6 | 7 | environments.forEach((name) => { 8 | if (!process.env[name]) { 9 | throw new Error(`${name}: ${process.env[name]}`); 10 | } 11 | }); 12 | 13 | module.exports = { 14 | NODE_ENV: process.env.NODE_ENV, 15 | PORT: process.env.PORT, 16 | HEROES: process.env.HEROES, 17 | MOVIES: process.env.MOVIES 18 | }; 19 | -------------------------------------------------------------------------------- /heroes/src/hero/hero.router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const catchErrors = require('express-catch-errors'); 3 | 4 | const router = express.Router(); 5 | const { create, list, remove, update, view } = require('./hero.controller'); 6 | 7 | router 8 | .route('/') 9 | .get(catchErrors(list)) 10 | .post(catchErrors(create)); 11 | 12 | router 13 | .route('/:id') 14 | .get(catchErrors(view)) 15 | .put(catchErrors(update)) 16 | .delete(catchErrors(remove)); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /gateway/src/heroes/heroes.router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const catchErrors = require('express-catch-errors'); 3 | 4 | const router = express.Router(); 5 | const { create, list, remove, update, view } = require('./heroes.service'); 6 | 7 | router 8 | .route('/') 9 | .get(catchErrors(list)) 10 | .post(catchErrors(create)); 11 | 12 | router 13 | .route('/:id') 14 | .get(catchErrors(view)) 15 | .put(catchErrors(update)) 16 | .delete(catchErrors(remove)); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /gateway/src/movies/movies.router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const catchErrors = require('express-catch-errors'); 3 | 4 | const router = express.Router(); 5 | const { create, list, remove, update, view } = require('./movies.service'); 6 | 7 | router 8 | .route('/') 9 | .get(catchErrors(list)) 10 | .post(catchErrors(create)); 11 | 12 | router 13 | .route('/:id') 14 | .get(catchErrors(view)) 15 | .put(catchErrors(update)) 16 | .delete(catchErrors(remove)); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /movies/src/error.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | 3 | const winston = require('winston'); 4 | 5 | module.exports.notFound = (req, res, next) => { 6 | winston.warn('Not found'); 7 | 8 | const error = new Error('Not found'); 9 | error.status = 404; 10 | 11 | next(error); 12 | }; 13 | 14 | module.exports.catchAll = (err, req, res, next) => { 15 | const status = err.status || 500; 16 | const message = err.message || 'Something broke!'; 17 | 18 | winston.error(message); 19 | 20 | res.status(status).json({ error: { message } }); 21 | }; 22 | -------------------------------------------------------------------------------- /gateway/src/error.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | 3 | const winston = require('winston'); 4 | 5 | module.exports.notFound = (req, res, next) => { 6 | winston.warn('Not found'); 7 | 8 | const error = new Error('Not found'); 9 | error.status = 404; 10 | 11 | next(error); 12 | }; 13 | 14 | module.exports.catchAll = (err, req, res, next) => { 15 | const status = err.status || 500; 16 | const message = err.message || 'Something broke!'; 17 | 18 | winston.error(message); 19 | 20 | res.status(status).json({ error: { message } }); 21 | }; 22 | -------------------------------------------------------------------------------- /movies/src/app.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const express = require('express'); 3 | 4 | const app = express(); 5 | const { catchAll, notFound } = require('./error'); 6 | const movieRouter = require('./movie/movie.router'); 7 | 8 | app.use(bodyParser.urlencoded({ extended: false })); 9 | app.use(bodyParser.json()); 10 | 11 | app.get('/', (req, res) => { 12 | res.json({ message: 'It works!!!' }); 13 | }); 14 | 15 | app.use('/api/movies', movieRouter); 16 | 17 | app.use(notFound); 18 | app.use(catchAll); 19 | 20 | module.exports = app; 21 | -------------------------------------------------------------------------------- /movies/src/movie/movie.router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const catchErrors = require('express-catch-errors'); 3 | 4 | const router = express.Router(); 5 | const { 6 | check, 7 | create, 8 | list, 9 | remove, 10 | update, 11 | view 12 | } = require('./movie.controller'); 13 | 14 | router 15 | .route('/') 16 | .get(catchErrors(list)) 17 | .post(catchErrors(create)); 18 | 19 | router 20 | .route('/:id') 21 | .get(catchErrors(check), catchErrors(view)) 22 | .put(catchErrors(check), catchErrors(update)) 23 | .delete(catchErrors(check), catchErrors(remove)); 24 | 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /gateway/src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | 4 | const app = express(); 5 | const { catchAll, notFound } = require('./error'); 6 | const heroesRouter = require('./heroes/heroes.router'); 7 | const moviesRouter = require('./movies/movies.router'); 8 | 9 | app.use(bodyParser.urlencoded({ extended: false })); 10 | app.use(bodyParser.json()); 11 | 12 | app.get('/', (req, res) => { 13 | res.json({ message: 'GATEWAY!' }); 14 | }); 15 | 16 | app.use('/api/heroes', heroesRouter); 17 | app.use('/api/movies', moviesRouter); 18 | 19 | app.use(notFound); 20 | app.use(catchAll); 21 | 22 | module.exports = app; 23 | -------------------------------------------------------------------------------- /heroes/src/index.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const winston = require('winston'); 3 | 4 | const app = require('./app'); 5 | const config = require('./config'); 6 | 7 | mongoose.connect(config.DATABASE); 8 | mongoose.Promise = global.Promise; 9 | 10 | mongoose.connection.on('connected', () => { 11 | winston.info('Mongoose connected!'); 12 | }); 13 | 14 | mongoose.connection.on('disconnected', () => { 15 | winston.info('Mongoose disconnected!'); 16 | }); 17 | 18 | mongoose.connection.on('error', (err) => { 19 | winston.error(err.message); 20 | }); 21 | 22 | app.listen(config.PORT, () => { 23 | Object.keys(config).forEach((key) => winston.info(`${key}: ${config[key]}`)); 24 | }); 25 | -------------------------------------------------------------------------------- /movies/src/index.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const winston = require('winston'); 3 | 4 | const app = require('./app'); 5 | const config = require('./config'); 6 | 7 | mongoose.connect(config.DATABASE); 8 | mongoose.Promise = global.Promise; 9 | 10 | mongoose.connection.on('connected', () => { 11 | winston.info('Mongoose connected!'); 12 | }); 13 | 14 | mongoose.connection.on('disconnected', () => { 15 | winston.info('Mongoose disconnected!'); 16 | }); 17 | 18 | mongoose.connection.on('error', (err) => { 19 | winston.error(err.message); 20 | }); 21 | 22 | app.listen(config.PORT, () => { 23 | Object.keys(config).forEach((key) => winston.info(`${key}: ${config[key]}`)); 24 | }); 25 | -------------------------------------------------------------------------------- /heroes/src/hero/hero.controller.js: -------------------------------------------------------------------------------- 1 | const Hero = require('./hero.model'); 2 | 3 | module.exports.create = async (req, res) => { 4 | const hero = new Hero(req.body); 5 | await hero.save(); 6 | 7 | res.json(hero); 8 | }; 9 | 10 | module.exports.list = async (req, res) => { 11 | const heroes = await Hero.find(); 12 | 13 | res.json(heroes); 14 | }; 15 | 16 | module.exports.remove = async (req, res) => { 17 | await Hero.findByIdAndRemove(req.params.id); 18 | 19 | res.json({ id: req.params.id }); 20 | }; 21 | 22 | module.exports.update = async (req, res) => { 23 | const hero = await Hero.findOneAndUpdate({ _id: req.params.id }, req.body, { 24 | new: true 25 | }); 26 | 27 | res.json(hero); 28 | }; 29 | 30 | module.exports.view = async (req, res) => { 31 | const hero = await Hero.findById(req.params.id); 32 | 33 | res.json(hero); 34 | }; 35 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | gateway: 5 | build: 6 | context: ./gateway 7 | dockerfile: Dockerfile 8 | ports: 9 | - "5000:5000" 10 | volumes: 11 | - ./gateway:/usr/src/app 12 | networks: 13 | - app-network 14 | links: 15 | - heroes 16 | - movies 17 | 18 | heroes: 19 | build: 20 | context: ./heroes 21 | dockerfile: Dockerfile 22 | ports: 23 | - "3000:3000" 24 | volumes: 25 | - ./heroes:/usr/src/app 26 | networks: 27 | - app-network 28 | links: 29 | - mongodb 30 | 31 | movies: 32 | build: 33 | context: ./movies 34 | dockerfile: Dockerfile 35 | ports: 36 | - "4000:4000" 37 | volumes: 38 | - ./movies:/usr/src/app 39 | networks: 40 | - app-network 41 | links: 42 | - mongodb 43 | 44 | mongodb: 45 | image: mongo 46 | networks: 47 | - app-network 48 | 49 | networks: 50 | app-network: 51 | driver: bridge 52 | -------------------------------------------------------------------------------- /gateway/src/heroes/heroes.service.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const config = require('../config'); 4 | 5 | module.exports.create = async (req, res) => { 6 | const response = await axios.post(config.HEROES, req.body); 7 | 8 | res.json(response.data); 9 | }; 10 | 11 | module.exports.list = async (req, res) => { 12 | const response = await axios.get(config.HEROES); 13 | 14 | res.json(response.data); 15 | }; 16 | 17 | module.exports.remove = async (req, res) => { 18 | const { id } = req.params; 19 | 20 | const response = await axios.delete(`${config.HEROES}/${id}`); 21 | 22 | res.json(response.data); 23 | }; 24 | 25 | module.exports.update = async (req, res) => { 26 | const { id } = req.params; 27 | 28 | const response = await axios.put(`${config.HEROES}/${id}`, req.body); 29 | 30 | res.json(response.data); 31 | }; 32 | 33 | module.exports.view = async (req, res) => { 34 | const { id } = req.params; 35 | 36 | const response = await axios.get(`${config.HEROES}/${id}`); 37 | 38 | res.json(response.data); 39 | }; 40 | -------------------------------------------------------------------------------- /gateway/src/movies/movies.service.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const config = require('../config'); 4 | 5 | module.exports.create = async (req, res) => { 6 | const response = await axios.post(config.MOVIES, req.body); 7 | 8 | res.json(response.data); 9 | }; 10 | 11 | module.exports.list = async (req, res) => { 12 | const response = await axios.get(config.MOVIES); 13 | 14 | res.json(response.data); 15 | }; 16 | 17 | module.exports.remove = async (req, res) => { 18 | const { id } = req.params; 19 | 20 | const response = await axios.delete(`${config.MOVIES}/${id}`); 21 | 22 | res.json(response.data); 23 | }; 24 | 25 | module.exports.update = async (req, res) => { 26 | const { id } = req.params; 27 | 28 | const response = await axios.put(`${config.MOVIES}/${id}`, req.body); 29 | 30 | res.json(response.data); 31 | }; 32 | 33 | module.exports.view = async (req, res) => { 34 | const { id } = req.params; 35 | 36 | const response = await axios.get(`${config.MOVIES}/${id}`); 37 | 38 | res.json(response.data); 39 | }; 40 | -------------------------------------------------------------------------------- /movies/src/movie/movie.controller.js: -------------------------------------------------------------------------------- 1 | const Movie = require('./movie.model'); 2 | 3 | module.exports.check = async (req, res, next) => { 4 | const movie = await Movie.findById(req.params.id); 5 | if (!movie) { 6 | throw new Error('Not found'); 7 | } 8 | 9 | return next(); 10 | }; 11 | 12 | module.exports.create = async (req, res) => { 13 | const movie = new Movie(req.body); 14 | await movie.save(); 15 | 16 | res.json(movie); 17 | }; 18 | 19 | module.exports.list = async (req, res) => { 20 | const moviees = await Movie.find(); 21 | 22 | res.json(moviees); 23 | }; 24 | 25 | module.exports.remove = async (req, res) => { 26 | await Movie.findByIdAndRemove(req.params.id); 27 | 28 | res.json({ id: req.params.id }); 29 | }; 30 | 31 | module.exports.update = async (req, res) => { 32 | const movie = await Movie.findOneAndUpdate({ _id: req.params.id }, req.body, { 33 | new: true 34 | }); 35 | 36 | res.json(movie); 37 | }; 38 | 39 | module.exports.view = async (req, res) => { 40 | const movie = await Movie.findById(req.params.id); 41 | 42 | res.json(movie); 43 | }; 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Roberto Achar 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gateway", 3 | "version": "1.0.0", 4 | "description": "A playground for Express Gateway and Docker.", 5 | "author": "Roberto Achar ", 6 | "homepage": "https://github.com/robertoachar/docker-express-gateway#readme", 7 | "keywords": ["node"], 8 | "main": "src/index.js", 9 | "files": ["src"], 10 | "scripts": { 11 | "lint": "eslint src/**/*.js", 12 | "start": "nodemon src/index.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/robertoachar/docker-express-gateway.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/robertoachar/docker-express-gateway/issues" 20 | }, 21 | "dependencies": { 22 | "axios": "0.18.0", 23 | "body-parser": "1.18.2", 24 | "dotenv": "5.0.1", 25 | "express": "4.16.3", 26 | "express-catch-errors": "0.1.0", 27 | "winston": "2.4.2" 28 | }, 29 | "devDependencies": { 30 | "eslint": "4.19.1", 31 | "eslint-config-airbnb-base": "12.1.0", 32 | "eslint-config-prettier": "2.9.0", 33 | "eslint-plugin-import": "2.11.0", 34 | "eslint-plugin-prettier": "2.6.0", 35 | "nodemon": "1.17.3", 36 | "prettier": "1.12.1" 37 | }, 38 | "license": "MIT" 39 | } 40 | -------------------------------------------------------------------------------- /heroes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heroes", 3 | "version": "1.0.0", 4 | "description": "A playground for Express Gateway and Docker.", 5 | "author": "Roberto Achar ", 6 | "homepage": "https://github.com/robertoachar/docker-express-gateway#readme", 7 | "keywords": ["node"], 8 | "main": "src/index.js", 9 | "files": ["src"], 10 | "scripts": { 11 | "lint": "eslint src/**/*.js", 12 | "start": "nodemon src/index.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/robertoachar/docker-express-gateway.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/robertoachar/docker-express-gateway/issues" 20 | }, 21 | "dependencies": { 22 | "body-parser": "1.18.2", 23 | "dotenv": "5.0.1", 24 | "express": "4.16.3", 25 | "express-catch-errors": "0.1.0", 26 | "mongoose": "5.0.15", 27 | "winston": "2.4.2" 28 | }, 29 | "devDependencies": { 30 | "eslint": "4.19.1", 31 | "eslint-config-airbnb-base": "12.1.0", 32 | "eslint-config-prettier": "2.9.0", 33 | "eslint-plugin-import": "2.11.0", 34 | "eslint-plugin-prettier": "2.6.0", 35 | "nodemon": "1.17.3", 36 | "prettier": "1.12.1" 37 | }, 38 | "license": "MIT" 39 | } 40 | -------------------------------------------------------------------------------- /movies/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movies", 3 | "version": "1.0.0", 4 | "description": "A playground for Express Gateway and Docker.", 5 | "author": "Roberto Achar ", 6 | "homepage": "https://github.com/robertoachar/docker-express-gateway#readme", 7 | "keywords": ["node"], 8 | "main": "src/index.js", 9 | "files": ["src"], 10 | "scripts": { 11 | "lint": "eslint src/**/*.js", 12 | "start": "nodemon src/index.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/robertoachar/docker-express-gateway.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/robertoachar/docker-express-gateway/issues" 20 | }, 21 | "dependencies": { 22 | "body-parser": "1.18.2", 23 | "dotenv": "5.0.1", 24 | "express": "4.16.3", 25 | "express-catch-errors": "0.1.0", 26 | "mongoose": "5.0.15", 27 | "winston": "2.4.2" 28 | }, 29 | "devDependencies": { 30 | "eslint": "4.19.1", 31 | "eslint-config-airbnb-base": "12.1.0", 32 | "eslint-config-prettier": "2.9.0", 33 | "eslint-plugin-import": "2.11.0", 34 | "eslint-plugin-prettier": "2.6.0", 35 | "nodemon": "1.17.3", 36 | "prettier": "1.12.1" 37 | }, 38 | "license": "MIT" 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gateway 2 | 3 | Generated by [Node Project Generator](https://github.com/robertoachar/generator-node). 4 | 5 | [![License][license-badge]][license-url] 6 | 7 | > A playground for Express Gateway and Docker. 8 | 9 | # Development 10 | 11 | * Cloning the repo 12 | 13 | ```bash 14 | $ git clone https://github.com/robertoachar/docker-express-gateway.git 15 | ``` 16 | 17 | * Installing dependencies 18 | 19 | ```bash 20 | $ cd heroes 21 | $ npm install 22 | $ cd .. 23 | 24 | $ cd movies 25 | $ npm install 26 | $ cd .. 27 | 28 | $ cd gateway 29 | $ npm install 30 | $ cd .. 31 | ``` 32 | 33 | * Running scripts 34 | 35 | | Action | Usage | 36 | | ------------------------- | -------------- | 37 | | Starting development mode | `npm start` | 38 | | Linting code | `npm run lint` | 39 | 40 | # Docker 41 | 42 | * Building an image 43 | 44 | ```bash 45 | $ docker-compose build 46 | ``` 47 | 48 | * Running a container 49 | 50 | ```bash 51 | $ docker-compose up 52 | ``` 53 | 54 | * Stopping a container 55 | 56 | ```bash 57 | $ docker-compose down 58 | ``` 59 | 60 | # Rest API 61 | 62 | ### Heroes Service 63 | 64 | | Method | Endpoint | Description | 65 | | ------ | --------------- | -------------------------- | 66 | | GET | /api/heroes | Retrieves a list of heroes | 67 | | POST | /api/heroes | Retrieves a specific hero | 68 | | GET | /api/heroes/:id | Creates a new hero | 69 | | PUT | /api/heroes/:id | Updates hero | 70 | | DELETE | /api/heroes/:id | Deletes hero | 71 | 72 | ### Movies Service 73 | 74 | | Method | Endpoint | Description | 75 | | ------ | --------------- | -------------------------- | 76 | | GET | /api/movies | Retrieves a list of movies | 77 | | POST | /api/movies | Retrieves a specific movie | 78 | | GET | /api/movies/:id | Creates a new movie | 79 | | PUT | /api/movies/:id | Updates movie | 80 | | DELETE | /api/movies/:id | Deletes movie | 81 | 82 | # Postman 83 | 84 | * postman/docker-express-gateway.postman.json 85 | 86 | # Author 87 | 88 | [Roberto Achar](https://twitter.com/robertoachar) 89 | 90 | # License 91 | 92 | [MIT](https://github.com/robertoachar/docker-express-gateway/blob/master/LICENSE) 93 | 94 | [license-badge]: https://img.shields.io/github/license/robertoachar/docker-express-gateway.svg 95 | [license-url]: https://opensource.org/licenses/MIT 96 | -------------------------------------------------------------------------------- /postman/docker-express-gateway.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "0c2d54ca-345a-41c1-a3a6-00365fbb58de", 4 | "name": "docker-express-gateway", 5 | "schema": 6 | "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 7 | }, 8 | "item": [ 9 | { 10 | "name": "/", 11 | "request": { 12 | "method": "GET", 13 | "header": [], 14 | "body": {}, 15 | "url": { 16 | "raw": "http://localhost:5000/", 17 | "protocol": "http", 18 | "host": ["localhost"], 19 | "port": "5000", 20 | "path": [""] 21 | } 22 | }, 23 | "response": [] 24 | }, 25 | { 26 | "name": "/heroes", 27 | "request": { 28 | "method": "GET", 29 | "header": [], 30 | "body": {}, 31 | "url": { 32 | "raw": "http://localhost:5000/api/heroes", 33 | "protocol": "http", 34 | "host": ["localhost"], 35 | "port": "5000", 36 | "path": ["api", "heroes"] 37 | } 38 | }, 39 | "response": [] 40 | }, 41 | { 42 | "name": "/heroes", 43 | "request": { 44 | "method": "POST", 45 | "header": [ 46 | { 47 | "key": "Content-Type", 48 | "value": "application/x-www-form-urlencoded" 49 | } 50 | ], 51 | "body": { 52 | "mode": "urlencoded", 53 | "urlencoded": [ 54 | { 55 | "key": "name", 56 | "value": "Superman", 57 | "description": "", 58 | "type": "text" 59 | } 60 | ] 61 | }, 62 | "url": { 63 | "raw": "http://localhost:5000/api/heroes", 64 | "protocol": "http", 65 | "host": ["localhost"], 66 | "port": "5000", 67 | "path": ["api", "heroes"] 68 | } 69 | }, 70 | "response": [] 71 | }, 72 | { 73 | "name": "/heroes/:id", 74 | "request": { 75 | "method": "PUT", 76 | "header": [ 77 | { 78 | "key": "Content-Type", 79 | "value": "application/x-www-form-urlencoded" 80 | } 81 | ], 82 | "body": { 83 | "mode": "urlencoded", 84 | "urlencoded": [ 85 | { 86 | "key": "name", 87 | "value": "Wonder Woman", 88 | "description": "", 89 | "type": "text" 90 | } 91 | ] 92 | }, 93 | "url": { 94 | "raw": "http://localhost:5000/api/heroes/5adaa4d25714e60015552c0f", 95 | "protocol": "http", 96 | "host": ["localhost"], 97 | "port": "5000", 98 | "path": ["api", "heroes", "5adaa4d25714e60015552c0f"] 99 | } 100 | }, 101 | "response": [] 102 | }, 103 | { 104 | "name": "/heroes/:id", 105 | "request": { 106 | "method": "GET", 107 | "header": [], 108 | "body": {}, 109 | "url": { 110 | "raw": "http://localhost:5000/api/heroes/5adaa4d25714e60015552c0f", 111 | "protocol": "http", 112 | "host": ["localhost"], 113 | "port": "5000", 114 | "path": ["api", "heroes", "5adaa4d25714e60015552c0f"] 115 | } 116 | }, 117 | "response": [] 118 | }, 119 | { 120 | "name": "/heroes/:id", 121 | "request": { 122 | "method": "DELETE", 123 | "header": [], 124 | "body": {}, 125 | "url": { 126 | "raw": "http://localhost:5000/api/heroes/5adaa5225714e60015552c12", 127 | "protocol": "http", 128 | "host": ["localhost"], 129 | "port": "5000", 130 | "path": ["api", "heroes", "5adaa5225714e60015552c12"] 131 | } 132 | }, 133 | "response": [] 134 | }, 135 | { 136 | "name": "/movies", 137 | "request": { 138 | "method": "GET", 139 | "header": [], 140 | "body": {}, 141 | "url": { 142 | "raw": "http://localhost:5000/api/movies", 143 | "protocol": "http", 144 | "host": ["localhost"], 145 | "port": "5000", 146 | "path": ["api", "movies"] 147 | } 148 | }, 149 | "response": [] 150 | }, 151 | { 152 | "name": "/movies", 153 | "request": { 154 | "method": "POST", 155 | "header": [ 156 | { 157 | "key": "Content-Type", 158 | "value": "application/x-www-form-urlencoded" 159 | } 160 | ], 161 | "body": { 162 | "mode": "urlencoded", 163 | "urlencoded": [ 164 | { 165 | "key": "name", 166 | "value": "Avengers", 167 | "description": "", 168 | "type": "text" 169 | } 170 | ] 171 | }, 172 | "url": { 173 | "raw": "http://localhost:5000/api/movies", 174 | "protocol": "http", 175 | "host": ["localhost"], 176 | "port": "5000", 177 | "path": ["api", "movies"] 178 | } 179 | }, 180 | "response": [] 181 | }, 182 | { 183 | "name": "/movies/:id", 184 | "request": { 185 | "method": "PUT", 186 | "header": [ 187 | { 188 | "key": "Content-Type", 189 | "value": "application/x-www-form-urlencoded" 190 | } 191 | ], 192 | "body": { 193 | "mode": "urlencoded", 194 | "urlencoded": [ 195 | { 196 | "key": "name", 197 | "value": "Avengers", 198 | "description": "", 199 | "type": "text" 200 | } 201 | ] 202 | }, 203 | "url": { 204 | "raw": "http://localhost:5000/api/movies/5adaa6d67b8b6f0010f44b6d", 205 | "protocol": "http", 206 | "host": ["localhost"], 207 | "port": "5000", 208 | "path": ["api", "movies", "5adaa6d67b8b6f0010f44b6d"] 209 | } 210 | }, 211 | "response": [] 212 | }, 213 | { 214 | "name": "/movies/:id", 215 | "request": { 216 | "method": "GET", 217 | "header": [], 218 | "body": {}, 219 | "url": { 220 | "raw": "http://localhost:5000/api/movies/5adaa7107b8b6f0010f44b6e", 221 | "protocol": "http", 222 | "host": ["localhost"], 223 | "port": "5000", 224 | "path": ["api", "movies", "5adaa7107b8b6f0010f44b6e"] 225 | } 226 | }, 227 | "response": [] 228 | }, 229 | { 230 | "name": "/movies/:id", 231 | "request": { 232 | "method": "DELETE", 233 | "header": [], 234 | "body": {}, 235 | "url": { 236 | "raw": "http://localhost:5000/api/movies/5adaa7107b8b6f0010f44b6e", 237 | "protocol": "http", 238 | "host": ["localhost"], 239 | "port": "5000", 240 | "path": ["api", "movies", "5adaa7107b8b6f0010f44b6e"] 241 | } 242 | }, 243 | "response": [] 244 | } 245 | ] 246 | } 247 | --------------------------------------------------------------------------------