├── .gitignore ├── .babelrc ├── index.js ├── src ├── services │ ├── PostService.js │ └── Service.js ├── controllers │ ├── PostController.js │ └── Controller.js └── models │ └── Post.js ├── config ├── server.js ├── routes.js └── database.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import './config/database'; 2 | import server from './config/server'; 3 | 4 | const PORT = process.env.PORT || 5000; 5 | server.listen(PORT, () => { 6 | console.log(`app running on port ${PORT}`); 7 | }); -------------------------------------------------------------------------------- /src/services/PostService.js: -------------------------------------------------------------------------------- 1 | import Service from './Service'; 2 | 3 | class PostService extends Service { 4 | constructor(model) { 5 | super(model); 6 | } 7 | }; 8 | 9 | export default PostService; 10 | -------------------------------------------------------------------------------- /config/server.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import bodyParser from "body-parser"; 3 | const server = express(); 4 | 5 | server.use(bodyParser.json()); 6 | 7 | import setRoutes from "./routes"; 8 | setRoutes(server); 9 | 10 | export default server; -------------------------------------------------------------------------------- /config/routes.js: -------------------------------------------------------------------------------- 1 | import PostController from './../src/controllers/PostController'; 2 | 3 | export default (app) => { 4 | 5 | // POST ROUTES 6 | app.get(`/api/post`, PostController.getAll); 7 | app.get(`/api/post/:params`, PostController.get); 8 | app.post(`/api/post`, PostController.insert) 9 | app.put(`/api/post/:id`, PostController.update); 10 | app.delete(`/api/post/:id`, PostController.delete); 11 | 12 | } -------------------------------------------------------------------------------- /src/controllers/PostController.js: -------------------------------------------------------------------------------- 1 | import Controller from './Controller'; 2 | import PostService from "./../services/PostService"; 3 | import Post from "./../models/Post"; 4 | const postService = new PostService( 5 | new Post().getInstance() 6 | ); 7 | 8 | class PostController extends Controller { 9 | 10 | constructor(service) { 11 | super(service); 12 | } 13 | 14 | } 15 | 16 | export default new PostController(postService); 17 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | class Connection { 4 | constructor() { 5 | const url = 6 | process.env.MONGODB_URI || `mongodb://localhost:27017/node-starter`; 7 | console.log("Establish new connection with url", url); 8 | mongoose.Promise = global.Promise; 9 | mongoose.set("useNewUrlParser", true); 10 | mongoose.set("useFindAndModify", false); 11 | mongoose.set("useCreateIndex", true); 12 | mongoose.set("useUnifiedTopology", true); 13 | mongoose.connect(url); 14 | } 15 | } 16 | 17 | export default new Connection(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-starter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "babel-node index.js", 9 | "dev:start": "clear; nodemon --exec babel-node index.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "body-parser": "^1.19.0", 16 | "express": "^4.17.1", 17 | "mongoose": "^5.7.7", 18 | "mongoose-unique-validator": "^2.0.3", 19 | "slugify": "^1.3.5" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.6.4", 23 | "@babel/node": "^7.6.3", 24 | "@babel/preset-env": "^7.6.3", 25 | "babel-loader": "^8.0.6", 26 | "nodemon": "^1.19.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/models/Post.js: -------------------------------------------------------------------------------- 1 | import mongoose, { Schema } from "mongoose"; 2 | import uniqueValidator from "mongoose-unique-validator"; 3 | import slugify from 'slugify'; 4 | 5 | class Post { 6 | 7 | initSchema() { 8 | const schema = new Schema({ 9 | title: { 10 | type: String, 11 | required: true, 12 | }, 13 | slug: String, 14 | subtitle: { 15 | type: String, 16 | required: false, 17 | }, 18 | description: { 19 | type: String, 20 | required: false, 21 | }, 22 | content: { 23 | type: String, 24 | required: true, 25 | } 26 | }, { timestamps: true }); 27 | schema.pre( 28 | "save", 29 | function(next) { 30 | let post = this; 31 | if (!post.isModified("title")) { 32 | return next(); 33 | } 34 | post.slug = slugify(post.title, "_"); 35 | console.log('set slug', post.slug); 36 | return next(); 37 | }, 38 | function(err) { 39 | next(err); 40 | } 41 | ); 42 | schema.plugin(uniqueValidator); 43 | mongoose.model("posts", schema); 44 | } 45 | 46 | getInstance() { 47 | this.initSchema(); 48 | return mongoose.model("posts"); 49 | } 50 | } 51 | 52 | export default Post; 53 | -------------------------------------------------------------------------------- /src/controllers/Controller.js: -------------------------------------------------------------------------------- 1 | class Controller { 2 | 3 | constructor(service) { 4 | this.service = service; 5 | this.getAll = this.getAll.bind(this); 6 | this.get = this.get.bind(this); 7 | this.insert = this.insert.bind(this); 8 | this.update = this.update.bind(this); 9 | this.delete = this.delete.bind(this); 10 | } 11 | 12 | async getAll(req, res) { 13 | return res.status(200).send(await this.service.getAll(req.query)); 14 | } 15 | 16 | async get(req, res) { 17 | let response = await this.service.get(req.params) 18 | return res.status(response.statusCode).send(response); 19 | } 20 | 21 | async insert(req, res) { 22 | let response = await this.service.insert(req.body); 23 | if (response.error) return res.status(response.statusCode).send(response); 24 | return res.status(201).send(response); 25 | } 26 | 27 | async update(req, res) { 28 | const { id } = req.params; 29 | 30 | let response = await this.service.update(id, req.body); 31 | 32 | return res.status(response.statusCode).send(response); 33 | } 34 | 35 | async delete(req, res) { 36 | const { id } = req.params; 37 | 38 | let response = await this.service.delete(id); 39 | 40 | return res.status(response.statusCode).send(response); 41 | } 42 | 43 | } 44 | 45 | export default Controller; -------------------------------------------------------------------------------- /src/services/Service.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | class Service { 4 | constructor(model) { 5 | this.model = model; 6 | this.getAll = this.getAll.bind(this); 7 | this.insert = this.insert.bind(this); 8 | this.update = this.update.bind(this); 9 | this.delete = this.delete.bind(this); 10 | } 11 | 12 | async getAll(query) { 13 | let { skip, limit } = query; 14 | 15 | skip = skip ? Number(skip) : 0; 16 | limit = limit ? Number(limit) : 10; 17 | 18 | delete query.skip; 19 | delete query.limit; 20 | 21 | if (query._id) { 22 | try { 23 | query._id = new mongoose.mongo.ObjectId(query._id); 24 | } catch (error) { 25 | console.log("not able to generate mongoose id with content", query._id); 26 | } 27 | } 28 | 29 | try { 30 | let items = await this.model 31 | .find(query) 32 | .skip(skip) 33 | .limit(limit); 34 | let total = await this.model.count(); 35 | 36 | return { 37 | error: false, 38 | statusCode: 200, 39 | data: items, 40 | total 41 | }; 42 | } catch (errors) { 43 | return { 44 | error: true, 45 | statusCode: 500, 46 | errors 47 | }; 48 | } 49 | } 50 | 51 | async insert(data) { 52 | try { 53 | let item = await this.model.create(data); 54 | if (item) 55 | return { 56 | error: false, 57 | item 58 | }; 59 | } catch (error) { 60 | console.log("error", error); 61 | return { 62 | error: true, 63 | statusCode: 500, 64 | message: error.errmsg || "Not able to create item", 65 | errors: error.errors 66 | }; 67 | } 68 | } 69 | 70 | async update(id, data) { 71 | try { 72 | let item = await this.model.findByIdAndUpdate(id, data, { new: true }); 73 | return { 74 | error: false, 75 | statusCode: 202, 76 | item 77 | }; 78 | } catch (error) { 79 | return { 80 | error: true, 81 | statusCode: 500, 82 | error 83 | }; 84 | } 85 | } 86 | 87 | async delete(id) { 88 | try { 89 | let item = await this.model.findByIdAndDelete(id); 90 | if (!item) 91 | return { 92 | error: true, 93 | statusCode: 404, 94 | message: "item not found" 95 | }; 96 | 97 | console.log("removed item", item); 98 | 99 | if (item.path) { 100 | console.log("unlink item", item.path); 101 | fs.unlink(item.path, function(err) { 102 | if (err) { 103 | console.log("error deleting file"); 104 | throw err; 105 | } 106 | console.log("File deleted!"); 107 | }); 108 | } 109 | 110 | return { 111 | error: false, 112 | deleted: true, 113 | statusCode: 202, 114 | item 115 | }; 116 | } catch (error) { 117 | return { 118 | error: true, 119 | statusCode: 500, 120 | error 121 | }; 122 | } 123 | } 124 | } 125 | 126 | export default Service; 127 | --------------------------------------------------------------------------------