├── .gitignore ├── nginx ├── static │ └── index.html ├── Dockerfile └── nginx.conf ├── dist ├── models │ ├── Cat.js │ ├── Dog.js │ ├── Bird.js │ ├── Bird.js.map │ ├── Cat.js.map │ └── Dog.js.map ├── server.js.map ├── server.js ├── app.js.map ├── app.js ├── router.js.map ├── router.js └── swagger.json ├── cosmo1.png ├── docs ├── flag_au.gif ├── flag_br.gif ├── flag_ca.gif ├── flag_de.gif ├── flag_es.gif ├── flag_fr.gif ├── flag_in.gif ├── flag_it.gif ├── flag_jp.gif ├── flag_mx.gif ├── flag_nl.gif ├── flag_uk.gif ├── flag_us.gif ├── swagger.png ├── get-example.png ├── put-example.png ├── post-example.png ├── tssock-course.png ├── delete-example.png ├── dp-in-typescript.jpg ├── get-id-example.png ├── dp_typescript_116.jpg ├── dp_typescript_250.jpg ├── tssock-course-w100.png ├── dp-in-typescript_w100.png ├── threejs-course-image.png ├── tssock-course_43x24.gif ├── threejs-typescript-250.jpg ├── threejs_typescript_116.jpg ├── threejs-course-image-43x24.gif └── threejs-course-image-w100.png ├── src ├── models │ ├── Cat.ts │ ├── Dog.ts │ └── Bird.ts ├── server.ts ├── app.ts ├── router.ts └── swagger.json ├── Dockerfile ├── tsconfig.json ├── app.json ├── docker-compose.yml ├── .github └── FUNDING.yml ├── package.json ├── LICENSE ├── .gitlab-ci.yml └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist/ -------------------------------------------------------------------------------- /nginx/static/index.html: -------------------------------------------------------------------------------- 1 | put the static html generated from you favourite front end framework here. -------------------------------------------------------------------------------- /dist/models/Cat.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /dist/models/Dog.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /dist/models/Bird.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /cosmo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/cosmo1.png -------------------------------------------------------------------------------- /dist/models/Bird.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Bird.js","sourceRoot":"","sources":["../../src/models/Bird.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/models/Cat.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Cat.js","sourceRoot":"","sources":["../../src/models/Cat.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/models/Dog.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Dog.js","sourceRoot":"","sources":["../../src/models/Dog.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /docs/flag_au.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_au.gif -------------------------------------------------------------------------------- /docs/flag_br.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_br.gif -------------------------------------------------------------------------------- /docs/flag_ca.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_ca.gif -------------------------------------------------------------------------------- /docs/flag_de.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_de.gif -------------------------------------------------------------------------------- /docs/flag_es.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_es.gif -------------------------------------------------------------------------------- /docs/flag_fr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_fr.gif -------------------------------------------------------------------------------- /docs/flag_in.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_in.gif -------------------------------------------------------------------------------- /docs/flag_it.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_it.gif -------------------------------------------------------------------------------- /docs/flag_jp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_jp.gif -------------------------------------------------------------------------------- /docs/flag_mx.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_mx.gif -------------------------------------------------------------------------------- /docs/flag_nl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_nl.gif -------------------------------------------------------------------------------- /docs/flag_uk.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_uk.gif -------------------------------------------------------------------------------- /docs/flag_us.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/flag_us.gif -------------------------------------------------------------------------------- /docs/swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/swagger.png -------------------------------------------------------------------------------- /docs/get-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/get-example.png -------------------------------------------------------------------------------- /docs/put-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/put-example.png -------------------------------------------------------------------------------- /docs/post-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/post-example.png -------------------------------------------------------------------------------- /docs/tssock-course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/tssock-course.png -------------------------------------------------------------------------------- /docs/delete-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/delete-example.png -------------------------------------------------------------------------------- /docs/dp-in-typescript.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/dp-in-typescript.jpg -------------------------------------------------------------------------------- /docs/get-id-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/get-id-example.png -------------------------------------------------------------------------------- /docs/dp_typescript_116.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/dp_typescript_116.jpg -------------------------------------------------------------------------------- /docs/dp_typescript_250.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/dp_typescript_250.jpg -------------------------------------------------------------------------------- /docs/tssock-course-w100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/tssock-course-w100.png -------------------------------------------------------------------------------- /docs/dp-in-typescript_w100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/dp-in-typescript_w100.png -------------------------------------------------------------------------------- /docs/threejs-course-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/threejs-course-image.png -------------------------------------------------------------------------------- /docs/tssock-course_43x24.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/tssock-course_43x24.gif -------------------------------------------------------------------------------- /docs/threejs-typescript-250.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/threejs-typescript-250.jpg -------------------------------------------------------------------------------- /docs/threejs_typescript_116.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/threejs_typescript_116.jpg -------------------------------------------------------------------------------- /docs/threejs-course-image-43x24.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/threejs-course-image-43x24.gif -------------------------------------------------------------------------------- /docs/threejs-course-image-w100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/HEAD/docs/threejs-course-image-w100.png -------------------------------------------------------------------------------- /src/models/Cat.ts: -------------------------------------------------------------------------------- 1 | type Cat = { 2 | genus: String; 3 | name: String; 4 | isHungry: Boolean; 5 | lastFedDate: Date; 6 | } 7 | export default Cat 8 | 9 | -------------------------------------------------------------------------------- /src/models/Dog.ts: -------------------------------------------------------------------------------- 1 | type Dog = { 2 | genus: String; 3 | name: String; 4 | isHungry: Boolean; 5 | lastFedDate: Date; 6 | } 7 | export default Dog 8 | 9 | -------------------------------------------------------------------------------- /src/models/Bird.ts: -------------------------------------------------------------------------------- 1 | type Bird = { 2 | genus: String; 3 | name: String; 4 | isHungry: Boolean; 5 | lastFedDate: Date; 6 | } 7 | export default Bird 8 | 9 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | LABEL github=https://github.com/Sean-Bradley 3 | COPY /nginx.conf /etc/nginx/nginx.conf 4 | #COPY /server.crt /etc/nginx/server.crt 5 | #COPY /server.key /etc/nginx/server.key 6 | COPY /static /static 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | LABEL github=https://github.com/Sean-Bradley 4 | 5 | COPY src /nodejs/src 6 | COPY package.json /nodejs/package.json 7 | COPY tsconfig.json /nodejs/tsconfig.json 8 | 9 | WORKDIR /nodejs 10 | 11 | RUN npm install 12 | 13 | EXPOSE 3000:3000 14 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import app from './app' 2 | 3 | const port = parseInt(process.env.PORT || '3000') 4 | 5 | const server = new app().Start(port) 6 | .then(port => console.log(`Server running on port ${port}`)) 7 | .catch(error => { 8 | console.log(error) 9 | process.exit(1); 10 | }); 11 | 12 | export default server; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "resolveJsonModule": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist", 8 | //"sourceMap": true 9 | }, 10 | "files": [ 11 | "./node_modules/@types/node/index.d.ts" 12 | ], 13 | "include": [ 14 | "src/**/*.ts" 15 | ], 16 | "exclude": [ 17 | "node_modules" 18 | ] 19 | } -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seans-typescript-nodejs-crud-rest-api-boilerplate", 3 | "description": "A barebones TypeScript Node.js CRUD REST API", 4 | "repository": "https://github.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate", 5 | "logo": "https://github.com/Sean-Bradley/Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate/blob/master/cosmo1.png", 6 | "keywords": ["node", "typescript", "crud", "seanwasere"] 7 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | nginx: 4 | build: 5 | context: nginx 6 | dockerfile: Dockerfile 7 | ports: 8 | - "80:80" 9 | command: nginx -g "daemon off"; 10 | depends_on: 11 | - nodejs 12 | 13 | nodejs: 14 | build: 15 | context: . 16 | dockerfile: Dockerfile 17 | image: service-cats:1.01 18 | expose: 19 | - "3000" 20 | command: npm start 21 | 22 | -------------------------------------------------------------------------------- /dist/server.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;AAAA,gDAAuB;AAEvB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAA;AAErC,MAAM,MAAM,GAAG,IAAI,aAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;KACjC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;KAC3D,KAAK,CAAC,KAAK,CAAC,EAAE;IACb,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEL,kBAAe,MAAM,CAAC"} -------------------------------------------------------------------------------- /dist/server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const app_1 = __importDefault(require("./app")); 7 | const port = parseInt(process.env.PORT || '3000'); 8 | const server = new app_1.default().Start(port) 9 | .then(port => console.log(`Server running on port ${port}`)) 10 | .catch(error => { 11 | console.log(error); 12 | process.exit(1); 13 | }); 14 | exports.default = server; 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Sean-Bradley]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: seanwasere 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: sean_bradley 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /dist/app.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,sDAA6B;AAC7B,sDAA6B;AAC7B,4EAA0C;AAC1C,gEAAiD;AACjD,wDAAyC;AAEzC,MAAM,GAAG;IAGP;QAWO,UAAK,GAAG,CAAC,IAAY,EAAE,EAAE;YAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAErC,IAAI,CAAC,UAAU,CAAC,MAAM,CACpB,IAAI,EACJ,GAAG,EAAE;oBACH,OAAO,CAAC,IAAI,CAAC,CAAA;gBACf,CAAC,CAAC;qBACD,EAAE,CAAC,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC/C,CAAC,CAAC,CAAA;QACJ,CAAC,CAAA;QApBC,IAAI,CAAC,UAAU,GAAG,iBAAO,EAAE,CAAA;QAE3B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QAEvC,IAAI,gBAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE5B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,4BAAS,CAAC,KAAK,EAAE,4BAAS,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IACrF,CAAC;CAaF;AAED,kBAAe,GAAG,CAAC"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seans-typescript-nodejs-crud-rest-api-boilerplate", 3 | "version": "1.0.1", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "tsc && node dist/server.js", 8 | "build": "tsc", 9 | "dev": "concurrently --kill-others \"tsc -w\" \"nodemon dist/server.js\"" 10 | }, 11 | "keywords": [ 12 | "typescript", 13 | "nodejs", 14 | "crud", 15 | "rest", 16 | "swagger" 17 | ], 18 | "author": "Sean Bradley", 19 | "license": "ISC", 20 | "dependencies": { 21 | "@types/express": "^4.17.11", 22 | "@types/node": "^13.13.40", 23 | "body-parser": "^1.20.2", 24 | "cors": "^2.8.5", 25 | "express": "^4.18.2", 26 | "swagger-ui-express": "^5.0.0", 27 | "typescript": "^5.2.2", 28 | "uuid": "^9.0.1" 29 | }, 30 | "devDependencies": { 31 | "concurrently": "^8.2.2", 32 | "nodemon": "^3.0.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import Router from './router' 3 | import swaggerUi from 'swagger-ui-express' 4 | import * as swaggerDocument from './swagger.json' 5 | import * as bodyParser from 'body-parser' 6 | 7 | class App { 8 | private httpServer: any 9 | 10 | constructor() { 11 | this.httpServer = express() 12 | 13 | this.httpServer.use(bodyParser.urlencoded({ extended: true })); 14 | this.httpServer.use(bodyParser.json()); 15 | 16 | new Router(this.httpServer); 17 | 18 | this.httpServer.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); 19 | } 20 | 21 | public Start = (port: number) => { 22 | return new Promise((resolve, reject) => { 23 | 24 | this.httpServer.listen( 25 | port, 26 | () => { 27 | resolve(port) 28 | }) 29 | .on('error', (err: object) => reject(err)); 30 | }) 31 | } 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 seanwasere youtube 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 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 1; 3 | pid /run/nginx.pid; 4 | events { 5 | worker_connections 768; 6 | } 7 | http { 8 | sendfile off; 9 | tcp_nopush on; 10 | tcp_nodelay on; 11 | keepalive_timeout 65; 12 | types_hash_max_size 2048; 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | #access_log /var/log/nginx/access.log; 16 | error_log /var/log/nginx/error.log; 17 | gzip on; 18 | gzip_disable "msie6"; 19 | server { 20 | listen 80; 21 | server_name localhost; 22 | #ssl_certificate server.crt; 23 | #ssl_certificate_key server.key; 24 | location / { 25 | root /static; 26 | index index.html; 27 | } 28 | location /api/ { 29 | proxy_pass_header Server; 30 | proxy_set_header Host $http_host; 31 | proxy_set_header X-Real-IP $remote_addr; 32 | proxy_set_header X-Scheme $scheme; 33 | proxy_set_header X-Real-IP $remote_addr; 34 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 35 | proxy_redirect off; 36 | proxy_connect_timeout 20; 37 | proxy_read_timeout 20; 38 | proxy_pass http://nodejs:3000/; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: docker:latest 2 | services: 3 | - docker:dind 4 | 5 | stages: 6 | - test 7 | - deploy 8 | 9 | step-develop: 10 | stage: test 11 | before_script: 12 | - export DYNAMIC_ENV_VAR=DEVELOP 13 | only: 14 | - develop 15 | tags: 16 | - develop 17 | script: 18 | - echo running tests in $DYNAMIC_ENV_VAR 19 | 20 | step-uat: 21 | stage: deploy 22 | before_script: 23 | - export DYNAMIC_ENV_VAR=UAT 24 | only: 25 | - uat 26 | tags: 27 | - uat 28 | script: 29 | - echo setting up env $DYNAMIC_ENV_VAR 30 | - sudo apt-get install -y python-pip 31 | - sudo pip install docker-compose 32 | - sudo docker image prune -f 33 | - sudo docker-compose -f docker-compose.yml build --no-cache 34 | - sudo docker-compose -f docker-compose.yml up -d 35 | 36 | step-deploy-staging: 37 | stage: deploy 38 | before_script: 39 | - export DYNAMIC_ENV_VAR=STAGING 40 | only: 41 | - staging 42 | tags: 43 | - staging 44 | script: 45 | - echo setting up env $DYNAMIC_ENV_VAR 46 | - sudo apt-get install -y python-pip 47 | - sudo pip install docker-compose 48 | - sudo docker image prune -f 49 | - sudo docker-compose -f docker-compose.yml build --no-cache 50 | - sudo docker-compose -f docker-compose.yml up -d 51 | 52 | step-deploy-production: 53 | stage: deploy 54 | before_script: 55 | - export DYNAMIC_ENV_VAR=PRODUCTION 56 | only: 57 | - production 58 | tags: 59 | - production 60 | script: 61 | - echo setting up env $DYNAMIC_ENV_VAR 62 | - sudo apt-get install -y python-pip 63 | - sudo pip install docker-compose 64 | - sudo docker image prune -f 65 | - sudo docker-compose -f docker-compose.yml build --no-cache 66 | - sudo docker-compose -f docker-compose.yml up -d 67 | when: manual 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /dist/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | const express_1 = __importDefault(require("express")); 30 | const router_1 = __importDefault(require("./router")); 31 | const swagger_ui_express_1 = __importDefault(require("swagger-ui-express")); 32 | const swaggerDocument = __importStar(require("./swagger.json")); 33 | const bodyParser = __importStar(require("body-parser")); 34 | class App { 35 | constructor() { 36 | this.Start = (port) => { 37 | return new Promise((resolve, reject) => { 38 | this.httpServer.listen(port, () => { 39 | resolve(port); 40 | }) 41 | .on('error', (err) => reject(err)); 42 | }); 43 | }; 44 | this.httpServer = (0, express_1.default)(); 45 | this.httpServer.use(bodyParser.urlencoded({ extended: true })); 46 | this.httpServer.use(bodyParser.json()); 47 | new router_1.default(this.httpServer); 48 | this.httpServer.use('/swagger', swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(swaggerDocument)); 49 | } 50 | } 51 | exports.default = App; 52 | -------------------------------------------------------------------------------- /dist/router.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express' 2 | import Cat from './models/Cat' 3 | import { v4 as uuid } from 'uuid'; 4 | import cors from 'cors' 5 | 6 | class Router { 7 | 8 | constructor(server: express.Express) { 9 | const router = express.Router() 10 | 11 | const cats = new Map(); 12 | cats[uuid()] = { genus: "feline", name: "Cosmo", isHungry: true, lastFedDate: new Date() } 13 | cats[uuid()] = { genus: "feline", name: "Emmy", isHungry: true, lastFedDate: new Date() } 14 | 15 | router.get('/', (req: express.Request, res: express.Response) => { 16 | res.json({ 17 | message: `Nothing to see here, [url]/cats instead.` 18 | }) 19 | }) 20 | 21 | //get all cats 22 | router.get('/cats', cors(), (req: express.Request, res: express.Response) => { 23 | res.json({ 24 | cats 25 | }) 26 | }) 27 | 28 | //create new cat 29 | router.post('/cats', cors(), (req: express.Request, res: express.Response) => { 30 | try { 31 | let cat: Cat = {} as Cat; 32 | Object.assign(cat, req.body) 33 | const newUUID = uuid(); 34 | cats[newUUID] = cat; 35 | res.json({ 36 | uuid: newUUID 37 | }) 38 | } catch (e) { 39 | res.status(400).send(JSON.stringify({ "error": "problem with posted data" })); 40 | } 41 | }) 42 | 43 | //get cat by id 44 | router.get('/cats/:id', cors(), (req: express.Request, res: express.Response) => { 45 | if (!!cats[req.params.id]) { 46 | res.json({ 47 | cat: cats[req.params.id] 48 | }) 49 | } else { 50 | res.status(404).send(JSON.stringify({ "error": "no such cat" })); 51 | } 52 | }) 53 | 54 | //update cat 55 | router.put('/cats/:id', cors(), (req: express.Request, res: express.Response) => { 56 | try { 57 | if (!!cats[req.params.id]) { 58 | let cat: Cat = {} as Cat; 59 | Object.assign(cat, req.body) 60 | cats[req.params.id] = cat; 61 | res.json({ 62 | cat: cats[req.params.id] 63 | }) 64 | } else { 65 | res.status(404).send(JSON.stringify({ "error": "no such cat" })); 66 | } 67 | } catch (e) { 68 | res.status(400).send(JSON.stringify({ "error": "problem with posted data" })); 69 | } 70 | }) 71 | 72 | //delete cat 73 | router.delete('/cats/:id', cors(), (req: express.Request, res: express.Response) => { 74 | if (!!cats[req.params.id]) { 75 | delete cats[req.params.id] 76 | res.json({ 77 | uuid: req.params.id 78 | }) 79 | } else { 80 | res.status(404).send(JSON.stringify({ "error": "no such cat" })); 81 | } 82 | }); 83 | 84 | router.options('*', cors()); 85 | 86 | server.use('/', router) 87 | } 88 | } 89 | 90 | export default Router; -------------------------------------------------------------------------------- /dist/router.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | const express = __importStar(require("express")); 30 | const uuid_1 = require("uuid"); 31 | const cors_1 = __importDefault(require("cors")); 32 | class Router { 33 | constructor(server) { 34 | const router = express.Router(); 35 | const cats = new Map(); 36 | cats[(0, uuid_1.v4)()] = { genus: "feline", name: "Cosmo", isHungry: true, lastFedDate: new Date() }; 37 | cats[(0, uuid_1.v4)()] = { genus: "feline", name: "Emmy", isHungry: true, lastFedDate: new Date() }; 38 | router.get('/', (req, res) => { 39 | res.json({ 40 | message: `Nothing to see here, [url]/cats instead.` 41 | }); 42 | }); 43 | //get all cats 44 | router.get('/cats', (0, cors_1.default)(), (req, res) => { 45 | res.json({ 46 | cats 47 | }); 48 | }); 49 | //create new cat 50 | router.post('/cats', (0, cors_1.default)(), (req, res) => { 51 | try { 52 | let cat = {}; 53 | Object.assign(cat, req.body); 54 | const newUUID = (0, uuid_1.v4)(); 55 | cats[newUUID] = cat; 56 | res.json({ 57 | uuid: newUUID 58 | }); 59 | } 60 | catch (e) { 61 | res.status(400).send(JSON.stringify({ "error": "problem with posted data" })); 62 | } 63 | }); 64 | //get cat by id 65 | router.get('/cats/:id', (0, cors_1.default)(), (req, res) => { 66 | if (!!cats[req.params.id]) { 67 | res.json({ 68 | cat: cats[req.params.id] 69 | }); 70 | } 71 | else { 72 | res.status(404).send(JSON.stringify({ "error": "no such cat" })); 73 | } 74 | }); 75 | //update cat 76 | router.put('/cats/:id', (0, cors_1.default)(), (req, res) => { 77 | try { 78 | if (!!cats[req.params.id]) { 79 | let cat = {}; 80 | Object.assign(cat, req.body); 81 | cats[req.params.id] = cat; 82 | res.json({ 83 | cat: cats[req.params.id] 84 | }); 85 | } 86 | else { 87 | res.status(404).send(JSON.stringify({ "error": "no such cat" })); 88 | } 89 | } 90 | catch (e) { 91 | res.status(400).send(JSON.stringify({ "error": "problem with posted data" })); 92 | } 93 | }); 94 | //delete cat 95 | router.delete('/cats/:id', (0, cors_1.default)(), (req, res) => { 96 | if (!!cats[req.params.id]) { 97 | delete cats[req.params.id]; 98 | res.json({ 99 | uuid: req.params.id 100 | }); 101 | } 102 | else { 103 | res.status(404).send(JSON.stringify({ "error": "no such cat" })); 104 | } 105 | }); 106 | router.options('*', (0, cors_1.default)()); 107 | server.use('/', router); 108 | } 109 | } 110 | exports.default = Router; 111 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate 2 | 3 | > To help support this TypeScript boilerplate, please take a moment to look at my official **Design Patterns in TypeScript** book and **TypeScript Courses**.
4 | > [Three.js and TypeScript](https://www.udemy.com/course/threejs-tutorials/?referralCode=4C7E1DE91C3E42F69D0F) 5 | > 6 | > [Socket.IO and TypeScript](https://www.udemy.com/course/typescript-socketio/?referralCode=2F6E227AC7EB9D147327) 7 | > 8 | > Three.js and TypeScript : [ASIN B094716FD6](https://www.amazon.com/dp/B09GYTKRCH) 9 | > 10 | > Design Patterns in TypeScript : Paperback [ASIN B0948BCH24](https://www.amazon.com/dp/B0948BCH24), eBook : [ASIN B094716FD6](https://www.amazon.com/dp/B094716FD6) 11 | 12 | ### MIT License 13 | 14 | Remember, No guarantees, or even fit for a particular purpose. 15 | 16 | If you have a suggestion, or you want to contribute some code, you can make a pull request. 17 | 18 | Your contributions will be visible since this project is public. 19 | 20 | ### Setup 21 | 22 | ```bash 23 | npm install 24 | ``` 25 | 26 | ### Development with nodemon and tsc --watch 27 | 28 | ```bash 29 | npm run dev 30 | ``` 31 | 32 | Then visit `http://localhost:3000/cats` 33 | 34 | ### Run without nodemon and tsc --watch 35 | 36 | ```bash 37 | npm start 38 | ``` 39 | 40 | Then visit `http://localhost:3000/cats` 41 | 42 | ## Swagger 43 | 44 | Visit `http://localhost:3000/swagger` to view the OPENAPI document in Swagger-UI 45 | ![Swagger-UI](docs/swagger.png) 46 | 47 | ### Video tutorial on setting up Swagger in an existing NodeJS TypeScript API 48 | 49 | [![Add Swagger-UI Documentation To Existing NodeJS TypeScript API](https://img.youtube.com/vi/qemG0CWOx1I/0.jpg)](https://youtu.be/qemG0CWOx1I) 50 | 51 | ## Continuous Integration and Deployment 52 | 53 | I've also added gitlab-ci.yml and dockerised with Docker-Compose. See video tutorial on how all this works. 54 | [![CI/CD a NodeJS API with Docker-Compose and GitLab](https://img.youtube.com/vi/Qlj6NiOy5jM/0.jpg)](https://youtu.be/Qlj6NiOy5jM) 55 | 56 | ## Usage 57 | 58 | ### List all records 59 | 60 | ![Example Get all records](docs/get-example.png) 61 | 62 | ### Post (Create) Record 63 | 64 | ![Example Post (Create) new record](docs/post-example.png) 65 | 66 | ### Get by Id 67 | 68 | ![Example Get by ID](docs/get-id-example.png) 69 | 70 | ### Put (Update) Record 71 | 72 | ![Example Put (Update)](docs/put-example.png) 73 | 74 | ### Delete Record 75 | 76 | ![Example Delete](docs/delete-example.png) 77 | 78 | # TypeScript Courses 79 | 80 | If you got this far, you probably like TypeScript just like I do, 81 | I have created two TypeScript courses specializing in the [Three.js](https://www.udemy.com/course/threejs-tutorials/?referralCode=4C7E1DE91C3E42F69D0F) and [Socket.IO](https://www.udemy.com/course/typescript-socketio/?referralCode=2F6E227AC7EB9D147327) 82 | libraries that you may find useful. 83 | 84 | ## Threejs and TypeScript Course 85 | 86 | [![TypeScript Threejs Introduction](docs/threejs-course-image.png)](https://youtu.be/BcF3yuVqfwo) 87 | 88 | ## Socket.io and TypeScript Course 89 | 90 | [![TypeScript SocketIO Introduction](docs/tssock-course.png)](https://youtu.be/3uLSNctzkkw) 91 | 92 | # Programming Books 93 | 94 | To help support my projects, please check out my books. 95 | 96 | ## Three.js and TypeScript 97 | 98 | 99 | 100 |    https://www.amazon.com/dp/B09GYTKRCH
101 |    https://www.amazon.co.uk/dp/B09GYTKRCH
102 |    https://www.amazon.in/dp/B09GYTKRCH
103 |    https://www.amazon.de/dp/B09GYTKRCH
104 |    https://www.amazon.fr/dp/B09GYTKRCH
105 |    https://www.amazon.es/dp/B09GYTKRCH
106 |    https://www.amazon.it/dp/B09GYTKRCH
107 |    https://www.amazon.nl/dp/B09GYTKRCH
108 |    https://www.amazon.co.jp/dp/B09GYTKRCH
109 |    https://www.amazon.ca/dp/B09GYTKRCH
110 |    https://www.amazon.com.br/dp/B09GYTKRCH
111 |    https://www.amazon.com.mx/dp/B09GYTKRCH
112 |    https://www.amazon.com.au/dp/B09GYTKRCH 113 | 114 | (ASIN : B09GZM9KGJ / B09GYTKRCH) 115 | 116 | **Design Patterns in TypeScript**. 117 | 118 | 119 | 120 |    https://www.amazon.com/dp/B0948BCH24
121 |    https://www.amazon.co.uk/dp/B0948BCH24
122 |    https://www.amazon.in/dp/B094716FD6
123 |    https://www.amazon.de/dp/B0948BCH24
124 |    https://www.amazon.fr/dp/B0948BCH24
125 |    https://www.amazon.es/dp/B0948BCH24
126 |    https://www.amazon.it/dp/B0948BCH24
127 |    https://www.amazon.co.jp/dp/B0948BCH24
128 |    https://www.amazon.ca/dp/B0948BCH24
129 |    https://www.amazon.com.au/dp/B094716FD6 130 | 131 | (ASIN : B0948BCH24 / B094716FD6) 132 | 133 | --- 134 | 135 | Thanks 136 | 137 | Sean 138 | -------------------------------------------------------------------------------- /src/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate", 6 | "description": "A minimal and easy to follow example of what you need to create a CRUD style API in NodeJs using TypeScript", 7 | "license": { 8 | "name": "MIT", 9 | "url": "https://opensource.org/licenses/MIT" 10 | } 11 | }, 12 | "servers": [ 13 | { 14 | "url": "/", 15 | "description": "Local Dev, or from Heroku" 16 | }, 17 | { 18 | "url": "/api/", 19 | "description": "With docker-compose and nginx proxy" 20 | } 21 | ], 22 | "tags": [ 23 | { 24 | "name": "Cats", 25 | "description": "API for cats in the system" 26 | } 27 | ], 28 | "consumes": [ 29 | "application/json" 30 | ], 31 | "produces": [ 32 | "application/json" 33 | ], 34 | "paths": { 35 | "/cats": { 36 | "get": { 37 | "tags": [ 38 | "Cats" 39 | ], 40 | "summary": "Get all cats in system", 41 | "responses": { 42 | "200": { 43 | "description": "OK", 44 | "schema": { 45 | "$ref": "#/definitions/Cats" 46 | } 47 | } 48 | } 49 | }, 50 | "post": { 51 | "tags": [ 52 | "Cats" 53 | ], 54 | "summary": "Create a new cat in system", 55 | "requestBody": { 56 | "description": "Cat Object", 57 | "required": true, 58 | "content": { 59 | "application/json": { 60 | "schema": { 61 | "$ref": "#/definitions/Cat" 62 | } 63 | } 64 | } 65 | }, 66 | "produces": [ 67 | "application/json" 68 | ], 69 | "responses": { 70 | "200": { 71 | "description": "OK", 72 | "schema": { 73 | "$ref": "#/definitions/id" 74 | } 75 | }, 76 | "400": { 77 | "description": "Failed. Bad post data." 78 | } 79 | } 80 | } 81 | }, 82 | "/cats/{id}": { 83 | "parameters": [ 84 | { 85 | "name": "id", 86 | "in": "path", 87 | "required": true, 88 | "description": "ID of the cat that we want to match", 89 | "type": "string" 90 | } 91 | ], 92 | "get": { 93 | "tags": [ 94 | "Cats" 95 | ], 96 | "summary": "Get cat with given ID", 97 | "parameters": [ 98 | { 99 | "in": "path", 100 | "name": "id", 101 | "required": true, 102 | "description": "Cat with id", 103 | "schema": { 104 | "$ref": "#/definitions/id" 105 | } 106 | } 107 | ], 108 | "responses": { 109 | "200": { 110 | "description": "OK", 111 | "schema": { 112 | "$ref": "#/definitions/Cat" 113 | } 114 | }, 115 | "404": { 116 | "description": "Failed. Cat not found." 117 | } 118 | } 119 | }, 120 | "put": { 121 | "summary": "Update cat with given ID", 122 | "tags": [ 123 | "Cats" 124 | ], 125 | "requestBody": { 126 | "description": "Cat Object", 127 | "required": true, 128 | "content": { 129 | "application/json": { 130 | "schema": { 131 | "$ref": "#/definitions/Cat" 132 | } 133 | } 134 | } 135 | }, 136 | "parameters": [ 137 | { 138 | "in": "path", 139 | "name": "id", 140 | "required": true, 141 | "description": "Cat with new values of properties", 142 | "schema": { 143 | "$ref": "#/definitions/id" 144 | } 145 | } 146 | ], 147 | "responses": { 148 | "200": { 149 | "description": "OK", 150 | "schema": { 151 | "$ref": "#/definitions/Cat" 152 | } 153 | }, 154 | "400": { 155 | "description": "Failed. Bad post data." 156 | }, 157 | "404": { 158 | "description": "Failed. Cat not found." 159 | } 160 | } 161 | }, 162 | "delete": { 163 | "summary": "Delete cat with given ID", 164 | "tags": [ 165 | "Cats" 166 | ], 167 | "parameters": [ 168 | { 169 | "in": "path", 170 | "name": "id", 171 | "required": true, 172 | "description": "Delete Cat with id", 173 | "schema": { 174 | "$ref": "#/definitions/id" 175 | } 176 | } 177 | ], 178 | "responses": { 179 | "200": { 180 | "description": "OK", 181 | "schema": { 182 | "$ref": "#/definitions/id" 183 | } 184 | }, 185 | "404": { 186 | "description": "Failed. Cat not found." 187 | } 188 | } 189 | } 190 | } 191 | }, 192 | "definitions": { 193 | "id": { 194 | "properties": { 195 | "uuid": { 196 | "type": "string" 197 | } 198 | } 199 | }, 200 | "Cat": { 201 | "type": "object", 202 | "properties": { 203 | "genus": { 204 | "type": "string" 205 | }, 206 | "name": { 207 | "type": "string" 208 | }, 209 | "isHungry": { 210 | "type": "boolean" 211 | }, 212 | "lastFedDate": { 213 | "type": "string" 214 | } 215 | } 216 | }, 217 | "Cats": { 218 | "type": "object", 219 | "properties": { 220 | "cats": { 221 | "type": "object", 222 | "additionalProperties": { 223 | "$ref": "#/definitions/Cat" 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } -------------------------------------------------------------------------------- /dist/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Seans-TypeScript-NodeJS-CRUD-REST-API-Boilerplate", 6 | "description": "A minimal and easy to follow example of what you need to create a CRUD style API in NodeJs using TypeScript", 7 | "license": { 8 | "name": "MIT", 9 | "url": "https://opensource.org/licenses/MIT" 10 | } 11 | }, 12 | "servers": [ 13 | { 14 | "url": "/", 15 | "description": "Local Dev, or from Heroku" 16 | }, 17 | { 18 | "url": "/api/", 19 | "description": "With docker-compose and nginx proxy" 20 | } 21 | ], 22 | "tags": [ 23 | { 24 | "name": "Cats", 25 | "description": "API for cats in the system" 26 | } 27 | ], 28 | "consumes": [ 29 | "application/json" 30 | ], 31 | "produces": [ 32 | "application/json" 33 | ], 34 | "paths": { 35 | "/cats": { 36 | "get": { 37 | "tags": [ 38 | "Cats" 39 | ], 40 | "summary": "Get all cats in system", 41 | "responses": { 42 | "200": { 43 | "description": "OK", 44 | "schema": { 45 | "$ref": "#/definitions/Cats" 46 | } 47 | } 48 | } 49 | }, 50 | "post": { 51 | "tags": [ 52 | "Cats" 53 | ], 54 | "summary": "Create a new cat in system", 55 | "requestBody": { 56 | "description": "Cat Object", 57 | "required": true, 58 | "content": { 59 | "application/json": { 60 | "schema": { 61 | "$ref": "#/definitions/Cat" 62 | } 63 | } 64 | } 65 | }, 66 | "produces": [ 67 | "application/json" 68 | ], 69 | "responses": { 70 | "200": { 71 | "description": "OK", 72 | "schema": { 73 | "$ref": "#/definitions/id" 74 | } 75 | }, 76 | "400": { 77 | "description": "Failed. Bad post data." 78 | } 79 | } 80 | } 81 | }, 82 | "/cats/{id}": { 83 | "parameters": [ 84 | { 85 | "name": "id", 86 | "in": "path", 87 | "required": true, 88 | "description": "ID of the cat that we want to match", 89 | "type": "string" 90 | } 91 | ], 92 | "get": { 93 | "tags": [ 94 | "Cats" 95 | ], 96 | "summary": "Get cat with given ID", 97 | "parameters": [ 98 | { 99 | "in": "path", 100 | "name": "id", 101 | "required": true, 102 | "description": "Cat with id", 103 | "schema": { 104 | "$ref": "#/definitions/id" 105 | } 106 | } 107 | ], 108 | "responses": { 109 | "200": { 110 | "description": "OK", 111 | "schema": { 112 | "$ref": "#/definitions/Cat" 113 | } 114 | }, 115 | "404": { 116 | "description": "Failed. Cat not found." 117 | } 118 | } 119 | }, 120 | "put": { 121 | "summary": "Update cat with given ID", 122 | "tags": [ 123 | "Cats" 124 | ], 125 | "requestBody": { 126 | "description": "Cat Object", 127 | "required": true, 128 | "content": { 129 | "application/json": { 130 | "schema": { 131 | "$ref": "#/definitions/Cat" 132 | } 133 | } 134 | } 135 | }, 136 | "parameters": [ 137 | { 138 | "in": "path", 139 | "name": "id", 140 | "required": true, 141 | "description": "Cat with new values of properties", 142 | "schema": { 143 | "$ref": "#/definitions/id" 144 | } 145 | } 146 | ], 147 | "responses": { 148 | "200": { 149 | "description": "OK", 150 | "schema": { 151 | "$ref": "#/definitions/Cat" 152 | } 153 | }, 154 | "400": { 155 | "description": "Failed. Bad post data." 156 | }, 157 | "404": { 158 | "description": "Failed. Cat not found." 159 | } 160 | } 161 | }, 162 | "delete": { 163 | "summary": "Delete cat with given ID", 164 | "tags": [ 165 | "Cats" 166 | ], 167 | "parameters": [ 168 | { 169 | "in": "path", 170 | "name": "id", 171 | "required": true, 172 | "description": "Delete Cat with id", 173 | "schema": { 174 | "$ref": "#/definitions/id" 175 | } 176 | } 177 | ], 178 | "responses": { 179 | "200": { 180 | "description": "OK", 181 | "schema": { 182 | "$ref": "#/definitions/id" 183 | } 184 | }, 185 | "404": { 186 | "description": "Failed. Cat not found." 187 | } 188 | } 189 | } 190 | } 191 | }, 192 | "definitions": { 193 | "id": { 194 | "properties": { 195 | "uuid": { 196 | "type": "string" 197 | } 198 | } 199 | }, 200 | "Cat": { 201 | "type": "object", 202 | "properties": { 203 | "genus": { 204 | "type": "string" 205 | }, 206 | "name": { 207 | "type": "string" 208 | }, 209 | "isHungry": { 210 | "type": "boolean" 211 | }, 212 | "lastFedDate": { 213 | "type": "string" 214 | } 215 | } 216 | }, 217 | "Cats": { 218 | "type": "object", 219 | "properties": { 220 | "cats": { 221 | "type": "object", 222 | "additionalProperties": { 223 | "$ref": "#/definitions/Cat" 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | --------------------------------------------------------------------------------