Meteor Reactivity Alongside GraphQL Apollo — Implemented

It appears that MDG is also working on integrating Meteor with Apollo based on Meteor Roadmap: GraphQL Data: SQL, REST, performance:

We are working on providing better tools and documentation for Meteor developers to integrate Apollo into their apps, and welcome contributions in this area. The next priority for Apollo and Meteor is enabling Meteor developers to choose to replace MongoDB entirely with GraphQL on top of other storage engines.

An official integration would be preferred over third party integrations, but we don’t know when that would happen (in version 1.7?). Since I want an integration now, in the meantime I’ve been looking at third party integrations, with one requirement being that it should be easy (i.e. wouldn’t require much code rewriting) to switch to the official integration when it becomes available. ddp-apollo is quite simple so would fit that requirement.

I will look further into cultofcoders:apollo to see whether it has enough advantages over ddp-apollo that make it worth switching to it before the official integration is released.

@tab00 ddp-apollo and cultofcoders:apollo they are esentially different beasts with different goals in mind. One is a Transport Layer for GraphQL, the other is a set of tools to quickly bootstrap your GraphQL server, not migrate to it.

One of the problems I had with ddp:apollo is lack of support for GraphiQL (but you can still use Apollo Dev Tools), so some would think it’s not that big of a deal. I personally do (https://github.com/Swydo/ddp-apollo/issues/145). Another problem is lack of propper support for other platforms such as React Native and such. DDP doesn’t have much support outside Meteor.

You can still use apollo-live functionality with ddp:apollo too, there is absolutely no issue! apollo-live does not depend on cultofcoders:apollo in any regard.

I personally believe ddp-apollo is freaking awesome and @jamiter did an awesome job, and for those looking to migrate their large code-bases, it’s a good way to start.

Meteor guys already have their official apollo integration package: https://github.com/apollographql/meteor-integration and to be honest with you, it’s very simple to add, the problem happens when you want reactivity. I mean honestly, they actually delivered on their promise, and in 1.6.2 we have ability to opt-out of mongodb entirely, and that’s it. They achieved their goal, you can do whatever you want.

Now coming back to this package, the vision is that it will contain a set of very powerful tools, tools gathered from the community, tools you will definitely need, like “@auth” directives, Date JSON scalars, and so on.

1 Like

Well said. One of my next steps is hacking directives. Definitely will need object and field level @auth directive. I’m thinking it’s possible to leverage the alanning:roles package.

type Thing @auth(group: "thing_managers") {
   _id: ID!
   name: String
   secret: String @auth(role: "admin")
}

My current mindset is that I’ll be using @auth directives and others like this to generate the resolvers with authentication & probably business logic too. :grimacing:

Anyway… you’re killin’ it dude. I hadn’t even seen this thread yet. Sorry I’m up your ass all the sudden. I’m just falling in love with Meteor all over again. Thanks to you and your work on Grapher + Apollo + live queries working with Redis and its all optimized for Meteor & MongoDB… daaaaamn son!

What do you eat for breakfast?!

type Thing @mongo(name: "things") {
    _id: ID!
    name: String!
  }
  type Query {
    things: [Thing] @allow(roles: ["user"])
  }

Glad you have your Meteor enthusiasm back, there’s a lot of reason to be excited about, there was a lot of effort put behind this, but heck this is the future.

Regarding @auth directives, I’ve been thinking about them for some time now, and my main issue is that they are business logic, why would I put business logic / security logic inside api schema definition ? While convenient, it may lead to unreadable code in time, this is why, in my thinking the best place to put them is at query level. Not sure if via a directive at query, but something should be done in the resolver to simplify this process. Or have a sort of separate YAML file to configure these security options like allow fields by role etc.

I read this whole thread and I get the don’t mix business logic argument but I’m in the business of generating the whole data layer based on a schema. So to me it doesn’t matter where the code goes… it’s generated.

There’s one comment in that thread that really got my attention and it was like an authorization query that gets called before and/or after the real query gets resolved. Just seems like there’s still some hashing out to do before the general “right” way emerges.

The problem is not only because we’re mixing concerns, it’s also the fact that there are much more efficient and elegant ways to do it. The PermissionMap or storing this security logic separately and have something like @secure(model: “User”) at type level and somewhere have a UserSecurityModel in which we define permissions. I think that’s the way to do it.

This is also good:

I’ve worked a bit more on the roles-directives package I posted above. Directives are turning out to feel really powerful. Mainly because you can do different things to the executable schema based on where the directive is located. For instance, imagine 3 directives:

  1. On a field of a Thing type - the specific field gets protected for any result of a query or mutation.
  2. On a mutation updateThing - the mutation gets protected.
  3. On a field in the ThingInput used by the updateThing mutation - the arguments gets protected.

What happens when the mutation is called?

  1. The mutation’s wrapped resolver will determine if the user is allowed to call the mutation.
  2. Check #3. answers “Is the user allowed to update these fields”?
  3. Finally check #1, answers, “Is the user allowed to see this field?”

There’s also some optimizations in my code that prevent resolvers from getting wrapped multiple times. Pretty cool right?

I feel that mixing schema with security is bound to bite our asses at some point. It looks convenient for simple things, but for example, how would you enforce a rule like I only allow the user to see this field if he is in this project, and things like that. For simple stuff like “requires authentication or requires admin role” above queries and mutations, I think it’s ok, but for Schema nope. They tried to do the same thing with SimpleSchema and put business logic security rules in there. I don’t like it because we’re tangling 2 important concerns together.

You’re probably right. And it’s concerning to think what a schema would look like when there are directives for everything under the sun. But regardless, directives are very flexible and powerful. I believe directives can still solve the other types of permissions like the document owner example.

@ownerId on a field and @allow(owner: true) on a field, type, query, mutation, argument, etc…

Depending on the location of the directive, it can do very different things, and also has the ability to store tons of info on the executable schema itself, which resolvers have access to. When I looked at graphql-shield, all I saw was a map between auth functions and resolvers, and it’s like, wait that’s all I’m doing, I just happen to be storing the map on the schema itself and generating the auth functions at runtime.

In my project, it may still turn out that there becomes too many rules to encode in directives. In that case, I’ll look into your idea of a separate yaml file, but then I would need to inject the resolvers differently. Right now I’m piggybacking on apollo’s directives because the api is fantastic.

YAML files won’t necessarily do you good, it’s hard to encode logic in yaml. From my experience I’ve seen that the safest way is to have like the “immune system” separated as a module, have a Security services that is in charge of performing these checks, and simply call them in your resolvers.

How can I have multiple queries, like compose wrapper, while using the Apollo client package you’ve developed?

If you want that custom functionality, you would have to do a little bit of manual work, and manually use the change-event reducer:

import { reduceStore } from 'apollo-live-client';

^ It’s mentioned in https://github.com/cult-of-coders/apollo-live-client

We made it really simple for one query and for the subscription of that query, if you want more queries, a more custom approach you have to do it yourself, but with reduceStore it’s made simple enough to translate the incomming changes into a new store.

1 Like

i was also looking to run multiple queries

can anyone help to do it?... .