Building Rate Limiter based on IP Address with Netlify Blobs and Edge Functions

reeshee

Rishi Raj Jain

Posted on November 15, 2023

Building Rate Limiter based on IP Address with Netlify Blobs and Edge Functions

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Setup Netlify Configuration

Create a netlify.toml at the root of your project with the following content:

[[edge_functions]]
  function = "index"
  path = "/*"

[build]
  publish="."
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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 idenitifers

  • Try 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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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.`)
}
Enter fullscreen mode Exit fullscreen mode

Deployment and Live Demo

All done, let's deploy our rate limiter now!

Deploying to Netlify

# Deploy to Netlify
netlify deploy --prod
Enter fullscreen mode Exit fullscreen mode

Live Demo

Explore the live example at tryblobs.netlify.app, and witness the Rate Limiter in action.

πŸ’– πŸ’ͺ πŸ™… 🚩
reeshee
Rishi Raj Jain

Posted on November 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related