PRD Studio

Smoke Test App

Product Requirements Document

Approved

Smoke Test App

1. Overview

Smoke Test App is a minimal, single-user todo application built with Next.js and PostgreSQL. It allows a user to add todo items, mark them as complete, and see how many todos remain incomplete. Its primary purpose is to serve as a fast, verifiable smoke test for a full-stack Next.js + PostgreSQL setup — proving that the UI renders, the API layer works, and the database persists data correctly.

2. Goals & Non-Goals

Goals

  • Provide a working end-to-end todo experience: create → complete → count remaining.
  • Persist all todos in PostgreSQL so they survive page refreshes and server restarts.
  • Keep the surface area tiny so the entire app can be verified in under 2 minutes.
  • Serve as a reliable reference implementation / smoke test for the stack.

Non-Goals

  • Multi-user support or authentication of any kind.
  • Editing the text of an existing todo after creation.
  • Deleting todos.
  • Filtering or searching todos.
  • Due dates, priorities, tags, or any metadata beyond title and completion status.
  • Mobile-native or offline support.

3. Target Users

Single internal/developer user. There is no sign-up flow. Anyone who opens the app URL has full access to the shared todo list. The app is intended for internal smoke-testing, not public production use.

4. User Flows

Flow 1 — Add a Todo

  1. User opens the app at /.
  2. User sees a text input field with placeholder text "What needs to be done?" and an Add button.
  3. User types a non-empty todo title into the input field.
  4. User clicks Add (or presses Enter).
  5. App sends a POST /api/todos request with the title.
  6. New todo appears at the bottom of the todo list with a checkbox (unchecked) and its title.
  7. The remaining count (e.g. "3 left") increments by 1.
  8. The text input is cleared and focused, ready for the next entry.

Flow 2 — Mark a Todo Complete

  1. User sees an existing unchecked todo in the list.
  2. User clicks the checkbox next to the todo's title.
  3. App sends a PATCH /api/todos/:id request toggling completed to true.
  4. The checkbox becomes checked and the todo title renders with a strikethrough style.
  5. The remaining count decrements by 1.

Flow 3 — Mark a Completed Todo Incomplete

  1. User sees an existing checked (completed) todo in the list.
  2. User clicks the checked checkbox.
  3. App sends a PATCH /api/todos/:id request toggling completed to false.
  4. The checkbox becomes unchecked and the strikethrough styling is removed.
  5. The remaining count increments by 1.

Flow 4 — View Persisted Todos Across Sessions

  1. User adds one or more todos (Flow 1) and/or completes some (Flow 2).
  2. User refreshes the page or navigates away and returns.
  3. App performs a GET /api/todos on load.
  4. All previously created todos appear in the list with their correct completion state.
  5. The remaining count reflects the persisted state accurately.

Flow 5 — Empty-State View

  1. User opens the app when no todos exist in the database.
  2. User sees the input field and Add button.
  3. User sees an empty-state message: "No todos yet — add one above!"
  4. The remaining count displays "0 left".

5. Features

F1 — Todo List Display (MUST)

  • Render all todos fetched from GET /api/todos ordered by creation time ascending.
  • Each row shows: checkbox, title text, and completion styling (strikethrough when complete).
  • Show an empty-state message when the list is empty.

F2 — Add Todo (MUST)

  • Text input + Add button (also triggered by Enter key).
  • Client-side validation: disallow empty or whitespace-only submissions.
  • On success, append new todo to the list without a full page reload.
  • Clear and re-focus the input after successful addition.

F3 — Toggle Todo Completion (MUST)

  • Clicking a todo's checkbox toggles its completed state via PATCH /api/todos/:id.
  • UI updates immediately (optimistic or post-response update acceptable).
  • Strikethrough applied to completed todos; removed when toggled back.

F4 — Remaining Count Display (MUST)

  • Display a count of todos where completed = false, formatted as "N left".
  • Count updates in real time whenever a todo is added or toggled.
  • Displays "0 left" when all todos are complete or list is empty.

F5 — Data Persistence via PostgreSQL (MUST)

  • All todos stored in a PostgreSQL todos table.
  • Data survives server restarts and page refreshes.
  • Database connection configured via environment variable DATABASE_URL.

F6 — REST API (MUST)

  • GET /api/todos — return all todos ordered by created_at ASC.
  • POST /api/todos — create a new todo; body: { "title": string }.
  • PATCH /api/todos/:id — toggle completed; body: { "completed": boolean }.
  • All endpoints return JSON; error responses include an error string field and appropriate HTTP status codes.

F7 — Basic Styling (NICE-TO-HAVE)

  • Clean, centered single-column layout; readable on a desktop browser.
  • Visual distinction between completed and incomplete todos (strikethrough + muted color).
  • No external UI component library required; plain CSS or CSS Modules acceptable.

6. Data Model

Entity: todos

ColumnTypeConstraintsDescription
idSERIAL / INTEGERPRIMARY KEYAuto-incrementing unique identifier
titleTEXTNOT NULL, length > 0The text of the todo item
completedBOOLEANNOT NULL, DEFAULT falseWhether the todo has been completed
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Timestamp of creation, used for ordering

DDL:

CREATE TABLE IF NOT EXISTS todos (
  id         SERIAL PRIMARY KEY,
  title      TEXT        NOT NULL CHECK (char_length(trim(title)) > 0),
  completed  BOOLEAN     NOT NULL DEFAULT false,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

No other entities. No joins. No foreign keys.

7. Non-Functional Requirements

#Requirement
NFR-1Performance: Page initial load (including todo fetch) must complete in < 2 seconds on localhost.
NFR-2Reliability: API endpoints must return correct HTTP status codes (200, 201, 400, 404, 500).
NFR-3Data Integrity: The database schema enforces non-empty titles at the DB level via a CHECK constraint.
NFR-4Environment Config: Database credentials must be supplied via DATABASE_URL env var; no credentials hardcoded in source.
NFR-5Error Visibility: If an API call fails, the UI must display a human-readable error message (e.g. "Failed to add todo. Please try again.").
NFR-6No Auth: Zero authentication or session management complexity.
NFR-7Browser Support: Must work in the latest stable versions of Chrome, Firefox, and Safari.

8. Tech Stack

LayerTechnologyNotes
FrameworkNext.js 14+ (App Router)Pages at app/ directory; API routes as Route Handlers
LanguageTypeScriptStrict mode enabled
DatabasePostgreSQL 15+Accessed via the pg (node-postgres) npm package
ORM / QueryRaw SQL with pgNo ORM; keeps the smoke-test surface minimal
StylingCSS ModulesScoped styles; no external UI library
RuntimeNode.js 18+Required by Next.js 14
Environment.env.localDATABASE_URL=postgres://...
Package Managernpmpackage.json lockfile committed

9. Acceptance Criteria

Setup

  • npm install && npm run dev starts the app on localhost:3000 without errors.
  • A DATABASE_URL env var is documented in a .env.example file.
  • Running the app against a fresh PostgreSQL DB auto-creates (or migrates) the todos table without manual SQL steps.

Todo List Display

  • Navigating to / renders a page with a visible input field and Add button.
  • When the database is empty, the text "No todos yet — add one above!" is visible.
  • "0 left" is displayed when there are no todos.
  • All todos from the DB are listed in creation-time ascending order on load.

Add Todo

  • Typing "Buy milk" and clicking Add results in "Buy milk" appearing in the list.
  • Pressing Enter after typing a title also submits the form.
  • Submitting an empty or whitespace-only input does NOT create a todo or call the API.
  • After adding, the input field is cleared and focused.
  • The remaining count increases by 1 after a successful add.
  • POST /api/todos with { "title": "Buy milk" } returns HTTP 201 and a JSON object with id, title, completed: false, created_at.
  • POST /api/todos with { "title": "" } returns HTTP 400 with an error field.

Toggle Completion

  • Clicking the checkbox of an incomplete todo marks it visually complete (checkbox checked, title has strikethrough).
  • Clicking the checkbox of a complete todo marks it visually incomplete (checkbox unchecked, strikethrough removed).
  • The remaining count decrements by 1 when a todo is marked complete.
  • The remaining count increments by 1 when a todo is marked incomplete.
  • PATCH /api/todos/:id with { "completed": true } returns HTTP 200 and the updated todo object.
  • PATCH /api/todos/:id with a non-existent ID returns HTTP 404.

Persistence

  • After adding todos and refreshing the page, all todos are still present with correct completion state.
  • The remaining count after refresh matches the count before refresh.

Error Handling

  • If the API returns an error on add, a visible error message is shown to the user.
  • No unhandled promise rejections appear in the browser console during normal use.

10. Out of Scope

  • User authentication, sessions, or per-user todo lists.
  • Editing the text of an existing todo.
  • Deleting todos (individually or bulk "clear completed").
  • Filtering todos (All / Active / Completed views).
  • Due dates, priorities, labels, or any additional todo metadata.
  • Real-time updates between multiple browser tabs (no WebSockets or polling).
  • Deployment configuration (Vercel, Docker, etc.).
  • Mobile responsive design or PWA features.
  • Dark mode.
  • Pagination or infinite scroll (no limit enforced on todo count for this smoke test).
  • End-to-end test suite (e.g. Playwright/Cypress) — the acceptance criteria above are for manual or external automated verification.

Revision chat

Ask to add features, change a flow, or clarify anything.

e.g. "Add email reminders" or "Change signup to use magic links".