vercel-kv
Integrate Redis-compatible Vercel KV for caching, session management, and rate limiting in Next.js applications. Powered by Upstash with strong consistency and TTL support. Use when implementing cache strategies, storing temporary data with expiration, building rate limiters, or troubleshooting missing environment variables, serialization errors, or rate limit issues.
What this skill does
# Vercel KV (Redis-Compatible Storage) **Status**: Production Ready **Last Updated**: 2025-10-29 **Dependencies**: None **Latest Versions**: `@vercel/[email protected]` --- ## Quick Start (3 Minutes) ### 1. Create Vercel KV Database ```bash # In your Vercel project dashboard # Storage → Create Database → KV # Pull environment variables locally vercel env pull .env.local ``` This automatically creates: - `KV_REST_API_URL` - Your KV database URL - `KV_REST_API_TOKEN` - Auth token - `KV_REST_API_READ_ONLY_TOKEN` - Read-only token (optional) ### 2. Install Package ```bash npm install @vercel/kv ``` ### 3. Use in Your App **Next.js Server Action:** ```typescript 'use server'; import { kv } from '@vercel/kv'; export async function incrementViews(slug: string) { const views = await kv.incr(`views:${slug}`); return views; } ``` **Edge API Route:** ```typescript import { kv } from '@vercel/kv'; export const runtime = 'edge'; export async function GET(request: Request) { const value = await kv.get('mykey'); return Response.json({ value }); } ``` **CRITICAL:** - Always set TTL for temporary data: `await kv.setex('key', 3600, value)` - Use namespacing for keys: `user:${id}:profile` instead of just `${id}` - JSON values must be serializable (no functions, circular refs) --- ## The 5-Step Setup Process ### Step 1: Create KV Database **Option A: Vercel Dashboard** 1. Go to your Vercel project 2. Storage → Create Database → KV 3. Name your database 4. Copy the environment variables **Option B: Vercel CLI** ```bash vercel env pull .env.local ``` This creates: ```bash # .env.local (automatically created) KV_REST_API_URL="https://xyz.kv.vercel-storage.com" KV_REST_API_TOKEN="your-token-here" KV_REST_API_READ_ONLY_TOKEN="your-readonly-token" ``` **Key Points:** - One KV database per project recommended - Free tier: 30,000 commands/month, 256MB storage - Environment variables are automatically set for Vercel deployments --- ### Step 2: Install and Configure ```bash npm install @vercel/kv ``` **For local development**, create `.env.local`: ```bash # .env.local KV_REST_API_URL="https://your-db.kv.vercel-storage.com" KV_REST_API_TOKEN="your-token" ``` **For production**, environment variables are automatically available. **Cloudflare Workers** (using Vercel KV): ```toml # wrangler.toml [vars] KV_REST_API_URL = "https://your-db.kv.vercel-storage.com" [[secrets]] KV_REST_API_TOKEN = "your-token" ``` --- ### Step 3: Basic Operations **Set/Get:** ```typescript import { kv } from '@vercel/kv'; // Set a value await kv.set('user:123', { name: 'Alice', email: '[email protected]' }); // Get a value const user = await kv.get('user:123'); // Returns: { name: 'Alice', email: '[email protected]' } // Set with TTL (expires in 1 hour) await kv.setex('session:abc', 3600, { userId: 123 }); // Check if key exists const exists = await kv.exists('user:123'); // Returns 1 if exists, 0 if not // Delete a key await kv.del('user:123'); ``` **Atomic Operations:** ```typescript // Increment counter const views = await kv.incr('views:post:123'); // Decrement counter const stock = await kv.decr('inventory:item:456'); // Increment by amount await kv.incrby('score:user:789', 10); // Set if not exists (returns 1 if set, 0 if key already exists) const wasSet = await kv.setnx('lock:process', 'running'); ``` **Multiple Operations:** ```typescript // Get multiple keys const values = await kv.mget('user:1', 'user:2', 'user:3'); // Returns: [{ name: '...' }, { name: '...' }, null] // Set multiple keys await kv.mset({ 'user:1': { name: 'Alice' }, 'user:2': { name: 'Bob' } }); // Delete multiple keys await kv.del('key1', 'key2', 'key3'); ``` **Key Points:** - Values are automatically JSON-serialized - `null` is returned for non-existent keys - All operations are atomic - TTL is in seconds --- ### Step 4: Advanced Patterns **Caching Pattern:** ```typescript import { kv } from '@vercel/kv'; async function getPost(slug: string) { // Try cache first const cached = await kv.get(`post:${slug}`); if (cached) return cached; // Fetch from database const post = await db.select().from(posts).where(eq(posts.slug, slug)); // Cache for 1 hour await kv.setex(`post:${slug}`, 3600, post); return post; } ``` **Rate Limiting:** ```typescript import { kv } from '@vercel/kv'; async function checkRateLimit(ip: string): Promise<boolean> { const key = `ratelimit:${ip}`; const limit = 10; // 10 requests const window = 60; // per 60 seconds const current = await kv.incr(key); if (current === 1) { // First request, set TTL await kv.expire(key, window); } return current <= limit; } // Usage in API route export async function POST(request: Request) { const ip = request.headers.get('x-forwarded-for') || 'unknown'; if (!await checkRateLimit(ip)) { return new Response('Rate limit exceeded', { status: 429 }); } // Process request... } ``` **Session Management:** ```typescript import { kv } from '@vercel/kv'; import { cookies } from 'next/headers'; export async function createSession(userId: number) { const sessionId = crypto.randomUUID(); const sessionData = { userId, createdAt: Date.now() }; // Store session for 7 days await kv.setex(`session:${sessionId}`, 7 * 24 * 3600, sessionData); // Set cookie cookies().set('session', sessionId, { httpOnly: true, secure: true, maxAge: 7 * 24 * 3600 }); return sessionId; } export async function getSession() { const sessionId = cookies().get('session')?.value; if (!sessionId) return null; return await kv.get(`session:${sessionId}`); } ``` **Pipeline (Batch Operations):** ```typescript import { kv } from '@vercel/kv'; // Execute multiple commands in a single round-trip const pipeline = kv.pipeline(); pipeline.set('user:1', { name: 'Alice' }); pipeline.incr('counter'); pipeline.get('config'); const results = await pipeline.exec(); // Returns: ['OK', 1, { ... }] ``` --- ### Step 5: Key Naming Conventions **Use Namespaces:** ```typescript // ❌ Bad: No structure await kv.set('123', data); // ✅ Good: Clear namespace await kv.set('user:123', data); await kv.set('post:abc:views', 100); await kv.set('cache:homepage:en', html); ``` **Naming Patterns:** - `user:{id}:profile` - User profile data - `post:{slug}:views` - View counter for post - `cache:{page}:{locale}` - Cached page content - `session:{token}` - Session data - `ratelimit:{ip}:{endpoint}` - Rate limit tracking - `lock:{resource}` - Distributed locks --- ## Critical Rules ### Always Do ✅ **Set TTL for temporary data** - Avoid memory leaks and stale data ✅ **Use namespaced keys** - `user:123` not `123` (prevents collisions) ✅ **Handle null returns** - Non-existent keys return `null` ✅ **Use pipeline for multiple operations** - Reduces latency (single round-trip) ✅ **Serialize JSON-compatible data only** - No functions, circular references, etc. ✅ **Use SETNX for distributed locks** - Prevents race conditions ✅ **Monitor command usage** - Stay within free tier limits (30K commands/month) ✅ **Use read-only token for public reads** - Better security ### Never Do ❌ **Never store sensitive data without encryption** - KV is not encrypted at rest by default ❌ **Never forget to set TTL** - Keys without TTL stay forever (memory leak) ❌ **Never use generic key names** - `data`, `cache`, `temp` will collide ❌ **Never store large values (>1MB)** - Use Vercel Blob for large files ❌ **Never use KV as primary database** - It's a cache, not persistent storage ❌ **Never exceed rate limits** - 30K commands/month on free tier ❌ **Never assume strong durability** - KV is for ephemeral data, not critical data ❌ **Never commit `.env.local`** - Contains KV tokens (add to `.gitignore`) --- ## Known Issues Prevention This skill prevents **10 documented issues**: ### Issue #1: Missing Environment Variables **Error**: `Error: KV_REST_API_URL is not defined` or `KV_REST_API_TOKEN is not defined`
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.