Claude Code can build a full-stack student progress dashboard in one session: a personalized checklist for students, an admin view for the instructor, and a Supabase database connecting them. The build used Claude Code’s plan mode to produce a 10-section plan, then generated seven files covering the database schema, API layer, authentication, and frontend views. Three bugs emerged after deployment: a misnamed environment variable, Gmail’s dot-insensitivity breaking email matching, and a course picker that trapped users on error pages.
What is a full-stack application?
A full-stack application has three layers: a frontend that users interact with (the student checklist and admin dashboard), a backend that handles logic and API requests (the Cloudflare Pages functions), and a database that stores data (the Supabase PostgreSQL database). Claude Code can build all three layers in a single session.
Here’s what “full-stack” means before we go any further: you have something students see, something the instructor sees, and a database in the middle holding it all together. Three layers. In this case: a personalized weekly checklist for each student, an admin view showing everyone’s progress at once, and a Supabase database tracking every checkbox state. Built in one Claude Code session, deployed to a live URL, and then immediately broken in three different ways.
The breaking part is the interesting part.
What the system does
Every student in the course sees a checklist of tasks for the current week. When they check something off, it saves instantly to the database without a page reload. The progress bar at the top of their page updates in real time.
The instructor opens a separate admin view and sees every student’s name, how many tasks they’ve completed, a percentage, and a status label: ON TRACK, NEEDS ATTENTION, or NOT STARTED. Each student row has a mini progress bar for every week of the course, so you can see at a glance not just where someone is now but how they’ve been tracking across the whole cohort.
The curriculum itself is a JSON file. No CMS (content management system, meaning no admin interface for editing course content), no database table for lessons. Just a JavaScript file with the week titles and task lists. Editing the course means editing a file. That’s a feature, not a shortcut.
The build: plan mode first
Claude Code has a mode called plan mode, where it reads your codebase, thinks through what needs to be built, and produces a detailed plan before writing a single line of code. You can review the plan, push back on it, change the approach, and only then say “go build it.”
The plan for this feature came out with ten sections: the database schema, the file list, the build order, the API endpoints, the authentication approach, the deployment checklist. It named seven new files that needed to be created. It wrote out the SQL (structured query language, the commands you use to create and query a database) for the tables.
For running Supabase queries from the terminal, see Supabase for Claude Code Users.
Supabase, for context, is an open-source database platform. “Open-source” means the code is publicly available and you can self-host it, but most people (including here) use Supabase’s hosted version. It gives you a PostgreSQL database, an API that auto-generates from your tables, and built-in row-level security so you can write rules like “a student can only read their own progress, not anyone else’s.” You connect to it from your frontend using a small JavaScript library.
Cloudflare Pages is where the site lives. It’s a free hosting platform for static sites and web apps. You push your files to a GitHub repo, connect it to Cloudflare Pages, and every time you push, it deploys automatically. The whole thing is free for normal usage volumes.
The plan was thorough. The plan was also wrong about three things. But we didn’t know that yet.
The build: seven files in one session
Claude Code wrote all seven files without stopping to ask questions. Here’s roughly what they were:
- The Supabase schema file with two tables: one for student progress records, one for course entitlements (which students have access to which courses)
- The JavaScript module that handles all Supabase communication
- The student dashboard page
- The admin dashboard page
- An authentication check that runs before any protected page loads
- The curriculum data file (that JSON file with the week and task definitions)
- A deployment configuration file for Cloudflare Pages
After the files were written, the schema got applied to the Supabase project (meaning the SQL ran and the actual tables were created in the database), and the whole thing got deployed to Cloudflare Pages.
The site was live. Time to break it.
Stress testing: clicking all the boxes at once
The first test was a stress test. Instead of clicking checkboxes one at a time and waiting to see if they saved, a chunk of JavaScript got injected directly into the browser console to click all seven checkboxes simultaneously.
The browser console is a tool built into every browser where you can type JavaScript commands and they run immediately on the current page. It’s useful for testing things that would take a long time to do manually.
All seven checkboxes saved. The progress bar jumped to 100% instantly. That part worked.
But three bugs showed up during broader testing.
Bug 1: the missing environment variable
An environment variable is a piece of configuration stored outside your code. Instead of putting your Supabase URL and secret key directly in a JavaScript file (which would expose them to anyone who views your source), you store them as environment variables and your code reads them at runtime.
On Cloudflare Pages, you set environment variables in the dashboard. The plan specified which variables were needed. But one variable name in the plan didn’t match the variable name the code was actually looking for. The code was asking for SUPABASE_SERVICE_KEY, the plan said to create SUPABASE_SERVICE_ROLE_KEY, and the Cloudflare dashboard had the right value under the wrong name.
The fix: add the correctly-named variable to the Cloudflare dashboard. Redeploy. Done. This one took about two minutes.
Bug 2: Gmail ignores periods in email addresses
This one is subtle and annoying. Gmail treats [email protected] and [email protected] as the same inbox. The periods don’t matter to Gmail. But they matter to JavaScript.
The entitlements check, which is how the app determines whether a logged-in student has access to the course, was doing a strict equality check: studentEmail === enrolledEmail. Strict equality in JavaScript means the strings have to match exactly, character for character. A period in one and not the other fails the check.
So: sign up with [email protected], log in with [email protected] (or vice versa), and the app says you don’t have access to a course you’ve already paid for.
The fix required adding both versions of the email address to the entitlements table, and updating the equality check to normalize the email before comparing (remove all periods in the local part, lowercase everything). That change touched six different files because the email handling was spread across the authentication module, the entitlements check, the admin view, and a few other places.
Worth noting: this is a real problem that affects real users. It’s not a theoretical edge case. A lot of people type their Gmail address inconsistently.
Bug 3: the course picker that traps you
This one is a classic example of “the happy path works, the error path is broken.”
The happy path is what happens when everything goes right: you select a valid course from the picker, the page loads, you see your checklist. That worked fine.
The error path is what happens when something goes wrong: you somehow select a course that doesn’t exist in your entitlements (in this case, “Cohort 1” when you’re only enrolled in “Cohort 2”), and you land on an error page.
Here’s the bug: when the error page rendered, it didn’t render the course picker. So you were stuck. You couldn’t see your actual courses. You couldn’t switch. The only way out was to manually edit the URL.
The fix: render the course picker even on the error page. Show the error message, but still give the user a way to navigate somewhere valid.
This bug wouldn’t show up in normal testing because you’d never intentionally select a course you don’t have access to. It showed up because the test was trying every option in the picker, including one that wasn’t supposed to work. That’s why stress testing matters.
The debugging loop
All three bugs got fixed without leaving the Claude Code session. The flow was:
- Spot the bug in the browser
- Describe it to Claude Code
- Claude Code identifies the cause and proposes the fix
- Review the fix
- Apply it
- Redeploy to Cloudflare Pages
- Confirm it’s fixed on the live site
For the email bug, step 2 was: “Gmail treats periods as optional in the local part of the address. Our entitlement check is failing for users who signed up with dots and logged in without them.” Claude Code understood the problem immediately, found every place the email comparison happened, and updated all six files in one pass.
The whole debug-and-redeploy cycle for each bug ran under ten minutes. Not because bugs are always easy to fix, but because the codebase was small and Claude Code had written all of it, so it knew exactly where every piece was.
What the admin view looks like
The admin dashboard shows a summary row at the top: total enrolled students, average completion percentage across the cohort, and an overall status. Below that, one row per student: their name, enrollment date, progress percentage, status badge, and a mini bar chart showing week-by-week completion.
The status labels are calculated from the completion percentage: 80% and above is ON TRACK, 40-79% is NEEDS ATTENTION, under 40% is NOT STARTED.
What the plan got right and wrong
The ten-section plan was genuinely useful. It meant the code came out in the right order (you have to create the database before you write the code that connects to it), the file structure was clean, and there weren’t any “wait, where does this go?” moments during the build.
But planning doesn’t eliminate debugging. Three things were still wrong:
- The environment variable name mismatch: the plan specified one name, the code expected another
- The Gmail period issue: the plan didn’t account for Gmail’s normalization behavior
- The course picker error state: the plan covered the happy path but not what happens when the course lookup fails
None of those were hard to fix. But they all required actually running the thing and hitting the edge cases, not just reading the plan and nodding.
The plan is worth doing. Just don’t confuse a good plan with a working system.
After building this, we added tests to prevent regressions. That process is in Unit Tests for Non-Developers.
The curriculum as a JSON file
One decision worth highlighting: the course curriculum is just a JavaScript file that exports an array of objects. Each object has a week number, a title, and a list of tasks for that week. No database table. No admin UI.
export const curriculum = [
{
week: 1,
title: "Getting Started",
tasks: [
"Set up your tools",
"Complete the intro exercise",
"Post in the community"
]
},
// ...
]
This is intentionally simple. The course content doesn’t change that often, and when it does, editing a file is faster than building an admin interface. The tradeoff is that a non-technical person can’t update the curriculum without touching code. For this course, that’s fine. For a course with a lot of collaborators, you’d want something different.
Further reading
- Supabase documentation for getting started with the database, row-level security, and the JavaScript client
- Cloudflare Pages documentation for deployment, environment variables, and build configuration
- Claude Code documentation for plan mode, how to start a session, and what the different modes do
- PostgreSQL row-level security for the security model that keeps students from reading each other’s data
- MDN on strict equality for why
===is case- and character-sensitive and when that matters
Common Questions
Can Claude Code build a full-stack web application?
Yes. Claude Code can generate database schemas, API endpoints, authentication checks, and frontend pages in a single session. Using plan mode first helps structure the build order. The result is deployable to Cloudflare Pages with a Supabase backend.
What is Claude Code plan mode?
Plan mode tells Claude to read the codebase and produce a detailed build plan before writing code. The plan covers database schema, file list, build order, API endpoints, and deployment steps. You review and adjust the plan before saying “go build it.”
Why did Gmail cause a bug in the student dashboard?
Gmail ignores dots in email addresses, treating [email protected] and [email protected] as the same inbox. But JavaScript’s strict equality (===) sees them as different strings. The fix required normalizing emails by removing dots before comparison.
What is Supabase?
Supabase is an open-source backend platform providing a PostgreSQL database, auto-generated APIs, user authentication, and row-level security. It gives you a real relational database with SQL queries, hosted in the cloud with a web dashboard for management.
A note from Alex: hi i’m alex - i run code for creatives. i’m a writer so i feel that it is important to say - i had claude write this piece based on my ideas and ramblings, voice notes, and teachings. the concepts were mine but the words themselves aren’t. i want to say that because its important for me to distinguish, as a writer, what is written ‘by me’ and what’s not. maybe that idea will seem insane and antiquated in a year, i’m not sure, but for now it helps me feel okay about putting stuff out there like this that a) i know is helpful and b) is not MY voice but exists within the umbrella of my business and work. If you have any thoughts or musings on this, i’d genuinely love to hear them - its an open question, all of this stuff, and my guess is as good as yours.