Files
gh-greyhaven-ai-claude-code…/skills/project-scaffolding/examples/full-stack-scaffold-example.md
2025-11-29 18:29:07 +08:00

8.5 KiB

Full-Stack Application Scaffold Example

Complete monorepo with React frontend (TanStack) and Cloudflare Worker backend with shared database.

Duration: 30 min | Files: 35 | LOC: ~850 | Stack: React + Vite + TanStack + Cloudflare Worker + D1


Monorepo Structure

my-fullstack-app/
├── frontend/                    # React + Vite + TypeScript
│   ├── src/
│   │   ├── main.tsx            # Entry point
│   │   ├── routes/             # TanStack Router routes
│   │   ├── components/         # React components
│   │   ├── services/           # API client
│   │   └── lib/                # Utilities
│   ├── tests/
│   ├── package.json
│   ├── vite.config.ts
│   └── tsconfig.json
├── backend/                     # Cloudflare Worker
│   ├── src/
│   │   ├── index.ts            # Hono app
│   │   ├── routes/             # API routes
│   │   ├── middleware/         # Auth, CORS
│   │   └── services/           # Business logic
│   ├── tests/
│   ├── wrangler.toml
│   ├── package.json
│   └── tsconfig.json
├── packages/                    # Shared code
│   └── types/
│       ├── src/
│       │   ├── api.ts          # API types
│       │   └── models.ts       # Data models
│       ├── package.json
│       └── tsconfig.json
├── docs/
│   ├── README.md               # Project overview
│   ├── ARCHITECTURE.md         # System architecture
│   └── API.md                  # API documentation
├── .github/
│   └── workflows/
│       └── deploy.yml          # CI/CD pipeline
├── package.json                 # Root workspace config
├── pnpm-workspace.yaml         # pnpm workspaces
└── README.md

Key Features

Frontend (React + TanStack)

Tech Stack:

  • Vite: Fast build tool
  • TanStack Router: Type-safe routing
  • TanStack Query: Server state management
  • TanStack Table: Data tables
  • Zod: Runtime validation

File: frontend/src/main.tsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { routeTree } from './routeTree.gen';

const queryClient = new QueryClient();
const router = createRouter({ routeTree });

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <RouterProvider router={router} />
    </QueryClientProvider>
  </StrictMode>,
);

File: frontend/src/services/api.ts

import { apiClient } from '@my-app/types';

const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8787';

export const api = {
  users: {
    list: () => fetch(`${API_BASE}/api/users`).then(r => r.json()),
    get: (id: string) => fetch(`${API_BASE}/api/users/${id}`).then(r => r.json()),
    create: (data: any) =>
      fetch(`${API_BASE}/api/users`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      }).then(r => r.json()),
  },
};

Backend (Cloudflare Worker)

File: backend/src/index.ts

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { userRoutes } from './routes/users';

const app = new Hono();

app.use('*', cors({ origin: process.env.FRONTEND_URL || '*' }));
app.route('/api/users', userRoutes);

export default app;

Shared Types

File: packages/types/src/models.ts

export interface User {
  id: string;
  email: string;
  name: string;
  created_at: string;
}

export interface CreateUserInput {
  email: string;
  name: string;
}

export interface UpdateUserInput {
  email?: string;
  name?: string;
}

File: packages/types/src/api.ts

import type { User } from './models';

export interface ApiResponse<T> {
  data?: T;
  error?: string;
}

export interface UserListResponse extends ApiResponse<User[]> {
  total: number;
}

export interface UserResponse extends ApiResponse<User> {}

Workspace Configuration

Root package.json (pnpm workspaces)

{
  "name": "my-fullstack-app",
  "private": true,
  "scripts": {
    "dev": "concurrently \"pnpm --filter frontend dev\" \"pnpm --filter backend dev\"",
    "build": "pnpm --filter \"./packages/*\" build && pnpm --filter frontend build && pnpm --filter backend build",
    "test": "pnpm --recursive test",
    "deploy": "pnpm --filter backend deploy && pnpm --filter frontend deploy"
  },
  "devDependencies": {
    "concurrently": "^8.2.2",
    "typescript": "^5.3.3"
  }
}

pnpm-workspace.yaml

packages:
  - 'frontend'
  - 'backend'
  - 'packages/*'

Development Workflow

1. Setup

# Clone and install
git clone <repo>
cd my-fullstack-app
pnpm install

# Setup database
cd backend
wrangler d1 create my-app-db
# Update wrangler.toml with database ID
cd ..

# Create .env files
cp frontend/.env.example frontend/.env
cp backend/.env.example backend/.env

2. Development

# Start both frontend and backend
pnpm dev

# Frontend: http://localhost:5173
# Backend:  http://localhost:8787

3. Testing

# Run all tests
pnpm test

# Test specific workspace
pnpm --filter frontend test
pnpm --filter backend test

4. Deployment

# Deploy backend (Cloudflare Workers)
cd backend
pnpm deploy

# Deploy frontend (Cloudflare Pages)
cd ../frontend
pnpm build
wrangler pages deploy dist

CI/CD Pipeline

File: .github/workflows/deploy.yml

name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'

      - run: pnpm install
      - run: pnpm test
      - run: pnpm build

  deploy-backend:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - run: pnpm install
      - run: pnpm --filter backend deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

  deploy-frontend:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - run: pnpm install
      - run: pnpm --filter frontend build
      - uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: my-app
          directory: frontend/dist

Architecture

Data Flow

User → React App → TanStack Query → API Client
                                        ↓
                              Cloudflare Worker
                                        ↓
                                     D1 Database

Authentication Flow

// frontend/src/lib/auth.ts
export async function login(email: string, password: string) {
  const response = await fetch(`${API_BASE}/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password }),
  });

  const { token } = await response.json();
  localStorage.setItem('token', token);
  return token;
}

// backend/src/middleware/auth.ts
export const auth = () => async (c, next) => {
  const token = c.req.header('Authorization')?.replace('Bearer ', '');
  if (!token) return c.json({ error: 'Unauthorized' }, 401);

  // Verify JWT token
  const user = await verifyToken(token);
  c.set('user', user);
  await next();
};

Metrics

Component Files LOC Tests Coverage
Frontend 15 ~350 8 85%
Backend 12 ~300 6 90%
Shared 4 ~80 2 100%
Docs 4 ~120 - -
Total 35 ~850 16 88%

Next Steps

  1. Run pnpm install to install dependencies
  2. Setup D1 database and update configuration
  3. Run pnpm dev to start development servers
  4. Implement your business logic
  5. Deploy with pnpm deploy

Setup Time: 30 minutes Production Ready: Yes Deployment: Cloudflare Pages + Workers Monitoring: Built-in observability