Orchestrating AWS Lambda with GraphQL and Apollo Connectors

AWS Lambda is a computing service that enables you to run arbitrary code functions without needing to provision, manage, or scale servers. It’s often used in the logic tier of a multi-tier architecture to handle tasks such as processing files in S3 o...

Mar 25, 2025 - 20:49
 0
Orchestrating AWS Lambda with GraphQL and Apollo Connectors

AWS Lambda is a computing service that enables you to run arbitrary code functions without needing to provision, manage, or scale servers. It’s often used in the logic tier of a multi-tier architecture to handle tasks such as processing files in S3 or performing CRUD operations on a database.

AWS also offers an API Gateway, allowing developers to invoke AWS Lambda functions, which provides enhanced security and performance features like rate limiting. But even with the API Gateway, you have to coordinate these microservices, as your client applications likely each have unique data needs. Data might need to be transformed, filtered, or combined before it is returned to the client.

These orchestration tasks can reduce your productivity and take time and effort away from solving the business problem your application is trying to solve.

Apollo GraphQL is an API orchestration layer that helps teams ship new features faster and more independently by composing any number of underlying services and data sources into a single endpoint. This allows clients on-demand access to precisely what the experience needs, regardless of the source of that data.

This article will teach you how to orchestrate AWS Lambda functions using Apollo GraphQL. Specifically, here’s what we will cover:

GraphQL Primer

For those unfamiliar with GraphQL, here’s a primer that offers some background on the challenges GraphQL addresses and how data is typically managed through REST APIs in GraphQL before the emergence of Apollo Connectors. If you’re familiar with GraphQL, feel free to skip this section.

GraphQL is a query language for APIs. This query language and corresponding runtime enable clients to specify exactly the data they require, minimizing over-fetching and under-fetching.

In contrast to REST, which necessitates multiple endpoints for various data requirements, GraphQL streamlines queries into a single request, enhancing performance and reducing network latency.

GraphQL also uses a strongly typed schema. This improves API documentation and makes validation, early error detection, and immersive developer tooling easy.

To illustrate the difference between REST APIs and GraphQL, consider the following REST API call: /user/123

Response:

{
  "id": 123,
  "name": "Alice Johnson",
  "email": "alice@example.com",
  "phone": "555-1234",
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "state": "IL",
    "zip": "62704"
  },
  "createdAt": "2022-01-01T12:00:00Z",
  "updatedAt": "2022-05-15T14:30:00Z",
  "isAdmin": false
}

If you were only interested in the name and email, using REST would be a lot of data returned from the network to the client for no reason. Using GraphQL, the GraphQL query to return the name and email would be the following:

query {
  user(id: 123) {
    name
    email
  }
}

The result set is just the data the client needs:

{
  "data": {
    "user": {
      "name": "Alice Johnson",
      "email": "alice@example.com"
    }
  }
}

This is a simple example showing the benefit of not over-fetching data, but GraphQL has many other advantages. One of them is the separation between client and server. Since both parties leverage and respect the GraphQL type schema, both teams can operate more independently with the back end defining where the data resides and the front end only asking for data it needs.

So how does GraphQL know how to populate data for every field in your schema? It does this through resolvers. Resolvers can fetch data from a back-end databases or third-party API such as REST APIs, gRPC, and so on. These functions comprise procedural code compiled and maintained for each field in the schema. Thus, one field can have a resolver that queries a REST API and another can query a gRPC endpoint.

To illustrate resolvers, consider the example above. Let’s add a field, status, that queries a REST API to determine if the user is full-time, part-time, or terminated.

First we have defined our schema as:

type User {
  id: ID!
  name: String!
  email: String!
  status: String!  # Need this from an external REST API
}

type Query {
  user(id: ID!): User
}

The user query in this case will accept a user id and return a type User. The resolver function to support the data fetching resembles the following:

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      // Fetch user details from one REST API
      const userResponse = await fetch(`https://api.company.com/users/${id}`);
      const userData = await userResponse.json();

      // Fetch employee status from another REST API
      const statusResponse = await fetch(`https://api.company.com/employees/${id}/status`);
      const statusData = await statusResponse.json();

      return {
        id: userData.id,
        name: userData.name,
        email: userData.email,
        status: statusData.status, // e.g., "Full-Time", "Part-Time", "Terminated"
      };
    },
  },
};

Notice that not only are there two fetches needed to obtain the information the query requires, but we also need to write procedural code and deploy it.

A better approach would be to declaratively specify to GraphQL where the REST API is located and what data to return. Apollo Connectors is the solution to this challenge, simplifying the process and allowing you to declaratively integrate REST API data without requiring code compilation and maintenance.

Now that you have a general idea of GraphQL and the challenges it addresses, let’s delve into the example we will build out.

Tutorial Overview

In this tutorial, you will create two AWS Lambda functions that return product information, which are described as follows:

Products Request:

POST /2015-03-31/functions/products/invocations

Response:

{
  "statusCode": 200,
  "body": [
    {
      "id": "RANQi6AZkUXCbZ",
      "name": "OG Olive Putter - Blade",
      "description": "The traditional Block in a blade shape is made from a solid block of Olive wood. The head weight is approximately 360 grams with the addition of pure tungsten weights. Paired with a walnut center-line and white accents colors.",
      "image": "https://keynote-strapi-production.up.railway.app/uploads/thumbnail_IMG_9102_3119483fac.png"
    },
    {
      "id": "RANYrWRy876AA5",
      "name": "Butter Knife Olive Putter- Blade",
      "description": "The traditional Block in a extremely thin blade shape (~1\") is made from a solid block of Olive wood. The head weight is approximately 330 grams with the addition of pure tungsten weights.",
      "image": "https://keynote-strapi-production.up.railway.app/uploads/thumbnail_IMG_9104_97c221e79c.png"
    },...

Product-price request:

POST: /2015-03-31/functions/product-price/invocations

Response:

{
  "default_price": 49900,
  "is_active": true,
  "currency": "usd",
  "billing_schema": "per_unit",
  "recurring": {
    "interval": 0,
    "interval_count": 3
  }
}

To expose these two lambda microservices, you need to create API Gateway triggers. This involves either setting up a distinct API Gateway for each lambda or consolidating them under one or a few API Gateway instances with specified routes for each lambda.

Creating a trigger may feel tedious and repetitive in a microservices setup. But there is an alternative available. You could directly invoke those functions via REST using the InvokeFunction permission assigned to an IAM user. This article will show you this method and guide you through function creation, necessary AWS IAM permissions, and configuring the Apollo Connector to invoke the function.

Prerequisites

To follow along in this tutorial, you will need to have a basic understanding of AWS Lambda functions as well as AWS security. You’ll also need access to the following:

We will also use the following tools:

  • VS Code: Microsoft VS Code is a free source code editor from Microsoft

  • Apollo Rover CLI: Rover is the command-line interface for managing and maintaining graphs

  • Apollo Studio: A web-based portal used for managing all aspects of your graph

  • Apollo Connectors Mapping Playground: A website that takes a JSON document and helps developers create the selection mapping used with Apollo Connectors

Section 1: Create the AWS Resources

First, let’s configure our AWS environment, starting with security. In our scenario, we will create an IAM User, “ConnectorUser,” with access to an AWS Policy, “ConnectorLambdaPolicy,” with the minimum permissions needed to access the AWS Lambda functions.

Note that you could create user groups and assign permission policies to those groups in a production environment. But for this article, we are reducing the number of administrative steps to focus on the core integration with GraphQL.

Step 1: Create an AWS Policy

To create a policy, navigate to IAM within the AWS Management console, then select “Policies” under Access Management. Click “Create Policy”. This will open the policy editor page, as shown below:

specify permissions

Choose the “Lambda” service and under the Access level select “InvokeFunction” from the Write drop down menu as shown below:

InvokeFunction checkmarked

Under the Resources menu, you can choose either All ARNs or a specific option. It's a best practice to be as granular as possible when defining security configurations. In this example, let’s limit our selection to the “us-east-1” region by clicking on the “Specific” option and then “Add ARNs.” Enter “us-east-1” in the resource region and select “Any function name.”

Specify ARN dialog

With the policy created, we can assign an IAM user to that policy.

Step 2: Create the IAM User and Attach a Policy

Click on Users under “Access Management” then Create User. Provide a name for the user, “ConnectorUser”.

Permission policy

Next, select “Attach policies directly,” choose the policy we just created, “ConnectorLambdaPolicy,” and click “Create User.”

Step 3: Create AWS Lambda Functions

In your AWS console, create a new NodeJS AWS Lambda function, “products”.

AWS create function dialog

Select “Node.JS” for the runtime then click “Create function”. Once created, paste in the the function code from this Gist.

AWS function showing code source

Repeat this process, creating another function for, “product-price” and use the function code from this Gist.

Section 2: Create an Apollo Connector

In this section, we will install the Apollo Rover CLI tool, create an Apollo Studio free tier account, and clone the Apollo Connectors repository. If you already have an Apollo environment available, you can skip steps 1 and 2.

Step 1: Install Rover

Rover is the command-line interface for managing and maintaining graphs. It also provides a modern hot-reloading experience for developing and running your connectors locally. If you don’t have Rover installed, install it by following the steps here.

Step 2: Create an Apollo Studio Free Tier Account

Apollo Studio is a cloud-based management platform designed to explore, deliver, and collaborate on graphs. If you do not have an Apollo Studio account, create one on a free plan by navigating here.

Apollo Studio

Step 3: Clone the Apollo Connectors Repository

To help you start your first Apollo Connector, a GitHub repository provides sample connectors and a template script. When run, this script will create all the necessary files and configurations you need to begin.

Go ahead and clone the repository from here.

Note: While not required, I recommended using VS Code, as this repo leverages VS Code-specific settings files.

Step 4: Create a .env File

Before you run the Create Connectors template script, create a .env locally with a user API key from your Apollo Studio. You can create and obtain this key here. Populating this .env file will add this API key to the connector template you create in the next step.

.env file

Step 5: Create Your New Connector from a Template

Execute npm start and provide a location to create the connector template. You can use default values for the remaining questions.

npmstart

This script will create all the necessary files to run a local Apollo GraphQL instance in the specified directory. Load the newly created connector using VS Code or your preferred code editor. You will return to this editor soon, but first, we need to obtain some access keys from AWS.

Step 6: Create an AWS Access Key

Since we connect to AWS using SigV4, we must create an AWS access key and enter the KEY values in the settings.json file. Return to the AWS IAM Console and select the ConnectorUser you created in Step 1. Create a new access key by clicking on “Create access key”.

You will be presented with multiple options as far as where the use of this key will originate. Since we are first running locally, select “Third-party service” and then continue the wizard until you are presented with the key and secret key as shown below:

retrieve access key dialog

Add the access key and secret access key to the settings.json file as “AWS_ACCESS_KEY_ID” and “AWS_SECRET_ACCESS_KEY” respectively.

vscode settings file

You'll need to reload the window since VS Code only loads these files under the .vscode directory once.

vscode task window showing reload option

Note: In this step, we saved the key to the settings.json file. While this is acceptable for development, consider saving environment variables in .env files.

Step 7: Configure the Graph

The supergraph.yaml file is used to define all the subgraphs that are part of this federation. Modify the supergraph.yaml file as follows:

federation_version: =2.10.0
subgraphs:
  awsconnector:
    routing_url: http://lambda
    schema:
      file: connector.graphql

Step 8: Configure Apollo Router

Apollo Router supports AWS SigV4 authentication. To configure the connector to use this, modify the router.yaml file and add an authentication section as follows:

authentication:
  connector:
    sources:
      awsconnector.lambda:   # subgraph name . connector source name
        aws_sig_v4:
          default_chain:
            region: "us-east-1"
            service_name: "lambda"

There are other AWS security configuration options available, including using assume role. The full documentation for subgraph authentication is available here.

Step 9: Build the connector

Now that we have configured the environment variables and authentication information, we are ready to build the connector. Open the connector.graphql file and erase the contents. Next, copy the following extend schema:

extend schema
  @link(
    url: "https://specs.apollo.dev/federation/v2.10"
    import: ["@key"]
  )
  @link(
    url: "https://specs.apollo.dev/connect/v0.1"
    import: ["@source", "@connect"]
  )

  @source(
    name: "lambda"
    http: { baseURL: "https://lambda.us-east-1.amazonaws.com" }
  )

Extend schema is used to link the Apollo Connectors directives into the current schema. In this article we are defining the base URL of our lambda function. If your REST API has HTTP headers that apply to all references of this source, such as Content-Length restrictions, you can add them here in the @source declaration. Next, let’s define the Product schema:

type Product {
  id: ID!
  name: String
  description: String
  image: String
  price: Price
    @connect(
      source: "lambda"
      http: {
        POST: "/2015-03-31/functions/product-price/invocations"
        body: """
        product_id: $this.id
        """
      }
      selection: """
      amount: default_price
      isActive: is_active
      currency
      recurringInterval: recurring.interval -> match(
        [0,"ONE_TIME"],
        [1,"DAILY"],
        [2,"MONTHLY"],
        [3,"ANNUALLY"],
      )
      recurringCount: recurring.interval_count
      """
    )
}

Notice our query Products has an @connect directive that defines, at a minimum, the source name. Here, you can add the HTTP-specific configuration you need for this field, such as Authorizations headers. In this scenario, since we only defined a baseUrl in the extend schema section, we need to put the specific URL for the InvokeFunction, which is /2015-03-31/functions/product-price/invocations.

The selection field allows you to transform and map values returned from the REST API using the mapping definition defined in the selection field. While a complete discussion of selection mapping is beyond the scope of this article, check out the documentation for a detailed look at Mapping GraphQL Responses. Apollo provides a free online tool that makes building mappings intuitive and fast.

connectors mapping playground

Next, let’s define the Price schema and products Query.

type Price {
  amount: Float
  isActive: Boolean
  currency: String
  recurringInterval: RecurringInterval
  recurringCount: Int
}
enum RecurringInterval {
  ONE_TIME
  DAILY
  MONTHLY
  ANNUALLY
}

type Query {
  products: [Product]
    # https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html
    @connect(
      source: "lambda"
      http: { POST: "/2015-03-31/functions/products/invocations" }
      selection: """
      $.body {
        id
        name
        description
        image
      }
      """
    )
}

Now we're ready to run our connector and issue queries to our graph! The complete configuration script is available at this Gist.

Step 10: Run the Connector

If you're using VS Code, the repository includes a tasks.json file that adds a “rover dev” task, which launches Rover locally.

{
    "version": "2.0.0",
    "tasks": [{
        "label": "rover dev",
        "command": "rover", // Could be any other shell command
        "args": ["dev", "--supergraph-config","supergraph.yaml", "--router-config","router.yaml"],
        "type": "shell",
        "problemMatcher": [],
    }]
}

If you are not using VS Code, you can start your graph by executing rover dev –supergraph-config supergraph.yaml –router-config router.yaml from a terminal window.

If everything is configured correctly, you’ll see the following:

running rover dev command

Section 3: How to Use Apollo Sandbox

The rover dev command you launched in the previous step configures a local Apollo Router instance for development mode. This mode makes it easy for developers to create, execute, and debug ad-hoc GraphQL queries using the Apollo Sandbox web portal. This portal is located at http://localhost:4000 by default.

Launch the portal and click on the products field. This will populate the Operation pane with all the available fields in the schema. In the operation pane, you can modify and build your GraphQL query. Clicking the Run button (which displays the query name, Products, in our example) will execute the query and show the results in the Response panel, as illustrated in the figure above.

In this example, you can see that data has been returned from our AWS Lambda function. To confirm, you can view the query plan by selecting "Query Plan” from the Response drop-down menu.

query plan menu item

The query plan illustrates the orchestration of our two AWS Lambda functions that fetch product and product price data.

query plan

A helpful debugging feature is the Connectors Debugger, available in the drop-down as shown in the previous figure.

debugger showing request overview

The Connection Debugger provides a comprehensive view of the HTTP request, including headers, body, response code, and the selection mapping used in the query. If you’re experiencing difficulties running queries, use this debugger – it will save you a lot of time.

Summary

In this article, you learned how to:

  • Set up AWS IAM User, Policies, and Lambda functions

  • Create an Apollo Connector to obtain data from an AWS Lambda function

  • Configure the Apollo Router

  • Execute and debug queries using Apollo Sandbox

Integrating AWS Lambda with Apollo Connectors offers a simplified, resolver-free method for incorporating cloud functions into your GraphQL API. By utilizing Apollo Connectors, you can declaratively link REST-based Lambda functions to your supergraph while ensuring secure authentication with AWS SigV4.

You can learn more about Apollo Connectors from the following resources:

  1. Tutorial: GraphQL meets REST, with Apollo Connectors

  2. Blog: Discover how Apollo Connectors integrate with Apollo Federation through insights from Apollo's Founder & CTO: REST API Orchestration with GraphQL.

  3. Blog: Delve into the engineering journey behind Apollo Connectors and the process of their creation: Our Journey to Apollo Connectors

  4. Webinar: Apollo Connectors GA Launch Webinar