├── client ├── style │ └── style.css ├── index.js └── index.html ├── .gitignore ├── .babelrc ├── server ├── models │ ├── index.js │ ├── lyric.js │ └── song.js ├── schema │ ├── schema.js │ ├── song_type.js │ ├── lyric_type.js │ ├── root_query_type.js │ └── mutations.js └── server.js ├── index.js ├── README.md ├── webpack.config.js └── package.json /client/style/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_STORE 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /server/models/index.js: -------------------------------------------------------------------------------- 1 | require('./song'); 2 | require('./lyric'); 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const app = require('./server/server'); 2 | 3 | app.listen(4000, () => { 4 | console.log('Listening'); 5 | }); 6 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | const Root = () => { 5 | return
Lyrical
6 | }; 7 | 8 | ReactDOM.render( 9 | , 10 | document.querySelector('#root') 11 | ); 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lyrical-GraphQL 2 | 3 | Starter project from a GraphQL course on Udemy.com 4 | 5 | ### Setup 6 | 7 | - Run `npm install --legacy-peer-deps` in the root of the project to install dependencies 8 | - Access the application at `localhost:4000` in your browser 9 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | -------------------------------------------------------------------------------- /server/schema/schema.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const graphql = require('graphql'); 3 | const { GraphQLSchema } = graphql; 4 | 5 | const RootQueryType = require('./root_query_type'); 6 | const mutations = require('./mutations'); 7 | 8 | module.exports = new GraphQLSchema({ 9 | query: RootQueryType, 10 | mutation: mutations 11 | }); 12 | -------------------------------------------------------------------------------- /server/models/lyric.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const LyricSchema = new Schema({ 5 | song: { 6 | type: Schema.Types.ObjectId, 7 | ref: 'song' 8 | }, 9 | likes: { type: Number, default: 0 }, 10 | content: { type: String } 11 | }); 12 | 13 | LyricSchema.statics.like = function(id) { 14 | const Lyric = mongoose.model('lyric'); 15 | 16 | return Lyric.findById(id) 17 | .then(lyric => { 18 | ++lyric.likes; 19 | return lyric.save(); 20 | }) 21 | } 22 | 23 | mongoose.model('lyric', LyricSchema); 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './client/index.js', 6 | output: { 7 | path: '/', 8 | filename: 'bundle.js' 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | use: 'babel-loader', 14 | test: /\.js$/, 15 | exclude: /node_modules/ 16 | }, 17 | { 18 | use: ['style-loader', 'css-loader'], 19 | test: /\.css$/ 20 | } 21 | ] 22 | }, 23 | plugins: [ 24 | new HtmlWebpackPlugin({ 25 | template: 'client/index.html' 26 | }) 27 | ] 28 | }; 29 | -------------------------------------------------------------------------------- /server/schema/song_type.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const graphql = require('graphql'); 3 | const { GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList } = graphql; 4 | const LyricType = require('./lyric_type'); 5 | const Song = mongoose.model('song'); 6 | 7 | const SongType = new GraphQLObjectType({ 8 | name: 'SongType', 9 | fields: () => ({ 10 | id: { type: GraphQLID }, 11 | title: { type: GraphQLString }, 12 | lyrics: { 13 | type: new GraphQLList(LyricType), 14 | resolve(parentValue) { 15 | return Song.findLyrics(parentValue.id); 16 | } 17 | } 18 | }) 19 | }); 20 | 21 | module.exports = SongType; 22 | -------------------------------------------------------------------------------- /server/schema/lyric_type.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const graphql = require('graphql'); 3 | const { 4 | GraphQLObjectType, 5 | GraphQLList, 6 | GraphQLID, 7 | GraphQLInt, 8 | GraphQLString 9 | } = graphql; 10 | const Lyric = mongoose.model('lyric'); 11 | 12 | const LyricType = new GraphQLObjectType({ 13 | name: 'LyricType', 14 | fields: () => ({ 15 | id: { type: GraphQLID }, 16 | likes: { type: GraphQLInt }, 17 | content: { type: GraphQLString }, 18 | song: { 19 | type: require('./song_type'), 20 | resolve(parentValue) { 21 | return Lyric.findById(parentValue).populate('song') 22 | .then(lyric => { 23 | console.log(lyric) 24 | return lyric.song 25 | }); 26 | } 27 | } 28 | }) 29 | }); 30 | 31 | module.exports = LyricType; 32 | -------------------------------------------------------------------------------- /server/models/song.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const SongSchema = new Schema({ 5 | title: { type: String }, 6 | user: { 7 | type: Schema.Types.ObjectId, 8 | ref: 'user' 9 | }, 10 | lyrics: [{ 11 | type: Schema.Types.ObjectId, 12 | ref: 'lyric' 13 | }] 14 | }); 15 | 16 | SongSchema.statics.addLyric = function(id, content) { 17 | const Lyric = mongoose.model('lyric'); 18 | 19 | return this.findById(id) 20 | .then(song => { 21 | const lyric = new Lyric({ content, song }) 22 | song.lyrics.push(lyric) 23 | return Promise.all([lyric.save(), song.save()]) 24 | .then(([lyric, song]) => song); 25 | }); 26 | } 27 | 28 | SongSchema.statics.findLyrics = function(id) { 29 | return this.findById(id) 30 | .populate('lyrics') 31 | .then(song => song.lyrics); 32 | } 33 | 34 | mongoose.model('song', SongSchema); 35 | -------------------------------------------------------------------------------- /server/schema/root_query_type.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const graphql = require('graphql'); 3 | const { GraphQLObjectType, GraphQLList, GraphQLID, GraphQLNonNull } = graphql; 4 | const SongType = require('./song_type'); 5 | const LyricType = require('./lyric_type'); 6 | const Lyric = mongoose.model('lyric'); 7 | const Song = mongoose.model('song'); 8 | 9 | const RootQuery = new GraphQLObjectType({ 10 | name: 'RootQueryType', 11 | fields: () => ({ 12 | songs: { 13 | type: new GraphQLList(SongType), 14 | resolve() { 15 | return Song.find({}); 16 | } 17 | }, 18 | song: { 19 | type: SongType, 20 | args: { id: { type: new GraphQLNonNull(GraphQLID) } }, 21 | resolve(parentValue, { id }) { 22 | return Song.findById(id); 23 | } 24 | }, 25 | lyric: { 26 | type: LyricType, 27 | args: { id: { type: new GraphQLNonNull(GraphQLID) } }, 28 | resolve(parnetValue, { id }) { 29 | return Lyric.findById(id); 30 | } 31 | } 32 | }) 33 | }); 34 | 35 | module.exports = RootQuery; 36 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const models = require('./models'); 3 | const expressGraphQL = require('express-graphql'); 4 | const mongoose = require('mongoose'); 5 | const bodyParser = require('body-parser'); 6 | const schema = require('./schema/schema'); 7 | 8 | const app = express(); 9 | 10 | // Replace with your Mongo Atlas URI 11 | const MONGO_URI = ''; 12 | if (!MONGO_URI) { 13 | throw new Error('You must provide a Mongo Atlas URI'); 14 | } 15 | 16 | mongoose.Promise = global.Promise; 17 | mongoose.connect(MONGO_URI); 18 | mongoose.connection 19 | .once('open', () => console.log('Connected to Mongo Atlas instance.')) 20 | .on('error', (error) => 21 | console.log('Error connecting to Mongo Atlas:', error) 22 | ); 23 | 24 | app.use(bodyParser.json()); 25 | app.use( 26 | '/graphql', 27 | expressGraphQL({ 28 | schema, 29 | graphiql: true 30 | }) 31 | ); 32 | 33 | const webpackMiddleware = require('webpack-dev-middleware'); 34 | const webpack = require('webpack'); 35 | const webpackConfig = require('../webpack.config.js'); 36 | app.use(webpackMiddleware(webpack(webpackConfig))); 37 | 38 | module.exports = app; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lyrical", 3 | "version": "1.0.0", 4 | "description": "Starter point for a graphQL course", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/StephenGrider/Lyrical-GraphQL" 9 | }, 10 | "scripts": { 11 | "dev": "nodemon index.js --ignore client" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "apollo-client": "^0.8.1", 17 | "axios": "^0.15.3", 18 | "babel-core": "^6.22.1", 19 | "babel-loader": "^6.2.10", 20 | "babel-preset-env": "^1.1.8", 21 | "babel-preset-react": "^6.22.0", 22 | "body-parser": "^1.16.0", 23 | "connect-mongo": "^1.3.2", 24 | "css-loader": "^0.26.1", 25 | "express": "^4.14.0", 26 | "express-graphql": "0.6.1", 27 | "express-session": "^1.15.0", 28 | "graphql": "^0.8.2", 29 | "html-webpack-plugin": "^2.26.0", 30 | "lodash": "^4.17.4", 31 | "mongoose": "^7.3.1", 32 | "nodemon": "^2.0.22", 33 | "passport": "^0.3.2", 34 | "passport-local": "^1.0.0", 35 | "react": "^15.4.2", 36 | "react-apollo": "^0.9.0", 37 | "react-dom": "^15.4.2", 38 | "react-router": "^3.0.2", 39 | "style-loader": "^0.13.1", 40 | "webpack": "^2.2.0", 41 | "webpack-dev-middleware": "^1.9.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /server/schema/mutations.js: -------------------------------------------------------------------------------- 1 | const graphql = require('graphql'); 2 | const { GraphQLObjectType, GraphQLString, GraphQLID } = graphql; 3 | const mongoose = require('mongoose'); 4 | const Song = mongoose.model('song'); 5 | const Lyric = mongoose.model('lyric'); 6 | const SongType = require('./song_type'); 7 | const LyricType = require('./lyric_type'); 8 | 9 | const mutation = new GraphQLObjectType({ 10 | name: 'Mutation', 11 | fields: { 12 | addSong: { 13 | type: SongType, 14 | args: { 15 | title: { type: GraphQLString } 16 | }, 17 | resolve(parentValue, { title }) { 18 | return new Song({ title }).save(); 19 | } 20 | }, 21 | addLyricToSong: { 22 | type: SongType, 23 | args: { 24 | content: { type: GraphQLString }, 25 | songId: { type: GraphQLID } 26 | }, 27 | resolve(parentValue, { content, songId }) { 28 | return Song.addLyric(songId, content); 29 | } 30 | }, 31 | likeLyric: { 32 | type: LyricType, 33 | args: { id: { type: GraphQLID } }, 34 | resolve(parentValue, { id }) { 35 | return Lyric.like(id); 36 | } 37 | }, 38 | deleteSong: { 39 | type: SongType, 40 | args: { id: { type: GraphQLID } }, 41 | resolve(parentValue, { id }) { 42 | return Song.findByIdAndRemove(id); 43 | } 44 | } 45 | } 46 | }); 47 | 48 | module.exports = mutation; 49 | --------------------------------------------------------------------------------