Next.js / Intermediate: authority, freshness and route experience / Lesson 10
Mutation results, tags and path revalidation
Connect a confirmed domain write to the correct cache and UI refresh behavior without confusing revalidation with atomicity.
On this page
Lesson goals
- Distinguish revalidateTag with a stale profile, updateTag and revalidatePath.
- Preserve request identity and canonical results across lost responses.
Commit the business operation before updating presentation caches
An action should validate input, resolve current authority and perform one coherent domain operation before invalidating affected representations. A cache API is not a database transaction and cannot guarantee that an external message was delivered once. Use expected revisions for conflicting new edits and a logical operation key for safe replay where required. The two identities solve different problems. If the response disappears after commit, the client must reconcile the original result rather than assuming failure and repeating the effect. Return canonical values or a stable operation status so client state does not rely solely on its optimistic prediction.
Choose the freshness semantics the user needs
In Next 16, revalidateTag(tag,"max") marks tagged data stale and permits stale-while-revalidate behavior. updateTag is for Server Actions and immediate expiry when the action needs read-your-own-write behavior. revalidatePath targets a page or layout path and has context-specific timing. These APIs are not interchangeable global purges. The fragment uses updateTag after a confirmed rename under its Server Action context. The tag must have been assigned to the relevant cached representation. Source behavior, imports and authentication are assumed, and no invalidation runs here.
"use server";
import {updateTag} from "next/cache";
export async function rename(input:RenameInput) {
const principal=await currentPrincipal();
const result=await catalog.renameFor(principal,validateRename(input));
updateTag(`course:${result.id}`);
return {id:result.id,title:result.title,revision:result.revision};
}
The action context and assigned tag are prerequisites. This example does not execute a write or prove any deployed read-your-own-write path.
Invalidate the representations that actually depend on the write
A title change can affect a card, search membership, sorted lists and metadata. Updating one entity or expiring one tag does not necessarily recompute all those consumers. Document tag ownership and path dependencies, keep tag vocabulary bounded and avoid putting private credentials in tags. A Route Handler that calls a revalidation API may affect future visits rather than the current client’s already displayed tree. router.refresh can request new server output but may still receive cached source data. Test data freshness and client reconciliation separately, including stale fills that race with invalidation.
Verify logical replay without claiming durable storage
The local map records one synthetic write outcome and returns it again for the same key and fingerprint. A changed fingerprint under that identity rejects. This synchronous model does not protect concurrent workers, survive restart or coordinate a real transaction and invalidation. Its purpose is to show why a retry after missing response must remain the same logical operation. A production design must bind keys to principal and operation scope and define retention. Do not reuse a key for a new user edit just because the object ID is unchanged.
const results=new Map();let effects=0;
function write(key,fingerprint) {
if (results.has(key)) {const old=results.get(key);if(old.fingerprint!==fingerprint)throw new Error("KEY_REUSED");return old.result;}
const result={revision:++effects};results.set(key,{fingerprint,result});return result;
}
console.assert(write("r1","c1@4").revision===1);
console.assert(write("r1","c1@4").revision===1);
let rejected=false;try {write("r1","c1@5");}catch {rejected=true;}
console.assert(rejected && effects===1);
One effect is retained for identical replay; changed input is rejected. No cache, transaction or Server Action is executed.
Test commit and display behavior as separate boundaries
Use fixtures for invalid input with no write, denied scope, expected conflict, first success, identical replay and response loss after commit. Then verify assigned tags, affected paths and the current client result under the selected cache mode. An invalidation failure after commit requires reconciliation and safe incident evidence, not a blind repetition of the domain effect. The single-argument revalidateTag form is deprecated in the reviewed API; use an explicit supported profile or appropriate updateTag context. This course executes the replay map only, leaving real cache timing, actions, path invalidation and browser updates untested.
Common errors and fixes
A stale profile is treated as immediate read-your-own-write
- Why it happens
- Different revalidation semantics were assumed interchangeable.
- How to fix it
- Choose the supported API and profile for the required freshness.
- Prevent it next time
- Test the current user’s read after the mutation.
A failed refresh repeats a committed effect
- Why it happens
- Presentation failure was interpreted as domain nonexecution.
- How to fix it
- Reconcile by logical operation identity before retrying.
- Prevent it next time
- Test commit followed by invalidation or response failure.
A renamed card stays in the wrong filtered list
- Why it happens
- The write’s semantic dependencies were not included in invalidation.
- How to fix it
- Review list predicates, ordering and associated path/tag ownership.
- Prevent it next time
- Use a rename fixture that crosses a visible list boundary.
Check your understanding
1. Does updateTag make the preceding database write atomic with cache invalidation?
Show worked solution
No. The domain write and cache operation remain distinct boundaries. The write can commit before invalidation fails. Preserve its canonical outcome and operation identity, then reconcile or repair the representation safely. Do not repeat an already committed business effect merely to retry a cache update.
2. Why must the cache tag already belong to the affected data?
Show worked solution
An invalidation label has no effect on unrelated entries. The producer must assign the tag under the reviewed data contract, and the writer must use the matching vocabulary. Verify real consumers and path dependencies rather than assuming a similarly named tag refreshes every place where the course appears.
Key takeaways
- Revalidation semantics must match the domain freshness requirement.
- Cache repair and business replay require separate ownership.