node.jsapollonockrestdatasource

Context is undefined in Apollo RESTDataSource


While upgrading from Apollo v2 to v3, I'm trying to make some external API calls to an auth server. Currently, I'm using nock to intercept the requests inside of a unit test.

The GET request keeps failing when I try to attach a token in the request header because the context is undefined. Why would the context be undefined here? This worked prior to upgrading.

server.js

const {ApolloServer} = require('apollo-server-koa'),
CustomApi = require('../service/customapi'),
...;

const apolloServer = new ApolloServer({
    typeDefs: [DateTypeDefinition, root.typeDef, user.typeDef, ...],
    resolvers: [{Date: DateResolver}, root.resolvers, user.resolvers, ...],
    validationRules: [depthLimit(10)],
    plugins: [
        graphqlLogger
    ],
    dataSources: () => {
        return {
            customApi: new CustomApi()
        };
    },
    context: async (req) => {
        const user = await extract(req);
        return {
            user,
            ...
        }
    }
})

module.exports = apolloServer;

resolvers.js

syncAccount: async (parent, _, context) => {
        allowed(context.user);
        const reviewer = await getReviewerByEmail(context.user.email);
        if (reviewer && reviewer.status === 'INVITED') {
            const account = await context.dataSources.customApi.getAccount();
            ...
}

customapi.js

const {RESTDataSource} = require('@apollo/datasource-rest'),
config = require('config')

class CustomAPI extends RESTDataSource {
        constructor() {
            super();
            this.baseURL = `${config.customapi.url}/path/api`;
        }

        willSendRequest(path, request) {
            //ERROR HERE
            console.log("willSendRequest - this.context: ", this.context); // prints undefined
            request.headers.set('Authorization', this.context.user.token);
        }

        async getAccount() {
            return await this.get('account');
        }
        ...
}

package.json versions:

"@apollo/datasource-rest": "^6.2.2",
"@koa/cors": "^3.1.0",
"apollo-server": "^3.13.0",
"apollo-server-koa": "^3.13.0",
"dataloader": "^2.0.0",
"graphql": "^16.8.1",
"graphql-depth-limit": "^1.1.0",
"graphql-scalars": "^1.22.4",
"graphql-upload": "^13.0.0",
"koa": "^2.13.0",
"koa-bodyparser": "^4.3.0",
"koa-jwt": "^4.0.0",
"koa-router": "^9.4.0",
...

Solution

  • Instead of retrieving the context from within the RESTDataSource, I deleted the willSendRequest overridden function and instead passed in the token:

    async getAccount(token) {
            return await this.get('account', undefined, {
                headers: {
                    'Authorization': token
                }
            });
        }
    

    EDIT:

    If you are using apollo/datasource-rest version 5 or 6 now, the function has changed to use only 2 function parameters now, combine your url params with your headers:

    async getAccount(token) {
            return await this.get('account', {
                param1: param1,
                param2: param2,
                headers: {
                    'Authorization': token
                }
            });
        }