On this page

Stores Overview

Stores are the backend that hitlimit uses to track request counts. Choose the right store based on your deployment needs.

Available Stores

StorePersistenceMulti-instanceBest For
MemoryNoNoDevelopment, single instance
SQLiteYesNoSingle server, persistence needed
RedisYesYesDistributed systems, high traffic
ValkeyYesYesDistributed, open-source Redis alternative
DragonflyDBYesYesHigh-throughput distributed
PostgresYesYesDistributed, existing Postgres infra
MongoDBYesYesDistributed, NoSQL / MEAN/MERN stacks
MySQLYesYesDistributed, LAMP stacks / MariaDB

hitlimit-bun is memory-first by default with 7.73M+ ops/sec performance. Optional persistence available with native bun:sqlite (372K ops/sec), Redis, Postgres, or MongoDB when you need distributed rate limiting.

StorePersistenceMulti-instanceBest For
MemoryNoNoDefault — 7.73M+ ops/sec
SQLiteYesNobun:sqlite, zero deps — 372K ops/sec
RedisYesYesDistributed (network-bound)
ValkeyYesYesDistributed, open-source — same as Redis
DragonflyDBYesYesHigh-throughput distributed
PostgresYesYesDistributed SQL (network-bound)
MongoDBYesYesDistributed NoSQL
MySQLYesYesDistributed SQL — MySQL/MariaDB

Quick Comparison

import { hitlimit, memoryStore } from '@joint-ops/hitlimit'
import { sqliteStore } from '@joint-ops/hitlimit/stores/sqlite'
import { redisStore } from '@joint-ops/hitlimit/stores/redis'

// Memory - default, no config needed
hitlimit({ limit: 100, window: '1m' })

// SQLite - persistent storage
hitlimit({
  limit: 100,
  window: '1m',
  store: sqliteStore({ path: './limits.db' })
})

// Redis - distributed
hitlimit({
  limit: 100,
  window: '1m',
  store: redisStore({ url: 'redis://localhost:6379' })
})

// Valkey - open-source Redis alternative
import { valkeyStore } from '@joint-ops/hitlimit/stores/valkey'
hitlimit({ limit: 100, window: '1m', store: valkeyStore({ url: 'redis://localhost:6379' }) })

// DragonflyDB - high-throughput Redis alternative
import { dragonflyStore } from '@joint-ops/hitlimit/stores/dragonfly'
hitlimit({ limit: 100, window: '1m', store: dragonflyStore({ url: 'redis://localhost:6379' }) })
import { hitlimit, sqliteStore, redisStore } from '@joint-ops/hitlimit-bun'

// Memory - default, no config needed
hitlimit({ limit: 100, window: '1m' })

// SQLite - persistent via bun:sqlite
hitlimit({
  limit: 100,
  window: '1m',
  store: sqliteStore({ path: './limits.db' })
})

// Redis - distributed
hitlimit({
  limit: 100,
  window: '1m',
  store: redisStore({ url: 'redis://localhost:6379' })
})

// Valkey - open-source Redis alternative
import { valkeyStore } from '@joint-ops/hitlimit-bun/stores/valkey'
hitlimit({ limit: 100, window: '1m', store: valkeyStore({ url: 'redis://localhost:6379' }) })

// DragonflyDB - high-throughput Redis alternative
import { dragonflyStore } from '@joint-ops/hitlimit-bun/stores/dragonfly'
hitlimit({ limit: 100, window: '1m', store: dragonflyStore({ url: 'redis://localhost:6379' }) })

Choosing a Store

  • Development: Use the default memory store for simplicity
  • Single server production: SQLite provides persistence without external dependencies
  • Multiple servers: Redis ensures consistent rate limiting across all instances
  • Multiple servers (open-source): Valkey provides BSD-licensed distributed rate limiting
  • High-throughput distributed: DragonflyDB's multi-threaded architecture for maximum throughput
  • Existing MongoDB: MongoDB store for MEAN/MERN stacks with TTL index auto-cleanup
  • Existing MySQL: MySQL store for LAMP stacks with InnoDB transactions
  • Custom requirements: Create your own store

Store Interface

All stores implement the same interface:

interface Store {
  increment(key: string, window: number): Promise<{
    count: number
    resetAt: number
  }>
  reset(key: string): Promise<void>
}

Migrating Between Stores

Swapping stores requires changing only the store option. Your application code stays the same:

// Stage 1: Start with memory (fastest, no deps)
const store = memoryStore()

// Stage 2: Add persistence (survives restarts)
// const store = sqliteStore({ path: './data/rate-limits.db' })

// Stage 3: Go distributed (shared state across instances)
// const store = redisStore({ url: 'redis://localhost:6379' })

const limiter = hitlimit({
  limit: 100,
  window: '1m',
  store
})

All eight stores implement the same interface. No changes to your route handlers, middleware, or rate limit logic.