Web Analytics Made Easy - Statcounter
BaseKV
Sign InSign Up
Back to Articles

Using a Key-Value Store with Next.js App Router

A complete guide to integrating a remote key-value store with Next.js 15 App Router. Server actions, caching, and persistence explained.

BaseKV Team5 min read
nextjstutorialweb-dev

The Next.js App Router (introduced in version 13/14) completely changed how we build React applications. We moved from useEffect data fetching to Server Components and Server Actions.

This shift makes a remote Key-Value Store an even more powerful tool.

The "Serverless" State Problem

In the App Router, your components run on the server.

  • Layouts: Run on the server.
  • Pages: Run on the server.
  • Actions: Run on the server.

But "The Server" is often a serverless function (Vercel, AWS Lambda). It has no persistent memory. You can't just save global.userCache = {}.

BaseKV + Next.js: The Perfect Match

You need a database that acts like a global memory. Because BaseKV supports HTTP connections (via standard fetch or client libraries), it is firewall-friendly and works perfectly in edge environments.

Patterns

1. Caching API Responses (Memoization)

Don't hit your slow CMS on every request. Cache it.

// app/blog/[slug]/page.tsx
import { redis } from '@/lib/db';

export default async function BlogPost({ params }) {
  const cacheKey = `post:${params.slug}`;
  
  // 1. Try Cache
  let post = await redis.get(cacheKey);
  
  if (!post) {
    // 2. Fetch Source
    post = await fetchFromCMS(params.slug);
    // 3. Save to KV (expire in 1 hour)
    await redis.set(cacheKey, JSON.stringify(post), 'EX', 3600);
  } else {
    post = JSON.parse(post);
  }

  return <Article data={post} />;
}

2. Rate Limiting Server Actions

Prevent abuse of your forms.

// app/actions.ts
'use server'
import { redis } from '@/lib/db';
import { headers } from 'next/headers';

export async function submitComment(formData: FormData) {
  const ip = headers().get('x-forwarded-for') || '127.0.0.1';
  const key = `ratelimit:${ip}`;
  
  const count = await redis.incr(key);
  if (count === 1) {
    await redis.expire(key, 60); // Reset every minute
  }
  
  if (count > 5) {
    throw new Error("Too many requests!");
  }
  
  // process comment...
}

Why not just use unstable_cache?

Next.js has a built-in Data Cache. It's great for static data. But for dynamic user data (like sessions, rate limits, shopping carts, feature flags), you need a real database that gives you:

  • Atomic increments (INCR).
  • Immediate consistency (no stale-while-revalidate lag).
  • Persistence beyond the build cache.

Conclusion

A Key-Value store is the missing "State/Memory" layer for your stateless Next.js application. It enables patterns that static caching cannot handle.

supercharge your Next.js app with BaseKV.