11

set cookies inside a lambda serverless with apollo-server-lambda

I am migrating from apollo-server to the serverless version. Is there a way I can access the response object or another way to set cookies?

context: ({ event, context }) => ({
   headers: event.headers,
   functionName: context.functionName,
   event,
   context,
 }),

I was expecting in the context to have access to the res object like it was in the apollo-server.

Popa Alex
  • 325
  • 2
  • 8

3 Answers3

5

I couldn't find a way to do that using apollo-server-lambda, so what a I did was use apollo-server-express and serverless-http in conjuction. The code below is using import/export because I am using typescript.

serverless-http accepts a variety of express-like frameworks.

import express from 'express'; // <-- IMPORTANT
import serverlessHttp from 'serverless-http'; // <-- IMPORTANT
import { ApolloServer } from 'apollo-server-express'; // <-- IMPORTANT
import typeDef from './typeDef';
import resolvers from './resolvers';

export const server = new ApolloServer({
    typeDef,
    resolvers,
    context: async ({ req, res }) => {
        /**
         * you can do anything here like check if req has a session,
         * check if the session is valid, etc...
         */
        return {
            // things that it'll be available to the resolvers
            req,
            res,
        };
    },
});

const app = express(); // <-- IMPORTANT

server.applyMiddleware({ app }); // <-- IMPORTANT

// IMPORTANT
// by the way, you can name the handler whatever you want
export const graphqlHandler = serverlessHttp(app, {
    /** 
     * **** IMPORTANT ****
     * this request() function is important because 
     * it adds the lambda's event and context object 
     * into the express's req object so you can access
     * inside the resolvers or routes if your not using apollo
     */
    request(req, event, context) { 
        req.event = event;
        req.context = context;
    },
});

Now for instance you can use res.cookie() inside the resolver

import uuidv4 from 'uuid/v4';

export default async (parent, args, context) => {
// ... function code

const sessionID = uuidv4();

// a example of setting the cookie
context.res.cookie('session', sessionID, {
        httpOnly: true,
        secure: true,
        path: '/',
        maxAge: 1000 * 60 * 60 * 24 * 7,
    });
}

another useful resource

  • I had a lot of problems when trying to make both servers, Express and Lambda, work together with cookies using this method... the best solution was to remove cookies and sessions and use JWT –  May 07 '20 at 02:34
4

You need a way to set the response headers in your resolvers.

What you can do is to set a value to the context object in your resolver.

const resolver = (parent, args, { context }) => {
  context.addHeaders = [{ key: 'customheader', value: 'headervalue'}]
}

You can catch the context in willSendResponse event in the server lifecycle by creating a Apollo Server plugin. You can then add your headers from customHeaders property to the GraphQLResponse object.

const customHeadersPlugin = {
  requestDidStart(requestContext) {
    return {
      willSendResponse(requestContext) {
        const {
          context: { addHeaders = [] }
        } = requestContext.context

        addHeaders.forEach(({ key, value }) => {
          requestContext.response.http.headers.append(key, value)
        })

        return requestContext
      }
    }
  }
}

You need to load the plugin in Apollo Server.

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [customHeadersPlugin],
  context: ({ context }) => ({
    context
  })
})

Now you've got a way to modify the response headers in your resolvers. To be able to set a cookie you can either set the Set-Cookie header manually with a cookie string or using a cookie library.


Thanks to Trevor Scheer of the Apollo GraphQL team for pointing me in the right direction when I needed to implement this myself.

shahin8r
  • 113
  • 2
  • 7
4

You can use the apollo-server-plugin-http-headers package.

Usage is as simple as this from within your resolvers:

context.setCookies.push({
    name: "cookieName",
    value: "cookieContent",
    options: {
        domain: "example.com",
        expires: new Date("2021-01-01T00:00:00"),
        httpOnly: true,
        maxAge: 3600,
        path: "/",
        sameSite: true,
        secure: true
    }
});
b2a3e8
  • 71
  • 2