Next.js Curriculum
MediumServer Actions100% Free30 min read

Server Action Closures, Revalidation, and Redirection

The 30-Second Interview Answer

Server Actions can close over server-side scope and receive bound parameters via Function.prototype.bind. After executing mutations, Server Actions orchestrate data updates through revalidatePath/revalidateTag and page navigation via redirect(). Critically, redirect() internally throws a NEXT_REDIRECT exception that must not be caught by unhandled try/catch blocks.

Learning Objectives

Pass bound variables securely to Server Actions using the .bind() pattern.

Execute sequential cache invalidation across multiple tags and paths.

Understand why redirect() throws an internal NEXT_REDIRECT error and how to structure try/catch safely.

Control permanent vs temporary redirects from Server Actions.

Prerequisites & Background Knowledge
JavaScript Closures and Function.bindNext.js navigation redirect()Exception handling patterns

Why Interviewers Care

The redirect-inside-try-catch trap is one of the most common mistakes made in Next.js coding interviews.

The Challenge

Explain the closure and binding patterns used with Server Actions. Demonstrate how to update an entity, revalidate cached pages, and redirect the user while properly handling the NEXT_REDIRECT exception.

Code Architecture & Implementation

1// app/actions/articles.ts
2'use server';
3
4import { revalidatePath, revalidateTag } from 'next/cache';
5import { redirect } from 'next/navigation';
6import { db } from '@/lib/db';
7
8// Using .bind() allows passing server-managed variables (like articleId)
9// without creating hidden inputs in the client HTML
10export async function updateArticleAction(articleId: string, formData: FormData) {
11 const title = formData.get('title') as string;
12 const content = formData.get('content') as string;
13
14 try {
15 // 1. Database mutation
16 await db.articles.update({
17 where: { id: articleId },
18 data: { title, content },
19 });
20
21 // 2. Surgical cache revalidation
22 revalidateTag(`article-${articleId}`);
23 revalidatePath('/articles');
24 } catch (err) {
25 console.error('Database update failed:', err);
26 return { error: 'Failed to update article. Please try again.' };
27 }
28
29 // 3. IMPORTANT: redirect() must be called OUTSIDE the try/catch block!
30 // redirect() works by throwing an internal NEXT_REDIRECT error.
31 redirect(`/articles/${articleId}`);
32}

Architectural Explanation

In Next.js, redirect() does not immediately send an HTTP 302 response header. Instead, it throws a special control-flow error: NEXT_REDIRECT. The Next.js framework catches this error at the top-level handler to initiate navigation. If developers wrap the entire action in try { ... redirect(); } catch (err) { return err; }, the catch block intercepts NEXT_REDIRECT, treats it as a database failure, and aborts navigation. Always place redirect() after the try/catch block.

Common Mistakes & Anti-Patterns

Calling redirect() inside a catch-all try/catch block, breaking redirection entirely.

Using hidden `<input type="hidden" name="id" value={id}>` for sensitive IDs instead of `.bind()`, allowing malicious users to edit someone else's entity.

Forgetting that revalidatePath must be called before redirect(), because code after redirect() is unreachable.

Interview Follow-Up Traps

The interviewer asks: "Why does redirect() throw an error instead of returning a response object?" Because throwing an error immediately halts the execution of subsequent code in the function without requiring explicit return statements.

Frequently Asked Questions

Can you call redirect() inside Client Components?

No. `redirect()` is for Server Components, Server Actions, and Route Handlers. Client Components use `useRouter().push()`.

Server Actions use HTTP 303 (See Other) redirection to ensure the browser follows the redirect with an HTTP GET request.

Yes! `permanentRedirect()` issues a 308 permanent redirect, commonly used for URL slug changes.