restaurant-feature-workflow
This skill should be used when adding new restaurant features across the full stack (Backend → Shared → Web → Mobile).Trigger keywords: "레스토랑 검색/필터/정렬 추가", "restaurant search/filter/sort", "레스토랑 API 수정", "restaurant list UI 업데이트".Provides step-by-step workflow for database migrations, backend API updates, shared layer updates, and frontend UI implementation.
When & Why to Use This Skill
This Claude skill provides a comprehensive full-stack development workflow for implementing new features in restaurant-based applications. It guides developers through the entire lifecycle—from database migrations and backend API updates to shared logic and frontend UI implementation for both Web and Mobile platforms—ensuring architectural consistency and reducing development time through a standardized, layer-by-layer approach.
Use Cases
- Implementing complex search, filtering, and sorting capabilities for restaurant discovery features across the full stack.
- Updating backend APIs and SQLite database schemas to support new data fields while maintaining data integrity.
- Synchronizing shared business logic and custom React hooks across React Native Web and Mobile applications.
- Streamlining end-to-end feature deployment by following a structured checklist for database, repository, route, and UI layers.
- Debugging and validating full-stack implementations using integrated testing steps and common pitfall solutions.
| name | restaurant-feature-workflow |
|---|---|
| description | | |
| Trigger keywords | "레스토랑 검색/필터/정렬 추가", "restaurant search/filter/sort", "레스토랑 API 수정", "restaurant list UI 업데이트". |
Restaurant Feature Workflow
Purpose: Guide for adding new restaurant features across the full stack (Backend → Shared → Web → Mobile)
Complexity: Medium | Est. Time: 2-4 hours per feature
🎯 When to Use This Skill
Invoke this skill when user requests include:
- "레스토랑 검색 기능 추가해줘"
- "레스토랑 필터/정렬 추가"
- "Add restaurant search/filter/sort"
- "레스토랑 API 수정"
- "레스토랑 목록 UI 업데이트"
Keywords: restaurant, 레스토랑, full stack, API + UI
✅ What This Skill Covers
- Database migrations (SQLite)
- Backend API updates (Fastify)
- Shared layer updates (Hooks, API Service)
- Web UI updates (React Native Web)
- Mobile UI updates (React Native)
- Testing & documentation
❌ Out of Scope
- Initial project setup → See ARCHITECTURE.md
- Deployment → See DEVELOPMENT.md
- Detailed API docs → See FRIENDLY-RESTAURANT.md
📁 Project Structure Quick Reference
niney-life-pickr/
├── servers/friendly/ # Backend (Fastify + SQLite)
│ ├── src/db/migrations/ # Database migrations
│ ├── src/db/repositories/ # Data access layer
│ └── src/routes/ # API routes
├── apps/shared/ # Shared logic (Web + Mobile)
│ ├── hooks/ # Custom React hooks
│ └── services/ # API service
├── apps/web/ # Web UI (React Native Web)
│ └── src/components/Restaurant/
└── apps/mobile/ # Mobile UI (React Native)
└── src/screens/RestaurantListScreen.tsx
🌲 Quick Decision Tree
Before starting, answer these questions:
1. Does feature need DATABASE changes?
├─ YES → Create migration → Update repository
└─ NO → Skip to Step 2
2. Does feature need NEW API endpoint?
├─ YES → Add new route + schema
└─ NO → Modify existing route
3. Does feature need NEW STATE in frontend?
├─ YES → Update useRestaurantList hook
└─ NO → Use existing state
4. Does feature affect UI?
├─ YES → Update Shared → Web → Mobile
└─ NO → Done, update docs only
📋 Layer-by-Layer Checklist
🗄️ Layer 1: Database (if needed)
When: New column, index, or table needed
File: servers/friendly/src/db/migrations/{number}_{description}.sql
Steps:
- ✅ Find highest migration number (e.g.,
005) - ✅ Create new file:
006_add_restaurant_{feature}.sql - ✅ Write migration:
-- Add index for performance CREATE INDEX IF NOT EXISTS idx_restaurants_{column_name} ON restaurants({column_name}); - ✅ Restart server to apply:
cd servers/friendly && npm run dev - ✅ Verify in logs: "All migrations completed"
Tool: Use Write tool for migration file
⚙️ Layer 2: Backend API
2A. Repository Layer
File: servers/friendly/src/db/repositories/restaurant.repository.ts
Steps:
- ✅ Read current
findAll()andcount()methods - ✅ Add new parameter to method signature:
async findAll( limit: number = 20, offset: number = 0, category?: string, newParam?: string // ADD THIS ): Promise<RestaurantDB[]> - ✅ Update SQL query logic:
const conditions: string[] = []; const params: any[] = []; if (newParam && newParam.trim()) { conditions.push('column_name LIKE ?'); params.push(`%${newParam.trim()}%`); } if (conditions.length > 0) { query += ' WHERE ' + conditions.join(' AND '); } - ✅ Update
count()method with same logic
Tools:
Read→ Read repository file firstEdit→ Modify methods
2B. Route Layer
File: servers/friendly/src/routes/restaurant.routes.ts
Steps:
- ✅ Read current route schema
- ✅ Add new parameter to TypeBox schema:
querystring: Type.Object({ // ... existing params newParam: Type.Optional(Type.String({ description: 'Description here', minLength: 1, maxLength: 100 })) }) - ✅ Update request handler:
const { limit, offset, category, newParam } = request.query as { // ... add type newParam?: string; }; const [restaurants, total] = await Promise.all([ restaurantRepository.findAll(limit, offset, category, newParam), restaurantRepository.count(category, newParam) ]);
Tools:
Read→ Read routes fileEdit→ Update schema and handler
2C. Test Backend API
Commands:
# Test with curl
curl "http://localhost:4000/api/restaurants?newParam=test&limit=5"
# Or use Swagger UI
open http://localhost:4000/docs
Expected: 200 OK with filtered results
Tools: Bash → Run curl commands
🔗 Layer 3: Shared Layer
3A. API Service
File: apps/shared/services/api.service.ts
Steps:
- ✅ Read
getRestaurants()method - ✅ Add new parameter:
async getRestaurants( limit: number = 1000, offset: number = 0, category?: string, newParam?: string // ADD THIS ): Promise<ApiResponse<RestaurantListResponse>> { let url = `/api/restaurants?limit=${limit}&offset=${offset}`; if (category) { url += `&category=${encodeURIComponent(category)}`; } // ADD THIS BLOCK if (newParam && newParam.trim()) { url += `&newParam=${encodeURIComponent(newParam.trim())}`; } return this.request<RestaurantListResponse>(url, { method: 'GET' }); }
Tools:
Read→ Read api.service.tsEdit→ Add parameter
3B. Custom Hook
File: apps/shared/hooks/useRestaurantList.ts
Steps:
- ✅ Read hook file
- ✅ Add new state:
const [newState, setNewState] = useState('') - ✅ Update
fetchRestaurants():const response = await apiService.getRestaurants( limit, offset, selectedCategory || undefined, newState || undefined // Pass new state ) - ✅ Add debounced effect (if search/filter):
useEffect(() => { const timer = setTimeout(() => { fetchRestaurants() }, 300) // 300ms debounce return () => clearTimeout(timer) }, [newState]) - ✅ Update return object:
return { // ... existing returns newState, setNewState, }
Tools:
Read→ Read hook fileEdit→ Add state, update fetch, add effect
🖥️ Layer 4: Web UI
4A. Component Props
File: apps/web/src/components/Restaurant/RestaurantList.tsx
Steps:
- ✅ Read component file
- ✅ Update
RestaurantListPropsinterface:interface RestaurantListProps { // ... existing props newState: string setNewState: (value: string) => void } - ✅ Update component parameters:
const RestaurantList: React.FC<RestaurantListProps> = ({ // ... existing params newState, setNewState, }) => {
Tools:
Read→ Read componentEdit→ Update interface and params
4B. Add UI Elements
In same file: Add UI for new feature
{/* Add this UI block */}
<View style={styles.newFeatureContainer}>
<TextInput
style={[styles.input, { borderColor: colors.border, color: colors.text }]}
placeholder="Placeholder text..."
placeholderTextColor={colors.textSecondary}
value={newState}
onChangeText={setNewState}
/>
{newState.length > 0 && (
<TouchableOpacity onPress={() => setNewState('')} style={styles.clearButton}>
<FontAwesomeIcon icon={faTimes} size={16} color={colors.textSecondary} />
</TouchableOpacity>
)}
</View>
Add styles:
const styles = StyleSheet.create({
// ... existing styles
newFeatureContainer: {
marginBottom: 16,
},
clearButton: {
position: 'absolute',
right: 12,
top: 14,
padding: 4,
},
})
Tools: Edit → Add UI and styles
4C. Parent Component
File: apps/web/src/components/Restaurant.tsx
Steps:
- ✅ Read parent component
- ✅ Extract new state from hook:
const { // ... existing state newState, setNewState, } = restaurantState - ✅ Pass to child component(s):
<RestaurantList // ... existing props newState={newState} setNewState={setNewState} /> - ✅ Update
DesktopLayoutinterface if needed
Tools:
Read→ Read parent componentEdit→ Extract state and pass props
📱 Layer 5: Mobile UI
File: apps/mobile/src/screens/RestaurantListScreen.tsx
Steps:
- ✅ Read screen file
- ✅ Extract new state from hook:
const { // ... existing newState, setNewState, } = useRestaurantList({ /* callbacks */ }) - ✅ Add UI (same pattern as Web):
<View style={styles.newFeatureContainer}> <TextInput style={[styles.input, { /* theme colors */ }]} placeholder="Placeholder text..." placeholderTextColor={colors.textSecondary} value={newState} onChangeText={setNewState} keyboardAppearance={theme === 'dark' ? 'dark' : 'light'} /> {newState.length > 0 && ( <TouchableOpacity onPress={() => setNewState('')} style={styles.clearButton}> <Text style={{ fontSize: 16, color: colors.textSecondary }}>✕</Text> </TouchableOpacity> )} </View> - ✅ Add styles to
StyleSheet.create()
Tools:
Read→ Read screen fileEdit→ Extract state, add UI, add styles
✅ Validation Steps
After each layer, verify:
Backend Validation
# Server running?
cd servers/friendly && npm run dev
# API works?
curl "http://localhost:4000/api/restaurants?newParam=test"
# Expected: 200 OK with data
Shared Validation
# TypeScript compiles?
cd apps/shared && npm run type-check
# No errors? → Good to go
Web Validation
# Start web app
cd apps/web && npm run dev
# Open browser
open http://localhost:3000/restaurant
# Test:
# 1. UI renders correctly
# 2. Feature works as expected
# 3. No console errors
Mobile Validation
# Start mobile app
cd apps/mobile && npm start
# Test on iOS/Android
npm run ios
# OR
npm run android
# Test:
# 1. UI renders correctly
# 2. Feature works
# 3. No red screen errors
🚨 Common Pitfalls & Solutions
❌ TypeBox validation fails (400 Bad Request)
Cause: Schema not updated in routes
Fix: Add parameter to Type.Object({ ... }) in route schema
❌ Props type error in React
Cause: Props not passed from parent Fix:
- Check hook exports new state
- Check parent extracts from hook
- Check parent passes to child
- Check child interface includes prop
❌ API returns old data
Cause: Server not restarted after code changes Fix:
cd servers/friendly
npm run kill # Kill old process
npm run dev # Restart
❌ Hook not re-fetching
Cause: Missing useEffect dependency Fix: Add new state to useEffect dependency array
🛠️ Claude Code Tools to Use
🔍 Before Implementing
// Find existing patterns
Grep: pattern="useRestaurantList", output_mode="files_with_matches"
Grep: pattern="getRestaurants", output_mode="files_with_matches"
// Find all restaurant files
Glob: pattern="**/*Restaurant*.{ts,tsx}"
📖 During Implementation
// Read files before editing
Read: file_path="servers/friendly/src/db/repositories/restaurant.repository.ts"
Read: file_path="apps/shared/hooks/useRestaurantList.ts"
// Edit files
Edit: old_string="...", new_string="..."
// Test immediately
Bash: command="curl http://localhost:4000/api/restaurants?test=value"
✅ After Implementation
// Run tests
Bash: command="cd servers/friendly && npm test"
Bash: command="cd apps/web && npm run type-check"
// Check git status
Bash: command="git status"
💡 Example: Adding Restaurant Name Search
User Request: "레스토랑 이름 검색 기능 추가해줘"
Quick Steps:
- ✅ DB: Create
005_add_restaurant_name_index.sqlwith index onname - ✅ Repository: Add
searchName?: stringtofindAll()andcount() - ✅ Routes: Add
searchNameto TypeBox schema, pass to repository - ✅ API Service: Add
searchNameparameter togetRestaurants() - ✅ Hook: Add
searchNamestate, updatefetchRestaurants(), add debounced effect - ✅ Web UI: Add search input, pass props from parent
- ✅ Mobile UI: Add search input, extract from hook
- ✅ Test: Backend curl, Web browser, Mobile simulator
Time: ~2 hours
📚 Related Documentation
- Architecture: ARCHITECTURE.md
- Database: DATABASE.md
- Backend APIs: FRIENDLY-RESTAURANT.md
- Web UI: WEB-RESTAURANT.md
- Mobile UI: MOBILE-RESTAURANT-LIST.md
- Testing: FRIENDLY-TESTING.md
🎬 Skill Invocation
When user says:
- "Add restaurant [feature]"
- "레스토랑 [기능] 추가"
- "Modify restaurant API"
- "Update restaurant list"
Claude should:
- Use this skill as a checklist
- Follow layer-by-layer approach
- Validate after each layer
- Reference detailed docs when needed
- Create TODO list for tracking progress
Estimated workflow time: 2-4 hours depending on complexity
Version: 2.0 (Refined with skill-creator) Last Updated: 2025-10-29 Maintained by: Claude Code