This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- 常に日本語で会話する
- 原則としてAPIの開発はテスト駆動開発(TDD)で進める
- 期待される入出力に基づき、まずテストを作成する
- 実装コードは書かず、テストのみを用意する
- テストを実行し、失敗を確認する
- テストが正しいことを確認できた段階でコミットする
- その後、テストをパスさせる実装を進める
- 実装中はテストを変更せず、コードを修正し続ける
- すべてのテストが通過するまで繰り返す
yarn dev # Start development server with Turbopack
yarn build # Build for production
yarn start # Start production server
yarn lint # Run ESLint
yarn fix # Run ESLint with auto-fix
yarn test # Run tests with Jest
yarn test:watch # Run tests in watch modeyarn schema:gen # Generate Prisma client types after schema changes
yarn migrate:gen <name> # Create a new migration
yarn migrate # Apply migrations (production)
yarn migrate:dev # Apply migrations (development with seeding)
yarn migrate:dev:reset # Reset database and reseed (development only)
yarn seed # Run database seedingdocker-compose up -d # Start PostgreSQL database
docker-compose down # Stop database- Next.js 15 with App Router and Turbopack
- TypeScript with strict type checking
- Prisma ORM with PostgreSQL
- tRPC v11 for type-safe API layer
- NextAuth v5 (beta) for authentication
- TanStack Query v5 for data fetching and caching
- Material-UI (MUI) v7 for UI components and styling (primary framework)
- TailwindCSS v4 (legacy - migrating to MUI)
- Vercel Blob for file storage
- OpenAI SDK for AI features
/src
├── /app # Next.js App Router pages and API routes
│ ├── /api # API endpoints
│ │ ├── /auth/[...nextauth] # NextAuth authentication
│ │ ├── /trpc/[trpc] # tRPC endpoint
│ │ └── /upload # File upload (Vercel Blob)
│ ├── /(main) # Main authenticated layout group
│ │ └── /dashboard # Dashboard pages
│ └── /users # Public user pages
├── /trpc # tRPC layer
│ ├── /routers # Domain-specific routers
│ ├── /usecases # Business logic layer
│ ├── init.ts # tRPC initialization
│ ├── server.tsx # Server-side tRPC client
│ └── client.tsx # Client-side tRPC provider
├── /entities # Domain entities (Post, Thread, User)
├── /features # Feature modules (dashboard, threadDetail, settings)
├── /shared # Shared utilities and UI components
│ ├── /components # MUI-based shared components
│ ├── /ui # Legacy UI components (Radix UI)
│ ├── /lib # Utility functions
│ └── /consts # Constants
├── /contexts # React Context providers
└── /types # Type definitions
The application uses three types of procedures:
publicProcedure- No authentication requiredprotectedProcedure- Requires authenticated userpremiumPlanProcedure- Requires premium subscription
Routers are organized by domain in /src/trpc/routers/:
threadRouter- Thread CRUD operationspostRouter- Post managementuserRouter- User profile operationsfileRouter- File upload handlingtokenRouter- LLM token usage trackingogpRouter- OGP metadata fetchingworkSpaceRouter- Workspace management
Complex business logic is separated into use cases in /src/trpc/usecases/:
dashboard/- Dashboard-related operationsthreadDetail/- Thread detail operationsnewThread/- Thread creation operations
Use cases follow this pattern:
export class ListThreadsUseCase {
async execute(args: Args) {
// Business logic here
const threads = await prisma.thread.findMany({ ... })
return { threads, nextCursor, totalCount }
}
}Key models and relationships:
Userhas manyThread,Post,File,LLMTokenUsageThreadhas manyPost(cascade delete)Postcan have parent/child relationships (nested replies viaparentId)- Soft deletes via
isArchivedflag on posts - Public/private threads via
isPublicflag - Thread status:
WIPorCLOSED
- NextAuth v5 with Google OAuth provider
- Session stored in database via Prisma adapter
- Protected routes check session in server components
- API protection via tRPC middleware (
protectedProcedure)
- Uses Vercel Blob for storage (100MB limit per user)
- Files tracked in database with metadata
- Upload endpoint at
/api/upload - File management in user settings
- OpenAI SDK integrated for AI features
- Token usage tracked in
LLMTokenUsagetable - Model selection available in post creation
Components are organized by feature with this pattern:
Feature/
├── index.tsx # Exports
├── Feature.tsx # Main server component
├── Feature.client.tsx # Client component ("use client")
├── Feature.layout.tsx # Layout-only component
└── Feature.skeleton.tsx # Loading skeleton
import { trpc } from "@/trpc/server"
import { HydrateClient } from "@/trpc/server"
export default async function Page() {
// Prefetch data on server
await trpc.thread.listThreadsByCurrentUser.prefetch({ ... })
return (
<HydrateClient>
{/* Client components will use hydrated data */}
</HydrateClient>
)
}"use client"
import { trpc } from "@/trpc/client"
export function ThreadList() {
const { data } = trpc.thread.listThreadsByCurrentUser.useQuery({ ... })
// TanStack Query + tRPC integration
}For lists with pagination:
const { data, hasNextPage, fetchNextPage } =
trpc.thread.listThreadsByCurrentUser.useInfiniteQuery(
{ limit: 20 },
{ getNextPageParam: (lastPage) => lastPage.nextCursor }
)-
Define the Use Case (if complex logic needed)
- Create file in
/src/trpc/usecases/<domain>/ - Write tests first (TDD)
- Implement business logic
- Create file in
-
Add tRPC Procedures
- Add to appropriate router in
/src/trpc/routers/ - Use
publicProcedure,protectedProcedure, orpremiumPlanProcedure - Define input schema with Zod
- Call use case or Prisma directly
- Add to appropriate router in
-
Create UI Components
- Add to
/src/features/<featureName>/ - Use MUI components from
/src/shared/components/ - Create skeleton for loading states
- Separate server and client components
- Add to
-
Add Page/Route
- Create page in
/src/app/following App Router conventions - Use server components for data fetching
- Wrap with
HydrateClientfor client-side hydration
- Create page in
- Edit
/prisma/schema.prisma - Run
yarn migrate:gen <migration-name>to create migration - Run
yarn migrate:devto apply locally - Run
yarn schema:gento update TypeScript types - Update affected use cases and routers
- Primary Framework: Use Material-UI (MUI) for all new components
- Shared components in
/src/shared/components/ - Theme configuration in
/src/contexts/MuiThemeProvider.tsx - Legacy components may use Radix UI or TailwindCSS, but migrate to MUI when updating
- Use MUI's
sxprop for styling instead of TailwindCSS classes - Import from
@mui/material(e.g.,Button,TextField,Box,Stack) - Use MUI icons from
@mui/icons-material
Example MUI component:
import { Box } from "@/shared/components/Box"
import { Button } from "@/shared/components/Button"
<Box display="flex" gap="16px" p="16px">
<Button variant="contained" size="large">
Click me
</Button>
</Box>- Server Components: Use for data fetching when possible
- Suspense Boundaries: Wrap async components for streaming
- Infinite Queries: Use cursor-based pagination for large lists
- Virtual Scrolling: Use
react-virtualizedfor long lists - Image Optimization: Use
next/imagewith proper sizing
- Use Jest for unit and integration tests
- Test files use
.spec.tsextension - Focus on testing use cases (business logic)
- Mock Prisma and external services
- Run
yarn test:watchduring development
// Server Component
import { auth } from "@/auth"
import { redirect } from "next/navigation"
export default async function ProtectedPage() {
const session = await auth()
if (!session) redirect("/")
// Page content
}"use client"
import { trpc } from "@/trpc/client"
import { useForm } from "@tanstack/react-form"
const mutation = trpc.thread.createThread.useMutation()
const form = useForm({ ... })- Use
react-error-boundaryfor error boundaries - Show toast notifications with
sonner - tRPC errors include typed error codes
Required environment variables:
DATABASE_URL- PostgreSQL connection stringDIRECT_URL- Direct PostgreSQL connection (for Neon)AUTH_SECRET- NextAuth secretAUTH_URL- Application URLGOOGLE_ID/GOOGLE_SECRET- OAuth credentialsVERCEL_BLOB_TOKEN- Vercel Blob storage tokenOPENAI_API_KEY- OpenAI API key (optional)
- File Naming: Use PascalCase for components, camelCase for utilities
- Imports: Use absolute imports with
@/prefix (via tsconfig paths) - TypeScript: Enable strict mode, avoid
anytypes - Commits: Follow conventional commits format
- Code Style: Use Prettier via ESLint, run
yarn fixbefore committing