Project Structure
Understanding the codebase organization.
Overview
This template is one Next.js application. A single App Router catch-all mounts the embedded Hono API, while business logic stays in server-only services.
Directory Structure
Key Directories
src/app/
Next.js App Router pages and the catch-all that hosts the Hono API.
| Path | Purpose |
|---|---|
(marketing)/ | Landing and pricing pages |
api/ | One [[...route]] adapter that passes /api/* requests to Hono |
dashboard/ | Server-protected dashboard routes using the shared dashboard shell and navigation |
demo/dashboard/ | Public fixed-data component preview with no customer, provider, or persistent data access |
docs/ | Documentation pages |
signin/, signup/ | Authentication pages |
The Dashboard layout verifies the Supabase user on the server before it renders
the shared sidebar and header. The authenticated Dashboard and public demo use
the same DashboardSidebar component and item set; they provide different route
prefixes, organization state, and user data. Activity and Records appear as
subitems under Operations and reuse the public preview's fixed
founder-revenue compositions. Authenticated Account, Billing, and enabled
Organizations pages use the signed-in user's real state. Analytics, Activity,
Records, Onboarding, Settings, and UI states remain clearly labelled component
showcases when they use fixed data or local-only actions. Add or replace
navigation in src/components/dashboard/dashboard-sidebar.tsx, then provide a
real page for both route prefixes.
The default authenticated installation keeps the shared Organizations item visible but disabled. Enabling the complete Organizations schema activates that same item and replaces the application header with the real organization chooser.
The separate /demo/dashboard route previews the included dashboard
compositions with a fixed dummy user and sample records. It is noindex, does
not call auth, billing, Organizations, customer, or provider APIs, and does not
persist its sample actions.
src/services/
Server-only business logic. These files use the "server-only" pragma to prevent client imports.
| Service | Purpose |
|---|---|
auth/ | Sign up, sign in, OAuth, and account updates |
billing/ | Stripe checkout, portal, webhooks |
user/ | User profile queries |
src/api/
The embedded Hono app owns HTTP paths, methods, input parsing, cookies, headers, and responses. Domain modules call services; they do not own business or persistence rules.
| Path | Purpose |
|---|---|
app.ts | Mounts domain routes and the small health, search, and test-capture endpoints |
routes/auth.ts | Supabase Auth HTTP endpoints |
routes/billing.ts | Stripe checkout and portal endpoints |
routes/user.ts | Private account and billing readers |
routes/webhooks.ts | Raw Stripe webhook ingress |
src/hooks/
Client-side React hooks for data fetching and state.
| Hook | Purpose |
|---|---|
auth/ | Session management, auth mutations |
billing/ | Checkout, user status |
src/lib/
Shared utilities and client configurations.
| Library | Purpose |
|---|---|
supabase/ | Browser and server Supabase clients |
stripe/ | Stripe client configuration |
src/content/
MDX content for documentation and blog posts.
Data Flow
The browser calls the same-origin /api/* URL. The Next.js catch-all passes the
request to Hono, the matching Hono handler calls a service, and the service uses
the existing Supabase, Stripe, or repository owner.
Key Files
| File | Purpose |
|---|---|
src/app/layout.tsx | Root layout with providers |
src/app/dashboard/layout.tsx | Server-side Dashboard authentication admission and shared shell |
src/app/dashboard/account/page.tsx | Display-name and email settings |
src/app/dashboard/billing/page.tsx | Persisted access status and Stripe portal entry |
src/components/dashboard/dashboard-sidebar.tsx | Shared authenticated and demo Dashboard navigation |
src/app/api/[[...route]]/route.ts | Next.js-to-Hono runtime adapter |
src/api/app.ts | Canonical /api/* route registry |
src/proxy.ts | Refreshes Supabase session cookies for matched requests |
src/lib/supabase/server.ts | Server-side Supabase client |
src/lib/supabase/middleware.ts | Cookie refresh helper called by src/proxy.ts |
src/services/billing/service.ts | Stripe checkout and portal logic |
src/lib/source.ts | Fumadocs content source configuration |
Services Pattern
Services use the "server-only" pragma to ensure they only run on the server:
import "server-only";
import { createSupabaseServerClient } from "@/lib/supabase/server";
export async function signUp({ email, password }) {
const supabase = await createSupabaseServerClient();
const { data, error } = await supabase.auth.signUp({ email, password });
// ...
}Hono routes import and call these services:
import { Hono } from "hono";
import { signUp } from "@/services/auth";
export const authRoutes = new Hono();
authRoutes.post("/sign-up", async (c) => {
const body = await c.req.json();
const result = await signUp(body);
return c.json(result, result.ok ? 200 : 400);
});This pattern keeps business logic separate from HTTP handling while maintaining type safety.
Was this page helpful?