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.
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.
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.
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 🎉
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
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",
},
],
});
},
});
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),
};
}
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
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! 🚀🚀🚀