Contents

atacan/RateLimiter

[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fatacan%2FRateLimiter%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/atacan/RateLimiter) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fatacan%2FRateLimiter%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/atacan/RateLimiter)

Basic Usage (Core Library)

Import RateLimiter and create an instance of PublicApiRateLimiter. You'll typically need a Logger instance.

import RateLimiter
import Logging // You'll need swift-log

// Assuming you have a logger configured
let logger = Logger(label: "com.example.MyApp.RateLimiter")

// Create the rate limiter (it's an actor)
let rateLimiter = PublicApiRateLimiter(logger: logger)

// Somewhere in your request handling logic:
let clientIP = "192.168.1.100" // Get the client IP address

do {
    try await rateLimiter.check(ipAddress: clientIP)
    // Request allowed, proceed with handling
    print("Request from \(clientIP) allowed.")
} catch RateLimitingError.tooManyRequests {
    // Request denied due to rate limiting
    print("Rate limit exceeded for \(clientIP).")
    // Return an appropriate error response (e.g., HTTP 429 Too Many Requests)
} catch {
    // Handle other potential errors
    print("An unexpected error occurred: \(error)")
}

The PublicApiRateLimiter uses an in-memory store (a dictionary) and limits requests to 15 per second per IP address by default, which can be configured in the initialization. It automatically cleans up stale entries.

Usage with Hummingbird

First, ensure your RequestContext conforms to IPRequestContext. This protocol requires you to provide a way to access the client's remote IP address.

import Hummingbird
import RateLimiterHummingbird // Import the Hummingbird integration
import Logging
import NIOCore            // For SocketAddress

// Example Request Context
struct AppRequestContext: IPRequestContext {
    var coreContext: CoreRequestContextStorage
    // Required by Hummingbird.RemoteAddressRequestContext
    var remoteAddress: SocketAddress? { coreContext.channel.remoteAddress }
    // Required by IPRequestContext: Provide a way to get the remote address
    var xForwardedFor: String?

    init(channel: Channel, logger: Logger) {
        self.coreContext = .init(channel: channel, logger: logger)
        self.xForwardedFor = nil // Or parse from headers if applicable
    }
}

Then, add the InMemoryRateLimitMiddleware to your Hummingbird application router.

let logger = Logger(label: "my-hummingbird-app")
let router = Router(context: AppRequestContext.self)

// Add the rate limiting middleware
router.add(middleware: InMemoryRateLimitMiddleware(
    logger: logger,
    configuration: .init(
        limitPerSecond: 10,
        cleanupInterval: 60.0,
        entryTTL: 300.0
    )
))

// ... add your routes ...

let app = Application(
    router: router,
    context: AppRequestContext.init,
    logger: logger
)
try await app.runService()

The InMemoryRateLimitMiddleware uses the PublicApiRateLimiter internally and automatically checks first the X-Forwarded-For header, if nil then remoteAddress from your IPRequestContext. If the limit is exceeded, it throws an HTTPError(.tooManyRequests).

Installation

Add the RateLimiter package to your Package.swift dependencies:

dependencies: [
    .package(url: "https://github.com/your-username/RateLimiter.git", from: "0.0.1")
]

And add RateLimiter to your target's dependencies:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "RateLimiter", package: "RateLimiter"),
        // ... other dependencies
    ]
),

If you are using the Hummingbird integration, also add RateLimiterHummingbird:

.target(
    name: "YourApp",
    dependencies: [
        .product(name: "RateLimiterHummingbird", package: "RateLimiter"),
        .product(name: "Hummingbird", package: "hummingbird"),
        // ... other dependencies
    ]
),

Package Metadata

Repository: atacan/RateLimiter

Stars: 1

Forks: 0

Open issues: 0

Default branch: main

Primary language: swift

License: MIT

README: Readme.md