Alright, let’s talk integrations. This is where the rubber meets the road for your API Gateway. You’ve defined your route, and now you have to tell the Gateway what to do when a request hits it. Think of it less like a “gateway” and more like a hyper-intelligent, slightly pedantic traffic cop. It won’t do the work itself, but it will direct the request to the service that will, and it’s very particular about how you package the instructions.

The four integration types are your cop’s list of possible destinations. Choosing the right one is the difference between a smooth ride and getting stuck in a bureaucratic roundabout.

Lambda Proxy: The “Just Handle It” Integration

This is the one you’ll use 90% of the time if you’re building serverless applications. It’s the “look, just take the whole mess of the incoming request and figure it out yourself” option.

Here’s the deal: API Gateway does the absolute minimum transformation. It takes the entire HTTP request—headers, query string parameters, path parameters, body, the kitchen sink—wraps it into a big JSON object, and shoves it directly into your Lambda function.

Your Lambda function is then responsible for unpacking that JSON object, doing the work, and returning a very specific response format that API Gateway can then transform back into an HTTP response.

Why you’d use it: It’s incredibly flexible. Since your Lambda gets the raw request, you can handle any HTTP event shape you want. It’s also dead simple to set up.

The big gotcha: The response format. Forget this and you’ll get a dreaded { "message": "Internal server error" } with a 502 status code. It’s the Gateway’s way of saying, “I have no idea what my Lambda just handed me.” Your Lambda must return an object with statusCode, headers, and body.

// A Lambda function for a Lambda Proxy integration
exports.handler = async (event) => {
    // The entire request is in the 'event' object
    const name = event.queryStringParameters?.name || 'World';
    const userId = event.pathParameters.userId; // from a route like /users/{userId}

    console.log('The full event:', event); // Seriously, do this once. It's a sight to behold.

    // You MUST format the response like this. No exceptions.
    const response = {
        statusCode: 200,
        headers: {
            'Content-Type': 'application/json',
            'My-Custom-Header': 'SomeValue'
        },
        body: JSON.stringify({
            message: `Hello, ${name}!`,
            userId: userId
        }),
    };
    return response;
};

HTTP Proxy: The “Pass-Through” Integration

This is for when you need to integrate with an existing HTTP backend—like a legacy application running on EC2, or even a different API somewhere on the internet. The word “proxy” is key here. API Gateway essentially forwards the request to your backend with minimal modification.

It’s similar to Lambda Proxy in that it passes the request through, but the response handling is different. With Lambda Proxy, your Lambda must format the response correctly. With HTTP Proxy, your backend just needs to return a standard HTTP response (status, headers, body). API Gateway will then map that response back to the client.

Why you’d use it: Integrating with non-AWS, existing HTTP services.

The sneaky pitfall: You might think you can use this to call a third-party API directly. You can, but you probably shouldn’t. You’re now paying for API Gateway’s data transfer out twice (in to Gateway, out to the internet, back in to Gateway, back out to the client). It’s also adding latency. It’s often better for the client to call the API directly, or for your Lambda to handle the external call.

AWS Service: The “Direct Line” Integration

This is where API Gateway gets really powerful. You can have an API endpoint directly interact with an AWS service without a Lambda function in the middle acting as a translator. Want a POST /users to put an item directly into DynamoDB? Want a GET /usage-report to start a Step Function execution? This is how.

It works by using AWS’s proprietary signature version 4 (SigV4) to sign the request and transform the incoming HTTP request into the target service’s API call.

Why you’d use it: It can be cheaper and faster for simple operations, as you’ve completely cut out the Lambda compute cost.

The massive “but”: The configuration is a beast. You’re using Velocity Template Language (VTL) to map the incoming request to the service’s API call, and then mapping the service’s response back to an HTTP response. VTL is… an acquired taste. It’s powerful but feels like a different, more verbose era of AWS.

# A snippet of the OpenAPI spec for a direct DynamoDB PutItem integration.
# This is why most people use Lambda Proxy.
x-amazon-apigateway-integration:
  type: aws
  httpMethod: POST
  uri: arn:aws:apigateway:us-east-1:dynamodb:action/PutItem
  credentials: arn:aws:iam::123456789012:role/apigateway-dynamodb-role
  requestTemplates:
    application/json: |
      {
        "TableName": "MyTable",
        "Item": {
          "id": {
            "S": "$input.path('$.id')"
          },
          "name": {
            "S": "$input.path('$.name')"
          }
        }
      }
  responses:
    default:
      statusCode: "200"

Mock: The “Fake It ‘Til You Make It” Integration

This does exactly what it says on the tin. It returns a hardcoded response without calling any backend. It’s not for production, but it’s incredibly useful.

Use cases:

  1. Testing & Development: You’ve designed your API but haven’t built the backend logic yet. Mock integrations let you prototype the frontend against real endpoints.
  2. API Models: You can use it to return examples that match your OpenAPI specification.
  3. CORS Preflight (OPTIONS) Requests: The classic use case. You can mock the response to OPTIONS requests to return the CORS headers without hitting a backend.

Best practice: Use it heavily in development, but remember to swap it out for a real integration before you deploy. It’s the training wheels of API Gateway.