Mocking API endpoints with Amazon API Gateway Mock Integration
Tags: aws, serverless, typescript, cdk
Published: 2021-08-08 13:23:27

---
title: Mocking API endpoints with Amazon API Gateway Mock Integration
published: true
description: API Gateway integrates with many services, let's get to know the Mock Integration.
tags: aws, serverless, typescript, cdk
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jb74k73qql52frm27qcf.jpeg
---

Amazon API Gateway is a fully managed service for creating REST or WebSocket APIs that scale. The service integrates with many different AWS offerings enabling developers to build backends for their applications.

This blog post will look at one such integration, mainly the _Mock Integration_. The _Mock Integration_ is excellent for prototyping APIs in a fast and efficient manner. It could also be used to unblock teams that depend on the API before the development is complete.

> Code examples in this blog post are written in _Typescript_, and as my IaC of choice, I used AWS CDK.

Let us dive in.

## Hypothetical scenario

The team you are working on is tasked with building a pets API. The business needs are well defined - the user can create a pet resource and list the pet resources he had created.

Your colleagues working on the application's backend need some time to agree on development practices and processes they will stick to during the development. But the frontend team had a head start. They are ready to start interacting with the API.

Since there are still many decisions to be made regarding the backend, you have decided to hack the "create a pet resource" endpoint using the _API Gateway Mock Integration_ unblocking the frontend team in the process.

## Anatomy of the Mock Integration

The beauty of the _Mock Integration_ is that it does not interact with other AWS services like DynamoDB or AWS Lambda. This means that there are no IAM policies or table schemas to define.

The not-so-fun part of the integration are the exposed configuration options. They might seem vague or unnecessarily complicated at first sight. There is a lot of things you could do but most likely do not need to do.

Here is our starting point in terms of the code.

```ts
import * as cdk from "aws-cdk-lib";

const mockPetsAPI = new cdk.aws_apigateway.RestApi(this, "mockPetsAPI", {
  defaultCorsPreflightOptions: {
    allowOrigins: cdk.aws_apigateway.Cors.ALL_ORIGINS,
    allowMethods: cdk.aws_apigateway.Cors.ALL_METHODS
  }
});

mockPetsAPI.root.addMethod(
  "POST",
  new cdk.aws_apigateway.MockIntegration({
    passthroughBehavior: cdk.aws_apigateway.PassthroughBehavior.NEVER,
    requestTemplates: {
      /* ... */
    },
    integrationResponses: [
      /* ... */
    ]
  })
);
```

### The `passthroughBehavior`

This parameter is related to the `requestTemplates` parameter. It controls whether API Gateway will apply the transformations defined within the `requestTemplates` to the request and under what circumstances.

For our use case, I've specified `NEVER` as the `passthroughBehavior`. This means that the API Gateway will try to apply the transformations defined in the aforementioned `requestTemplates` section, no matter the request encoding. If we did not specify a request template for a given encoding, API Gateway would return 415 to the caller.

> For an in-depth deep dive into different passthrough behaviors, please refer to the [official AWS documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/integration-passthrough-behaviors.html).

### The `requestTemplates`

This parameter defines key-value pairs where the key is the request encoding, like `application/json`, and the value is a mapping template used to transform the request.

Think of plucking only the necessary fields from the request body that will be passed to an AWS Lambda function so that you do not have to do any transformations within the Lambda code itself.

Since we are using a Mock Integration, the mapping template will be very concise.

```ts
requestTemplates: {
    "application/json": `{
        "statusCode": 201
    }`
}
```

We are telling API Gateway that all requests sent to this endpoint should be handled with an `integrationResponse` defined for a `statusCode` of 201.
I assumed that all requests that the frontend team will make would be encoded using `application/json` scheme.

Let us talk about the `integrationResponses` next.

## The `integrationResponses`

This parameter defines an array of responses that our _Mock Integration_ can return. As I eluded earlier, the responses are tied to a `statusCode` defined in a given request mapping template.

In our case, we need to define only one integration response - for the 201 `statusCode`.

```ts
integrationResponses: [
  {
    statusCode: "201",
    responseTemplates: {
      "application/json": `{
            "id": "$context.requestId",
            "createdAt": $context.requestTimeEpoch,
            "updatedAt": $context.requestTimeEpoch
        }`
    }
  }
];
```

> Please reference the [API Gateway mapping template documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html) for more information regarding the `$context` variable.

With `integrationResponses` specified, our method configuration looks as follows.

```ts
mockAPI.root.addMethod(
  "POST",
  new cdk.aws_apigateway.MockIntegration({
    passthroughBehavior: cdk.aws_apigateway.PassthroughBehavior.NEVER,
    requestTemplates: {
      "application/json": `{
              "statusCode": 201
            }
          `
    },
    integrationResponses: [
      {
        statusCode: "201",
        responseTemplates: {
          "application/json": `{
                "id": "$context.requestId",
                "createdAt": $context.requestTimeEpoch,
                "updatedAt": $context.requestTimeEpoch
            }`
        }
      }
    ]
  })
);
```

We still have some issues that we have to tackle. One of them being CORS.

## CORS

Since the endpoint we are building is handling `POST` requests, the _Mock Integration_ should return appropriate CORS headers. Otherwise, consumers of the endpoint will be able to make requests to it from the browser environment.

Adding CORS to the _Mock Integration_ is a two-step process.

First, a _method response_ needs to be configured for a given `statusCode`. Within the _method response_, we specify what kind of _request parameters_ the _integration response_ can populate (in our case, the parameters will correspond to CORS headers).

```ts
mockPetsAPI.root.addMethod(
  "POST",
  new cdk.aws_apigateway.MockIntegration({
    /* Previously defined parameters */
  }),
  {
    methodResponses: [
      {
        statusCode: "200",
        responseParameters: {
          "method.response.header.Access-Control-Allow-Origin": true,
          "method.response.header.Access-Control-Allow-Methods": true,
          "method.response.header.Access-Control-Allow-Headers": true
        }
      }
    ]
  }
);
```

Similar to `integrationResponses`, the `methodResponses` is an array of _method responses_. The association between a given _integration response_ and a _method response_ is carried through the `statusCode` parameter.

Secondly, we need to amend our _integration response_ to include the values for the CORS headers.

```ts
mockPetsAPI.root.addMethod(
  "POST",
  new cdk.aws_apigateway.MockIntegration({
    /* Previously defined parameters */
    integrationResponses: [
      {
        statusCode: "201",
        responseTemplates: {
          /* Previously defined template */
        },
        // Populating the CORS headers with specific values.
        responseParameters: {
          "method.response.header.Access-Control-Allow-Methods":
            "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'",
          "method.response.header.Access-Control-Allow-Headers":
            "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
          "method.response.header.Access-Control-Allow-Origin": "'*'"
        }
      }
    ]
  }),
  {
    methodResponses: [
      {
        statusCode: "200",
        responseParameters: {
          "method.response.header.Access-Control-Allow-Origin": true,
          "method.response.header.Access-Control-Allow-Methods": true,
          "method.response.header.Access-Control-Allow-Headers": true
        }
      }
    ]
  }
);
```

As you can see, I'm generous in terms of how open the CORS settings are. I would advise fine-tuning them according to your needs.

## Working with the request body

So far, our API definition looks as follows.

{% details Click to expand %}

```ts
import * as cdk from "aws-cdk-lib";

const mockPetsAPI = new cdk.aws_apigateway.RestApi(this, "mockPetsAPI", {
  defaultCorsPreflightOptions: {
    allowOrigins: cdk.aws_apigateway.Cors.ALL_ORIGINS,
    allowMethods: cdk.aws_apigateway.Cors.ALL_METHODS
  }
});

mockPetsAPI.root.addMethod(
  "POST",
  new cdk.aws_apigateway.MockIntegration({
    passthroughBehavior: cdk.aws_apigateway.PassthroughBehavior.NEVER,
    requestTemplates: {
      // For this particular request (in our case every request that is made to this endpoint)
      // respond with integration response defined for statusCode 201.
      "application/json": `{
            "statusCode": 201
        }`
    },
    integrationResponses: [
      {
        statusCode: "201",
        responseTemplates: {
          "application/json": `{
                "id": "$context.requestId",
                "createdAt": $context.requestTimeEpoch,
                "updatedAt": $context.requestTimeEpoch
            }`
        },
        // These definitions would not be possible without the definition within the `responseParameters` section.
        responseParameters: {
          "method.response.header.Access-Control-Allow-Methods":
            "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'",
          "method.response.header.Access-Control-Allow-Headers":
            "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
          "method.response.header.Access-Control-Allow-Origin": "'*'"
        }
      }
    ]
  }),
  {
    methodResponses: [
      {
        // Everything defined in this section is applicable to the integration response with `statusCode` of 201.
        statusCode: "201",
        responseParameters: {
          // What kind of response parameters are allowed to be defined within the `responseParameters` section.
          "method.response.header.Access-Control-Allow-Origin": true,
          "method.response.header.Access-Control-Allow-Methods": true,
          "method.response.header.Access-Control-Allow-Headers": true
        }
      }
    ]
  }
);
```

{% enddetails %}

Notice that we are not using the request body in any way.

I could not find any information in the official documentation regarding how to return parts of the request body in the context of _Mock Integration_. Thankfully, a very helpful person presented a way how it could be done. [Please refer to this post on StackOverflow](https://stackoverflow.com/a/61482410).

The solution feels hacky because it is. Sadly the _Mock Integration_ is limited in terms of manipulating and referencing the request body.

For full implementation, including returning parts of the body as a response to the POST request, you could refer to an example I have on GitHub.

{% github WojciechMatuszewski/apigw-mockintegration-cognito %}

## Summary

The API Gateway is very powerful. I've written this blog post because I struggled to understand how its pieces together. Learning about different transformations and integrations that are available to me definitely made me a better developer.

I hope that this guide will be helpful to you as writing it was helpful for me.

Questions? Want to reach out? Maybe something I wrote is not necessarily correct? (I'm happy to be corrected).

You can find me on Twitter - [@wm_matuszewski](https://twitter.com/wm_matuszewski)

Thank you for your time.

---

Thumbnail by [Sam Moqadam](https://unsplash.com/@itssammoqadam) from [unsplash](https://unsplash.com/photos/DSn1k2kCriA)