Vercel Logo

Set Up the Project

Every cooking school has opinions. The knife skills instructor thinks the claw grip lesson is flawless. The bread baking teacher is convinced nobody reads the hydration guide. And the students? They have their own opinions, scattered across emails, sticky notes, and hallway conversations.

We're going to collect those opinions in one place. A feedback API backed by a JSON file, with real course names, real comments, and enough structure that we can query it, filter it, and summarize it. Nothing fancy on the frontend. We're building an API, so the terminal is our kitchen.

Outcome

Scaffold a Next.js project with a Feedback type and a JSON file of seed data.

Fast Track

  1. Deploy the starter repo to Vercel, then clone it locally
  2. Review the Feedback interface in lib/types.ts and the seed data in data/feedback.json
  3. Implement the three functions in lib/data.ts

Hands-on exercise

Start by deploying the starter repo. This gets you a live URL right away, which we'll need when we add agent-friendly docs later in the course.

Deploy with Vercel

Once Vercel creates your project, clone it locally and install dependencies:

git clone <your-repo-url>
cd cooking-school-feedback
pnpm install

The starter gives you a Next.js app with the App Router and TypeScript already configured. No styling libraries needed for this course since we're building an API, not a UI.

The starter includes the type definition and seed data already. Your job is to implement the data utility. Here's what's in the box:

lib/types.ts already has the Feedback interface. Open it and confirm it has these fields:

  • id (string), unique identifier like "fb-001"
  • courseSlug (string), which course the feedback belongs to
  • lessonSlug (string), which lesson within the course
  • rating (number), integer from 1 to 5
  • comment (string), the feedback text
  • author (string), who submitted it
  • createdAt (string), ISO 8601 timestamp

data/feedback.json already contains 10 seed feedback entries across three courses at our cooking school: knife-skills, bread-baking, and pasta-from-scratch. Real names, real comments, and a range of ratings. A mix of glowing reviews and constructive criticism that will make the summary endpoint more interesting later.

The real work is in lib/data.ts. Open it up. The function signatures are there, but the implementations are stubs. You'll need to fill in three functions:

  • getAllFeedback(), reads the file and returns the parsed array
  • getFeedbackById(id), finds a single entry by its id
  • addFeedback(entry), generates an id and createdAt, appends to the file, returns the new entry

Use fs/promises for file operations and path.join(process.cwd(), "data", "feedback.json") for the file path.

ID generation

A simple pattern: count the existing entries and pad the number. If there are 10 entries, the next id is fb-011. Not production-grade, but perfect for a course project.

Try It

After implementing the functions, verify the setup by running the dev server and checking that the project compiles:

pnpm dev

You should see:

▲ Next.js 16.2.1 (Turbopack)
- Local: http://localhost:3000

No errors. The data utility won't be reachable from the browser yet (we'll wire up the API routes in the next lesson), but the project should compile cleanly.

You can also verify the types work by opening lib/data.ts in your editor and confirming there are no TypeScript errors on the Feedback import.

JSON file path

The process.cwd() approach works in development and in next build. If you see a "file not found" error, make sure the data/ folder is at the project root, not inside app/.

If you see 'fs is not defined'

Make sure you're importing from fs/promises, not fs. And double-check the import is import fs from "fs/promises", not a named import like import { readFile } from "fs". The data utility runs on the server, so Node built-ins are available, but the import path matters.

If TypeScript complains about your function signatures

Each function needs to be async since fs.readFile and fs.writeFile return Promises. If your editor shows a type error on the return value, check that you have async before the function keyword and that the return type matches (Promise<Feedback[]>, Promise<Feedback | undefined>, Promise<Feedback>).

Commit

git add -A && git commit -m "feat(api): scaffold project with feedback types and seed data"

Done-When

  • lib/types.ts exports a Feedback interface with all 7 fields (provided by starter)
  • data/feedback.json contains 10 seed feedback entries across three courses (provided by starter)
  • lib/data.ts has working implementations of getAllFeedback, getFeedbackById, and addFeedback
  • pnpm dev starts without errors

A typed interface, structured seed data, and a data utility that reads and writes actual JSON. The foundation is in place for an API that agents can understand without guessing.

Solution

lib/types.ts
export interface Feedback {
  id: string;
  courseSlug: string;
  lessonSlug: string;
  rating: number;
  comment: string;
  author: string;
  createdAt: string;
}
lib/data.ts
import fs from "fs/promises";
import path from "path";
import type { Feedback } from "./types";
 
const DATA_PATH = path.join(process.cwd(), "data", "feedback.json");
 
export async function getAllFeedback(): Promise<Feedback[]> {
  const raw = await fs.readFile(DATA_PATH, "utf-8");
  return JSON.parse(raw);
}
 
export async function getFeedbackById(
  id: string
): Promise<Feedback | undefined> {
  const all = await getAllFeedback();
  return all.find((fb) => fb.id === id);
}
 
export async function addFeedback(
  entry: Omit<Feedback, "id" | "createdAt">
): Promise<Feedback> {
  const all = await getAllFeedback();
  const newEntry: Feedback = {
    ...entry,
    id: `fb-${String(all.length + 1).padStart(3, "0")}`,
    createdAt: new Date().toISOString(),
  };
  all.push(newEntry);
  await fs.writeFile(DATA_PATH, JSON.stringify(all, null, 2));
  return newEntry;
}
data/feedback.json
[
  {
    "id": "fb-001",
    "courseSlug": "knife-skills",
    "lessonSlug": "the-claw-grip",
    "rating": 5,
    "comment": "Finally understand why my onion cuts were uneven. The claw grip changed everything.",
    "author": "Priya Sharma",
    "createdAt": "2026-03-01T10:30:00Z"
  },
  {
    "id": "fb-002",
    "courseSlug": "knife-skills",
    "lessonSlug": "dicing-onions",
    "rating": 4,
    "comment": "Great technique breakdown but I wish there was a slow-motion video. Hard to follow the horizontal cut at full speed.",
    "author": "Marcus Chen",
    "createdAt": "2026-03-01T14:15:00Z"
  },
  {
    "id": "fb-003",
    "courseSlug": "bread-baking",
    "lessonSlug": "hydration-basics",
    "rating": 5,
    "comment": "I had no idea that flour-to-water ratio mattered this much. My loaves have been bricks for months. Not anymore.",
    "author": "Aisha Johnson",
    "createdAt": "2026-03-02T09:00:00Z"
  },
  {
    "id": "fb-004",
    "courseSlug": "knife-skills",
    "lessonSlug": "the-claw-grip",
    "rating": 3,
    "comment": "Felt a bit rushed. Would have liked more time on finger positioning before jumping to speed drills.",
    "author": "Tom Kowalski",
    "createdAt": "2026-03-02T11:45:00Z"
  },
  {
    "id": "fb-005",
    "courseSlug": "bread-baking",
    "lessonSlug": "shaping-boules",
    "rating": 5,
    "comment": "The surface tension explanation finally clicked. My dough used to spread flat every time.",
    "author": "Sofia Reyes",
    "createdAt": "2026-03-03T08:20:00Z"
  },
  {
    "id": "fb-006",
    "courseSlug": "bread-baking",
    "lessonSlug": "sourdough-starter",
    "rating": 4,
    "comment": "Good lesson, but the difference between a stiff starter and a liquid starter took me a second read.",
    "author": "James Okafor",
    "createdAt": "2026-03-03T16:00:00Z"
  },
  {
    "id": "fb-007",
    "courseSlug": "knife-skills",
    "lessonSlug": "julienne-cuts",
    "rating": 5,
    "comment": "Matchstick carrots used to take me forever. The guide-hand technique cut my prep time in half.",
    "author": "Lin Wei",
    "createdAt": "2026-03-04T10:10:00Z"
  },
  {
    "id": "fb-008",
    "courseSlug": "bread-baking",
    "lessonSlug": "hydration-basics",
    "rating": 2,
    "comment": "I already knew this from other baking courses. Wish there was a fast-track for people who have the fundamentals.",
    "author": "Derek Miles",
    "createdAt": "2026-03-04T13:30:00Z"
  },
  {
    "id": "fb-009",
    "courseSlug": "knife-skills",
    "lessonSlug": "dicing-onions",
    "rating": 5,
    "comment": "Best lesson in the course. No more crying over mangled onion pieces.",
    "author": "Nora Eriksson",
    "createdAt": "2026-03-05T09:45:00Z"
  },
  {
    "id": "fb-010",
    "courseSlug": "pasta-from-scratch",
    "lessonSlug": "egg-dough",
    "rating": 4,
    "comment": "The well method is so satisfying. Dough came together on the first try, which never happens to me.",
    "author": "Priya Sharma",
    "createdAt": "2026-03-05T15:20:00Z"
  }
]