[SOLVED] Help With Apollo and Meteor Accounts

Hey everyone! First off, thanks to everyone on the Apollo team! The tools you guys have built and are working on are awesome! :grinning: I am very excited for the future!

That being said I am still new to Apollo and I was trying to figure out how to use the Meteor Roles and Accounts system inside a resolver function. Please see below for my resolver function showing what I was trying to accomplish.


import { Meteor } from 'meteor/meteor'

export const resolvers = {
  Query: {
    getUsers (root, args, context) {
      if (Roles.userIsInRole(context.userId, ['admin'])) {
        return Meteor.users.find().fetch()
      }
      throw new Meteor.Error(403, 'Not authorized')
    }
  }
}

The Apollo docs state HERE:

Inside your resolvers, if the user is logged in, their id will be context.userId and their user doc will be context.user:

This actually works fine in Graphiql and only returns an array of Users based on my query shape if the user has the role “admin” but for some reason the check fails when I try to connect the data to React and I get the error message returned instead of the users collection.

I had the same issue when trying the check laid out in the Apollo docs.

      if (context.userId === args.id) {
        return context.user;
      }

Graphiql had no issues but it would not work from React.

If I take the boolean check away the data populates as expected so I am pretty sure the issue lies there. Any ideas? Thanks again!

So I have been playing with this issue and for some reason it seems that context.userId is undefined in my resolver in react but successfully returns the userId in Graphiql. It must be a load order issue but I am at a loss of how to fix this or keep it from happening in the future. Any help would be greatly appreciated!

Ok I figured it out! It seems like Graphiql automatically sends the user via context for some reason. If you want apollo-client to do the same you should use meteorClientConfig per here.

So when I wrote my resolvers and tested them in Graphiql and they worked I assumed they would also work in my client. But of course the docs show otherwise if I would just read closer! :stuck_out_tongue: Once I configured the apollo-client per the docs my code above works as expected. Thanks for listening! haha

1 Like

Would you mind sharing your ApolloClient configuration, @yellowspaceboots?

In an Angular-Meteor project using apollo-client@1.1.1 and meteor-apollo@0.7.2 and apollo-angular@0.13.0, I am encountering a similar issue with a completely empty context argument being passed into my resolver even though the user is logged in with Meteor.

I configure ApolloClient to useMeteorAccounts as follows:

import { ApolloClient, createNetworkInterface } from 'apollo-client';
import { ApolloClientConfig } from 'meteor/apollo';

export const serverUrlBrowser: string = 'http://localhost:3000';
export const graphQLpath: string = '/graphql';

export function graphQLClient(): ApolloClient {
	const graphQLURL: string = `${serverUrlBrowser}${graphQLpath}`;
	const apolloClientConfig: ApolloClientConfig = {
		networkInterface: createNetworkInterface({
			uri: graphQLURL,
			useMeteorAccounts: true
		}),
		dataIdFromObject: (object: any): string => {
			return object._id;
		}
	};

	return new ApolloClient(apolloClientConfig); 	// http://dev.apollodata.com/core/meteor.html#meteorClientConfig

}

Any troubleshooting tips appreciated!

Nevermind! After many hours of searching, I found a solution to the problem of the empty context resolver argument myself. From this Github thread and the meteor-integration code tour, I learned that with meteor/apollo, the Apollo GraphQL server looks for a 'meteor-login-token' header which was absent from what apollo-client was sending even though the meteor-integration client code should have been providing it automatically. In my case, I think that may be due to the fact that my Angular-Meteor client is running via Angular CLI rather than Meteor and I had not provided the meteor-integration apollo Atmosphere package to the client with meteor-client-bundler. (In fact, when I now attempt that, it gives me errors about not finding apollo-client, even though that module is working in my client project.)

Regardless, to rectify, I manually added a middleware function to the ApolloClient configuration noted in my previous post in order to provide the meteor-login-token:

	networkInterface.use([{
		async applyMiddleware(req: any, next: any) {
			if (!req.options.headers) {
				req.options.headers = {};  // Create the header object if needed.
			}
			const authenticationToken = await localStorage.getItem('Meteor.loginToken');
			req.options.headers['meteor-login-token'] = authenticationToken;
			next();
		}
	}]);
2 Likes

@KeithGillette I am glad that you figured out your problem. Sorry for the late response! Yes the meteor/apollo package provides a handy client configuration that will automatically assign the meteor user information to context which is useful when you are setting up the client side of your meteor app as your apollo client as well. All you have to do is the below.

import ApolloClient from 'apollo-client';
import { meteorClientConfig } from 'meteor/apollo';
const client = new ApolloClient(meteorClientConfig())

On the other hand, in my use case I was required to authenticate a user from a React Native application I am building and the meteor/apollo package made it so easy that when the same did not work from my apollo client located in my React Native app I ended up with almost the exact same solution as you!

If you are using

import { createApolloServer } from 'meteor/apollo'

than the apollo server is just looking for a header named ‘meteor-login-token’. As long as we send a valid meteor user token in the request header from our client with that name than we should be able to access the user information in our resolvers using ‘context’ from ANY apollo client. Well done!

2 Likes