Deploy a Serverless GeoIP API with SST Ion, AWS Lambda, and MaxMind's GeoLite2 Database

In this post, I’ll walk you through how to deploy a basic serverless GeoIP API using SST Ion, AWS Lambda, and MaxMind's GeoLite2 IP database. This API will help you retrieve geographic data from IP addresses, all without the need for a dedicated server.

Prerequisites

You will need an AWS account before getting started. You will also need to configure the AWS CLI and your AWS credentials to follow along.

Disclaimer

This example deploys a bare bones public API. For production environments, consider securing your API with IAM principals and Cognito User Pools for web and mobile clients, or AWS SigV4 signed requests for server clients.

Demo

Step 1: Set Up Your Project

Start by initializing a new directory and setting up the project using Yarn and SST Ion.

% mkdir serverless-geoip-api && cd serverless-geoip-api && yarn init -y

yarn init v1.22.19
warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications.
success Saved package.json
✨  Done in 0.03s.

Now, initialize SST:

% npx sst@latest init

   ███████╗███████╗████████╗
   ██╔════╝██╔════╝╚══██╔══╝
   ███████╗███████╗   ██║
   ╚════██║╚════██║   ██║
   ███████║███████║   ██║
   ╚══════╝╚══════╝   ╚═╝

>  JS project detected. This will...
   - use the JS template
   - create an sst.config.ts

✓  Template: js

✓  Using: aws

✓  Done 🎉

Step 2: Install Dependencies

You'll need MaxMind's GeoIP2-node library to read the GeoLite2 database. You'll need to sign up, download geo-city.mmdb, and place it in a directory named data.

% yarn add -D @maxmind/geoip2-node

Step 3: Configure Your SST Project

Add the following code to your SST configuration file to set up the API Gateway:

/// <reference path="./.sst/platform/config.d.ts" />

export default $config({
  app(input) {
    return {
      name: "serverless-geoip-api",
      removal: "remove",
      home: "aws",
    };
  },
  async run() {
    const api = new sst.aws.ApiGatewayV2("GeoIpApi");
    api.route("GET /", {
      handler: "index.geoip",
      copyFiles: [
        {
          from: "data/geo-city.mmdb",
          to: "data/geo-city.mmdb",
        },
      ],
    });
  },
});

Step 4: Add the GeoIP Lookup Handler Function

Add an index.ts file to the root directory with the code below. This example uses MaxMind’s GeoIP database to look up city data based on the client's IP address, taken from the request context:

import * as path from "path";
const Reader = require("@maxmind/geoip2-node").Reader;
const filePath = path.join(__dirname, "data/geo-city.mmdb");

export async function getGeoCityData(ipAddress: string) {
  if (!ipAddress) {
    return {};
  }
  try {
    const reader = await Reader.open(filePath);
    const response = reader.city(ipAddress);
    return response;
  } catch (error) {
    return {};
  }
}

export async function geoip(event) {
  const ipData = await getGeoCityData(event?.requestContext?.http?.sourceIp);
  return {
    statusCode: 200,
    body: JSON.stringify(ipData, null, 2),
  };
}

Step 5: Deploy Your API

Run the following command to deploy your API:

% npx sst deploy --stage production

SST 3.0.118  ready!

➜  App:        serverless-geoip-api
   Stage:      production

~  Deploy

✓  Complete
   GeoIpApi: https://9gc2wjo7ld.execute-api.us-west-1.amazonaws.com

Step 6: Test Your API

You can now test your API by sending a GET request to the endpoint provided in the output.

% curl https://9gc2wjo7ld.execute-api.us-west-1.amazonaws.com
{
  "continent": {
    "code": "NA",
    "geonameId": 6255149,
    "names": {
      "de": "Nordamerika",
      "en": "North America",
      "es": "Norteamérica",
      "fr": "Amérique du Nord",
      "ja": "北アメリカ",
      "pt-BR": "América do Norte",
      "ru": "Северная Америка",
      "zh-CN": "北美洲"
    }
  },

  ....
}

And that's it! You've successfully deployed a serverless GeoIP API using SST Ion, AWS Lambda, and MaxMind's GeoLite2 database! 🚀🚀🚀