Apollo Gateway on AWS Lambda Cannot read property 'content-type' of undefined

Viewed 1650

I have a Node Apollo Gateway service running on AWS Lambda, it delegates to two different GraphQL services, it all works fine locally on my Mac.

However, when running on AWS Lambda, the gateway gives me this error when I hit the end point (expecting to see the playground)

{
"errorType": "TypeError",
"errorMessage": "Cannot read property 'content-type' of undefined",
"trace": [
"TypeError: Cannot read property 'content-type' of undefined",
"    at fileUploadHandler (/var/task/node_modules/apollo-server-lambda/dist/ApolloServer.js:143:50)",
"    at Runtime.handler (/var/task/node_modules/apollo-server-lambda/dist/ApolloServer.js:166:13)",
"    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
}

I'm not doing anything with uploading, but looking at ApolloServer.js, I can see a handler is made.

My index.js looks like this:

const isLambda = !!process.env.LAMBDA_TASK_ROOT;

const { ApolloServer, gql } = isLambda ? require('apollo-server-lambda') : require('apollo-server');

const { ApolloGateway, RemoteGraphQLDataSource } = require("@apollo/gateway");

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'service1', url: !isLambda ? 'http://127.0.0.1:5090' : 'https://api.blah.com/search' },
    { name: 'service2', url: !isLambda ? 'http://127.0.0.1:5110' : 'https://api.elsewhere.com/other' },
 ],
});

const server = new ApolloServer({
  gateway,
  subscriptions: false,
  uploads: false,
  context: ({ event, context }) => ({
    headers: event.headers,
    functionName: context.functionName,
    event,
    context,
  }),
  playground: true,
  introspection: true,
});

if (!isLambda) {
    server.listen().then(({ url }) => {
      console.log(` Server ready at ${url}`);
    });
} else {
    exports.graphql = server.createHandler({
      cors: {
        origin: '*',
        credentials: true,
      },
    });
}

The NPM modules I'm using with this are:

npm install  @apollo/gateway apollo-server-lambda graphql

Any pointers on what might be going wrong here?

Update 25th May 2020 It seems to be related to whether I choose between HTTP or REST in AWS Gateway, HTTP gets me past this error, but I've no idea what I need to do to make it work in REST

Thanks

Chris

2 Answers

You won't make it work in REST because apollo's code was not written thinking about receiving input from AWS API Gateway.

It expects to handle the whole request. So either you use the API Gateway as proxy or you'll have to fake all the other properties that it is expecting to exist.

This is what it took for me to fix the problem without having to add AWS specific changes. Note that this is in Typescript, I hope that's okay:

I wrapped my call in a main function and put this in


function runApollo(event: APIGatewayProxyEvent, context: Context, apollo: any) {
  return new Promise((resolve, reject) => {
    const callback = (error: any, body: any) =>
      error ? reject(error) : resolve(body);
    apollo(event, context, callback);
  });
}

export async function main(
  event: APIGatewayProxyEvent,
  context: Context,
  cb: Callback
) {
  const apollo = server.createHandler({
    cors: {
      origin: "*",
      credentials: true,
    },
  });

  // HERE IS WHERE YOU BACKFILL THE HEADERS
  event.httpMethod = event.httpMethod || "POST";
  event.headers = {
    "content-type": "application/json",
    ...(event.headers || {}),
  };
 // END BACKFILL

  try {
    const response = await runApollo(event, context, apollo);
    return response;
  } catch (err) {
    cb(err, null);
  }
}

I have other reasons for the wrapper, adding monitoring, logging, and such. But this solved my problem. I haven't run this code (as I just pulled out the stuff that was specific to my approach), but the part you really need is in between the comments.

Related