DEV Community

Cover image for I Pointed Claude Code at My Hackathon API. It Built the Public Website
Jbee - codehooks.io
Jbee - codehooks.io

Posted on

I Pointed Claude Code at My Hackathon API. It Built the Public Website

Part 2 — Using Claude Code CLI to add a public-facing hackathon website to the admin dashboard from Part 1. Participant signup, event browsing, project submissions — all inside the same project.

In Part 1, we used it to build Hackathon HQ — a hackathon management platform with six interconnected collections: events, teams, participants, submissions, judges, and scores. Five minutes from zero to a deployed admin dashboard.

But participants don't care about your admin panel. They want a website. A place to browse upcoming events, sign up, join a team, and submit their project.

The good news: the admin panel already has a complete REST API with auto-generated OpenAPI documentation. We're going to point Claude Code at that spec and let it build public-facing pages — right inside the same project.

The react-admin-dashboard template for Codehooks.io lets you spin up a complete admin panel for any domain — CRM, project management, inventory, ticketing, you name it. Describe your data to an AI, paste the JSON datamodel, and you get a production app with authentication, role-based access, dynamic forms, file uploads, related collections, and auto-generated REST API docs. One JSON file drives the entire stack: React frontend, serverless backend, NoSQL database.

What We're Adding

Public pages alongside the existing admin dashboard — same project, same backend, same deploy:

  • Public landing page — hero section, grid of open events (read-only, via API key)
  • Event detail page — event info, teams, schedule
  • Participant signup & login — external users create accounts and sign in
  • Submission form — authenticated participants submit their projects

Admins keep using the admin panel at /admin. Participants get a clean public site at /. One Codehooks project serves both.

┌──────────────────────────────────────┐
│  Hackathon HQ (one Codehooks project)│
│                                      │
│  /admin/*  → Admin dashboard (Part 1)│
│  /         → Public site (Part 2)    │
│                                      │
│  Public reads use x-apikey           │
│  Participant writes use JWT (login)  │
└──────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Screenshot of the finished website:

Prerequisites

  • The Hackathon HQ project from Part 1 (deployed and running)
  • At least one event created in the admin panel with status other than "Planning" (e.g. "Registration Open") — otherwise the public site will have nothing to show
  • Claude Code CLI installed (npm i -g @anthropic-ai/claude-code)
  • Node.js 18+

Step 1: Create an API Key for Public Read Access

Note: Throughout this article I use hackathonhq as my project name. Replace it with whatever you named your project in Part 1.

The public landing page needs to fetch events without requiring users to log in. Codehooks has built-in API key support. Create a token:

cd hackathonhq
coho add-token --readonly --description "Public website read access"
Enter fullscreen mode Exit fullscreen mode

This key lets the public site fetch events and event details. Authenticated actions (signup, login, submitting projects) still go through the JWT auth system.

Step 2: Feed the OpenAPI Spec to Claude Code

Start Claude Code inside the hackathonhq project:

claude
Enter fullscreen mode Exit fullscreen mode

First, let Claude Code understand the existing API:

> Read the OpenAPI spec at https://hackathonhq.api.codehooks.io/dev/openapi.json
  and summarize the available API endpoints. I want to add public-facing pages
  to this project for hackathon participants.
Enter fullscreen mode Exit fullscreen mode

Claude Code fetches the spec and comes back with a full understanding of your API:

  • Events: GET /api/events with query filters and sorting
  • Teams: GET /api/teams with lookup references to events and participants
  • Submissions: POST /api/submissions with status tracking
  • Auth: POST /auth/login, POST /auth/logout, GET /auth/me
  • Query format: q for MongoDB-style filters, h for sort/limit/offset

It didn't hallucinate an API. It read the real one. Every field name, every enum value, every relationship — straight from your running backend.

Step 3: Add a Registration Endpoint

The admin panel from Part 1 only has login — admins create user accounts manually. For the public site, participants need to register themselves. Ask Claude Code to add it:

> Add a POST /auth/register endpoint to backend/index.js that lets new users
  sign up with username, email, and password. It should create a user with
  role "user", hash the password using the existing hashPassword function,
  check for duplicate usernames, and return the user info with a JWT cookie
  (same as the login endpoint). Make sure /auth/register is covered by the
  existing public auth bypass so it doesn't require authentication.
Enter fullscreen mode Exit fullscreen mode

Claude Code reads the existing auth code in backend/index.js, sees the hashPassword function, the findUserByUsername helper, and the JWT cookie pattern from the login endpoint — then generates a registration endpoint that follows the exact same conventions.

Important: After adding this, deploy the backend so the endpoint is live:

npm run deploy
Enter fullscreen mode Exit fullscreen mode

Step 4: Restructure the Routes

The admin dashboard currently lives at /. We need to move it and make room for public pages:

> I want to add public pages to this React app for hackathon participants.
  Move the existing admin dashboard routes under /admin/* (keep the existing
  Layout with sidebar). Add new public routes at the root:
  / → public landing page
  /events/:id → public event detail
  /signup → participant registration
  /login → participant login
  /submit → submission form (authenticated only)
  Create a PublicLayout component with a top navbar (no sidebar).
  Update App.jsx with both route groups.
Enter fullscreen mode Exit fullscreen mode

Claude Code reads the existing App.jsx, Layout.jsx, and routing structure, then restructures everything. The admin dashboard keeps working exactly as before — just under /admin now. Public pages get their own clean layout.

Important: Make sure all admin routes are fully updated — including collection detail views. Links like /:collection/:id need to become /admin/:collection/:id, otherwise clicking into a record from the admin panel will land you on the public site. Tell Claude Code explicitly:

> Double-check that all internal navigation links in the admin dashboard
  (sidebar, list rows, breadcrumbs, detail views, "New" buttons) use the
  /admin prefix. A detail view at /:collection/:id should be
  /admin/:collection/:id. Test by clicking through from the admin sidebar
  to a collection list, then into a record detail.
Enter fullscreen mode Exit fullscreen mode

After this change, the admin panel login moves to /admin/login. Bookmark it — you'll need it to manage events and review submissions.

Step 5: Build the Landing Page

> Create the public landing page at frontend/src/pages/public/LandingPage.jsx.
  Hero section with "Hackathon HQ" title and tagline. Below it, a responsive
  grid of event cards fetched from GET /api/events. Filter out events where
  status is "Planning" — show all other events (Registration Open, In Progress,
  Judging, Completed). Use an x-apikey header for authentication (the key is
  stored in an environment config). Each card shows name, dates, location,
  status badge, and a "View Details" link. Use shadcn Card components.
  Make it look great.
Enter fullscreen mode Exit fullscreen mode

Claude Code generates the page. Notice what it gets right because it read the spec:

// Public reads use the API key — no login required
const res = await fetch('/api/events?' + new URLSearchParams({
  q: JSON.stringify({
    status: { $ne: 'Planning' }
  }),
  h: JSON.stringify({ $sort: { startDate: -1 } })
}), {
  headers: { 'x-apikey': API_KEY }
});
Enter fullscreen mode Exit fullscreen mode

No guessing. No placeholder endpoints. The actual API format from your OpenAPI spec. The x-apikey header gives read access without requiring login. The filter excludes "Planning" events so only published events appear — everything from "Registration Open" through "Completed".

Tip: If the landing page shows no events, check your admin panel — make sure at least one event has a status other than "Planning".

Step 6: Event Detail Page

> Create an event detail page at frontend/src/pages/public/EventPage.jsx.
  Route: /events/:id. Fetch the event from GET /api/events/:id using the
  x-apikey header. Display name, description, dates, location, prizes
  (render as markdown), and banner image. Below the details, fetch teams
  for this event using GET /api/teams with query {"event._id": "<eventId>"}.
  Show teams as cards with team name, track, and member count.
  Add a "Sign up to participate" call-to-action.
Enter fullscreen mode Exit fullscreen mode

Claude Code understands the lookup relationship format (event._id) because it saw the x-lookup definitions in the spec. It builds the page with the correct query structure, markdown rendering for the prizes field, and responsive team cards.

Step 7: Participant Signup and Login

Now we need authenticated actions. Participants sign up and log in through the auth system:

> Create a signup page at frontend/src/pages/public/SignupPage.jsx and a
  login page at frontend/src/pages/public/LoginPage.jsx. Signup calls
  POST /auth/register with username, email, and password. Login calls
  POST /auth/login. Both use credentials: 'include' for the httpOnly
  JWT cookie. On success, redirect to the landing page. Create an
  AuthContext that checks GET /auth/me on mount and provides user state.
  Update the public navbar: show Login/Sign Up buttons when logged out,
  show username + Logout when logged in.
Enter fullscreen mode Exit fullscreen mode

One prompt. Claude Code creates:

  • AuthContext.jsx — wraps the app, checks session on load
  • SignupPage.jsx — registration form with validation
  • LoginPage.jsx — login form matching the app's style
  • Updated PublicNavbar.jsx — auth-aware navigation

Participants sign up and log in at /signup and /login. Admins use /admin/login to access the admin panel. Same auth system, different entry points.

Step 8: Submission Form

> Create a submission page at frontend/src/pages/public/SubmitPage.jsx.
  Only accessible when logged in — redirect to /login otherwise. The form
  posts to POST /api/submissions with: title, description, event (dropdown
  fetched from /api/events), team (dropdown filtered by selected event),
  track (enum from the spec), demoUrl, repoUrl, videoUrl. Use the JWT
  cookie for authentication (credentials: 'include'). Reference the
  Submission schema from the OpenAPI spec for field types.
Enter fullscreen mode Exit fullscreen mode

Claude Code reads the Submission component schema from the spec and knows every field, every enum value (Draft, Submitted, Under Review, Finalist, Winner, Honorable Mention), and every URL format. The event dropdown filters teams dynamically — pick an event, the team dropdown updates.

A participant submits their project here. An admin sees it immediately in the admin dashboard at /admin. Same data, two interfaces.

The Iterative Part

AI coding is not one prompt and done. Here's the back-and-forth that made it real:

> The event cards need a badge showing the event status. Use the shadcn
  Badge component with different colors for each status.
Enter fullscreen mode Exit fullscreen mode
> Add a countdown timer to events that are "Registration Open" — show
  days until startDate.
Enter fullscreen mode Exit fullscreen mode
> The submit page should show a success message after submission with
  a link back to the event page.
Enter fullscreen mode Exit fullscreen mode

Each time, Claude Code reads the existing files, understands the context, and makes targeted changes. It doesn't start over. It builds on what's already there.

Step 9: Test and Deploy

Test locally:

cd frontend
npm run dev
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:5173. Here's your test checklist:

  1. Landing page — events with status other than "Planning" should appear. No login needed.
  2. Event detail — click an event card, see details and teams.
  3. Sign up — create a participant account at /signup.
  4. Submit — log in, go to /submit, submit a project.
  5. Admin panel — visit /admin/login, log in as admin/admin, verify the submission appears.

When it looks right, deploy — same command as always:

cd ..
npm run deploy
Enter fullscreen mode Exit fullscreen mode

One project. One deploy. Both the admin dashboard and the public site go live together.

Project: hackathonhq-jvel  Space: dev
Deployed Codehook successfully! 🙌 

Check your logs with: coho logs --follow
Enter fullscreen mode Exit fullscreen mode

What We Built

One project, two interfaces:

Admin Dashboard (Part 1) Public Website (Part 2)
Routes /admin/* /
Login URL /admin/login /login
Purpose Manage hackathon data Participant-facing site
Read access JWT (admin/user roles) API key (no login needed)
Write access JWT (admin role) JWT (participant login)
Built with JSON datamodel Claude Code + OpenAPI spec
Time 5 minutes ~30 minutes
Visitor browses events ──→ x-apikey ──→ Read-only access
Participant submits    ──→ JWT login ──→ Write access
Admin manages data     ──→ JWT login ──→ Full admin access
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

OpenAPI specs are AI multipliers. Your backend already generates API docs. Point an AI coding tool at them and it writes frontend code that actually calls the right endpoints with the right parameters. The spec is more accurate than anything you'd type in a prompt.

Same project, two audiences. The admin panel and the public site live in the same codebase, share the same backend, and deploy together. API keys handle public reads. JWT handles authenticated writes. Clean separation, zero duplication.

Claude Code is iterative. It's not a magic "build my app" button. It's a conversation. Describe, generate, review, refine. Each round it reads what it already built and adds to it. The OpenAPI spec keeps it grounded in your real API.

Get Started

The admin dashboard template is open source:

GitHub: react-admin-dashboard

npm i -g codehooks @anthropic-ai/claude-code
coho create hackathonhq --template react-admin-dashboard
cd hackathonhq
claude
Enter fullscreen mode Exit fullscreen mode

Part 1 gives you the admin panel. Part 2 — you, Claude Code, and an OpenAPI spec. Same project, new audience.

Missed Part 1?

This article builds on the admin backend from Part 1. If you haven't read it yet, start there — in five minutes you'll have a deployed hackathon management platform with a full REST API, and you'll be ready to follow along here.

Read Part 1: I Asked AI to Design a Hackathon App. 5 Minutes Later It Was in Production.


What would you add next? A public leaderboard? Real-time score updates? A sponsor portal? Drop your ideas in the comments.

Top comments (0)