Occasionally you need to know the API Gateway URL for your services inside your Lambda. This happened to me recently when one of my Lambda’s needed to provide a callback URL to a third party service that it was using.

It seems that a lot of people are solving this problem by deploying their API’s using Serverless, then copying the URL and redeploying again with that URL hard coded into their serverless.yml as an environment variable.

If you’re one of those people then STOP. This is both the hard way and it has two very negative side effects.

  1. It’s difficult to deploy your application to other stages, and
  2. It introduces a risk that you’ll break something by depolying to one stage with the API Gateway URL for the different stage.

So how do I get around this?

Instead of hard coding the API GW URL you can reconstruct it using a combination of Serverless variables and CloudFormation. Here’s how I set the API gateway URL so it’s passed to my Lambda as the GW_URL environment variable.

provider:
  environment:
    GW_URL:
      Fn::Join:
        - ""
        - - "https://"
          - Ref: "ApiGatewayRestApi"
          - ".execute-api.${self:custom.region}.amazonaws.com/${self:custom.stage}"

Inside my code I can now reference the current API GW URL using.

process.env.GW_URL

If you’re using the serverless-offline plugin (or similar) then you will probably want to combine this with per stage environment variables. You’ll need to use per stage environment variables because this approach assumes you’re deploying with CloudFormation but running it locally means no CloudFormation to resolve the URL. Instead you’ll need to hard code a URL for local development.

The “cheat sheet” version is below but I strongly recommend you read the post as the full version explains it in more depth and covers topics like having separate files for each environment.

provider:
  environment:
    GW_URL: ${self:provider.${self:custom.stage}.GW_URL}

custom:
  stage: "${opt:stage, self:provider.stage}"
  prod:
    GW_URL: { "Fn::Join" : ["", [ "https://", { "Ref" : "ApiGatewayRestApi" }, ".execute-api.${self:custom.region}.amazonaws.com/${self:custom.stage}" ] ]  }
  dev:
    GW_URL: "http://localhost:3000/"

Using this technique you can also export your API Gateway URL from your CloudFormation stack so that it can be imported into other stacks. This is one way to solve the service discovery problem.

resources:
  Outputs:
    ApiUrl:
      Description: "The API Gateway URL"
      Value:
        Fn::Join:
          - ""
          - - "https://"
            - Ref: ApiGatewayRestApi
            - ".execute-api.${self:custom.region}.amazonaws.com/${self:custom.stage}"