├── .babelrc ├── .gitignore ├── Dockerfile ├── README.md ├── app.js ├── docker-compose.yml ├── graphql ├── get-projection.js ├── index.js ├── mutations │ ├── blog-post │ │ ├── add.js │ │ ├── index.js │ │ ├── remove-all.js │ │ └── remove.js │ ├── comment │ │ ├── add.js │ │ ├── index.js │ │ ├── remove-all.js │ │ └── remove.js │ └── index.js ├── queries │ ├── blog-post │ │ ├── index.js │ │ ├── mutiple.js │ │ └── single.js │ ├── comment │ │ ├── index.js │ │ ├── mutiple.js │ │ └── single.js │ └── index.js └── types │ ├── blog-post-input.js │ ├── blog-post.js │ ├── comment-input.js │ └── comment.js ├── index.js ├── models ├── blog-post.js └── comment.js └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Config 2 | .relaxrc 3 | 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # Commenting this out is preferred by some people, see 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 28 | node_modules 29 | 30 | # Users Environment Variables 31 | .lock-wscript 32 | 33 | /public/ 34 | dist/ 35 | 36 | .idea 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.6.0-slim 2 | MAINTAINER Eric Irwin 3 | 4 | # Expose the default port 5 | EXPOSE 8080 6 | 7 | # Create/Set the working directory 8 | RUN mkdir /app 9 | WORKDIR /app 10 | 11 | COPY package.json /app/package.json 12 | RUN npm install 13 | 14 | # Copy App 15 | COPY . /app 16 | 17 | # Set Command 18 | CMD npm start 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL-MongoDB Server 2 | 3 | GraphQL server in Node.js using Express, MongoDB and Mongoose. 4 | 5 | ## Running with Docker 6 | You can use `docker-compose up` to run an instance of GraphQL-MongoDB Server and MongoDB locally. 7 | 8 | ```bash 9 | docker-compose up 10 | ``` 11 | 12 | ## Requirements 13 | 14 | * [Node.js](http://nodejs.org/) 15 | * [MongoDB](https://www.mongodb.org/) 16 | 17 | ## License 18 | 19 | The MIT License (MIT) Copyright (c) 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import graphqlHTTP from 'express-graphql'; 3 | import mongoose from 'mongoose'; 4 | 5 | import schema from './graphql'; 6 | 7 | var app = express(); 8 | 9 | // GraphqQL server route 10 | app.use('/graphql', graphqlHTTP(req => ({ 11 | schema, 12 | pretty: true, 13 | graphiql: true 14 | }))); 15 | 16 | // Connect mongo database 17 | mongoose.connect('mongodb://mongo/graphql'); 18 | 19 | // start server 20 | var server = app.listen(8080, () => { 21 | console.log('Listening at port', server.address().port); 22 | }); 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | web: 4 | build: 5 | context: . 6 | links: 7 | - mongo 8 | ports: 9 | - "8080:8080" 10 | command: npm run start 11 | mongo: 12 | image: mongo 13 | -------------------------------------------------------------------------------- /graphql/get-projection.js: -------------------------------------------------------------------------------- 1 | export default function getProjection (fieldASTs) { 2 | return fieldASTs.selectionSet.selections.reduce((projections, selection) => { 3 | projections[selection.name.value] = 1; 4 | return projections; 5 | }, {}); 6 | } 7 | -------------------------------------------------------------------------------- /graphql/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLObjectType, 3 | GraphQLSchema 4 | } from 'graphql'; 5 | 6 | import mutations from './mutations'; 7 | import queries from './queries'; 8 | 9 | export default new GraphQLSchema({ 10 | query: new GraphQLObjectType({ 11 | name: 'Query', 12 | fields: queries 13 | }), 14 | mutation: new GraphQLObjectType({ 15 | name: 'Mutation', 16 | fields: mutations 17 | }) 18 | }); 19 | -------------------------------------------------------------------------------- /graphql/mutations/blog-post/add.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLNonNull, 3 | GraphQLBoolean 4 | } from 'graphql'; 5 | 6 | import blogPostInputType from '../../types/blog-post-input'; 7 | import BlogPostModel from '../../../models/blog-post'; 8 | 9 | export default { 10 | type: GraphQLBoolean, 11 | args: { 12 | data: { 13 | name: 'data', 14 | type: new GraphQLNonNull(blogPostInputType) 15 | } 16 | }, 17 | async resolve (root, params, options) { 18 | const blogPostModel = new BlogPostModel(params.data); 19 | const newBlogPost = await blogPostModel.save(); 20 | 21 | if (!newBlogPost) { 22 | throw new Error('Error adding new blog post'); 23 | } 24 | return true; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /graphql/mutations/blog-post/index.js: -------------------------------------------------------------------------------- 1 | import addBlogPost from './add'; 2 | import removeAllBlogPosts from './remove-all'; 3 | import removeBlogPost from './remove'; 4 | 5 | export default { 6 | addBlogPost, 7 | removeBlogPost, 8 | removeAllBlogPosts 9 | }; 10 | -------------------------------------------------------------------------------- /graphql/mutations/blog-post/remove-all.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLBoolean 3 | } from 'graphql'; 4 | 5 | import BlogPostModel from '../../../models/blog-post'; 6 | 7 | export default { 8 | type: GraphQLBoolean, 9 | resolve (root, params, options) { 10 | return BlogPostModel 11 | .remove({}) 12 | .exec(); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /graphql/mutations/blog-post/remove.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLNonNull, 3 | GraphQLID 4 | } from 'graphql'; 5 | 6 | import blogPostType from '../../types/blog-post'; 7 | import getProjection from '../../get-projection'; 8 | import BlogPostModel from '../../../models/blog-post'; 9 | 10 | export default { 11 | type: blogPostType, 12 | args: { 13 | _id: { 14 | name: '_id', 15 | type: new GraphQLNonNull(GraphQLID) 16 | } 17 | }, 18 | async resolve (root, params, options) { 19 | const projection = getProjection(options.fieldASTs[0]); 20 | const removedBlogPost = await BlogPostModel 21 | .findByIdAndRemove(params._id, { 22 | select: projection 23 | }) 24 | .exec(); 25 | 26 | if (!removedBlogPost) { 27 | throw new Error('Error removing blog post'); 28 | } 29 | 30 | return removedBlogPost; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /graphql/mutations/comment/add.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLNonNull, 3 | GraphQLBoolean 4 | } from 'graphql'; 5 | 6 | import commentInputType from '../../types/comment-input'; 7 | import CommentModel from '../../../models/comment'; 8 | 9 | export default { 10 | type: GraphQLBoolean, 11 | args: { 12 | data: { 13 | name: 'data', 14 | type: new GraphQLNonNull(commentInputType) 15 | } 16 | }, 17 | async resolve (root, params, options) { 18 | const commentModel = new CommentModel(params.data); 19 | const newComment = await commentModel.save(); 20 | 21 | if (!newComment) { 22 | throw new Error('Error adding new comment'); 23 | } 24 | return true; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /graphql/mutations/comment/index.js: -------------------------------------------------------------------------------- 1 | import addComment from './add'; 2 | import removeAllComments from './remove-all'; 3 | import removeComment from './remove'; 4 | 5 | export default { 6 | addComment, 7 | removeComment, 8 | removeAllComments 9 | }; 10 | -------------------------------------------------------------------------------- /graphql/mutations/comment/remove-all.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLBoolean 3 | } from 'graphql'; 4 | 5 | import CommentModel from '../../../models/comment'; 6 | 7 | export default { 8 | type: GraphQLBoolean, 9 | resolve (root, params, options) { 10 | return CommentModel 11 | .remove() 12 | .exec(); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /graphql/mutations/comment/remove.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLNonNull, 3 | GraphQLID 4 | } from 'graphql'; 5 | 6 | import commentType from '../../types/comment'; 7 | import getProjection from '../../get-projection'; 8 | import CommentModel from '../../../models/comment'; 9 | 10 | export default { 11 | type: commentType, 12 | args: { 13 | _id: { 14 | name: '_id', 15 | type: new GraphQLNonNull(GraphQLID) 16 | } 17 | }, 18 | async resolve (root, params, options) { 19 | const projection = getProjection(options.fieldASTs[0]); 20 | const removedComment = await CommentModel 21 | .findByIdAndRemove(params._id, { 22 | select: projection 23 | }) 24 | .exec(); 25 | 26 | if (!removedComment) { 27 | throw new Error('Error removing blog post'); 28 | } 29 | 30 | return removedComment; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /graphql/mutations/index.js: -------------------------------------------------------------------------------- 1 | import blogPost from './blog-post'; 2 | import comment from './comment'; 3 | 4 | export default { 5 | ...blogPost, 6 | ...comment 7 | }; 8 | -------------------------------------------------------------------------------- /graphql/queries/blog-post/index.js: -------------------------------------------------------------------------------- 1 | import blogPost from './single'; 2 | import blogPosts from './mutiple'; 3 | 4 | export default { 5 | blogPost, 6 | blogPosts 7 | }; 8 | -------------------------------------------------------------------------------- /graphql/queries/blog-post/mutiple.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLList 3 | } from 'graphql'; 4 | 5 | import blogPostType from '../../types/blog-post'; 6 | import getProjection from '../../get-projection'; 7 | import BlogPostModel from '../../../models/blog-post'; 8 | 9 | export default { 10 | type: new GraphQLList(blogPostType), 11 | args: {}, 12 | resolve (root, params, options) { 13 | const projection = getProjection(options.fieldASTs[0]); 14 | 15 | return BlogPostModel 16 | .find() 17 | .select(projection) 18 | .exec(); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /graphql/queries/blog-post/single.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLList, 3 | GraphQLID, 4 | GraphQLNonNull 5 | } from 'graphql'; 6 | import {Types} from 'mongoose'; 7 | 8 | import blogPostType from '../../types/blog-post'; 9 | import getProjection from '../../get-projection'; 10 | import BlogPostModel from '../../../models/blog-post'; 11 | 12 | export default { 13 | type: blogPostType, 14 | args: { 15 | id: { 16 | name: 'id', 17 | type: new GraphQLNonNull(GraphQLID) 18 | } 19 | }, 20 | resolve (root, params, options) { 21 | const projection = getProjection(options.fieldASTs[0]); 22 | 23 | return BlogPostModel 24 | .findById(params.id) 25 | .select(projection) 26 | .exec(); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /graphql/queries/comment/index.js: -------------------------------------------------------------------------------- 1 | import comment from './single'; 2 | import comments from './mutiple'; 3 | 4 | export default { 5 | comment, 6 | comments 7 | }; 8 | -------------------------------------------------------------------------------- /graphql/queries/comment/mutiple.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLList, 3 | GraphQLNonNull, 4 | GraphQLID 5 | } from 'graphql'; 6 | 7 | import commentType from '../../types/comment'; 8 | import getProjection from '../../get-projection'; 9 | import CommentModel from '../../../models/comment'; 10 | 11 | export default { 12 | type: new GraphQLList(commentType), 13 | args: { 14 | postId: { 15 | name: 'postId', 16 | type: new GraphQLNonNull(GraphQLID) 17 | } 18 | }, 19 | resolve (root, params, options) { 20 | const projection = getProjection(options.fieldASTs[0]); 21 | 22 | return CommentModel 23 | .find({ 24 | postId: params.postId 25 | }) 26 | .select(projection) 27 | .exec(); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /graphql/queries/comment/single.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLList, 3 | GraphQLID, 4 | GraphQLNonNull 5 | } from 'graphql'; 6 | import {Types} from 'mongoose'; 7 | 8 | import commentType from '../../types/comment'; 9 | import getProjection from '../../get-projection'; 10 | import CommentModel from '../../../models/comment'; 11 | 12 | export default { 13 | type: commentType, 14 | args: { 15 | id: { 16 | name: 'id', 17 | type: new GraphQLNonNull(GraphQLID) 18 | } 19 | }, 20 | resolve (root, params, options) { 21 | const projection = getProjection(options.fieldASTs[0]); 22 | 23 | return CommentModel 24 | .findById(params.id) 25 | .select(projection) 26 | .exec(); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /graphql/queries/index.js: -------------------------------------------------------------------------------- 1 | import blogPost from './blog-post'; 2 | import comment from './comment'; 3 | 4 | export default { 5 | ...blogPost, 6 | ...comment 7 | }; 8 | -------------------------------------------------------------------------------- /graphql/types/blog-post-input.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLInputObjectType, 3 | GraphQLString, 4 | GraphQLID 5 | } from 'graphql'; 6 | 7 | export default new GraphQLInputObjectType({ 8 | name: 'BlogPostInput', 9 | fields: { 10 | _id: {type: GraphQLID}, 11 | title: {type: GraphQLString}, 12 | description: {type: GraphQLString} 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /graphql/types/blog-post.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLObjectType, 3 | GraphQLNonNull, 4 | GraphQLString, 5 | GraphQLID 6 | } from 'graphql'; 7 | 8 | export default new GraphQLObjectType({ 9 | name: 'BlogPost', 10 | fields: { 11 | _id: { 12 | type: new GraphQLNonNull(GraphQLID) 13 | }, 14 | title: { 15 | type: GraphQLString 16 | }, 17 | description: { 18 | type: GraphQLString 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /graphql/types/comment-input.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLInputObjectType, 3 | GraphQLString, 4 | GraphQLID, 5 | GraphQLNonNull 6 | } from 'graphql'; 7 | 8 | export default new GraphQLInputObjectType({ 9 | name: 'CommentInput', 10 | fields: { 11 | _id: { 12 | type: new GraphQLNonNull(GraphQLID) 13 | }, 14 | postId: { 15 | type: new GraphQLNonNull(GraphQLID) 16 | }, 17 | text: { 18 | type: new GraphQLNonNull(GraphQLString) 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /graphql/types/comment.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLObjectType, 3 | GraphQLNonNull, 4 | GraphQLString, 5 | GraphQLID 6 | } from 'graphql'; 7 | 8 | export default new GraphQLObjectType({ 9 | name: 'Comment', 10 | fields: { 11 | _id: { 12 | type: new GraphQLNonNull(GraphQLID) 13 | }, 14 | postId: { 15 | type: new GraphQLNonNull(GraphQLID) 16 | }, 17 | text: { 18 | type: GraphQLString 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('babel/register'); 2 | require('./app'); 3 | -------------------------------------------------------------------------------- /models/blog-post.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | var blogPostSchema = new mongoose.Schema({ 4 | title: { 5 | type: String, 6 | required: true 7 | }, 8 | description: { 9 | type: String 10 | } 11 | }); 12 | 13 | export default mongoose.model('BlogPost', blogPostSchema); 14 | -------------------------------------------------------------------------------- /models/comment.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | var commentSchema = new mongoose.Schema({ 4 | postId: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | required: true 7 | }, 8 | text: { 9 | type: String, 10 | required: true 11 | } 12 | }); 13 | 14 | export default mongoose.model('Comment', commentSchema); 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphqlnode", 3 | "version": "0.0.0", 4 | "description": "Introduction to GraphQL in nodeJS", 5 | "license": "GPL", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/bruno12mota/graphql-nodejs" 9 | }, 10 | "scripts": { 11 | "start": "node index.js" 12 | }, 13 | "dependencies": { 14 | "babel": "^5.8.23", 15 | "express": "^4.13.3", 16 | "express-graphql": "^0.4.5", 17 | "graphql": "^0.4.14", 18 | "mongoose": "^4.3.5" 19 | } 20 | } 21 | --------------------------------------------------------------------------------