Documentation Index Fetch the complete documentation index at: https://mintlify.com/upstash/redis-js/llms.txt
Use this file to discover all available pages before exploring further.
This guide demonstrates how to use Upstash Redis in Next.js applications, covering both App Router and Pages Router patterns, Server Components, API Routes, and Server Actions.
Why Upstash Redis for Next.js?
Edge-ready : Works in Edge Runtime and Serverless Functions
Server Components : Native support for React Server Components
Fast : HTTP-based for quick cold starts on Vercel
Simple : No connection pooling required
Flexible : Use in API routes, Server Actions, or Server Components
Prerequisites
Create Next.js App
npx create-next-app@latest my-app
cd my-app
Install Upstash Redis
npm install @upstash/redis
Create Upstash Redis Database
Add Environment Variables
Create .env.local file: UPSTASH_REDIS_REST_URL = your_url_here
UPSTASH_REDIS_REST_TOKEN = your_token_here
App Router (Next.js 13+)
Server Component Example
Server Components can directly access Redis without API routes.
app/page.tsx
app/layout.tsx
.env.local
import { Redis } from "@upstash/redis" ;
const redis = Redis . fromEnv ();
export default async function Home () {
const count = await redis . incr ( "counter" );
return (
< div className = "flex h-screen w-screen items-center justify-center" >
< h1 className = "text-4xl font-bold" > Counter: { count } </ h1 >
</ div >
)
}
API Route Example
app/api/hello/route.ts
app/api/user/[id]/route.ts
import { Redis } from "@upstash/redis" ;
import { NextResponse } from "next/server" ;
const redis = Redis . fromEnv ();
export async function GET () {
const count = await redis . incr ( "counter" );
return NextResponse . json ({ count });
}
export const dynamic = 'force-dynamic'
Server Actions
Server Actions provide a simple way to mutate data from Client Components.
app/actions.ts
app/page.tsx
"use server"
import { Redis } from "@upstash/redis" ;
import { revalidatePath } from "next/cache" ;
const redis = Redis . fromEnv ();
export async function incrementCounter () {
const count = await redis . incr ( "counter" );
revalidatePath ( "/" );
return count ;
}
export async function resetCounter () {
await redis . set ( "counter" , 0 );
revalidatePath ( "/" );
return 0 ;
}
export async function addTodo ( text : string ) {
const id = Date . now (). toString ();
await redis . hset ( `todo: ${ id } ` , {
id ,
text ,
completed: false ,
createdAt: new Date (). toISOString (),
});
revalidatePath ( "/todos" );
return id ;
}
Pages Router
API Routes
pages/api/hello.ts
pages/api/cache/[key].ts
import type { NextApiRequest , NextApiResponse } from "next" ;
import { Redis } from "@upstash/redis" ;
const redis = Redis . fromEnv ();
type Data = {
count : number ;
};
export default async function handler (
req : NextApiRequest ,
res : NextApiResponse < Data >
) {
const count = await redis . incr ( "counter" );
res . status ( 200 ). json ({ count });
}
Server-Side Rendering (SSR)
import { Redis } from "@upstash/redis" ;
import type { GetServerSideProps } from "next" ;
const redis = Redis . fromEnv ();
interface Props {
count : number ;
}
export default function Home ({ count } : Props ) {
return (
< div className = "flex h-screen items-center justify-center" >
< h1 className = "text-4xl font-bold" > Counter : { count }</ h1 >
</ div >
);
}
export const getServerSideProps : GetServerSideProps < Props > = async () => {
const count = await redis . get < number >( "counter" ) || 0 ;
return {
props: {
count ,
},
};
};
Advanced Examples
Caching with Revalidation
import { Redis } from "@upstash/redis" ;
import { notFound } from "next/navigation" ;
const redis = Redis . fromEnv ();
interface Post {
id : string ;
title : string ;
content : string ;
author : string ;
}
async function getPost ( id : string ) : Promise < Post | null > {
const cacheKey = `post: ${ id } ` ;
// Try cache first
const cached = await redis . get < Post >( cacheKey );
if ( cached ) {
return cached ;
}
// Fetch from database (simulated)
const post : Post = {
id ,
title: "Example Post" ,
content: "This is an example post" ,
author: "John Doe" ,
};
// Cache for 5 minutes
await redis . set ( cacheKey , post , { ex: 300 });
return post ;
}
export default async function PostPage ({
params ,
} : {
params : { id : string };
}) {
const post = await getPost ( params . id );
if ( ! post ) {
notFound ();
}
return (
< article className = "max-w-2xl mx-auto p-8" >
< h1 className = "text-3xl font-bold mb-4" > {post. title } </ h1 >
< p className = "text-gray-600 mb-8" > By { post . author } </ p >
< div className = "prose" > {post. content } </ div >
</ article >
);
}
// Revalidate every 60 seconds
export const revalidate = 60 ;
Rate Limiting Middleware
import { Redis } from "@upstash/redis" ;
import { NextResponse } from "next/server" ;
import type { NextRequest } from "next/server" ;
const redis = new Redis ({
url: process . env . UPSTASH_REDIS_REST_URL ! ,
token: process . env . UPSTASH_REDIS_REST_TOKEN ! ,
});
const RATE_LIMIT = 10 ; // requests per minute
const WINDOW = 60 ; // seconds
export async function middleware ( request : NextRequest ) {
// Get client IP
const ip = request . ip || "unknown" ;
const key = `rate_limit: ${ ip } ` ;
// Increment counter
const requests = await redis . incr ( key );
// Set expiration on first request
if ( requests === 1 ) {
await redis . expire ( key , WINDOW );
}
// Check if limit exceeded
if ( requests > RATE_LIMIT ) {
return new NextResponse (
JSON . stringify ({ error: "Rate limit exceeded" }),
{
status: 429 ,
headers: {
"Content-Type" : "application/json" ,
"Retry-After" : WINDOW . toString (),
},
}
);
}
// Add rate limit headers
const response = NextResponse . next ();
response . headers . set ( "X-RateLimit-Limit" , RATE_LIMIT . toString ());
response . headers . set ( "X-RateLimit-Remaining" , ( RATE_LIMIT - requests ). toString ());
return response ;
}
export const config = {
matcher: "/api/:path*" ,
};
Session Management
import { Redis } from "@upstash/redis" ;
import { cookies } from "next/headers" ;
const redis = Redis . fromEnv ();
interface Session {
userId : string ;
email : string ;
createdAt : number ;
}
export async function getSession () : Promise < Session | null > {
const sessionId = cookies (). get ( "sessionId" )?. value ;
if ( ! sessionId ) {
return null ;
}
const session = await redis . get < Session >( `session: ${ sessionId } ` );
return session ;
}
export async function createSession ( userId : string , email : string ) : Promise < string > {
const sessionId = crypto . randomUUID ();
const session : Session = {
userId ,
email ,
createdAt: Date . now (),
};
// Store session for 7 days
await redis . set ( `session: ${ sessionId } ` , session , { ex: 60 * 60 * 24 * 7 });
cookies (). set ( "sessionId" , sessionId , {
httpOnly: true ,
secure: process . env . NODE_ENV === "production" ,
sameSite: "lax" ,
maxAge: 60 * 60 * 24 * 7 , // 7 days
});
return sessionId ;
}
export async function deleteSession () : Promise < void > {
const sessionId = cookies (). get ( "sessionId" )?. value ;
if ( sessionId ) {
await redis . del ( `session: ${ sessionId } ` );
cookies (). delete ( "sessionId" );
}
}
app/api/auth/login/route.ts
import { createSession } from "@/lib/session" ;
import { NextResponse } from "next/server" ;
export async function POST ( request : Request ) {
const { email , password } = await request . json ();
// Validate credentials (implement your own logic)
const userId = "user123" ; // Retrieved from your auth system
await createSession ( userId , email );
return NextResponse . json ({ success: true });
}
Real-time View Counter
import { Redis } from "@upstash/redis" ;
import ViewCounter from "./ViewCounter" ;
const redis = Redis . fromEnv ();
interface Props {
params : { slug : string };
}
export default async function BlogPost ({ params } : Props ) {
const viewKey = `views: ${ params . slug } ` ;
// Increment view count
const views = await redis . incr ( viewKey );
return (
< article >
< h1 > Blog Post </ h1 >
< ViewCounter views = { views } />
{ /* Post content */ }
</ article >
);
}
app/blog/[slug]/ViewCounter.tsx
"use client"
export default function ViewCounter ({ views } : { views : number }) {
return (
< div className = "text-sm text-gray-600" >
{ views . toLocaleString ()} views
</ div >
);
}
Best Practices
1. Initialize Client Once
Create a shared Redis instance:
import { Redis } from "@upstash/redis" ;
export const redis = Redis . fromEnv ();
Use it throughout your app:
import { redis } from "@/lib/redis" ;
const data = await redis . get ( "key" );
2. Type Safety
Use TypeScript generics for type-safe operations:
interface User {
id : string ;
name : string ;
email : string ;
}
const user = await redis . get < User >( "user:123" );
// user is typed as User | null
3. Error Handling
import { redis } from "@/lib/redis" ;
import { NextResponse } from "next/server" ;
export async function GET () {
try {
const data = await redis . get ( "key" );
return NextResponse . json ({ data });
} catch ( error ) {
console . error ( "Redis error:" , error );
return NextResponse . json (
{ error: "Failed to fetch data" },
{ status: 500 }
);
}
}
4. Caching Strategy
Implement cache-aside pattern:
async function getData ( id : string ) {
const cacheKey = `data: ${ id } ` ;
// Try cache
const cached = await redis . get ( cacheKey );
if ( cached ) return cached ;
// Fetch from source
const data = await fetchFromDatabase ( id );
// Store in cache
await redis . set ( cacheKey , data , { ex: 300 });
return data ;
}
Deployment
Vercel
Add Environment Variables
In your Vercel project settings, add:
UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN
Upstash Redis works on any platform that supports Next.js:
Netlify
AWS Amplify
Railway
Render
Just add the environment variables to your platform’s settings.
Next Steps
AWS Lambda Deploy with AWS Lambda
Cloudflare Workers Use Redis at the edge
Basic Usage Learn more Redis operations
GitHub Examples More Next.js examples