├── .gitignore ├── api ├── helpers │ └── README.md ├── controllers │ ├── README.md │ └── movie.js ├── mocks │ ├── README.md │ └── movie.js └── swagger │ └── swagger.yaml ├── config ├── README.md ├── default.yaml └── db.js ├── test └── api │ ├── helpers │ └── README.md │ └── controllers │ ├── README.md │ └── hello_world.js ├── README.md ├── app.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/bower_components/ 3 | 4 | -------------------------------------------------------------------------------- /api/helpers/README.md: -------------------------------------------------------------------------------- 1 | Place helper files in this directory. 2 | -------------------------------------------------------------------------------- /config/README.md: -------------------------------------------------------------------------------- 1 | Place configuration files in this directory. 2 | -------------------------------------------------------------------------------- /api/controllers/README.md: -------------------------------------------------------------------------------- 1 | Place your controllers in this directory. 2 | -------------------------------------------------------------------------------- /test/api/helpers/README.md: -------------------------------------------------------------------------------- 1 | Place your helper tests in this directory. 2 | -------------------------------------------------------------------------------- /api/mocks/README.md: -------------------------------------------------------------------------------- 1 | Place controllers for mock mode in this directory. 2 | -------------------------------------------------------------------------------- /test/api/controllers/README.md: -------------------------------------------------------------------------------- 1 | Place your controller tests in this directory. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # movie-collection 2 | Web API design and documentation with swagger 3 | 4 | Tutorial: https://scotch.io/tutorials/speed-up-your-restful-api-development-in-node-js-with-swagger 5 | -------------------------------------------------------------------------------- /api/mocks/movie.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | search: search 5 | }; 6 | 7 | function search(req, res, next) { 8 | console.log(111); 9 | res.json({ 10 | movies: [ 11 | {title: "Star Wars - The return of the Jedi", year: 1983}, 12 | {title: "Star Wars - A new hope", year: 1977} 13 | ] 14 | }); 15 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var SwaggerExpress = require('swagger-express-mw'); 4 | var app = require('express')(); 5 | var db = require('./config/db')(); 6 | 7 | module.exports = app; // for testing 8 | 9 | var config = { 10 | appRoot: __dirname // required config 11 | }; 12 | 13 | SwaggerExpress.create(config, function(err, swaggerExpress) { 14 | if (err) { throw err; } 15 | 16 | // install middleware 17 | swaggerExpress.register(app); 18 | 19 | var port = process.env.PORT || 10010; 20 | app.listen(port); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movie-collection", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "New Swagger API Project", 6 | "keywords": [], 7 | "author": "", 8 | "license": "", 9 | "main": "app.js", 10 | "dependencies": { 11 | "crypto": "0.0.3", 12 | "express": "^4.12.3", 13 | "swagger-express-mw": "^0.1.0" 14 | }, 15 | "devDependencies": { 16 | "should": "^7.1.0", 17 | "supertest": "^1.0.0" 18 | }, 19 | "scripts": { 20 | "start": "node app.js", 21 | "test": "swagger project test" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/default.yaml: -------------------------------------------------------------------------------- 1 | # swagger configuration file 2 | 3 | # values in the swagger hash are system configuration for swagger-node 4 | swagger: 5 | 6 | fittingsDirs: [ api/fittings ] 7 | defaultPipe: null 8 | swaggerControllerPipe: swagger_controllers # defines the standard processing pipe for controllers 9 | 10 | # values defined in the bagpipes key are the bagpipes pipes and fittings definitions 11 | # (see https://github.com/apigee-127/bagpipes) 12 | bagpipes: 13 | 14 | _router: 15 | name: swagger_router 16 | mockMode: false 17 | mockControllersDirs: [ api/mocks ] 18 | controllersDirs: [ api/controllers ] 19 | 20 | _swagger_validate: 21 | name: swagger_validator 22 | validateResponse: true 23 | 24 | # pipe for all swagger-node controllers 25 | swagger_controllers: 26 | - onError: json_error_handler 27 | - cors 28 | - swagger_security 29 | - _swagger_validate 30 | - express_compatibility 31 | - _router 32 | 33 | # pipe to serve swagger (endpoint is in swagger.yaml) 34 | swagger_raw: 35 | name: swagger_raw 36 | 37 | # any other values in this file are just loaded into the config for application access... 38 | -------------------------------------------------------------------------------- /test/api/controllers/hello_world.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var request = require('supertest'); 3 | var server = require('../../../app'); 4 | 5 | describe('controllers', function() { 6 | 7 | describe('hello_world', function() { 8 | 9 | describe('GET /hello', function() { 10 | 11 | it('should return a default string', function(done) { 12 | 13 | request(server) 14 | .get('/hello') 15 | .set('Accept', 'application/json') 16 | .expect('Content-Type', /json/) 17 | .expect(200) 18 | .end(function(err, res) { 19 | should.not.exist(err); 20 | 21 | res.body.should.eql('Hello, stranger!'); 22 | 23 | done(); 24 | }); 25 | }); 26 | 27 | it('should accept a name parameter', function(done) { 28 | 29 | request(server) 30 | .get('/hello') 31 | .query({ name: 'Scott'}) 32 | .set('Accept', 'application/json') 33 | .expect('Content-Type', /json/) 34 | .expect(200) 35 | .end(function(err, res) { 36 | should.not.exist(err); 37 | 38 | res.body.should.eql('Hello, Scott!'); 39 | 40 | done(); 41 | }); 42 | }); 43 | 44 | }); 45 | 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /config/db.js: -------------------------------------------------------------------------------- 1 | 'use strict;' 2 | //Include crypto to generate the movie id 3 | var crypto = require('crypto'); 4 | 5 | module.exports = function() { 6 | return { 7 | movieList : [], 8 | /* 9 | * Save the movie inside the "db". 10 | */ 11 | save(movie) { 12 | movie.id = crypto.randomBytes(20).toString('hex'); // fast enough for our purpose 13 | this.movieList.push(movie); 14 | return 1; 15 | }, 16 | /* 17 | * Retrieve a movie with a given id or return all the movies if the id is undefined. 18 | */ 19 | find(id) { 20 | if(id) { 21 | return this.movieList.find(element => { 22 | return element.id === id; 23 | }); 24 | }else { 25 | return this.movieList; 26 | } 27 | }, 28 | /* 29 | * Delete a movie with the given id. 30 | */ 31 | remove(id) { 32 | var found = 0; 33 | this.movieList = this.movieList.filter(element => { 34 | if(element.id === id) { 35 | found = 1; 36 | }else { 37 | return element.id !== id; 38 | } 39 | }); 40 | return found; 41 | }, 42 | /* 43 | * Update a movie with the given id 44 | */ 45 | update(id, movie) { 46 | var movieIndex = this.movieList.findIndex(element => { 47 | return element.id === id; 48 | }); 49 | if(movieIndex !== -1) { 50 | this.movieList[movieIndex].title = movie.title; 51 | this.movieList[movieIndex].year = movie.year; 52 | return 1; 53 | }else { 54 | return 0; 55 | } 56 | } 57 | } 58 | }; -------------------------------------------------------------------------------- /api/controllers/movie.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Include our "db" 3 | var db = require('../../config/db')(); 4 | // Exports all the functions to perform on the db 5 | module.exports = {getAll, save, getOne, update, delMovie}; 6 | 7 | //GET /movie operationId 8 | function getAll(req, res, next) { 9 | res.json({ movies: db.find()}); 10 | } 11 | //POST /movie operationId 12 | function save(req, res, next) { 13 | res.json({success: db.save(req.body), description: "Movie added to the list!"}); 14 | } 15 | //GET /movie/{id} operationId 16 | function getOne(req, res, next) { 17 | var id = req.swagger.params.id.value; //req.swagger contains the path parameters 18 | var movie = db.find(id); 19 | if(movie) { 20 | res.json(movie); 21 | }else { 22 | res.status(204).send(); 23 | } 24 | } 25 | //PUT /movie/{id} operationId 26 | function update(req, res, next) { 27 | var id = req.swagger.params.id.value; //req.swagger contains the path parameters 28 | var movie = req.body; 29 | if(db.update(id, movie)){ 30 | res.json({success: 1, description: "Movie updated!"}); 31 | }else{ 32 | res.status(204).send(); 33 | } 34 | 35 | } 36 | //DELETE /movie/{id} operationId 37 | function delMovie(req, res, next) { 38 | var id = req.swagger.params.id.value; //req.swagger contains the path parameters 39 | if(db.remove(id)){ 40 | res.json({success: 1, description: "Movie deleted!"}); 41 | }else{ 42 | res.status(204).send(); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /api/swagger/swagger.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | version: "0.0.1" 4 | title: MovieListApp 5 | # during dev, should point to your local machine 6 | host: localhost:10010 7 | # basePath prefixes all resource paths 8 | basePath: / 9 | # 10 | schemes: 11 | # tip: remove http to make production-grade 12 | - http 13 | - https 14 | # format of bodies a client can send (Content-Type) 15 | consumes: 16 | - application/json 17 | # format of the responses to the client (Accepts) 18 | produces: 19 | - application/json 20 | paths: 21 | /movie: 22 | # our controller name 23 | x-swagger-router-controller: movie 24 | get: 25 | operationId: getAll 26 | description: get the movies list 27 | # define the type of response for Success "200" and Error 28 | responses: 29 | "200": 30 | description: Success 31 | schema: 32 | $ref: "#/definitions/GetMoviesListResponse" 33 | default: 34 | description: Error 35 | schema: 36 | $ref: "#/definitions/ErrorResponse" 37 | post: 38 | operationId: save 39 | description: add a new movie to the list 40 | # movie info to be stored 41 | parameters: 42 | - name: title 43 | description: Movie properties 44 | in: body 45 | required: true 46 | schema: 47 | $ref: "#/definitions/Movie" 48 | responses: 49 | "200": 50 | description: Success 51 | schema: 52 | $ref: "#/definitions/GeneralResponse" 53 | default: 54 | description: Error 55 | schema: 56 | $ref: "#/definitions/ErrorResponse" 57 | /movie/{id}: 58 | # our controller name 59 | x-swagger-router-controller: movie 60 | get: 61 | operationId: getOne 62 | description: get a movie 63 | # define the type of response for Success "200" and Error 64 | parameters: 65 | - name: id 66 | type: string 67 | in: path 68 | required: true 69 | responses: 70 | "200": 71 | description: Success 72 | schema: 73 | $ref: "#/definitions/GetMovieResponse" 74 | default: 75 | description: Error 76 | schema: 77 | $ref: "#/definitions/ErrorResponse" 78 | put: 79 | operationId: update 80 | description: update a movie 81 | # define the parameters 82 | parameters: 83 | - name: id 84 | description: Movie id 85 | type: string 86 | in: path 87 | required: true 88 | - name: title 89 | description: Movie properties 90 | in: body 91 | required: true 92 | schema: 93 | $ref: "#/definitions/Movie" 94 | responses: 95 | "200": 96 | description: Success 97 | schema: 98 | $ref: "#/definitions/GeneralResponse" 99 | default: 100 | description: Error 101 | schema: 102 | $ref: "#/definitions/ErrorResponse" 103 | delete: 104 | operationId: delMovie 105 | description: delete a movie 106 | # define the parameters 107 | parameters: 108 | - name: id 109 | description: Movie id 110 | type: string 111 | in: path 112 | required: true 113 | responses: 114 | "200": 115 | description: Success 116 | schema: 117 | $ref: "#/definitions/GeneralResponse" 118 | default: 119 | description: Error 120 | schema: 121 | $ref: "#/definitions/ErrorResponse" 122 | /swagger: 123 | x-swagger-pipe: swagger_raw 124 | # complex objects have schema definitions 125 | definitions: 126 | # GET /movie successful response 127 | GetMoviesListResponse: 128 | required: 129 | - movies 130 | properties: 131 | # The array of movies 132 | movies: 133 | type: array 134 | items: 135 | type: object 136 | properties: 137 | id: 138 | type: string 139 | title: 140 | type: string 141 | year: 142 | type: number 143 | GetMovieResponse: 144 | required: 145 | - id 146 | - title 147 | - year 148 | properties: 149 | id: 150 | type: string 151 | title: 152 | type: string 153 | year: 154 | type: number 155 | Movie: 156 | type: object 157 | properties: 158 | title: 159 | type: string 160 | description: task object name 161 | year: 162 | type: number 163 | description: task description 164 | required: 165 | - title 166 | - year 167 | GeneralResponse: 168 | type: object 169 | properties: 170 | success: 171 | type: number 172 | description: returns 1 if successful 173 | description: 174 | type: string 175 | description: a short comment 176 | required: 177 | - success 178 | - description 179 | ErrorResponse: 180 | required: 181 | - message 182 | properties: 183 | message: 184 | type: string 185 | --------------------------------------------------------------------------------