├── config ├── keys │ ├── keys_prod.js │ ├── keys_dev.js │ └── keys.js └── tests │ ├── rtl.setup.js │ └── jest.config.js ├── Dockerfile ├── .gitignore ├── .env.example ├── server ├── config │ └── db │ │ └── index.js ├── models │ ├── Comment.js │ ├── User.js │ └── Post.js └── index.js ├── graphql ├── index.js ├── resolvers │ ├── index.js │ ├── merge.js │ ├── User │ │ └── index.js │ ├── Comment │ │ └── index.js │ └── Post │ │ └── index.js └── types │ ├── index.js │ ├── User │ └── index.js │ ├── Comment │ └── index.js │ └── Post │ └── index.js ├── docker-compose.yml ├── .babelrc ├── README.md └── package.json /config/keys/keys_prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongoURI: process.env.MONGO_URI, 3 | secretOrKey: process.env.SECRET_OR_KEY 4 | }; 5 | -------------------------------------------------------------------------------- /config/keys/keys_dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongoURI: "mongodb://leo:leoleo1@ds121753.mlab.com:21753/graphql-server", 3 | secretOrKey: "secret" 4 | }; 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY ./package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY ./ ./ 10 | 11 | CMD [ "npm", "run", "server" ] -------------------------------------------------------------------------------- /config/keys/keys.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === "production") { 2 | module.exports = require("./keys_prod"); 3 | } else { 4 | module.exports = require("./keys_dev"); 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | .env 6 | 7 | # IDEs and editors 8 | /.idea 9 | -------------------------------------------------------------------------------- /config/tests/rtl.setup.js: -------------------------------------------------------------------------------- 1 | // See https://github.com/kentcdodds/react-testing-library#global-config 2 | import 'jest-dom/extend-expect'; 3 | import 'react-testing-library/cleanup-after-each'; 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ################################### 2 | Set this file and copy it into .env 3 | ################################### 4 | mongoURI=mongodb://:@ds155653.mlab.com:55653/ 5 | secretOrKey=secret -------------------------------------------------------------------------------- /server/config/db/index.js: -------------------------------------------------------------------------------- 1 | import User from "../../models/User"; 2 | import Post from "../../models/Post"; 3 | import Comment from "../../models/Comment"; 4 | 5 | export const models = { 6 | User, 7 | Post, 8 | Comment 9 | }; 10 | -------------------------------------------------------------------------------- /graphql/index.js: -------------------------------------------------------------------------------- 1 | import { makeExecutableSchema } from "graphql-tools"; 2 | 3 | import typeDefs from "./types/"; 4 | import resolvers from "./resolvers/"; 5 | 6 | const schema = makeExecutableSchema({ 7 | typeDefs, 8 | resolvers 9 | }); 10 | 11 | export default schema; 12 | -------------------------------------------------------------------------------- /graphql/resolvers/index.js: -------------------------------------------------------------------------------- 1 | import { mergeResolvers } from "merge-graphql-schemas"; 2 | 3 | import User from "./User/"; 4 | import Post from "./Post/"; 5 | import Comment from "./Comment/"; 6 | 7 | const resolvers = [User, Post, Comment]; 8 | 9 | export default mergeResolvers(resolvers); 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | mongodb: 4 | image: 'mongo:latest' 5 | ports: 6 | - 27017:27017 7 | graphql-server: 8 | build: 9 | context: './' 10 | ports: 11 | - 4000:4000 12 | depends_on: 13 | - mongodb 14 | environment: 15 | - mongoURI=mongodb://mongodb:27017/graphql-development 16 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry" 7 | } 8 | ], 9 | "@babel/preset-react" 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-proposal-object-rest-spread", 13 | "@babel/plugin-transform-runtime", 14 | "@babel/plugin-transform-async-to-generator", 15 | "@babel/plugin-proposal-class-properties" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /graphql/types/index.js: -------------------------------------------------------------------------------- 1 | import { mergeTypes } from "merge-graphql-schemas"; 2 | 3 | import User from "./User/"; 4 | import Post from "./Post/"; 5 | import Comment from "./Comment/"; 6 | 7 | const typeDefs = [User, Post, Comment]; 8 | 9 | // NOTE: 2nd param is optional, and defaults to false 10 | // Only use if you have defined the same type multiple times in 11 | // different files and wish to attempt merging them together. 12 | export default mergeTypes(typeDefs, { all: true }); 13 | -------------------------------------------------------------------------------- /server/models/Comment.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import { ObjectID } from "mongodb"; 3 | 4 | const Schema = mongoose.Schema; 5 | 6 | ObjectID.prototype.valueOf = function() { 7 | return this.toString(); 8 | }; 9 | 10 | const CommentSchema = new Schema({ 11 | text: { 12 | type: String, 13 | required: true 14 | }, 15 | author: { 16 | type: Schema.Types.ObjectId, 17 | ref: "User" 18 | }, 19 | post: { 20 | type: Schema.Types.ObjectId, 21 | ref: "Post" 22 | } 23 | }); 24 | 25 | export default mongoose.model("Comment", CommentSchema); 26 | -------------------------------------------------------------------------------- /graphql/resolvers/merge.js: -------------------------------------------------------------------------------- 1 | import User from "./User"; 2 | 3 | const user = async userId => { 4 | try { 5 | const user = await User.findById(userId); 6 | return { 7 | ...user._doc, 8 | _id: user.id, 9 | createdPosts: postMessage.bind(this, user._doc.createdPosts) 10 | }; 11 | } catch (error) { 12 | throw error; 13 | } 14 | } 15 | 16 | const transformPost = event => { 17 | return { 18 | ...event._doc, 19 | _id: event.id, 20 | creator: user.bind(this, event.creator) 21 | } 22 | } 23 | 24 | exports.transformPost = transformPost; -------------------------------------------------------------------------------- /config/tests/jest.config.js: -------------------------------------------------------------------------------- 1 | // Jest configuration 2 | // https://facebook.github.io/jest/docs/en/configuration.html 3 | 4 | module.exports = { 5 | automock: false, 6 | browser: false, 7 | bail: false, 8 | collectCoverageFrom: [ 9 | 'src/**/*.{js,jsx}', 10 | '!**/node_modules/**', 11 | '!**/vendor/**' 12 | ], 13 | coverageDirectory: '/coverage', 14 | globals: { 15 | __DEV__: true 16 | }, 17 | moduleFileExtensions: ['js', 'json', 'jsx', 'node'], 18 | transform: { 19 | '^.+\\.js?$': 'babel-jest' 20 | }, 21 | verbose: true, 22 | setupTestFrameworkScriptFile: './rtl.setup.js' 23 | }; 24 | -------------------------------------------------------------------------------- /graphql/types/User/index.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | type User { 3 | _id: String! 4 | name: String! 5 | email: String! 6 | age: Int! 7 | posts: [Post!]! 8 | comments: [Comment!]! 9 | } 10 | 11 | type Query { 12 | user(_id: ID!): User! 13 | users: [User!]! 14 | } 15 | 16 | type Mutation { 17 | createUser(user: CreateUserInput): User! 18 | updateUser(_id: String!, user: UpdateUserInput!): User! 19 | deleteUser(_id: String!): User! 20 | } 21 | 22 | input CreateUserInput { 23 | name: String! 24 | email: String! 25 | age: Int! 26 | } 27 | 28 | input UpdateUserInput { 29 | name: String 30 | email: String 31 | age: Int 32 | } 33 | `; 34 | -------------------------------------------------------------------------------- /server/models/User.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import { ObjectID } from "mongodb"; 3 | 4 | const Schema = mongoose.Schema; 5 | 6 | ObjectID.prototype.valueOf = function() { 7 | return this.toString(); 8 | }; 9 | 10 | const UserSchema = new Schema({ 11 | name: { 12 | type: String, 13 | required: true 14 | }, 15 | email: { 16 | type: String, 17 | unique: true, 18 | required: true 19 | }, 20 | age: { 21 | type: Number, 22 | required: true 23 | }, 24 | posts: [ 25 | { 26 | type: Schema.Types.ObjectId, 27 | ref: "Post" 28 | } 29 | ], 30 | comments: [ 31 | { 32 | type: Schema.Types.ObjectId, 33 | ref: "Comment" 34 | } 35 | ] 36 | }); 37 | 38 | export default mongoose.model("User", UserSchema); 39 | -------------------------------------------------------------------------------- /server/models/Post.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import { ObjectID } from "mongodb"; 3 | 4 | const Schema = mongoose.Schema; 5 | 6 | ObjectID.prototype.valueOf = function() { 7 | return this.toString(); 8 | }; 9 | 10 | const PostSchema = new Schema({ 11 | title: { 12 | type: String, 13 | required: true 14 | }, 15 | body: { 16 | type: String, 17 | required: true 18 | }, 19 | published: { 20 | type: Boolean, 21 | required: true 22 | }, 23 | author: { 24 | type: Schema.Types.ObjectId, 25 | ref: "User" 26 | }, 27 | comments: [ 28 | { 29 | type: Schema.Types.ObjectId, 30 | ref: "Comment" 31 | } 32 | ], 33 | date: { 34 | published: { 35 | type: Date, 36 | default: Date.now() 37 | }, 38 | updated: { 39 | type: Date, 40 | default: Date.now() 41 | } 42 | } 43 | }); 44 | 45 | export default mongoose.model("Post", PostSchema); 46 | -------------------------------------------------------------------------------- /graphql/types/Comment/index.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | type Comment { 3 | _id: ID! 4 | text: String! 5 | author: User! 6 | post: Post! 7 | } 8 | 9 | type Query { 10 | comment(_id: ID!): [Comment!]! 11 | comments: [Comment!]! 12 | } 13 | 14 | type Mutation { 15 | createComment(comment: CreateCommentInput!): Comment! 16 | updateComment(_id: ID!, comment: UpdateCommentInput): Comment! 17 | deleteComment(_id: ID!): Comment! 18 | } 19 | 20 | type Subscription { 21 | comment(postId: ID!): CommentSubscriptionPayload! 22 | } 23 | 24 | type CommentSubscriptionPayload { 25 | mutation: MutationType! 26 | comment: Comment! 27 | } 28 | 29 | input CreateCommentInput { 30 | text: String! 31 | post: ID! 32 | author: ID! 33 | } 34 | 35 | input UpdateCommentInput { 36 | text: String 37 | } 38 | 39 | enum MutationType { 40 | CREATED 41 | DELETED 42 | UPDATED 43 | } 44 | `; 45 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | import { GraphQLServer, PubSub } from "graphql-yoga"; 3 | import mongoose from "mongoose"; 4 | 5 | import schema from "../graphql/"; 6 | import { models } from "./config/db/"; 7 | 8 | const { mongoURI: db } = process.env; 9 | 10 | const pubsub = new PubSub(); 11 | 12 | const options = { 13 | port: process.env.PORT || "4000", 14 | endpoint: "/graphql", 15 | subscriptions: "/subscriptions", 16 | playground: "/playground" 17 | }; 18 | 19 | const context = { 20 | models, 21 | pubsub 22 | }; 23 | 24 | // Connect to MongoDB with Mongoose. 25 | mongoose 26 | .connect( 27 | db, 28 | { 29 | useCreateIndex: true, 30 | useNewUrlParser: true 31 | } 32 | ) 33 | .then(() => console.log("MongoDB connected")) 34 | .catch(err => console.log(err)); 35 | 36 | const server = new GraphQLServer({ 37 | schema, 38 | context 39 | }); 40 | 41 | server.start(options, ({ port }) => { 42 | console.log(`🚀 Server is running on http://localhost:${port}`); 43 | }); 44 | -------------------------------------------------------------------------------- /graphql/types/Post/index.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | type Dates { 3 | published: String 4 | updated: String 5 | } 6 | 7 | type Post { 8 | _id: ID! 9 | title: String! 10 | body: String! 11 | published: Boolean! 12 | author: User! 13 | comments: [Comment!]! 14 | date: Dates 15 | } 16 | 17 | type Query { 18 | post(_id: ID!): Post! 19 | posts: [Post!]! 20 | } 21 | 22 | type Mutation { 23 | createPost(post: CreatePostInput): Post! 24 | updatePost(_id: ID!, post: UpdatePostInput): Post! 25 | deletePost(_id: ID!): Post! 26 | } 27 | 28 | type Subscription { 29 | post: PostSubscriptionPayload! 30 | } 31 | 32 | type PostSubscriptionPayload { 33 | mutation: MutationType! 34 | post: Post! 35 | } 36 | 37 | input DatesInput { 38 | published: String 39 | updated: String 40 | } 41 | 42 | input CreatePostInput { 43 | title: String! 44 | body: String! 45 | published: Boolean! 46 | author: ID! 47 | date: DatesInput 48 | } 49 | 50 | input UpdatePostInput { 51 | title: String 52 | body: String 53 | published: Boolean 54 | date: DatesInput 55 | } 56 | 57 | enum MutationType { 58 | CREATED 59 | DELETED 60 | UPDATED 61 | } 62 | `; 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | GraphQL MongoDB Server 4 |
5 |
6 | GraphQL MongoDB Server 7 |

8 | 9 |

A server boilerplate using GraphQL and MongoDB.

10 | 11 |

12 | 13 | PRs Welcome 14 | 15 | 16 | License MIT 17 | 18 |

19 | 20 |
21 | 22 | ## Introduction 23 | 24 | This is a server boilerplate using GraphQL and MongoDB. Support subscriptions using GraphQL Yoga. 25 | 26 | ## Getting started 27 | 28 | 1. Clone this repo using `https://github.com/leonardomso/graphql-mongodb-server.git` 29 | 2. Move to the appropriate directory: `cd graphql-mongodb-server`. 30 | 4. Run `yarn` or `npm install` to install dependencies. 31 | 5. Set `.env` file with your mongoURI. 32 | 6. Run `npm start` to see the example app at `http://localhost:4000/playground`. 33 | 34 | ## Commands 35 | 36 | - `npm start` - start the playground at `http://localhost:4000/playground` 37 | 38 | ## License 39 | 40 | MIT license, Copyright (c) 2018 Leonardo Maldonado. 41 | -------------------------------------------------------------------------------- /graphql/resolvers/User/index.js: -------------------------------------------------------------------------------- 1 | import User from "../../../server/models/User"; 2 | import Post from "../../../server/models/Post"; 3 | import Comment from "../../../server/models/Comment"; 4 | 5 | export default { 6 | Query: { 7 | user: async (parent, { _id }, context, info) => { 8 | return await User.findOne({ _id }).exec(); 9 | }, 10 | users: async (parent, args, context, info) => { 11 | const users = await User.find({}) 12 | .populate() 13 | .exec(); 14 | 15 | return users.map(u => ({ 16 | _id: u._id.toString(), 17 | name: u.name, 18 | email: u.email, 19 | age: u.age, 20 | posts: u.posts, 21 | comments: u.comments 22 | })); 23 | } 24 | }, 25 | Mutation: { 26 | createUser: async (parent, { user }, context, info) => { 27 | const newUser = await new User({ 28 | name: user.name, 29 | email: user.email, 30 | age: user.age 31 | }); 32 | 33 | return new Promise((resolve, reject) => { 34 | newUser.save((err, res) => { 35 | err ? reject(err) : resolve(res); 36 | }); 37 | }); 38 | }, 39 | updateUser: async (parent, { _id, user }, context, info) => { 40 | return new Promise((resolve, reject) => { 41 | User.findByIdAndUpdate(_id, { $set: { ...user } }, { new: true }).exec( 42 | (err, res) => { 43 | err ? reject(err) : resolve(res); 44 | } 45 | ); 46 | }); 47 | }, 48 | deleteUser: async (parent, { _id }, context, info) => { 49 | return new Promise((resolve, reject) => { 50 | User.findByIdAndDelete(_id).exec((err, res) => { 51 | err ? reject(err) : resolve(res); 52 | }); 53 | }); 54 | } 55 | }, 56 | User: { 57 | posts: async ({ _id }, args, context, info) => { 58 | return await Post.find({ author: _id }); 59 | }, 60 | comments: async ({ _id }, args, context, info) => { 61 | return await Comment.find({ author: _id }); 62 | } 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /graphql/resolvers/Comment/index.js: -------------------------------------------------------------------------------- 1 | import User from "../../../server/models/User"; 2 | import Post from "../../../server/models/Post"; 3 | import Comment from "../../../server/models/Comment"; 4 | 5 | export default { 6 | Query: { 7 | comment: async (parent, { _id }, context, info) => { 8 | return await Comment.find({ _id }); 9 | }, 10 | comments: async (parent, args, context, info) => { 11 | const res = await Comment.find({}) 12 | .populate() 13 | .exec(); 14 | 15 | return res.map(u => ({ 16 | _id: u._id.toString(), 17 | text: u.text, 18 | author: u.author, 19 | post: u.post 20 | })); 21 | } 22 | }, 23 | Mutation: { 24 | createComment: async (parent, { comment }, context, info) => { 25 | const newComment = await new Comment({ 26 | text: comment.text, 27 | author: comment.author, 28 | post: comment.post 29 | }); 30 | 31 | return new Promise((resolve, reject) => { 32 | newComment.save((err, res) => { 33 | err ? reject(err) : resolve(res); 34 | }); 35 | }); 36 | }, 37 | updateComment: async (parent, { _id, comment }, context, info) => { 38 | return new Promise((resolve, reject) => { 39 | Comment.findByIdAndUpdate( 40 | _id, 41 | { $set: { ...comment } }, 42 | { new: true } 43 | ).exec((err, res) => { 44 | err ? reject(err) : resolve(res); 45 | }); 46 | }); 47 | }, 48 | deleteComment: async (parent, { _id }, context, info) => { 49 | return new Promise((resolve, reject) => { 50 | Comment.findByIdAndDelete(_id).exec((err, res) => { 51 | err ? reject(err) : resolve(res); 52 | }); 53 | }); 54 | } 55 | }, 56 | Subscription: { 57 | comment: { 58 | subscribe: (parent, args, { pubsub }) => { 59 | //return pubsub.asyncIterator(channel) 60 | } 61 | } 62 | }, 63 | Comment: { 64 | author: async ({ author }, args, context, info) => { 65 | return await User.findById({ _id: author }); 66 | }, 67 | post: async ({ post }, args, context, info) => { 68 | return await Post.findById({ _id: post }); 69 | } 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-mongodb-server", 3 | "version": "1.0.0", 4 | "description": "A GraphQL MongoDB server.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm run server", 8 | "server": "nodemon --exec babel-node server/index.js" 9 | }, 10 | "keywords": [ 11 | "graphql", 12 | "mongodb", 13 | "boilerplate", 14 | "server" 15 | ], 16 | "author": "Leonardo Maldonado ", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@babel/cli": "^7.1.2", 20 | "@babel/core": "^7.1.2", 21 | "@babel/node": "^7.0.0", 22 | "@babel/plugin-proposal-class-properties": "^7.0.0", 23 | "@babel/plugin-proposal-decorators": "^7.0.0", 24 | "@babel/plugin-proposal-export-namespace-from": "^7.0.0", 25 | "@babel/plugin-proposal-function-sent": "^7.0.0", 26 | "@babel/plugin-proposal-json-strings": "^7.0.0", 27 | "@babel/plugin-proposal-numeric-separator": "^7.0.0", 28 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 29 | "@babel/plugin-proposal-throw-expressions": "^7.0.0", 30 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 31 | "@babel/plugin-syntax-import-meta": "^7.0.0", 32 | "@babel/plugin-transform-async-to-generator": "^7.1.0", 33 | "@babel/plugin-transform-runtime": "^7.1.0", 34 | "@babel/preset-env": "^7.1.0", 35 | "@babel/preset-react": "^7.0.0", 36 | "@babel/register": "^7.0.0", 37 | "@babel/runtime": "^7.1.2", 38 | "babel-core": "^7.0.0-bridge.0", 39 | "babel-eslint": "^10.0.1", 40 | "babel-jest": "^23.6.0", 41 | "babel-loader": "^8.0.4", 42 | "jest": "^23.6.0", 43 | "jest-cli": "^23.6.0", 44 | "jest-dom": "^2.1.1", 45 | "react-testing-library": "^5.2.3" 46 | }, 47 | "dependencies": { 48 | "concurrently": "^4.1.0", 49 | "dotenv": "^6.1.0", 50 | "graphql": "^14.0.2", 51 | "graphql-subscriptions": "^1.0.0", 52 | "graphql-tools": "^4.0.3", 53 | "graphql-yoga": "^1.16.7", 54 | "merge-graphql-schemas": "^1.5.7", 55 | "mongoose": "^5.3.13", 56 | "nodemon": "^1.18.10", 57 | "uuid": "^3.3.2" 58 | }, 59 | "jest": { 60 | "setupFiles": [ 61 | "/config/tests/jest.config" 62 | ], 63 | "transform": { 64 | "^.+\\.js$": "babel-jest" 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /graphql/resolvers/Post/index.js: -------------------------------------------------------------------------------- 1 | import User from "../../../server/models/User"; 2 | import Post from "../../../server/models/Post"; 3 | import Comment from "../../../server/models/Comment"; 4 | 5 | import { transformPost } from "../merge"; 6 | 7 | export default { 8 | Query: { 9 | post: async (parent, { _id }, context, info) => { 10 | return await Post.findOne({ _id }).exec(); 11 | }, 12 | posts: async (parent, args, context, info) => { 13 | const res = await Post.find({}) 14 | .populate() 15 | .exec(); 16 | 17 | return res.map(u => ({ 18 | _id: u._id.toString(), 19 | title: u.title, 20 | body: u.body, 21 | published: u.published, 22 | author: u.author, 23 | comments: u.comments, 24 | date: u.date 25 | })); 26 | } 27 | }, 28 | Mutation: { 29 | createPost: async (parent, { post }, context, info) => { 30 | const newPost = await new Post({ 31 | title: post.title, 32 | body: post.body, 33 | published: post.published, 34 | author: post.author, 35 | date: post.date 36 | }); 37 | let createdPost; 38 | try { 39 | // const result = await newPost.save(); 40 | const result = await new Promise((resolve, reject) => { 41 | newPost.save((err, res) => { 42 | err ? reject(err) : resolve(res); 43 | }); 44 | }); 45 | createdPost = transformPost(result); 46 | const creator = await User.findById(post.author); 47 | 48 | if (!creator) { 49 | throw new Error("User not found."); 50 | } 51 | creator.posts.push(newPost); 52 | await creator.save(); 53 | return createdPost; 54 | } catch (error) { 55 | console.log(error); 56 | throw error; 57 | } 58 | }, 59 | updatePost: async (parent, { _id, post }, context, info) => { 60 | return new Promise((resolve, reject) => { 61 | Post.findByIdAndUpdate(_id, { $set: { ...post } }, { new: true }).exec( 62 | (err, res) => { 63 | err ? reject(err) : resolve(res); 64 | } 65 | ); 66 | }); 67 | }, 68 | deletePost: async (parent, { _id }, context, info) => { 69 | try { 70 | // searching for creator of the post and deleting it from the list 71 | const post = await Post.findById(_id); 72 | const creator = await User.findById(post.author); 73 | if (!creator) { 74 | throw new Error("user not found."); 75 | } 76 | const index = creator.posts.indexOf(_id); 77 | if (index > -1) { 78 | creator.posts.splice(index, 1); 79 | } 80 | await creator.save(); 81 | return new Promise((resolve, reject) => { 82 | Post.findByIdAndDelete(_id).exec((err, res) => { 83 | err ? reject(err) : resolve(res); 84 | }); 85 | }); 86 | } catch (error) { 87 | console.log(error); 88 | throw error; 89 | } 90 | } 91 | }, 92 | Subscription: { 93 | post: { 94 | subscribe: (parent, args, { pubsub }) => { 95 | //return pubsub.asyncIterator(channel) 96 | } 97 | } 98 | }, 99 | Post: { 100 | author: async ({ author }, args, context, info) => { 101 | return await User.findById(author); 102 | }, 103 | comments: async ({ author }, args, context, info) => { 104 | return await Comment.find({ author }); 105 | } 106 | } 107 | }; 108 | --------------------------------------------------------------------------------