Building Rate Limiter based on IP Address with Netlify Blobs and Edge Functions
Rishi Raj Jain
Posted on November 15, 2023
In this guide, we'll explore a streamlined approach to rate limit users using recently released Netlify Blobs. By the end, you'll have an edge function that'd rate limit users based on their IP Address.
Setup a Node.js Project
Let's start by creating a directory and initializing a Node.js project:
mkdir rate-limiter
npm init -y
Install Netlify Blobs
Install Netlify Blobs
package to manage data storage of usage per ip address to rate limit the users with.
npm i @netlify/blobs
Setup Netlify Edge Functions
Get started by creating Netlify Edge Functions by creating the default netlify/edge-functions
directory with a index.js
file as one of the edge function(s).
mkdir netlify/edge-functions
touch index.js
Setup Netlify Configuration
Create a netlify.toml
at the root of your project with the following content:
[[edge_functions]]
function = "index"
path = "/*"
[build]
publish="."
The code above makes sure that index.js
is used to serve incoming requests for all paths on your deployment.
Set Rate Limiting Config
In the newly created index.js
, set the maximum number of reuqests (here, maxUpdates
) in the set interval (here, timeSpan
) that do not exceed the rate limits.
// File: index.js
const rateLimitConfig = { maxUpdates: 3, timeSpan: 86400 }
export default async (_, context) => {
// Rate limiting logic here
}
Create Unique User Identifier
Retrieve the user's IP address from the Netlify Edge Functions context object
Use the IP address as a unique identifier for rate limiting via creating the
identifier
Load the store (here, named
visits
) that keeps the collection of all the unique idenitifersTry to look for an existing record for the particular identifier
// File: index.js
import { getStore } from '@netlify/blobs'
export default async (_, context) => {
// Create a unique identifier based on user's IP
const identifier = 'limiter_' + context.ip
// Get the visits store
const visits = getStore('visits')
const t = await visits.get(identifier)
// If a rate limiting key is found
if (t) {
// Continue Rate limiting logic here
}
else {
// Continue Rate limiting logic here
}
}
Flow of Rate Limiting based on the Stored Value of User Usage Record
If a rate limiting key is found for the user's IP:
- Retrieve the rate limit data for the key
- Calculate the time difference since the last update
- Reset the count if the time window has passed
- Check if the request count is below the limit
- Increment the count if within the limit
- Return appropriate responses based on the conditions
// File: index.js
// If a rate limiting key is found
if (t) {
// Get rate limit data for the key
const { count_updates, last_update } = JSON.parse(t)
// Calculate time difference in seconds
const currentTime = new Date().getTime()
const timeDifference = Math.floor((currentTime - new Date(last_update).getTime()) / 1000)
// If time window has passed, reset count
if (timeDifference >= rateLimitConfig.timeSpan) {
await visits.set(identifier, JSON.stringify({ count_updates: 1, last_update: currentTime }))
// Continue with your processing
return new Response(`Reload 2 more times to exceed the rate limit.`)
}
// Check if the request count is below the limit
if (count_updates < rateLimitConfig.maxUpdates) {
// Increment the number of updates in the store
await visits.set(identifier, JSON.stringify({ count_updates: count_updates + 1, last_update: new Date().getTime() }))
// Continue with your processing
return new Response(`Reload ${rateLimitConfig.maxUpdates - count_updates} more time(s) to exceed the rate limit.`)
} else {
// If the limits equal or exceeds, return with a rate limit exceeded message
return new Response(`Rate limit exceeded.`)
}
}
else {
// If a key is not found, set the key with a single update and continue
}
If no rate limiting key is found:
- Sets a new key with a single update for the user
// File: index.js
// If a rate limiting key is found
if (t) {
// Continue Rate limiting logic here
}
else {
// If a key is not found, set the key with a single update and continue
await visits.set(identifier, JSON.stringify({ count_updates: 1, last_update: new Date().getTime() }))
return new Response(`Reload 2 more times to exceed the rate limit.`)
}
Deployment and Live Demo
All done, let's deploy our rate limiter now!
Deploying to Netlify
# Deploy to Netlify
netlify deploy --prod
Live Demo
Explore the live example at tryblobs.netlify.app, and witness the Rate Limiter in action.
Posted on November 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.