Let's think about this for a moment
This set is a step harder than Practice Set 1 and focuses on the data layer and mutation logic. Each task covers a pattern you'd actually run into in production apps — writing API endpoints, form validation, cache invalidation. As you work through these tasks, pay attention to error handling too.
Exercises
(1) Create a route handler at app/api/quotes/route.ts and have its GET method return an array of quotes (text, author) as JSON via Response.json(). (2) Add a POST method that validates the text field of the request body — check it's a string and longer than 3 characters — returning status 400 if it isn't. (3) Build a subscribe form (email input) backed by a "use server" Server Action that validates the email format with a regex and shows a success/error message using useFormState or simple state. (4) After a data mutation, call revalidateTag("quotes") and attach the { next: { tags: ["quotes"] } } option to a fetch() call.
Code Example
// Task 1-2 skeleton — app/api/quotes/route.ts
const quotes = [{ text: "Stay hungry, stay foolish.", author: "Steve Jobs" }];
export async function GET() {
return Response.json(quotes);
}
export async function POST(request: Request) {
const body = await request.json();
if (typeof body.text !== "string" || body.text.length <= 3) {
return Response.json({ error: "text is too short" }, { status: 400 });
}
quotes.push({ text: body.text, author: body.author ?? "Unknown" });
return Response.json({ ok: true }, { status: 201 });
}
// Task 3 skeleton — app/actions.ts
"use server";
export async function subscribe(prevState: unknown, formData: FormData) {
const email = String(formData.get("email") ?? "");
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (!isValid) return { error: "Invalid email address" };
return { success: true };
}Once all 4 tasks are done, you'll be able to write a full data flow yourself — a backend API endpoint with validation logic, a Server Action with form validation, and tag-based cache invalidation, all working together.Try it in 5 minutes
Within 5 minutes, test task (2)'s POST endpoint with a short text like curl -X POST -d '{"text":"hi"}' and confirm you get status 400 back.
A quick word of caution
Make sure to set the status code correctly for error cases in your route handler — if it defaults to 200, client-side code will assume success and miss the validation error.