Build a Geolocation API using AWS Lambda and MaxMind

Building a Geolocation API using AWS Lambda, API Gateway, and MaxMind DB and Serverless Framework. Building a Geolocation API using AWS Lambda, API Gateway, and MaxMind DB and Serverless Framework.

Build a Geolocation API using AWS Lambda and MaxMind
Photo by Tobias Rademacher / Unsplash
To build our API we will be using AWS Lambda, API Gateway and MaxMind DB. We’ll also need Serverless framework for developing and deploying our API.

To get started make sure you have Serverless framework installed, you can do that by just running the following command in terminal.

npm install -g serverless

Once we’ve serverless installed, we can create a new folder for our project and then inside the folder we can run the serverless create command for initializing our project.

➜  geo-api serverless create --template aws-nodejs
Serverless: Generating boilerplate...
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.27.2
 -------'
Serverless: Successfully generated boilerplate for template: "aws-nodejs"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name

Once the command is run successfully you should see what is shown above. Inside the folder, you can see that Serverless has created two files, handler.js and serverless.yml. Now that we’re ready to get our hands dirty, go ahead open the folder inside an editor and start coding.

handler.js will contain the actual javascript code that will be executed during lambda invocation. serverless.yml is the configuration file that basically defines your service. You can read more about the core concepts here.

The first step would be configuring our service using the serverless.yml file. Here is how it show look:

service: geolocation-api
provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: us-east-1
functions:
  fetchLocationData:
    handler: handler.fetchLocationData
    events:
      - http:
          path: location
          method: get
          cors: true
  • service — a unique name for the micro service, which will be used while creating the cloud formation stack, lambda functions, API gateways etc.
  • provider — the cloud provider that we will be using is aws and we will be running our code on nodejs8.10 runtime, which is the latest one.
  • stage — our environment name
  • region — AWS region to which we will be deploying
  • functions — list of functions in our service.

In the above file, we’ve defined a function fetchLocationData and the handler or the function that is supposed to be executed is inside the file handler.js and the function name is fetchLocationData. Also, we’ve added an API gateway trigger event for this lambda function. The path is /location and the method is GET, we’ve also enabled CORS for this endpoint. You can read more about API gateway event here.

Since we updated the runtime to nodejs8.10 we need to make some changes to the handler also. The new runtime natively supports async/await, so we can make use of that to make our code more concise and cleaner. You can read more about the new runtime here. Our updated code will be:

"use strict"
module.exports.fetchLocationData = async event => {
    const response = {
        statusCode: 200,
        body: JSON.stringify({
            message: "Go Serverless v1.0! Your function executed successfully!",
            input: event
        })
    }
return Promise.resolve(response)
}

As of now our code basically doesn’t nothing, it will just send a 200 OK response with a message and our input event. To see it in action lets deploy our service using the command sls deploy

➜  geo-api sls deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (338 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.................................
Serverless: Stack update finished...
Service Information
service: geolocation-api
stage: dev
region: us-east-1
stack: geolocation-api-dev
api keys:
  None
endpoints:
  GET - https://6ny7ze91yk.execute-api.us-east-1.amazonaws.com/dev/location
functions:
  fetchLocationData: geolocation-api-dev-fetchLocationData

After finishing the deployment you can see our new API URL and on opening that you should see our code come alive!

JSON response from our new API endpoint

Next we need to download GeoLite2 database from MaxMind https://dev.maxmind.com/geoip/geoip2/geolite2/, and we will also be using the maxmind npm package to query the GeoLite2 database. You can see the finished handler.js file in this github repository https://github.com/arjunkomath/serverless-geolocation-api, the code is fairly simple and easy to understand. Note that, in order to query the MaxMind database, we need the IP address of the client. We get this from the event object that is available inside our lambda function. The source IP of all request is available by default in the API gateway event that triggers our lambda. Alternatively, we can send the IP address as a parameter to our request, but we will just stick to the current implementation for now.

In case you have any doubts regarding the code, feel free to get in touch. And as always feel free to contribute to the project, PRs are always welcome!

Finally we need to add a plugin to ensure that our Lambdas never go cold. A cold start occurs when an AWS Lambda function is invoked after not being used for an extended period of time resulting in increased invocation latency. To avoid this we use a plugin — Serverless WarmUP Plugin (https://github.com/FidelLimited/serverless-plugin-warmup). We need to make few changes in our serverless.yml and handler.js file to support warmup plugin. Our updated serverless.yml file will be:

service: geolocation-api
provider:
  name: aws
  runtime: nodejs8.10
  memorySize: 256
  iamRoleStatements:
    - Effect: 'Allow'
      Action:
        - 'lambda:InvokeFunction'
      Resource:
      - Fn::Join:
        - ':'
        - - arn:aws:lambda
          - Ref: AWS::Region
          - Ref: AWS::AccountId
          - function:${self:service}-${opt:stage, self:provider.stage}-*
  stage: dev
  region: us-east-1
plugins:
  - serverless-plugin-warmup
functions:
  fetchLocationData:
    handler: handler.fetchLocationData
    warmup: true
    events:
      - http:
          path: location
          method: get
          cors: true

We’ve added an IAM Role that allows the warmup plugin to invoke our lambda functions, also added serverless-plugin-warmup to plugins and finally set warmup as true for our fetchLocationData function. Now you can deploy and start testing the API :)