Unkey Integration
Create, verify, revoke, update, and delete API keys, plus protect routes with key verification and rate-limit checks.
Configure Unkey
farm.config.ts
import { defineConfig } from "@farmjs/core";import { unkey } from "@farmjs/integrations/unkey";export default defineConfig({ integrations: { keys: unkey({ rootKey: process.env.UNKEY_ROOT_KEY, apiId: process.env.UNKEY_API_ID, }), },});
Create and verify keys
const created = await api.keys.create.post({ body: { name: "Production key", permissions: ["documents.read"], },});const verified = await api.keys.verify.post({ body: { key: created.data!.key, permissions: "documents.read", },});
Best fit
- API products where customers need their own keys.
- Internal platform keys for service-to-service requests.
- Route protection where key validity, permissions, credits, or rate limits matter.
What Unkey adds
| Area | Details |
|---|---|
| Create | Server-only route for creating customer or service keys. |
| Verify | Server-only route for checking validity, permissions, credits, and rate limits. |
| Update | Server-only route for changing metadata, roles, permissions, credits, and limits. |
| Revoke | Server-only route for disabling a key without deleting its record. |
| Delete | Server-only route for deleting a key when the app no longer needs it. |
| Middleware | Optional protected route matcher that verifies request keys before app code runs. |
Server-only callers
Unkey mutation and verification callers are marked as server-only. Use api from createIntegrations, not apiClient in browser components.
import { api } from "../lib/api";export async function createCustomerKey(userId: string) { const created = await api.keys.create.post({ body: { externalId: userId, permissions: ["documents.read"], ratelimits: [ { name: "documents", limit: 1000, duration: 60_000, }, ], }, }); return created.data;}
Protect routes
unkey({ rootKey: process.env.UNKEY_ROOT_KEY, apiId: process.env.UNKEY_API_ID, protectedRoutes: ["/api/public/[...path]"], protection: { header: "authorization", permissions: ["documents.read"], },});
When a request matches protectedRoutes, the integration verifies the key before the route handler runs.
Production notes
- Keep
UNKEY_ROOT_KEYserver-only. - Use server callers for key creation and verification.
- Prefer permissions and ratelimits over one global "valid key" check.
- Revoke keys before deleting them when users may need an audit trail.
- Test expired keys, missing permissions, exhausted credits, and rate-limit failures.