| name | feature-generator |
| description | Generate full-stack features with Goca backend and React frontend. Use when creating new features, adding CRUD operations, or scaffolding new pages. |
| allowed-tools | Read, Edit, Write, Bash, Glob, Grep |
Feature Generator
Create complete full-stack features following Clean Architecture with Goca.
Feature Structure
Backend (Go with Goca)
- Entity in
backend/internal/domain/→goca make entity - Repository in
backend/internal/repository/→goca make repository - UseCase in
backend/internal/usecase/→goca make usecase - Handler in
backend/internal/handler/→goca make handler
Frontend (React)
- Server Component for initial loading (no flicker)
- Client Component for interactivity
- Generated API hooks via Orval
- UI components with shadcn/ui
Quick Start: New Feature with Goca
cd backend
# Komplettes Feature generieren
goca feature Product --fields "name:string,price:float64,stock:int"
# Or individual layers
goca make entity Product
goca make repository Product
goca make usecase Product
goca make handler Product
# Generate API Client
cd ..
make api
Entity Registry (AutoMigrate)
After goca feature, the new entity must be registered in backend/internal/domain/registry.go:
// internal/domain/registry.go
func AllEntities() []interface{} {
return []interface{}{
&UserStats{},
&Product{}, // ← Add new entity here!
}
}
This is the ONLY place where new entities need to be registered!
Complete Workflow
# 1. Generate feature
cd backend
goca feature Product --fields "name:string,price:float64,stock:int"
# 2. Add entity to registry
# backend/internal/domain/registry.go → add &Product{}
# 3. Generate API
cd ..
make api
# 4. Restart backend (migration runs automatically)
make dev-backend
Server-Side Data Loading Pattern (HydrationBoundary)
Step 1: Server Component with prefetchQuery
// app/(protected)/products/page.tsx (Server Component)
import { dehydrate, HydrationBoundary } from "@tanstack/react-query"
import { cookies } from "next/headers"
import { redirect } from "next/navigation"
import { getGetProductsQueryKey, getProducts } from "@/api/endpoints/products/products"
import { getQueryClient } from "@/lib/get-query-client"
import { getSession } from "@/lib/auth-server"
import { ProductList } from "./product-list"
export default async function ProductsPage() {
// 1. Check session
const session = await getSession()
if (!session) redirect("/login")
// 2. Cookies for auth
const cookieStore = await cookies()
const cookieHeader = cookieStore.getAll().map((c) => `${c.name}=${c.value}`).join("; ")
// 3. Prefetch with Orval function
const queryClient = getQueryClient()
await queryClient.prefetchQuery({
queryKey: getGetProductsQueryKey(),
queryFn: () => getProducts({ headers: { Cookie: cookieHeader }, cache: "no-store" }),
})
// 4. Wrap with HydrationBoundary
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<div className="container py-8">
<h1 className="text-2xl font-bold mb-4">Products</h1>
<ProductList />
</div>
</HydrationBoundary>
)
}
Step 2: Client Component (no initialData needed!)
// app/(protected)/products/product-list.tsx
"use client"
import { useGetProducts } from "@/api/endpoints/products/products"
import { useSSE } from "@/hooks/use-sse"
export function ProductList() {
useSSE() // Real-time Updates
// Data is already hydrated - no initialData needed!
const { data: productsResponse } = useGetProducts()
const products = productsResponse?.status === 200 ? productsResponse.data : null
return (
<div className="grid gap-4">
{products?.map(product => (
<div key={product.id}>{product.name} - {product.price}€</div>
))}
</div>
)
}
Backend Handler mit Swagger
// internal/handler/product.go
// GetProducts godoc
// @Summary List all products
// @Description Get all products for the authenticated user
// @Tags products
// @Accept json
// @Produce json
// @Success 200 {array} ProductResponse
// @Failure 401 {object} ErrorResponse
// @Security BearerAuth
// @Router /products [get]
func (h *ProductHandler) GetProducts(w http.ResponseWriter, r *http.Request) {
// Implementation
}
After Backend Changes
# Always run after handler changes:
make api
Conventions
File Naming
- Go:
snake_case.go - React Pages:
page.tsxin route folder - Client Components:
kebab-case.tsx
Route Protection
- Public:
frontend/src/app/ - Protected:
frontend/src/app/(protected)/ - Auth:
frontend/src/app/(auth)/
No Skeleton/Flicker
- Server Component loads data before rendering
- Client Component receives
initialData - React Query takes over for updates
- SSE for real-time sync