apollo-server-integrations / apollo-server-integration-aws-lambda

An integration to use AWS Lambda as a hosting service with Apollo Server
MIT License
46 stars 9 forks source link

Support for multi value headers #65

Closed plutonian-io closed 1 year ago

plutonian-io commented 1 year ago

I am trying to use this with Netlify and seem to be getting the following error when I try to set multiple values for the "Set-Cookie" response header: error decoding lambda response: error decoding lambda response: invalid type "[]interface {}" for "headers" key "Set-cookie"

My code for setting cookies is as follows:

export const setCookie = (response, name, value, options) => {
  const signed = sign({ [name]: value }, process.env.SECRET_KEY, { expiresIn: 60 * 60 * 2 });
  const data = cookie.serialize(name, signed, options);
  const prev = response.http.headers.get("Set-Cookie") || [];
  const header = Array.isArray(prev) ? prev.concat(data) : [prev, data];
  response.http.headers.set("Set-Cookie", header);
};

Does this package support setting multi value headers via an array like this?

BlenderDude commented 1 year ago

Thanks for bringing this to our attention! I'll talk with a few other maintainers on how we want to go about this.

If you are using anything with a V2 event type (V2 Proxy, ALB, or Function URL), there is a cookies array presented to you on the response object. One option is to do something like this:

// Note the cookie string is now returned
const createCookie = (name, value, options) => {
  const signed = sign({ [name]: value }, process.env.SECRET_KEY, { expiresIn: 60 * 60 * 2 });
  return cookie.serialize(name, signed, options);
}

const server: ApolloServer = ...;
// Store the original handler
const apolloHandler = startServerAndCreateLambdaHandler(server);

export const handler = async (event, context, callback) => {
  // Call it yourself to get access to the result.
  const result = await apolloHandler(event, context, callback);
  // Cookie initialization here (it is not initialized to [] for you)
  result.cookies = [
    createCookie(..., ..., ...),
    createCookie(..., ..., ...),
  ];

  // Example of an optional cookie
  if (xyz) {
    result.cookies.push(createCookie(...,...,...);
  }

  return result;
}
BlenderDude commented 1 year ago

This is now even easier with v2.0.0's middleware syntax.

See the README for more info!

import {
  startServerAndCreateLambdaHandler,
  handlers,
} from '@as-integrations/aws-lambda';
import type { APIGatewayProxyEventV2 } from 'aws-lambda';
import { server } from './server';

async function regenerateCookie(event: APIGatewayProxyEventV2) {
  // ...
  return 'NEW_COOKIE';
}

export default startServerAndCreateLambdaHandler(
  server,
  handlers.createAPIGatewayProxyEventV2RequestHandler(),
  {
    middleware: [
      // Both event and result are intended to be mutable
      async (event) => {
        const cookie = await regenerateCookie(event);
        return (result) => {
          result.cookies.push(cookie);
        };
      },
    ],
  },
);