diff --git a/README.md b/README.md index 669f8f8..fd897b3 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,12 @@ Phiew that was a lot! Any space left on your device? Now let's add our package: meteor add cultofcoders:apollo ``` +We recommend this package so you can store your types inside `.gql` files so you can benefit of reactivity: + +```bash +meteor add swydo:graphql +``` + Now, if you start your Meteor app it will complain because you don't have any Query set up yet, just set up an easy one: ```js @@ -50,357 +56,19 @@ query { } ``` -## GraphQL Files - -It would be quite nice if we could write our types inside `.gql` or `.graphql` files right, so we can benefit of some nice syntax highlighting: - -```bash -meteor add swydo:graphql -``` - -If you don't want, just create `.js` files that export a string. - -## A more complex sample - -```js -# file: server/User.gql -type User { - _id: ID! - firstname: String - lastname: String - fullname: String -} - -type Query { - users: [User] -} -``` - -```js -// file: server/User.resolver.js -export default { - User: { - fullname(user) { - return user.firstname + ' ' + user.lastname; - }, - }, - Query: { - users() { - return [ - { - firstname: 'Theodor', - lastname: 'Diaconu', - }, - { - firstname: 'Claudiu', - lastname: 'Roman', - }, - ]; - }, - }, -}; -``` - -```js -// file: server/index.js -import { load } from 'graphql-load'; -import UserType from './User'; -import UserResolver from './User.resolver'; - -load({ - typeDefs: [UserType], - resolvers: [UserResolver], -}); -``` - -Now query in your GraphiQL: - -```js -query { - users { - fullname - } -} -``` - -Ofcourse, when you're dealing with a big project, your data structure is surely going to change, this is just for demo purposes. -Keep in mind, you can separate queries anyway you want, and `load()` them independently, the `load` function smartly merges types and resolvers and works with arrays and GraphQL modules as well [Read more about `graphql-load`](https://www.npmjs.com/package/graphql-load?activeTab=readme) - -## Live Data - -This package comes already with the [apollo-live-server](https://www.npmjs.com/package/apollo-live-server) npm package already set-up, but for client you will also need: [apollo-live-client](https://www.npmjs.com/package/apollo-live-client) - -These 2 packages work hand-in-hand with the live queries from Meteor, a seemless super easy to plug-in integration. - -Alongside [`redis-oplog`](https://github.com/cult-of-coders/redis-oplog) you can have large scale reactivity at low costs. - -Quick show-case: - -```js -# file: server/User.gql -# ... -type Subscription { - users: ReactiveEvent -} -``` - -```js -// file User.resolver.js - -// when dealing with subscriptions the typename needs to be set at collection level: -// Meteor.users acts as any other Mongo.Collection you may have -Meteor.users.setTypename('User'); - -// Simulate some reactivity ... -Meteor.setInterval(function () { - const userId = Accounts.createUser({ - username: 'Apollo is Live!', - }) - - Meteor.setTimeout(function () { - Meteor.users.remove({_id: userId}) - }, 500) -}, 2000); - -// our Subscription resolver -export default { - ... - Subscription: { - users: { - resolve: payload => payload, - subscribe() { - return Meteor.users.asyncIterator({}, { - fields: { - 'username': 1, - } - }); // filters, options - } - } - } -} -``` - -You can now test your query inside GraphiQL: - -```js -subscription { - users { - _id - type - event - doc - } -} -``` - -Read more about [apollo-live-client](https://www.npmjs.com/package/apollo-live-client) to integrate it in your React app very easily and intuitively. - -## userId in context - -In Apollo your resolver receives `root`, `args` and `context`. Inside `context` we store the current `userId`: - -```js -// file: server/ -export default { - Query: { - invoices(root, args, context) { - return Invoices.find({ - userId: context.userId, - }).fetch(); - }, - }, -}; -``` - -`userId` works with your client and with `/graphiql` because the `Meteor.loginToken` is stored in `localStorage` and it's on the same domain, making very easy for you -to test your queries and mutations. - -## Scalars - -This package comes with 2 scalars `Date` (because it's very common) and `JSON` (because we need it for easy reactivity inside `apollo-live-server`. - -You can use it in your types: - -```js -type User { - createdAt: Date! - dynamicInformation: JSON -} -``` - -## Accounts - -Adding accounts to your GraphQL Schema: +## Documentation -``` -meteor add accounts-password -meteor npm i -S bcrypt meteor-apollo-accounts -meteor add cultofcoders:apollo-accounts -``` +### Table of Contents -```js -// file: /server/accounts.js -import { initAccounts } from 'meteor/cultofcoders:apollo-accounts'; -import { load } from 'graphql-load'; - -// Load all accounts related resolvers and type definitions into graphql-loader -const AccountsModule = initAccounts({ - loginWithFacebook: false, - loginWithGoogle: false, - loginWithLinkedIn: false, - loginWithPassword: true, -}); // returns { typeDefs, resolvers } - -load(AccountsModule); // Make sure you have User type defined as it works directly with it -``` - -Now you can already start creating users, logging in, open up your GraphiQL and look in the Mutation documentations. - -If you want to test authentication live and you don't yet have a client-side setup. Just create a user: - -```js -mutation { - createUser( - username: "cultofcoders", - plainPassword: "12345", - ) { - token - } -} -``` - -Store it in localStorage, in your browser console: - -```js -localStorage.setItem('Meteor.loginToken', token); // the one you received from the query result -``` - -Create a quick `me` query: - -```js -const typeDefs = ` - type Query { - me: User - } -`; - -const resolvers = { - Query: { - me(_, args, context) { - return Meteor.users.findOne(context.userId); - }, - }, -}; - -export { typeDefs, resolvers }; -``` - -And try it out: - -```js -query { - me { - _id - } -} -``` - -And also register the proper clear-outs for live subscription authentication: - -```js -// file: client/accounts.js -import { onTokenChange } from 'meteor-apollo-accounts'; -import { wsLink, client } from 'meteor/cultofcoders:apollo'; - -onTokenChange(function() { - client.resetStore(); - wsLink.subscriptionClient.close(true); // it will restart the websocket connection -}); -``` - -If you are using SSR and want to benefit from authentication for server-renders, check out this comment https://github.com/apollographql/meteor-integration/issues/116#issuecomment-370923220 - -## React Client - -```js -// in client-side code -import { Meteor } from 'meteor/meteor'; -import { render } from 'react-dom'; -import React from 'react'; -import { ApolloProvider } from 'react-apollo'; -import App from '/imports/ui/App'; -import { client } from 'meteor/cultofcoders:apollo'; - -Meteor.startup(() => { - render( - - - , - document.getElementById('app') - ); -}); -``` - -## DDP Connection - -Currently the problem is that your Meteor client still tries to connect to DDP, even if you disable websockets it tries to fallback to polling. You cannot have both DDP and GraphQL Websockets at the same time. - -You have several options: - -1. Start your meteor app with DISABLE_WEBSOCKETS=true -2. Start a minimal meteor app that only uses npm packages and copy what is inside client/index.js and adapt it properly -3. Think about shifting your frontend to a separate Meteor application - -## Settings - -```js -{ - // By default we open the websocket that supports authentication - // You can only expose an HTTP Server and that's it - // If you are using the client from this package, you have to have the same config on the client-side as well - DISABLE_SUBSCRIPTIONS: false, - - // You can disable GraphiQL - // By default it's only enabled in development mode - DISABLE_GRAPHIQL: !Meteor.isDevelopment, - - // Context that is going to be passed to resolvers - CONTEXT: {}, - - // If engine key is present it will automatically instantiate it for you - ENGINE_API_KEY: null, - // Should be the same port as your Meteor port or `process.env.PORT` - ENGINE_PORT: 3000, - - // Here you can add certain validation rules to the GraphQL query - // If, for example, you want to disable introspection on production - // https://github.com/helfer/graphql-disable-introspection - GRAPHQL_VALIDATION_RULES: [], - - // Because we support authentication by default - // We inject { user, userId } into the context - // These fields represent what fields to retrieve from the logged in user on every request - // You can use `undefined` if you want all fields - USER_DEFAULT_FIELDS: { - _id: 1, - username: 1, - emails: 1, - profile: 1, - roles: 1, - }, -} -``` - -Change any value you like by doing: - -```js -// in some startup file on the server -import { Config } from 'meteor/cultofcoders:apollo'; - -Object.assign(Config, { - ENGINE_API_KEY: Meteor.settings.ENGINE_API_KEY, -}); -``` +* [Simple Usage](docs/sample.md) +* [Database](docs/db.md) +* [Client](docs/client.md) +* [Scalars](docs/scalars.md) +* [Live Queries](docs/live_queries.md) +* [Accounts](docs/accounts.md) +* [Settings](docs/settings.md) +* [DDP](docs/ddp.md) +* [Visualising](docs/visualising.md) ## Premium Support diff --git a/constants.js b/constants.js index f38a8ac..c775f08 100644 --- a/constants.js +++ b/constants.js @@ -1,6 +1,8 @@ import { Meteor } from 'meteor/meteor'; export const AUTH_TOKEN_KEY = 'meteor-login-token'; +export const AUTH_TOKEN_LOCALSTORAGE = 'Meteor.loginToken'; + export const GRAPHQL_SUBSCRIPTION_PATH = 'subscriptions'; export const GRAPHQL_SUBSCRIPTION_ENDPOINT = Meteor.absoluteUrl( GRAPHQL_SUBSCRIPTION_PATH diff --git a/docs/accounts.md b/docs/accounts.md new file mode 100644 index 0000000..2eee814 --- /dev/null +++ b/docs/accounts.md @@ -0,0 +1,144 @@ +# Meteor Accounts + +In Apollo your resolver receives `root`, `args` and `context`. Inside `context` we store the current `userId` and `user`: + +The data we fetch for user can be customised via config: + +```js +// file: server/ +export default { + Query: { + invoices(root, args, { user, userId }) { + return Invoices.find({ + userId, + }).fetch(); + }, + }, +}; +``` + +```js +import { Config } from 'meteor/cultofcoders:apollo'; + +Config.USER_DEFAULT_FIELDS: { + _id: 1, + username: 1, + emails: 1, + roles: 1, +}, +``` + +`userId` works with your client and with `/graphiql` because the `Meteor.loginToken` is stored in `localStorage` and it's on the same domain, making very easy for you to test your queries and mutations. + +Adding accounts to your GraphQL Schema: + +``` +meteor add accounts-password +meteor npm i -S bcrypt +meteor add cultofcoders:apollo-accounts +``` + +```js +// file: /server/accounts.js +import { initAccounts } from 'meteor/cultofcoders:apollo-accounts'; +import { load } from 'graphql-load'; + +// Load all accounts related resolvers and type definitions into graphql-loader +const AccountsModule = initAccounts({ + loginWithFacebook: false, + loginWithGoogle: false, + loginWithLinkedIn: false, + loginWithPassword: true, +}); // returns { typeDefs, resolvers } + +load(AccountsModule); // Make sure you have User type defined as it works directly with it +``` + +Now you can already start creating users, logging in, open up your GraphiQL and look in the Mutation documentations. + +If you want to test authentication live and you don't yet have a client-side setup. Just create a user: + +```js +mutation { + createUser( + username: "cultofcoders", + plainPassword: "12345", + ) { + token + } +} +``` + +Store it in localStorage, in your browser console: + +```js +localStorage.setItem('Meteor.loginToken', token); // the one you received from the query result +``` + +Create a quick `me` query: + +```js +const typeDefs = ` + type Query { + me: User + } +`; + +const resolvers = { + Query: { + me(_, args, context) { + return Meteor.users.findOne(context.userId); + }, + }, +}; + +export { typeDefs, resolvers }; +``` + +And try it out: + +```js +query { + me { + _id + } +} +``` + +And also register the proper clear-outs for live subscription authentication: + +```js +// file: client/accounts.js +import { onTokenChange } from 'meteor-apollo-accounts'; +import { wsLink, client } from 'meteor/cultofcoders:apollo'; + +onTokenChange(function() { + client.resetStore(); + wsLink.subscriptionClient.close(true); // it will restart the websocket connection +}); +``` + +To use them nicely inside your client: + +``` +meteor npm i -S bcrypt meteor-apollo-accounts +``` + +Read here: https://github.com/orionsoft/meteor-apollo-accounts#methods + +If you are using SSR and want to benefit from authentication for server-renders, check out this comment https://github.com/apollographql/meteor-integration/issues/116#issuecomment-370923220 + +If you wish to customize the mutations or resolvers exposed you can load different ones, after you loaded the ones from the package: + +```js +load({ + typeDefs: ` + createUser(): String + `, + resolvers: { + Mutation: { + createUser() { ... } + } + } +}) +``` diff --git a/docs/client.md b/docs/client.md new file mode 100644 index 0000000..fedbc93 --- /dev/null +++ b/docs/client.md @@ -0,0 +1,33 @@ +# Client + +Let's play with Apollo, but outside GraphiQL + +## React + +```js +// in client/main.html + +
+ +``` + +```js +// in client/main.js +import { render } from 'react-dom'; +import React from 'react'; +import { ApolloProvider } from 'react-apollo'; + +import { Meteor } from 'meteor/meteor'; +import { client } from 'meteor/cultofcoders:apollo'; + +import App from 'YOUR_APP'; + +Meteor.startup(() => { + render( + + + , + document.getElementById('app') + ); +}); +``` diff --git a/docs/db.md b/docs/db.md new file mode 100644 index 0000000..85cdc5e --- /dev/null +++ b/docs/db.md @@ -0,0 +1,81 @@ +# Mongo + +This package is tailored for Mongo database. If you're looking for a bare-bones API implementation, not coupled to mongo, you can look at: https://github.com/apollographql/meteor-integration + +This package depends on [`cultofcoders:grapher`](https://github.com/cult-of-coders/grapher), a very awesome tool, +which allows you too query related MongoDB objects at serious speeds. + +The difference is that we will never use the exposure mechanisms from Grapher which are used for Meteor's DDP (Methods & Publications), +but it's not a problem, we have many other nice things we can use. + +The advantage is that you can start using your database and related links in just your types, for example: + +```typescript +type Post @mongo(name: "posts") +{ + text: String! + author: Author @link(field: "authorId") +} + +type Author @mongo(name: "authors") +{ + name: String + posts: [Post] @link(to: "author") + groups: [Group] @link(field: "groupIds") +} + +type Group @mongo(name: "groups") { + name: String + authors: [Author] @link(to: "groups") +} +``` + +Above we have the following relationships: + +* Post has one author and it's stored in `authorId` +* Author has many posts +* Author belongs to many groups and it's stored in `groupIds` +* Groups have many authors + +And the beautiful part is that for prototyping this is so fast, because we inject the db inside our context: + +Read more about these directives here: +https://github.com/cult-of-coders/grapher-schema-directives + +```js +export default { + Query: { + posts(_, args, ctx, ast) { + // Performantly fetch the query using Grapher + // You don't need to implement resolvers for your links, it's all done automatically + + return ctx.db.posts.astToQuery(ast).fetch(); + // but you can do whatever you want here since ctx.db.posts is a Mongo.Collection + // https://docs.meteor.com/api/collections.html + }, + }, + Mutation: { + addPost(_, { title }, ctx) { + ctx.db.posts.insert({ + title, + }); + }, + }, + Subscription: { + posts(_, args, ctx) { + // You can also use astToBody from Grapher, to only follow the requested fields + // But since that is a rare case, we won't cover it here so we keep it simple: + // But note that reactivity only works at a single level. + ctx.db.posts.find( + {}, + { + fields: { status: 1 }, + } + ); + }, + }, +}; +``` + +Read more about Grapher's GraphQL bridge: +https://github.com/cult-of-coders/grapher/blob/master/docs/graphql.md diff --git a/docs/ddp.md b/docs/ddp.md new file mode 100644 index 0000000..7f74d84 --- /dev/null +++ b/docs/ddp.md @@ -0,0 +1,24 @@ +## DDP Connection + +Currently the problem is that your Meteor's client still tries to connect to DDP, even if you disable websockets it tries to fallback to sockjs. You cannot have both DDP and GraphQL Websockets at the same time. + +You have several options: + +1. Start your Meteor app with `DISABLE_WEBSOCKETS=true meteor run` and use `cultofcoders:fusion` +2. Create a new minimal Meteor app that only uses npm packages and copy what is inside this package's `client/index.js` and adapt it properly + +If you do not have the desire to built another app, you could use this package called `cultofcoders:fusion` which basically disabled connecting to DDP from the client at all. + +What it does, it overrides the ddp-client package, and conditionally connects to DDP. + +``` +mkdir packages +cd packages +git clone https://github.com/cult-of-coders/fusion.git +``` + +And that's about it, used in conjunction with `DISABLE_WEBSOCKETS` is perfect. + +The problem is that many useful packages in `Meteor` depend on `DDP`, it's not a problem if that happens server-side (at the expense of few hundred kbs), but it's a problem on the client, where few hundred kbs make a difference. + +The logic here is that you can use `fusion`, and when your app wants to scale and it makes sense, you can think about a separate Meteor app that is designated for the client only, and from which you connect to your `api` with ease diff --git a/docs/live_queries.md b/docs/live_queries.md new file mode 100644 index 0000000..93877cd --- /dev/null +++ b/docs/live_queries.md @@ -0,0 +1,84 @@ +# Live Data + +This package comes already with the [apollo-live-server](https://www.npmjs.com/package/apollo-live-server) npm package already set-up, but for client you will also need: [apollo-live-client](https://www.npmjs.com/package/apollo-live-client) + +These 2 packages work hand-in-hand with the live queries from Meteor, a seemless super easy to plug-in integration. + +Alongside [`redis-oplog`](https://github.com/cult-of-coders/redis-oplog) you can have large scale reactivity at low costs. + +## How it works + +Every subscription in apollo-live-server emits a `ReactiveEvent`, which is composed of: + +```js +{ + type, // the typename of the object + event, // added, changed, removed + _id, // guess! + doc, // full doc for added, changeset for changed, null for removed +} +``` + +```js +# file: server/User.gql +# ... +type Subscription { + users: ReactiveEvent +} +``` + +```js +// file User.resolver.js + +// when dealing with subscriptions the typename needs to be set at collection level: +Meteor.users.setTypename('User'); + +// our Subscription resolver +export default { + ... + Subscription: { + users: { + resolve: payload => payload, + subscribe() { + return Meteor.users.asyncIterator({}, { + fields: { + 'username': 1, + } + }); // asyncIterator accepts: filters, options + } + } + } +} +``` + +## Simulate reactivity + +```js +// Meteor.users acts as any other Mongo.Collection you may have + +// Simulate some reactivity ... +Meteor.setInterval(function() { + const userId = Accounts.createUser({ + username: 'Apollo is Live!', + }); + + Meteor.setTimeout(function() { + Meteor.users.remove({ _id: userId }); + }, 500); +}, 2000); +``` + +You can now test your query inside GraphiQL: + +```js +subscription { + users { + _id + type + event + doc + } +} +``` + +Read more about [apollo-live-client](https://www.npmjs.com/package/apollo-live-client) to integrate it in your React app very easily and intuitively. diff --git a/docs/sample.md b/docs/sample.md new file mode 100644 index 0000000..e49d19f --- /dev/null +++ b/docs/sample.md @@ -0,0 +1,67 @@ +# Sample Usage + +```js +# file: server/User.gql +type User { + _id: ID! + firstname: String + lastname: String + fullname: String +} + +type Query { + users: [User] +} +``` + +```js +// file: server/User.resolver.js +export default { + User: { + fullname(user) { + return user.firstname + ' ' + user.lastname; + }, + }, + Query: { + users() { + return [ + { + firstname: 'Theodor', + lastname: 'Diaconu', + }, + { + firstname: 'Claudiu', + lastname: 'Roman', + }, + ]; + }, + }, +}; +``` + +```js +// file: server/index.js +import { load } from 'graphql-load'; +import UserType from './User'; +import UserResolver from './User.resolver'; + +load({ + typeDefs: [UserType], + resolvers: [UserResolver], +}); +``` + +Now query in your GraphiQL: + +```js +query { + users { + fullname + } +} +``` + +Ofcourse, when you're dealing with a big project, your data structure is surely going to change, this is just for demo purposes. +Keep in mind, you can separate queries anyway you want, and `load()` them independently, the `load` function smartly merges types and resolvers and works with arrays and GraphQL modules as well. + +[Read more about `graphql-load`](https://www.npmjs.com/package/graphql-load?activeTab=readme) diff --git a/docs/scalars.md b/docs/scalars.md new file mode 100644 index 0000000..8c23cba --- /dev/null +++ b/docs/scalars.md @@ -0,0 +1,14 @@ +## Scalars + +This package comes with 2 scalars `Date` (because it's very common) and `JSON` (because we need it for easy reactivity inside `apollo-live-server`. + +You can use it in your types: + +```typescript +type User { + createdAt: Date! + dynamicInformation: JSON +} +``` + +The `Date` scalar parses `.toISOString()`, so, when you want to send a date from the client-side, make sure to apply that method to the date. diff --git a/docs/settings.md b/docs/settings.md new file mode 100644 index 0000000..a8c82bd --- /dev/null +++ b/docs/settings.md @@ -0,0 +1,71 @@ +# Settings + +The `{ Config }` object: + +```js +{ + // By default we open the websocket that supports authentication + // You can only expose an HTTP Server and that's it + // If you are using the client from this package, you have to have the same config on the client-side as well + DISABLE_SUBSCRIPTIONS: false, + + // You can disable GraphiQL + // By default it's only enabled in development mode + DISABLE_GRAPHIQL: !Meteor.isDevelopment, + + // Context that is going to be passed to resolvers + CONTEXT: { + db // Access to database context via Grapher + }, + + // If engine key is present it will automatically instantiate it for you + ENGINE_API_KEY: null, + // Should be the same port as your Meteor port or `process.env.PORT` + ENGINE_PORT: 3000, + + // Here you can add certain validation rules to the GraphQL query + // If, for example, you want to disable introspection on production + // https://github.com/helfer/graphql-disable-introspection + GRAPHQL_VALIDATION_RULES: [], + + // Specify the directives we want to use + GRAPHQL_SCHEMA_DIRECTIVES: {}, + + // Express middlewares, for example you may want to use 'cors' + EXPRESS_MIDDLEWARES: [], + + // Because we support authentication by default + // We inject { user, userId } into the context + // These fields represent what fields to retrieve from the logged in user on every request + // You can use `undefined` if you want all fields + USER_DEFAULT_FIELDS: { + _id: 1, + username: 1, + emails: 1, + roles: 1, + }, +} +``` + +Change any value you like by doing: + +```js +// in some startup file on the server +import { Config } from 'meteor/cultofcoders:apollo'; + +Object.assign(Config, { + ENGINE_API_KEY: Meteor.settings.ENGINE_API_KEY, +}); +``` + +Be careful when you want to extend the `CONTEXT`, do not override it, extend it: + +```js +import { Config } from 'meteor/cultofcoders:apollo'; + +Object.assign(Config.CONTEXT, { + services: { + User: new UserService(), + }, +}); +``` diff --git a/docs/visualising.md b/docs/visualising.md new file mode 100644 index 0000000..9826324 --- /dev/null +++ b/docs/visualising.md @@ -0,0 +1,68 @@ +# Visualising + +Various ways to visualise your GraphQL Schema with ease: + +``` +graphqlviz https://localhost:3000 | dot -Tpng -o graph.png +``` + +## Graphqlviz + +Easily export your API schema into a png file: +https://github.com/sheerun/graphqlviz + +## Voyager + +Nicely explore the schema of your GraphQL App, using an amazing app: https://github.com/APIs-guru/graphql-voyager + +Create a file `private/voyager.html` in your Meteor app: + +```html + + + + + + + + + + + + + +
Loading...
+ + + + +``` + +For this you may also need to enable CORS (Cross-origin resource sharing): + +``` +meteor npm i -S cors +``` + +```js +import { Config } from 'meteor/cultofcoders:apollo'; + +// Maybe you want thsi only in development +Meteor.isDevelopment && Config.EXPRESS_MIDDLEWARES.push(cors()); +``` + +Now open the html file directly in your browser, and engage in the nice schema view. diff --git a/package.js b/package.js index 9a50db8..d0a1b2c 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'cultofcoders:apollo', - version: '0.1.5', + version: '0.2.0', // Brief, one-line summary of the package. summary: 'Meteor & Apollo integration', // URL to the Git repository containing the source code for this package. @@ -15,6 +15,8 @@ Package.onUse(function(api) { api.use('ecmascript'); api.use('check'); api.use('mongo'); + api.use('cultofcoders:grapher@1.3.4'); + api.use('cultofcoders:grapher-schema-directives@0.1.4'); api.use('accounts-base', { weak: true }); api.mainModule('client/index.js', 'client'); diff --git a/server/config.js b/server/config.js index d66887c..f5bf99e 100644 --- a/server/config.js +++ b/server/config.js @@ -1,5 +1,7 @@ -export default { - CONTEXT: {}, +import { db } from 'meteor/cultofcoders:grapher'; + +let Config = { + CONTEXT: { db }, DISABLE_SUBSCRIPTIONS: false, DISABLE_GRAPHIQL: !Meteor.isDevelopment, ENGINE_API_KEY: null, @@ -8,6 +10,10 @@ export default { _id: 1, username: 1, emails: 1, - profile: 1, + roles: 1, }, + GRAPHQL_VALIDATION_RULES: [], + GRAPHQL_SCHEMA_DIRECTIVES: {}, }; + +export default Config; diff --git a/server/core/main-server.js b/server/core/main-server.js index f617ed6..e61418a 100644 --- a/server/core/main-server.js +++ b/server/core/main-server.js @@ -62,6 +62,7 @@ export const createApolloServer = (customOptions = {}, customConfig = {}) => { // GraphQL endpoint, enhanced with JSON body parser graphQLServer.use( config.path, + ...Config.EXPRESS_MIDDLEWARES, bodyParser.json(), graphqlExpress(async req => { try { diff --git a/server/directives/index.js b/server/directives/index.js new file mode 100644 index 0000000..2c0ce03 --- /dev/null +++ b/server/directives/index.js @@ -0,0 +1,8 @@ +import { directives as grapherDirectives } from 'meteor/cultofcoders:grapher-schema-directives'; +import { directiveDefinitions } from 'meteor/cultofcoders:grapher-schema-directives'; + +export const typeDefs = [directiveDefinitions]; + +export default { + ...grapherDirectives, +}; diff --git a/server/schema.js b/server/schema.js index bd1af29..1deb148 100644 --- a/server/schema.js +++ b/server/schema.js @@ -1,12 +1,20 @@ import { makeExecutableSchema } from 'graphql-tools'; import { load, getSchema } from 'graphql-load'; +import Config from './config'; +import directives from './directives'; const EMPTY_QUERY_ERROR = 'Error: Specified query type "Query" not found in document.'; export function getExecutableSchema() { try { - schema = makeExecutableSchema(getSchema()); + schema = makeExecutableSchema({ + ...getSchema(), + schemaDirectives: { + ...directives, + ...Config.GRAPHQL_SCHEMA_DIRECTIVES, + }, + }); } catch (error) { if (error.toString() === EMPTY_QUERY_ERROR) { throw '[cultofcoders:apollo] You do not have any Query loaded yet. Please use { load } from "graphql-load" package to initialize your typeDefs and resolvers.'; diff --git a/server/types/index.js b/server/types/index.js index b7b8d1a..df75aa2 100644 --- a/server/types/index.js +++ b/server/types/index.js @@ -1,6 +1,7 @@ import { ReactiveEventType } from 'apollo-live-server'; import { load } from 'graphql-load'; +import { typeDefs as directiveTypeDefs } from '../directives'; load({ - typeDefs: ReactiveEventType, + typeDefs: [ReactiveEventType, ...directiveTypeDefs], });