GraphQL works with Mongoose

This commit is contained in:
Riccardo
2021-01-03 10:31:08 +01:00
parent c752e3ec80
commit b5129342e3
17 changed files with 3588 additions and 261 deletions

View File

@@ -1,29 +1,10 @@
import React from 'react'; import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { setContext } from '@apollo/client/link/context';
import { AUTH_TOKEN } from './constants';
import { split } from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
// import AppointmentList from './components/AppointmentList';
// class App extends Component {
// render() {
// return <AppointmentList />;
// }
// }
// export default App;
// import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './styles/index.css'; import './styles/index.css';
import './styles/tachyons.min.css'
import App from './components/App'; import App from './components/App';
// // import * as serviceWorker from './serviceWorker'; // import * as serviceWorker from './serviceWorker';
// // 1 // 1
import { import {
ApolloProvider, ApolloProvider,
ApolloClient, ApolloClient,
@@ -36,57 +17,112 @@ const httpLink = createHttpLink({
uri: 'http://localhost:4000' uri: 'http://localhost:4000'
}); });
// attach the auth_token to all requests to GraphQL server
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem(AUTH_TOKEN);
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ''
}
};
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true,
connectionParams: {
authToken: localStorage.getItem(AUTH_TOKEN)
}
}
});
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return (
kind === 'OperationDefinition' &&
operation === 'subscription'
);
},
wsLink,
authLink.concat(httpLink)
);
// 3 // 3
const client = new ApolloClient({ const client = new ApolloClient({
link, link: httpLink,
cache: new InMemoryCache() cache: new InMemoryCache()
}); });
// 4 // 4
ReactDOM.render( ReactDOM.render(
<BrowserRouter>
<ApolloProvider client={client}> <ApolloProvider client={client}>
<App /> <App />
</ApolloProvider> </ApolloProvider>,
</BrowserRouter>,
document.getElementById('root') document.getElementById('root')
); );
// serviceWorker.unregister(); // serviceWorker.unregister();
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // import React from 'react';
// reportWebVitals(); // import { BrowserRouter } from 'react-router-dom';
// import { setContext } from '@apollo/client/link/context';
// import { AUTH_TOKEN } from './constants';
// import { split } from '@apollo/client';
// import { WebSocketLink } from '@apollo/client/link/ws';
// import { getMainDefinition } from '@apollo/client/utilities';
// // import AppointmentList from './components/AppointmentList';
// // class App extends Component {
// // render() {
// // return <AppointmentList />;
// // }
// // }
// // export default App;
// // import React from 'react';
// import ReactDOM from 'react-dom';
// import './styles/index.css';
// import './styles/tachyons.min.css'
// import App from './components/App';
// // // import * as serviceWorker from './serviceWorker';
// // // 1
// import {
// ApolloProvider,
// ApolloClient,
// createHttpLink,
// InMemoryCache
// } from '@apollo/client';
// // 2
// const httpLink = createHttpLink({
// uri: 'http://localhost:4000'
// });
// // attach the auth_token to all requests to GraphQL server
// const authLink = setContext((_, { headers }) => {
// const token = localStorage.getItem(AUTH_TOKEN);
// return {
// headers: {
// ...headers,
// authorization: token ? `Bearer ${token}` : ''
// }
// };
// });
// const wsLink = new WebSocketLink({
// uri: `ws://localhost:4000/graphql`,
// options: {
// reconnect: true,
// connectionParams: {
// authToken: localStorage.getItem(AUTH_TOKEN)
// }
// }
// });
// const link = split(
// ({ query }) => {
// const { kind, operation } = getMainDefinition(query);
// return (
// kind === 'OperationDefinition' &&
// operation === 'subscription'
// );
// },
// wsLink,
// authLink.concat(httpLink)
// );
// // 3
// const client = new ApolloClient({
// link,
// cache: new InMemoryCache()
// });
// // 4
// ReactDOM.render(
// <BrowserRouter>
// <ApolloProvider client={client}>
// <App />
// </ApolloProvider>
// </BrowserRouter>,
// document.getElementById('root')
// );
// // serviceWorker.unregister();
// // If you want to start measuring performance in your app, pass a function
// // to log results (for example: reportWebVitals(console.log))
// // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// // reportWebVitals();

12
server/.babelrc Normal file
View File

@@ -0,0 +1,12 @@
{
"presets": [
[
"env",
{
"targets": {
"node": "current"
}
}
]
]
}

6
server/.babelrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true
}

48
server/.eslintrc.json Normal file
View File

@@ -0,0 +1,48 @@
{
"plugins": [
"babel"
],
"extends": [
"eslint:recommended"
],
"rules": {
"no-console": 0,
"no-mixed-spaces-and-tabs": 1,
"comma-dangle": 0,
"no-unused-vars": 1,
"eqeqeq": [
2,
"smart"
],
"no-useless-concat": 2,
"default-case": 2,
"no-self-compare": 2,
"prefer-const": 2,
"object-shorthand": 1,
"array-callback-return": 2,
"valid-typeof": 2,
"arrow-body-style": 2,
"require-await": 2,
"react/prop-types": 0,
"no-var": 2,
"linebreak-style": [
2,
"unix"
],
"semi": [
1,
"always"
]
},
"env": {
"node": true
},
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2018,
"ecmaFeatures": {
"modules": true
}
}
}

5
server/husky.json Normal file
View File

@@ -0,0 +1,5 @@
"husky": {
"hooks": {
"pre-commit": "yarn lint"
}
}

View File

@@ -2,29 +2,53 @@
"name": "server", "name": "server",
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"type": "module",
"scripts": { "scripts": {
"start": "node src/index.js", "start": "node src/index.js",
"dev": "nodemon src/index.js" "dev": "nodemon src/index.js"
}, },
"dependencies": { "dependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/node": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"apollo-engine": "^1.1.2",
"apollo-server": "^2.19.0", "apollo-server": "^2.19.0",
"apollo-server-express": "^2.19.1", "apollo-server-express": "^2.19.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"chai": "^4.2.0", "chai": "^4.2.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^8.2.0",
"esm": "^3.2.25",
"express": "^4.17.1", "express": "^4.17.1",
"express-graphql": "^0.12.0", "express-graphql": "^0.12.0",
"graphql": "^14.7.0", "graphql": "^15.4.0",
"graphql-compose": "^7.23.0",
"graphql-compose-connection": "^8.0.1",
"graphql-compose-mongoose": "^9.0.0",
"graphql-middleware": "^6.0.0",
"graphql-tools": "^7.0.2", "graphql-tools": "^7.0.2",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
"migrate": "^1.7.0", "migrate": "^1.7.0",
"mocha": "^8.2.1", "mocha": "^8.2.1",
"mongodb": "^3.6.3", "mongodb": "^3.6.3",
"mongoose": "^5.11.9", "mongoose": "^5.11.9",
"mongoose-bcrypt": "^1.9.0",
"mongoose-timestamp": "^0.6.0",
"node-migrate": "^0.1.0" "node-migrate": "^0.1.0"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^2.0.6" "babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"babel-preset-env": "^1.7.0",
"eslint": "^7.17.0",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"fs-extra": "^9.0.1",
"nodemon": "^2.0.6",
"prettier": "^2.2.1"
} }
} }

10
server/scripts.js Normal file
View File

@@ -0,0 +1,10 @@
"scripts": {
"build": "babel src --out-dir dist",
"start": "node dist/index.js",
"dev": "nodemon --exec npx babel-node src/index.js",
"prettier": "prettier --config ./.prettierrc --write \"**/*.js\"",
"pretest": "eslint --ignore-path .gitignore .",
"postinstall": "rm -rf dist && yarn run build",
"lint": "yarn prettier --write --check --config ./.prettierrc \"**/*.js\" && eslint --fix ./src",
"release": "release-it patch --no-npm.publish"
}

View File

@@ -1,115 +1,159 @@
const { ApolloServer, PubSub } = require('apollo-server');
// const { Cors } = require('cors');
// const { Express } = require('express');
const express = require("express"); import dotenv from 'dotenv';
const { graphqlHTTP } = require('express-graphql'); import express from 'express';
const mongoose = require("mongoose"); // const express = require("express");
const graphqlSchema = require("./graphql/schema/schema") // const { ApolloServer, PubSub } = require('apollo-server');
const appointmentResolvers = require("./graphql/resolvers/appointment") import { ApolloServer } from 'apollo-server-express';
const userResolvers = require("./graphql/resolvers/user") // const mongoose = require("mongoose");
import mongoose from 'mongoose';
var MongoClient = require('mongodb', { useUnifiedTopology: true }).MongoClient; import './utils/db.js';
// import { MongoClient } from 'mongodb' // require('./utils/db')
const Query = require('./resolvers/Query'); import schema from './schema/index.js';
const Mutation = require('./resolvers/Mutation'); // require('./schema')
const Subscription = require('./resolvers/Subscription');
const User = require('./resolvers/User');
const Appointment = require('./resolvers/Appointment');
const Follow = require('./resolvers/Follow');
const fs = require('fs');
const path = require('path');
const { getUserId } = require('./utils');
const pubsub = new PubSub();
const app = express()
const graphqlResolvers = {
appointmentResolvers,
userResolvers
};
app.use(
"/graphql",
graphqlHTTP({
schema: graphqlSchema,
rootValue: appointmentResolvers,
graphiql: true,
})
)
const uri = `mongodb+srv://admin:hEbAjhvkrFDHAP3@cluster0.0hjtt.mongodb.net/Calendar?retryWrites=true&w=majority`
const options = { useNewUrlParser: true, useUnifiedTopology: true }
let db = mongoose
.connect(uri, options)
.then(() => app.listen(4000, console.log("Server is running")))
.catch(error => {
throw error
})
// const app = new Express(); dotenv.config();
// app.use(Cors());
// const mongo = new MongoClient({ const app = express();
// errorFormat: 'minimal'
// });
// const mongo = MongoClient.connect("mongodb+srv://admin:hEbAjhvkrFDHAP3@cluster0.0hjtt.mongodb.net/Calendar?retryWrites=true&w=majority", function (err, db) { const server = new ApolloServer({
schema,
cors: true,
playground: process.env.NODE_ENV === 'development' ? true : false,
introspection: true,
tracing: true,
path: '/',
});
// if (err) throw err; server.applyMiddleware({
// console.log("ALL good"); app,
// //Write databse Insert/Update/Query code here.. path: '/',
cors: true,
onHealthCheck: () =>
// eslint-disable-next-line no-undef
new Promise((resolve, reject) => {
if (mongoose.connection.readyState > 0) {
resolve();
} else {
reject();
}
}),
});
// }); app.listen({ port: process.env.PORT }, () => {
console.log(`🚀 Server listening on port ${process.env.PORT}`);
console.log(`😷 Health checks available at ${process.env.HEALTH_ENDPOINT}`);
});
// const dbClient = new MongoClient(
// 'mongodb+srv://admin:hEbAjhvkrFDHAP3@cluster0.0hjtt.mongodb.net/Calendar?retryWrites=true&w=majority',
// {
// useNewUrlParser: true,
// useUnifiedTopology: true,
// }
// )
// const resolvers = {
// Query,
// Mutation,
// Subscription,
// User,
// Appointment, // const { ApolloServer, PubSub } = require('apollo-server');
// Follow // // const { Cors } = require('cors');
// // const { Express } = require('express');
// const express = require("express");
// const { graphqlHTTP } = require('express-graphql');
// const mongoose = require("mongoose");
// const graphqlSchema = require("./graphql/schema/schema")
// const appointmentResolvers = require("./graphql/resolvers/appointment")
// const userResolvers = require("./graphql/resolvers/user")
// var MongoClient = require('mongodb', { useUnifiedTopology: true }).MongoClient;
// // import { MongoClient } from 'mongodb'
// const Query = require('./resolvers/Query');
// const Mutation = require('./resolvers/Mutation');
// const Subscription = require('./resolvers/Subscription');
// const User = require('./resolvers/User');
// const Appointment = require('./resolvers/Appointment');
// const Follow = require('./resolvers/Follow');
// const fs = require('fs');
// const path = require('path');
// const { getUserId } = require('./utils');
// const pubsub = new PubSub();
// const app = express()
// const graphqlResolvers = {
// appointmentResolvers,
// userResolvers
// }; // };
// let db;
// const server = new ApolloServer({ // app.use(
// typeDefs: fs.readFileSync( // "/graphql",
// path.join(__dirname, 'schema.graphql'), // graphqlHTTP({
// 'utf8' // schema: graphqlSchema,
// ), // rootValue: appointmentResolvers,
// resolvers, // graphiql: true,
// context: async ({ req }) => { // })
// if (!db) { // )
// try { // const uri = `mongodb+srv://admin:hEbAjhvkrFDHAP3@cluster0.0hjtt.mongodb.net/Calendar?retryWrites=true&w=majority`
// if (!dbClient.isConnected()) await dbClient.connect() // const options = { useNewUrlParser: true, useUnifiedTopology: true }
// mongo = dbClient.db('Calendar') // database name // let db = mongoose
// console.log(db); // .connect(uri, options)
// } catch (e) { // .then(() => app.listen(4000, console.log("Server is running")))
// console.log('--->error while connecting with graphql context (db)', e) // .catch(error => {
// } // throw error
// } // })
// // const app = new Express();
// // app.use(Cors());
// // const mongo = new MongoClient({
// // errorFormat: 'minimal'
// // });
// // const mongo = MongoClient.connect("mongodb+srv://admin:hEbAjhvkrFDHAP3@cluster0.0hjtt.mongodb.net/Calendar?retryWrites=true&w=majority", function (err, db) {
// // if (err) throw err;
// // console.log("ALL good");
// // //Write databse Insert/Update/Query code here..
// // });
// // const dbClient = new MongoClient(
// // 'mongodb+srv://admin:hEbAjhvkrFDHAP3@cluster0.0hjtt.mongodb.net/Calendar?retryWrites=true&w=majority',
// // {
// // useNewUrlParser: true,
// // useUnifiedTopology: true,
// // }
// // )
// // const resolvers = {
// // Query,
// // Mutation,
// // Subscription,
// // User,
// // Appointment,
// // Follow
// // };
// // let db;
// // const server = new ApolloServer({
// // typeDefs: fs.readFileSync(
// // path.join(__dirname, 'schema.graphql'),
// // 'utf8'
// // ),
// // resolvers,
// // context: async ({ req }) => {
// // if (!db) {
// // try {
// // if (!dbClient.isConnected()) await dbClient.connect()
// // mongo = dbClient.db('Calendar') // database name
// // console.log(db);
// // } catch (e) {
// // console.log('--->error while connecting with graphql context (db)', e)
// // }
// // }
// return {
// ...req,
// mongo,
// pubsub,
// userId:
// req && req.headers.authorization
// ? getUserId(req)
// : null
// }
// },
// // context: ({ req }) => {
// // return { // // return {
// // ...req, // // ...req,
// // mongo, // // mongo,
@@ -118,31 +162,42 @@ let db = mongoose
// // req && req.headers.authorization // // req && req.headers.authorization
// // ? getUserId(req) // // ? getUserId(req)
// // : null // // : null
// // }; // // }
// // }, // // },
// // subscriptions: { // // // context: ({ req }) => {
// // onConnect: (connectionParams) => { // // // return {
// // if (connectionParams.authToken) { // // // ...req,
// // return { // // // mongo,
// // mongo, // // // pubsub,
// // userId: getUserId( // // // userId:
// // null, // // // req && req.headers.authorization
// // connectionParams.authToken // // // ? getUserId(req)
// // ) // // // : null
// // }; // // // };
// // } else { // // // },
// // return { // // // subscriptions: {
// // mongo // // // onConnect: (connectionParams) => {
// // }; // // // if (connectionParams.authToken) {
// // } // // // return {
// // } // // // mongo,
// // } // // // userId: getUserId(
// }); // // // null,
// // // connectionParams.authToken
// // // )
// // // };
// // // } else {
// // // return {
// // // mongo
// // // };
// // // }
// // // }
// // // }
// // });
// // server.applyMiddleware({ app }); // // // server.applyMiddleware({ app });
// server // // server
// .listen() // // .listen()
// .then(({ url }) => // // .then(({ url }) =>
// console.log(`Server is running on ${url}`) // // console.log(`Server is running on ${url}`)
// ); // // );

34
server/src/models/task.js Normal file
View File

@@ -0,0 +1,34 @@
import mongoose from 'mongoose';
const { Schema } = mongoose;
import timestamps from 'mongoose-timestamp';
import { composeWithMongoose } from 'graphql-compose-mongoose';
export const TaskSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
task: {
type: String,
trim: true,
required: true,
},
description: {
type: String,
trim: true,
required: true,
},
},
{
collection: 'tasks',
}
);
TaskSchema.plugin(timestamps);
TaskSchema.index({ createdAt: 1, updatedAt: 1 });
export const Task = mongoose.model('Task', TaskSchema);
export const TaskTC = composeWithMongoose(Task);

31
server/src/models/user.js Normal file
View File

@@ -0,0 +1,31 @@
import mongoose from 'mongoose';
const { Schema } = mongoose
import timestamps from 'mongoose-timestamp';
import { composeWithMongoose } from 'graphql-compose-mongoose';
export const UserSchema = new Schema(
{
name: {
type: String,
trim: true,
required: true,
},
email: {
type: String,
lowercase: true,
trim: true,
unique: true,
required: true,
},
},
{
collection: 'users',
}
);
UserSchema.plugin(timestamps);
UserSchema.index({ createdAt: 1, updatedAt: 1 });
export const User = mongoose.model('User', UserSchema);
export const UserTC = composeWithMongoose(User);

View File

@@ -8,7 +8,7 @@ async function feed(parent, args, context, info) {
] ]
} }
: {}; : {};
console.log(context.mongo);
const appointments = await context.mongo.appointment.findMany({ const appointments = await context.mongo.appointment.findMany({
where, where,
skip: args.skip, skip: args.skip,

View File

@@ -0,0 +1,20 @@
import { SchemaComposer } from 'graphql-compose';
import db from '../utils/db.js';
const schemaComposer = new SchemaComposer();
import { UserQuery, UserMutation } from './user.js';
import { TaskQuery, TaskMutation } from './task.js';
schemaComposer.Query.addFields({
...UserQuery,
...TaskQuery,
});
schemaComposer.Mutation.addFields({
...UserMutation,
...TaskMutation,
});
export default schemaComposer.buildSchema();

24
server/src/schema/task.js Normal file
View File

@@ -0,0 +1,24 @@
import { Task, TaskTC } from '../models/task.js';
const TaskQuery = {
taskById: TaskTC.getResolver('findById'),
taskByIds: TaskTC.getResolver('findByIds'),
taskOne: TaskTC.getResolver('findOne'),
taskMany: TaskTC.getResolver('findMany'),
taskCount: TaskTC.getResolver('count'),
taskConnection: TaskTC.getResolver('connection'),
taskPagination: TaskTC.getResolver('pagination'),
};
const TaskMutation = {
taskCreateOne: TaskTC.getResolver('createOne'),
taskCreateMany: TaskTC.getResolver('createMany'),
taskUpdateById: TaskTC.getResolver('updateById'),
taskUpdateOne: TaskTC.getResolver('updateOne'),
taskUpdateMany: TaskTC.getResolver('updateMany'),
taskRemoveById: TaskTC.getResolver('removeById'),
taskRemoveOne: TaskTC.getResolver('removeOne'),
taskRemoveMany: TaskTC.getResolver('removeMany'),
};
export { TaskQuery, TaskMutation };

24
server/src/schema/user.js Normal file
View File

@@ -0,0 +1,24 @@
import { User, UserTC } from '../models/user.js';
const UserQuery = {
userById: UserTC.getResolver('findById'),
userByIds: UserTC.getResolver('findByIds'),
userOne: UserTC.getResolver('findOne'),
userMany: UserTC.getResolver('findMany'),
userCount: UserTC.getResolver('count'),
userConnection: UserTC.getResolver('connection'),
userPagination: UserTC.getResolver('pagination'),
};
const UserMutation = {
userCreateOne: UserTC.getResolver('createOne'),
userCreateMany: UserTC.getResolver('createMany'),
userUpdateById: UserTC.getResolver('updateById'),
userUpdateOne: UserTC.getResolver('updateOne'),
userUpdateMany: UserTC.getResolver('updateMany'),
userRemoveById: UserTC.getResolver('removeById'),
userRemoveOne: UserTC.getResolver('removeOne'),
userRemoveMany: UserTC.getResolver('removeMany'),
};
export { UserQuery, UserMutation };

View File

@@ -0,0 +1,31 @@
import fs from 'fs-extra';
import path from 'path';
import { graphql } from 'graphql';
import { introspectionQuery, printSchema } from 'graphql/utilities';
import Schema from '../schema';
async function buildSchema() {
await fs.ensureFile('../data/schema.graphql.json');
await fs.ensureFile('../data/schema.graphql');
fs.writeFileSync(
path.join(__dirname, '../data/schema.graphql.json'),
JSON.stringify(await graphql(Schema, introspectionQuery), null, 2)
);
fs.writeFileSync(
path.join(__dirname, '../data/schema.graphql.txt'),
printSchema(Schema)
);
}
async function run() {
await buildSchema();
console.log('Schema build complete!');
}
run().catch(e => {
console.log(e);
process.exit(0);
});

29
server/src/utils/db.js Normal file
View File

@@ -0,0 +1,29 @@
// const mongoose = require("mongoose");
import mongoose from 'mongoose';
// const dotenv = require("../../.env");
import dotenv from 'dotenv';
dotenv.config();
mongoose.Promise = global.Promise;
const connection = mongoose.connect(process.env.MONGODB_URI, {
autoIndex: true,
reconnectTries: Number.MAX_VALUE,
reconnectInterval: 500,
poolSize: 50,
bufferMaxEntries: 0,
keepAlive: 120,
useNewUrlParser: true,
});
mongoose.set('useCreateIndex', true);
connection
.then(db => db)
.catch(err => {
console.log(err);
});
export default connection;

File diff suppressed because it is too large Load Diff