Smoke Test App
Product Requirements Document
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
- User opens the app at
/. - User sees a text input field with placeholder text "What needs to be done?" and an Add button.
- User types a non-empty todo title into the input field.
- User clicks Add (or presses Enter).
- App sends a
POST /api/todosrequest with the title. - New todo appears at the bottom of the todo list with a checkbox (unchecked) and its title.
- The remaining count (e.g. "3 left") increments by 1.
- The text input is cleared and focused, ready for the next entry.
Flow 2 — Mark a Todo Complete
- User sees an existing unchecked todo in the list.
- User clicks the checkbox next to the todo's title.
- App sends a
PATCH /api/todos/:idrequest togglingcompletedtotrue. - The checkbox becomes checked and the todo title renders with a strikethrough style.
- The remaining count decrements by 1.
Flow 3 — Mark a Completed Todo Incomplete
- User sees an existing checked (completed) todo in the list.
- User clicks the checked checkbox.
- App sends a
PATCH /api/todos/:idrequest togglingcompletedtofalse. - The checkbox becomes unchecked and the strikethrough styling is removed.
- The remaining count increments by 1.
Flow 4 — View Persisted Todos Across Sessions
- User adds one or more todos (Flow 1) and/or completes some (Flow 2).
- User refreshes the page or navigates away and returns.
- App performs a
GET /api/todoson load. - All previously created todos appear in the list with their correct completion state.
- The remaining count reflects the persisted state accurately.
Flow 5 — Empty-State View
- User opens the app when no todos exist in the database.
- User sees the input field and Add button.
- User sees an empty-state message: "No todos yet — add one above!"
- The remaining count displays "0 left".
5. Features
F1 — Todo List Display (MUST)
- Render all todos fetched from
GET /api/todosordered 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
completedstate viaPATCH /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
todostable. - 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 bycreated_at ASC.POST /api/todos— create a new todo; body:{ "title": string }.PATCH /api/todos/:id— togglecompleted; body:{ "completed": boolean }.- All endpoints return JSON; error responses include an
errorstring 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
| Column | Type | Constraints | Description |
|---|---|---|---|
id | SERIAL / INTEGER | PRIMARY KEY | Auto-incrementing unique identifier |
title | TEXT | NOT NULL, length > 0 | The text of the todo item |
completed | BOOLEAN | NOT NULL, DEFAULT false | Whether the todo has been completed |
created_at | TIMESTAMPTZ | NOT 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-1 | Performance: Page initial load (including todo fetch) must complete in < 2 seconds on localhost. |
| NFR-2 | Reliability: API endpoints must return correct HTTP status codes (200, 201, 400, 404, 500). |
| NFR-3 | Data Integrity: The database schema enforces non-empty titles at the DB level via a CHECK constraint. |
| NFR-4 | Environment Config: Database credentials must be supplied via DATABASE_URL env var; no credentials hardcoded in source. |
| NFR-5 | Error 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-6 | No Auth: Zero authentication or session management complexity. |
| NFR-7 | Browser Support: Must work in the latest stable versions of Chrome, Firefox, and Safari. |
8. Tech Stack
| Layer | Technology | Notes |
|---|---|---|
| Framework | Next.js 14+ (App Router) | Pages at app/ directory; API routes as Route Handlers |
| Language | TypeScript | Strict mode enabled |
| Database | PostgreSQL 15+ | Accessed via the pg (node-postgres) npm package |
| ORM / Query | Raw SQL with pg | No ORM; keeps the smoke-test surface minimal |
| Styling | CSS Modules | Scoped styles; no external UI library |
| Runtime | Node.js 18+ | Required by Next.js 14 |
| Environment | .env.local | DATABASE_URL=postgres://... |
| Package Manager | npm | package.json lockfile committed |
9. Acceptance Criteria
Setup
-
npm install && npm run devstarts the app onlocalhost:3000without errors. - A
DATABASE_URLenv var is documented in a.env.examplefile. - Running the app against a fresh PostgreSQL DB auto-creates (or migrates) the
todostable 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/todoswith{ "title": "Buy milk" }returns HTTP 201 and a JSON object withid,title,completed: false,created_at. -
POST /api/todoswith{ "title": "" }returns HTTP 400 with anerrorfield.
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/:idwith{ "completed": true }returns HTTP 200 and the updated todo object. -
PATCH /api/todos/:idwith 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".