13 KiB
13 KiB
name, description
| name | description |
|---|---|
| grey-haven-project-structure | Organize Grey Haven projects following standard structures for TanStack Start (frontend) and FastAPI (backend). Use when creating new projects, organizing files, or refactoring project layout. |
Grey Haven Project Structure
Follow Grey Haven Studio's standardized project structures for TypeScript/React (TanStack Start) and Python/FastAPI projects.
Frontend Structure (TanStack Start + React 19)
Based on cvi-template - TanStack Start, React 19, Drizzle, Better-auth.
Directory Layout
project-root/
├── .claude/ # Claude Code configuration
├── .github/workflows/ # GitHub Actions (CI/CD)
├── public/ # Static assets
├── src/
│ ├── routes/ # TanStack Router file-based routes
│ │ ├── __root.tsx # Root layout
│ │ ├── index.tsx # Homepage
│ │ ├── _authenticated/ # Protected routes group
│ │ │ ├── _layout.tsx # Auth layout wrapper
│ │ │ ├── dashboard.tsx # /dashboard
│ │ │ └── profile.tsx # /profile
│ │ └── auth/ # Auth routes
│ │ ├── login.tsx # /auth/login
│ │ └── signup.tsx # /auth/signup
│ ├── lib/
│ │ ├── components/ # React components
│ │ │ ├── ui/ # Shadcn UI (PascalCase)
│ │ │ ├── auth/ # Auth components
│ │ │ ├── layout/ # Layout components
│ │ │ └── shared/ # Shared components
│ │ ├── server/ # Server-side code
│ │ │ ├── schema/ # Drizzle schema (snake_case)
│ │ │ ├── functions/ # Server functions
│ │ │ ├── auth.ts # Better-auth config
│ │ │ └── db.ts # Database connections
│ │ ├── config/ # Configuration
│ │ │ ├── env.ts # Environment validation
│ │ │ └── auth.ts # Auth configuration
│ │ ├── middleware/ # Route middleware
│ │ ├── hooks/ # Custom React hooks (use-*)
│ │ ├── utils/ # Utility functions
│ │ └── types/ # TypeScript types
│ └── tests/
│ ├── unit/ # Vitest unit tests
│ ├── integration/ # Vitest integration tests
│ └── e2e/ # Playwright E2E tests
├── migrations/ # Drizzle migrations
├── .env.example # Example environment variables
├── .prettierrc # Prettier (90 chars, double quotes)
├── .eslintrc # ESLint (any allowed, strict off)
├── tsconfig.json # TypeScript (~/* path alias)
├── commitlint.config.cjs # Commitlint (100 char header)
├── drizzle.config.ts # Drizzle ORM config
├── vite.config.ts # Vite configuration
├── vitest.config.ts # Vitest test configuration
├── package.json # Dependencies (use bun!)
└── README.md # Project documentation
Key Frontend Patterns
Path Imports - Always Use ~/* Alias
// ✅ Correct - Use ~/* path alias
import { Button } from "~/lib/components/ui/button";
import { getUserById } from "~/lib/server/functions/users";
import { env } from "~/lib/config/env";
import { useAuth } from "~/lib/hooks/use-auth";
// ❌ Wrong - Relative paths
import { Button } from "../../lib/components/ui/button";
File Naming Conventions
- Components: PascalCase (
UserProfile.tsx,LoginForm.tsx) - Routes: lowercase with hyphens (
user-profile.tsx,auth/login.tsx) - Utilities: camelCase or kebab-case (
formatDate.ts,use-auth.ts) - Server functions: camelCase (
auth.ts,users.ts) - Schema files: plural lowercase (
users.ts,organizations.ts)
Component Structure Order
import { useState } from "react"; // 1. External imports
import { useQuery } from "@tanstack/react-query";
import { Button } from "~/lib/components/ui/button"; // 2. Internal imports
import { getUserById } from "~/lib/server/functions/users";
interface UserProfileProps { // 3. Types/Interfaces
userId: string;
}
export default function UserProfile({ userId }: UserProfileProps) { // 4. Component
const [editing, setEditing] = useState(false); // 5. State hooks
const { data: user } = useQuery({ // 6. Queries/Mutations
queryKey: ["user", userId],
queryFn: () => getUserById(userId),
staleTime: 60000,
});
const handleSave = () => { // 7. Event handlers
// ...
};
return ( // 8. JSX
<div>
<h1>{user?.full_name}</h1>
<Button onClick={handleSave}>Save</Button>
</div>
);
}
Backend Structure (FastAPI + Python)
Based on cvi-backend-template - FastAPI, SQLModel, Alembic.
Directory Layout
project-root/
├── .github/workflows/ # GitHub Actions
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI app entry point
│ ├── routers/ # API routers (endpoints)
│ │ ├── __init__.py
│ │ ├── auth.py # /auth routes
│ │ ├── users.py # /users routes
│ │ └── health.py # /health routes
│ ├── services/ # Business logic layer
│ │ ├── __init__.py
│ │ ├── user_service.py
│ │ ├── auth_service.py
│ │ └── billing_service.py
│ ├── db/ # Database layer
│ │ ├── __init__.py
│ │ ├── models/ # SQLModel models (snake_case)
│ │ │ ├── __init__.py
│ │ │ ├── user.py
│ │ │ ├── tenant.py
│ │ │ └── organization.py
│ │ ├── repositories/ # Data access layer
│ │ │ ├── __init__.py
│ │ │ ├── base.py # Base repository
│ │ │ └── user_repository.py
│ │ └── session.py # Database sessions
│ ├── schemas/ # Pydantic schemas
│ │ ├── __init__.py
│ │ ├── user.py # UserCreate, UserResponse
│ │ ├── auth.py # AuthRequest, AuthResponse
│ │ └── common.py # Shared schemas
│ ├── middleware/ # FastAPI middleware
│ │ ├── __init__.py
│ │ ├── auth.py # JWT verification
│ │ └── tenant.py # Tenant context
│ ├── core/ # Core configuration
│ │ ├── __init__.py
│ │ ├── config.py # Settings (Doppler)
│ │ ├── security.py # Password hashing, JWT
│ │ └── deps.py # FastAPI dependencies
│ └── utils/ # Utility functions
│ ├── __init__.py
│ └── format.py
├── tests/ # Pytest tests
│ ├── __init__.py
│ ├── unit/ # @pytest.mark.unit
│ ├── integration/ # @pytest.mark.integration
│ └── e2e/ # @pytest.mark.e2e
├── alembic/ # Alembic migrations
│ ├── versions/
│ └── env.py
├── .env.example # Example environment variables
├── .env # Local environment (gitignored)
├── pyproject.toml # Ruff, mypy, pytest config
├── alembic.ini # Alembic configuration
├── Taskfile.yml # Task runner commands
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Development dependencies
└── README.md # Project documentation
Key Backend Patterns
Import Organization (Automatic with Ruff)
"""Module docstring describing purpose."""
# 1. Standard library imports
import os
from datetime import datetime
from typing import Optional
from uuid import UUID
# 2. Third-party imports
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import select
# 3. Local imports
from app.db.models.user import User
from app.db.repositories.user_repository import UserRepository
from app.schemas.user import UserCreate, UserResponse
File Naming Conventions
- Modules: snake_case (
user_service.py,auth_middleware.py) - Models: PascalCase class, snake_case file (
Userinuser.py) - Tests:
test_prefix (test_user_service.py)
Repository Pattern (with Tenant Isolation)
# app/db/repositories/base.py
from typing import Generic, TypeVar, Optional
from uuid import UUID
from sqlmodel import select
from sqlalchemy.ext.asyncio import AsyncSession
T = TypeVar("T")
class BaseRepository(Generic[T]):
"""Base repository with CRUD and tenant isolation."""
def __init__(self, session: AsyncSession, model: type[T]):
self.session = session
self.model = model
async def get_by_id(self, id: UUID, tenant_id: UUID) -> Optional[T]:
"""Get by ID with automatic tenant filtering."""
result = await self.session.execute(
select(self.model)
.where(self.model.id == id)
.where(self.model.tenant_id == tenant_id)
)
return result.scalar_one_or_none()
Service Layer Pattern
# app/services/user_service.py
from uuid import UUID
from app.db.repositories.user_repository import UserRepository
from app.schemas.user import UserCreate, UserResponse
class UserService:
"""User business logic."""
def __init__(self, user_repo: UserRepository):
self.user_repo = user_repo
async def create_user(self, data: UserCreate, tenant_id: UUID) -> UserResponse:
"""Create new user with validation."""
existing = await self.user_repo.get_by_email(data.email_address, tenant_id)
if existing:
raise ValueError("Email already registered")
user = await self.user_repo.create(data, tenant_id)
return UserResponse.model_validate(user)
Supporting Documentation
All supporting files are under 500 lines per Anthropic best practices:
-
examples/ - Complete project examples
- frontend-directory-structure.md - Full TanStack Start structure
- backend-directory-structure.md - Full FastAPI structure
- component-structure.md - React component patterns
- repository-pattern.md - Repository with tenant isolation
- service-pattern.md - Service layer examples
- INDEX.md - Examples navigation
-
reference/ - Structure references
- file-naming.md - Naming conventions
- import-organization.md - Import ordering rules
- path-aliases.md - Path alias configuration
- INDEX.md - Reference navigation
-
templates/ - Copy-paste ready templates
- tanstack-start-project/ - Frontend scaffold
- fastapi-project/ - Backend scaffold
-
checklists/ - Structure checklists
- project-setup-checklist.md - New project setup
- refactoring-checklist.md - Structure refactoring
When to Apply This Skill
Use this skill when:
- Creating new Grey Haven projects
- Organizing files in existing projects
- Refactoring project layout
- Setting up directory structure
- Deciding where to place new files
- Onboarding new team members to project structure
Template Reference
These patterns are from Grey Haven's production templates:
- Frontend: cvi-template (TanStack Start + React 19)
- Backend: cvi-backend-template (FastAPI + Python)
Critical Reminders
- Path aliases: Always use ~/* for TypeScript imports
- File naming: Components PascalCase, modules snake_case
- Import order: External → Internal → Local
- Component structure: Imports → Types → Component → Hooks → Handlers → JSX
- Tenant isolation: Repository pattern includes tenant_id filtering
- Database fields: snake_case in schema files (NOT camelCase)
- Test organization: unit/, integration/, e2e/ directories
- Configuration: Doppler for all environment variables
- Routes: TanStack file-based routing in src/routes/
- Backend layers: Routers → Services → Repositories → Models