Secure, flexible API key middleware and manager for Hono. Works in Node, Cloudflare Workers, and edge runtimes. Batteries included adapters (Memory, KV, Redis) and a clean StorageAdapter interface for custom backends.
- Zero dependencies - No external runtime dependencies
- Middleware-first:
apiKeyMiddleware(manager, options?) - Header (
x-api-key) or query (?api_key=) auth - Optional rate limiting (per-key or default)
- Type-safe context: access
c.get('apiKey') - Adapters: Memory, Cloudflare KV, Redis; easy to implement your own
- Tiny ESM/CJS builds, Typescript types included
# npm
npm install hono-api-key
# yarn
yarn add hono-api-key
# pnpm
pnpm add hono-api-keyimport { Hono } from 'hono';
import { apiKeyMiddleware, ApiKeyManager, MemoryAdapter } from 'hono-api-key';
const manager = new ApiKeyManager({ adapter: new MemoryAdapter() });
const created = await manager.createKey({ ownerId: 'owner-1', name: 'demo' });
const app = new Hono<{ Variables: { apiKey: Awaited<ReturnType<typeof manager.validateKey>> } }>();
app.use('*', apiKeyMiddleware(manager));
app.get('/secure', (c) => c.text(`hello ${c.get('apiKey')?.name}`));
// client: set header 'x-api-key: ' + created.keyapiKeyMiddleware(
manager: ApiKeyManager,
options?: {
headerName?: string // default: 'x-api-key'
queryName?: string // default: 'api_key'
rateLimitBypass?: boolean
}
)- Throws
HTTPException(401)for missing/invalid key - Throws
HTTPException(429)when rate limit exceeded - Stores sanitized key on context:
c.set('apiKey', keyInfo)
Helper for typing app Variables:
type ApiKeyInfo = Awaited<ReturnType<ApiKeyManager['validateKey']>>;
const app = new Hono<{ Variables: { apiKey: ApiKeyInfo } }>();createKey({ ownerId, name, permissions?, rateLimit?, expiresAt?, metadata? })getKeys(ownerId, includeKey=false)getKeyById(keyId, ownerId?)updateKey(keyId, ownerId, updates)deleteKey(keyId, ownerId)validateKey(keyValue)checkRateLimit(keyId, override?)
Records use ISO strings, sanitized variants omit key.
Built-in:
MemoryAdapterβ in-memory, great for tests/devKvAdapterβ Cloudflare Workers KVRedisAdapterβ Redis client-agnostic using a minimalRedisClientinterface
Implement your own by conforming to StorageAdapter in src/types.ts and pass it to ApiKeyManager.
import { Hono } from 'hono';
import { apiKeyMiddleware, ApiKeyManager, KvAdapter } from 'hono-api-key';
type Env = { KV: KVNamespace };
const app = new Hono<{
Bindings: Env;
Variables: { apiKey: Awaited<ReturnType<ApiKeyManager['validateKey']>>; manager: ApiKeyManager };
}>();
app.use('*', (c, next) => {
const manager = new ApiKeyManager({ adapter: new KvAdapter(c.env.KV, 'apikey:') });
c.set('manager', manager);
return next();
});
app.use('/secure/*', (c, n) => apiKeyMiddleware(c.get('manager'))(c, n));KV example is in examples/kv/ with Wrangler configs and routes.
import { Redis } from '@upstash/redis';
import { apiKeyMiddleware, ApiKeyManager, RedisAdapter } from 'hono-api-key';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
const manager = new ApiKeyManager({ adapter: new RedisAdapter(redis, 'apikey:') });Redis example is in examples/redis/ and supports Upstash or ioredis.
examples/basic.tsβ Node server with@hono/node-serverexamples/kv/β Cloudflare Workers KV example (Wrangler)examples/redis/β Redis adapter example (Node/Upstash)- examples/custom-d1/ β Custom adapter with Cloudflare D1 using Drizzle
- examples/libsql/ β Custom adapter with LibSQL / Turso using Drizzle (thanks to @darkterminal)
Scripts:
# Node example
pnpm run examples:basic
# Cloudflare KV example
pnpm run examples:kv
# Redis example
pnpm run examples:redispnpm install
pnpm test # Vitest
pnpm build # tsup (ESM+CJS, dts)
pnpm format # Prettier
pnpm changeset # create a changesetLafif Astahdziq (https://lafif.me)
MIT