DDP-Apollo
DDP-Apollo leverages the power of DDP for GraphQL queries and subscriptions. Meteor developers do not need an HTTP server or extra websocket connection, because DDP offers all we need and has been well tested over time.
- DDP-Apollo is one of the easiest ways to get GraphQL running for Meteor developers
- Works with the Meteor accounts packages out of the box, giving a userId in your resolvers
- Method calls and collection hooks will have
this.userId
when called within your resolvers - Doesn’t require an HTTP server to be setup, like with express, koa or hapi
- Supports GraphQL Subscriptions out-of-the-box
- Doesn’t require an extra websocket for GraphQL Subscriptions, because DDP already has a websocket
- Already have a server setup? Use
DDPSubscriptionLink
stand-alone for just Subscriptions support. Read more
Just another Apollo Link
Because it's "just another Apollo Link":
- It works with Apollo Dev Tools
- It's easy to combine with other Apollo Links
- It's front-end agnostic
Starter Kit
Checkout this starter kit to see Meteor, Apollo, DDP and React all work together.
Note: DDP-Apollo works with all front-ends, not just React
Contents
- Installation
- Client setup
- Server setup
- GraphQL subscriptions
- Rate limiting GraphQL calls
- HTTP support
- Sponsor
Installation
meteor add swydo:ddp-apollo
meteor npm install --save @apollo/client @swydo/apollo-link-ddp graphql
Client setup
All client code is in the @swydo/apollo-link-ddp
npm package. It gives you a DDPLink
for your Apollo Client. Creating an Apollo Client is the same as with any other Apollo Link.
1// Choose any cache implementation, but we'll use InMemoryCache as an example 2import { ApolloClient, InMemoryCache } from '@apollo/client'; 3import { DDPLink } from '@swydo/apollo-link-ddp'; 4 5export const client = new ApolloClient ({ 6 link: new DDPLink(), 7 cache: new InMemoryCache() 8});
Options
connection
: The DDP connection to use. DefaultMeteor.connection
.method
: The name of the method. Default__graphql
.publication
: The name of the publication. Default__graphql-subscriptions
.ddpRetry
: Retry failed DDP method calls. Defaulttrue
. Switch off and use apollo-link-retry for more control.socket
: Optionally pass a socket to listen to for messages. This makes it easy to integrate with non-Meteor DDP clients.
1// Pass options to the DDPLink constructor 2new DDPLink({ 3 connection: Meteor.connection 4});
Server setup
The server will add a method and publication that will be used by the DDP Apollo Link.
1import { schema } from './path/to/your/executable/schema'; 2import { setup } from 'meteor/swydo:ddp-apollo'; 3 4setup({ 5 schema, 6 ...otherOptions 7});
Options
schema
: The GraphQL schema. Defaultundefined
. Required when nogateway
is provided.gateway
: An Apollo Gateway. Defaultundefined
. Required when noschema
is provided.context
: A custom context. Either an object or a function returning an object. Optional.method
: The name of the method. Default__graphql
.publication
: The name of the publication. Default__graphql-subscriptions
.
Custom context
To modify or overrule the default context, you can pass a context
object or function to the setup:
1// As an object: 2const context = { 3 foo: 'bar' 4} 5 6// As a function, returning an object: 7const context = (currentContext) => ({ ...currentContext, foo: 'bar' }); 8 9// As an async function, returning a promise with an object 10const context = async (currentContext) => ({ ...currentContext, foo: await doAsyncStuff() }); 11 12setup({ 13 schema, 14 context, 15});
GraphQL subscriptions
Subscription support is baked into this package. Simply add the subscriptions to your schema and resolvers and everything works.
Note: Apollo Gateway does not yet support Subscriptions.
# schema.graphql type Query { name: String } type Subscription { message: String }
Setting up PubSub
meteor npm install --save graphql-subscriptions
1import { PubSub } from 'graphql-subscriptions'; 2 3// The pubsub mechanism of your choice, for instance: 4// - PubSub from graphql-subscriptions (in-memory, so not recommended for production) 5// - RedisPubSub from graphql-redis-subscriptions 6// - MQTTPubSub from graphql-mqtt-subscriptions 7const pubsub = new PubSub(); 8 9export const resolvers = { 10 Query: { 11 name: () => 'bar', 12 }, 13 Subscription: { 14 message: { 15 subscribe: () => pubsub.asyncIterator('SOMETHING_CHANGED'), 16 }, 17 }, 18}; 19 20// Later you can publish updates like this: 21pubsub.publish('SOMETHING_CHANGED', { message: 'hello world' });
See graphql-subscriptions package for more setup details and other pubsub mechanisms. It also explains why the default PubSub
isn't meant for production.
Using DDP only for subscriptions
If you already have an HTTP server setup and you are looking to support GraphQL Subscriptions in your Meteor application, you can use the DDPSubscriptionLink
stand-alone.
1import { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client'; 2import { DDPSubscriptionLink, isSubscription } from '@swydo/apollo-link-ddp'; 3 4const httpLink = new HttpLink({ uri: "/graphql" }); 5const subscriptionLink = new DDPSubscriptionLink(); 6 7const link = split( 8 isSubscription, 9 subscriptionLink, 10 httpLink, 11); 12 13export const client = new ApolloClient ({ 14 link, 15 cache: new InMemoryCache() 16});
Rate limiting GraphQL calls
Meteor supports rate limiting for DDP calls. This means you can rate limit DDP-Apollo as well!
meteor add ddp-rate-limiter
1import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; 2 3// Define a rule that matches graphql method calls. 4const graphQLMethodCalls = { 5 type: 'method', 6 name: '__graphql' 7}; 8 9// Add the rule, allowing up to 5 messages every 1000 milliseconds. 10DDPRateLimiter.addRule(graphQLMethodCalls, 5, 1000);
See DDP Rate Limit documentation.
HTTP support
There can be reasons to use HTTP instead of a Meteor method. There is support for it built in, but it requires a little different setup than the DDP version.
Installation
We'll need the HTTP link from Apollo and body-parser
on top of the default dependencies:
meteor npm install @apollo/client body-parser
Client setup
1import { ApolloClient, InMemoryCache } from '@apollo/client'; 2// Use the MeteorLink instead of the DDPLink 3// It uses HTTP for queries and Meteor subscriptions (DDP) for GraphQL subscriptions 4import { MeteorLink } from '@swydo/apollo-link-ddp'; 5 6export const client = new ApolloClient ({ 7 link: new MeteorLink(), 8 cache: new InMemoryCache() 9});
Server setup
1import { schema } from './path/to/your/executable/schema'; 2import { setupHttpEndpoint, createGraphQLPublication } from 'meteor/swydo:ddp-apollo'; 3 4setupHttpEndpoint({ 5 schema, 6 ...otherOptions, 7}); 8 9// For subscription support (not required) 10createGraphQLPublication({ schema });
Options
schema
: The GraphQL schema. Defaultundefined
. Required when nogateway
is provided.gateway
: An Apollo Gateway. Defaultundefined
. Required when noschema
is provided.context
: A custom context. Either an object or a function returning an object. Optional.path
: The name of the HTTP path. Default/graphql
.engine
: An Engine instance, in case you want monitoring on your HTTP endpoint. Optional.authMiddleware
: Middleware to get a userId and set it on the request. DefaultmeteorAuthMiddleware
, using a login token.jsonParser
: Custom JSON parser. Loadsbody-parser
from yournode_modules
by default and uses.json()
.
Sponsor
Want to work with Meteor and GraphQL? Join the team!