render-create 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +207 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.js +45 -0
  4. package/dist/commands/check.d.ts +8 -0
  5. package/dist/commands/check.js +96 -0
  6. package/dist/commands/init.d.ts +12 -0
  7. package/dist/commands/init.js +1201 -0
  8. package/dist/commands/sync.d.ts +8 -0
  9. package/dist/commands/sync.js +126 -0
  10. package/dist/types.d.ts +246 -0
  11. package/dist/types.js +4 -0
  12. package/dist/utils.d.ts +53 -0
  13. package/dist/utils.js +142 -0
  14. package/package.json +65 -0
  15. package/templates/LINTING_SETUP.md +205 -0
  16. package/templates/README_TEMPLATE.md +68 -0
  17. package/templates/STYLE_GUIDE.md +241 -0
  18. package/templates/assets/favicon.png +0 -0
  19. package/templates/assets/favicon.svg +17 -0
  20. package/templates/biome.json +43 -0
  21. package/templates/cursor/rules/drizzle.mdc +165 -0
  22. package/templates/cursor/rules/fastify.mdc +132 -0
  23. package/templates/cursor/rules/general.mdc +112 -0
  24. package/templates/cursor/rules/nextjs.mdc +89 -0
  25. package/templates/cursor/rules/python.mdc +89 -0
  26. package/templates/cursor/rules/react.mdc +200 -0
  27. package/templates/cursor/rules/sqlalchemy.mdc +205 -0
  28. package/templates/cursor/rules/tailwind.mdc +139 -0
  29. package/templates/cursor/rules/typescript.mdc +112 -0
  30. package/templates/cursor/rules/vite.mdc +169 -0
  31. package/templates/cursor/rules/workflows.mdc +349 -0
  32. package/templates/docker-compose.example.yml +55 -0
  33. package/templates/drizzle/db-index.ts +15 -0
  34. package/templates/drizzle/drizzle.config.ts +10 -0
  35. package/templates/drizzle/schema.ts +12 -0
  36. package/templates/env.example +15 -0
  37. package/templates/fastapi/app/__init__.py +1 -0
  38. package/templates/fastapi/app/config.py +12 -0
  39. package/templates/fastapi/app/database.py +16 -0
  40. package/templates/fastapi/app/models.py +13 -0
  41. package/templates/fastapi/main.py +22 -0
  42. package/templates/fastify/index.ts +40 -0
  43. package/templates/github/CODEOWNERS +10 -0
  44. package/templates/github/ISSUE_TEMPLATE/bug_report.md +39 -0
  45. package/templates/github/ISSUE_TEMPLATE/feature_request.md +23 -0
  46. package/templates/github/PULL_REQUEST_TEMPLATE.md +25 -0
  47. package/templates/gitignore/node.gitignore +41 -0
  48. package/templates/gitignore/python.gitignore +49 -0
  49. package/templates/multi-api/README.md +60 -0
  50. package/templates/multi-api/gitignore +28 -0
  51. package/templates/multi-api/node-api/drizzle.config.ts +10 -0
  52. package/templates/multi-api/node-api/package-simple.json +13 -0
  53. package/templates/multi-api/node-api/package.json +16 -0
  54. package/templates/multi-api/node-api/src/db/index.ts +13 -0
  55. package/templates/multi-api/node-api/src/db/schema.ts +9 -0
  56. package/templates/multi-api/node-api/src/index-simple.ts +36 -0
  57. package/templates/multi-api/node-api/src/index.ts +50 -0
  58. package/templates/multi-api/node-api/tsconfig.json +20 -0
  59. package/templates/multi-api/python-api/app/__init__.py +1 -0
  60. package/templates/multi-api/python-api/app/config.py +12 -0
  61. package/templates/multi-api/python-api/app/database.py +16 -0
  62. package/templates/multi-api/python-api/app/models.py +13 -0
  63. package/templates/multi-api/python-api/main-simple.py +25 -0
  64. package/templates/multi-api/python-api/main.py +44 -0
  65. package/templates/multi-api/python-api/requirements-simple.txt +3 -0
  66. package/templates/multi-api/python-api/requirements.txt +8 -0
  67. package/templates/next/globals.css +126 -0
  68. package/templates/next/layout.tsx +34 -0
  69. package/templates/next/next.config.static.ts +10 -0
  70. package/templates/next/page-fullstack.tsx +120 -0
  71. package/templates/next/page.tsx +72 -0
  72. package/templates/presets.json +581 -0
  73. package/templates/ruff.toml +30 -0
  74. package/templates/tsconfig.base.json +17 -0
  75. package/templates/vite/index.css +127 -0
  76. package/templates/vite/vite.config.ts +7 -0
  77. package/templates/worker/py/cron.py +53 -0
  78. package/templates/worker/py/worker.py +95 -0
  79. package/templates/worker/py/workflow.py +73 -0
  80. package/templates/worker/ts/cron.ts +49 -0
  81. package/templates/worker/ts/worker.ts +84 -0
  82. package/templates/worker/ts/workflow.ts +67 -0
@@ -0,0 +1,200 @@
1
+ ---
2
+ description: React patterns for state, forms, icons, and default UI style
3
+ globs: ["**/*.tsx", "**/*.jsx"]
4
+ ---
5
+
6
+ # React Conventions
7
+
8
+ ## Default UI Style: Brutalist
9
+
10
+ Unless the user specifies a different design direction, use this style:
11
+
12
+ ### Core Principles:
13
+
14
+ - **Background**: Black (`bg-black` or `bg-zinc-950`)
15
+ - **Text/Accents**: White (`text-white`)
16
+ - **Borders**: Sharp, 1-2px white borders (`border border-white`)
17
+ - **Corners**: NO rounded corners (`rounded-none` always)
18
+ - **Gradients**: NONE - flat colors only
19
+ - **Shadows**: Minimal or none
20
+
21
+ ### Component Examples:
22
+
23
+ ```tsx
24
+ // Button
25
+ <button className="bg-black text-white border border-white px-4 py-2 rounded-none hover:bg-white hover:text-black transition-colors">
26
+ Click me
27
+ </button>
28
+
29
+ // Card
30
+ <div className="bg-black border border-white p-6 rounded-none">
31
+ <h2 className="text-white text-xl font-bold">Title</h2>
32
+ </div>
33
+
34
+ // Input
35
+ <input className="bg-black text-white border border-white px-3 py-2 rounded-none focus:outline-none focus:ring-1 focus:ring-white" />
36
+ ```
37
+
38
+ ### Typography:
39
+
40
+ - Use system fonts or monospace for brutalist feel
41
+ - Bold headings, regular body text
42
+ - High contrast only
43
+
44
+ ### What to AVOID:
45
+
46
+ - `rounded-*` classes (except `rounded-none`)
47
+ - `bg-gradient-*` classes
48
+ - Soft shadows (`shadow-sm`, `shadow-md`)
49
+ - Pastel or muted colors
50
+ - Decorative elements
51
+
52
+ ### Required Footer:
53
+
54
+ Always include a footer with Render docs and GitHub project links:
55
+
56
+ ```tsx
57
+ <footer className="border-t border-white py-4 mt-auto">
58
+ <div className="flex justify-center gap-6 text-sm">
59
+ <a
60
+ href="https://docs.render.com"
61
+ target="_blank"
62
+ rel="noopener noreferrer"
63
+ className="text-white hover:underline"
64
+ >
65
+ Render Docs
66
+ </a>
67
+ <a
68
+ href="https://github.com/render-examples/PROJECT_NAME"
69
+ target="_blank"
70
+ rel="noopener noreferrer"
71
+ className="text-white hover:underline"
72
+ >
73
+ GitHub
74
+ </a>
75
+ </div>
76
+ </footer>
77
+ ```
78
+
79
+ Replace `PROJECT_NAME` with the actual repository name.
80
+
81
+ ## State Management
82
+
83
+ Use **React hooks + Context** (no external libs unless needed):
84
+
85
+ ```tsx
86
+ // Simple local state
87
+ const [count, setCount] = useState(0);
88
+
89
+ // Complex local state
90
+ const [state, dispatch] = useReducer(reducer, initialState);
91
+
92
+ // Shared state via Context
93
+ const UserContext = createContext<UserContextType | null>(null);
94
+
95
+ export function UserProvider({ children }: { children: React.ReactNode }) {
96
+ const [user, setUser] = useState<User | null>(null);
97
+
98
+ return (
99
+ <UserContext.Provider value={{ user, setUser }}>
100
+ {children}
101
+ </UserContext.Provider>
102
+ );
103
+ }
104
+
105
+ export function useUser() {
106
+ const context = useContext(UserContext);
107
+ if (!context) {
108
+ throw new Error("useUser must be used within UserProvider");
109
+ }
110
+ return context;
111
+ }
112
+ ```
113
+
114
+ ## Forms with React Hook Form
115
+
116
+ ```tsx
117
+ "use client";
118
+
119
+ import { useForm } from "react-hook-form";
120
+ import { zodResolver } from "@hookform/resolvers/zod";
121
+ import { z } from "zod";
122
+
123
+ const formSchema = z.object({
124
+ email: z.string().email("Invalid email"),
125
+ password: z.string().min(8, "Password must be at least 8 characters"),
126
+ });
127
+
128
+ type FormData = z.infer<typeof formSchema>;
129
+
130
+ export function LoginForm() {
131
+ const {
132
+ register,
133
+ handleSubmit,
134
+ formState: { errors, isSubmitting },
135
+ } = useForm<FormData>({
136
+ resolver: zodResolver(formSchema),
137
+ });
138
+
139
+ const onSubmit = async (data: FormData) => {
140
+ console.log(data);
141
+ };
142
+
143
+ return (
144
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
145
+ <div>
146
+ <input
147
+ {...register("email")}
148
+ type="email"
149
+ placeholder="Email"
150
+ className="w-full bg-black text-white border border-white px-3 py-2 rounded-none"
151
+ />
152
+ {errors.email && (
153
+ <p className="text-red-500 text-sm mt-1">{errors.email.message}</p>
154
+ )}
155
+ </div>
156
+
157
+ <button
158
+ type="submit"
159
+ disabled={isSubmitting}
160
+ className="w-full bg-white text-black px-4 py-2 rounded-none hover:bg-gray-200 disabled:opacity-50"
161
+ >
162
+ {isSubmitting ? "Loading..." : "Submit"}
163
+ </button>
164
+ </form>
165
+ );
166
+ }
167
+ ```
168
+
169
+ ## Icons with Lucide React
170
+
171
+ ```tsx
172
+ import { Search, Menu, X, ChevronRight, Loader2 } from "lucide-react";
173
+
174
+ // Basic usage
175
+ <Search className="w-5 h-5" />
176
+
177
+ // With color
178
+ <Menu className="w-6 h-6 text-white" />
179
+
180
+ // Loading spinner
181
+ <Loader2 className="w-5 h-5 animate-spin" />
182
+
183
+ // In buttons
184
+ <button className="flex items-center gap-2">
185
+ <Search className="w-4 h-4" />
186
+ Search
187
+ </button>
188
+ ```
189
+
190
+ ### Common Lucide Icons
191
+
192
+ | Icon | Use Case |
193
+ |------|----------|
194
+ | `Search` | Search inputs |
195
+ | `Menu`, `X` | Mobile nav toggle |
196
+ | `ChevronRight/Left/Up/Down` | Navigation, accordions |
197
+ | `Loader2` | Loading states (add `animate-spin`) |
198
+ | `Check`, `X` | Success/error states |
199
+ | `Plus`, `Minus` | Add/remove actions |
200
+ | `ExternalLink` | External links |
@@ -0,0 +1,205 @@
1
+ ---
2
+ description: SQLAlchemy conventions for FastAPI with PostgreSQL
3
+ globs: ["**/models/**/*.py", "**/db/**/*.py", "**/*_model.py"]
4
+ ---
5
+
6
+ # SQLAlchemy Conventions
7
+
8
+ ## Project Structure
9
+
10
+ ### Small projects: Single models file
11
+ ```
12
+ project/
13
+ ├── app.py
14
+ ├── config.py
15
+ ├── models.py # All models
16
+ └── db.py # Database connection
17
+ ```
18
+
19
+ ### Larger projects: Split by domain
20
+ ```
21
+ project/
22
+ ├── app.py
23
+ ├── config.py
24
+ ├── db.py
25
+ ├── models/
26
+ │ ├── __init__.py # Re-exports all models
27
+ │ ├── user.py
28
+ │ ├── post.py
29
+ │ └── comment.py
30
+ └── alembic/ # Migrations
31
+ ```
32
+
33
+ ## Database Connection (Async)
34
+
35
+ ```python
36
+ # db.py
37
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
38
+ from sqlalchemy.orm import sessionmaker, declarative_base
39
+
40
+ DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/dbname"
41
+
42
+ engine = create_async_engine(DATABASE_URL, echo=True)
43
+ async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
44
+
45
+ Base = declarative_base()
46
+
47
+ async def get_db():
48
+ async with async_session() as session:
49
+ yield session
50
+ ```
51
+
52
+ ## Model Definition
53
+
54
+ ```python
55
+ # models/user.py
56
+ from sqlalchemy import Column, String, DateTime
57
+ from sqlalchemy.dialects.postgresql import UUID
58
+ from sqlalchemy.sql import func
59
+ import uuid
60
+
61
+ from db import Base
62
+
63
+ class User(Base):
64
+ __tablename__ = "users"
65
+
66
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
67
+ email = Column(String, unique=True, nullable=False, index=True)
68
+ name = Column(String, nullable=False)
69
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
70
+ updated_at = Column(DateTime(timezone=True), onupdate=func.now())
71
+ ```
72
+
73
+ ## Pydantic Schemas
74
+
75
+ ```python
76
+ # schemas/user.py
77
+ from pydantic import BaseModel, EmailStr
78
+ from datetime import datetime
79
+ from uuid import UUID
80
+
81
+ class UserBase(BaseModel):
82
+ email: EmailStr
83
+ name: str
84
+
85
+ class UserCreate(UserBase):
86
+ pass
87
+
88
+ class UserResponse(UserBase):
89
+ id: UUID
90
+ created_at: datetime
91
+ updated_at: datetime | None
92
+
93
+ class Config:
94
+ from_attributes = True # Allows ORM model conversion
95
+ ```
96
+
97
+ ## FastAPI Integration
98
+
99
+ ```python
100
+ # app.py
101
+ from fastapi import FastAPI, Depends, HTTPException
102
+ from sqlalchemy.ext.asyncio import AsyncSession
103
+ from sqlalchemy import select
104
+
105
+ from db import get_db
106
+ from models.user import User
107
+ from schemas.user import UserCreate, UserResponse
108
+
109
+ app = FastAPI()
110
+
111
+ @app.post("/users", response_model=UserResponse)
112
+ async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db)):
113
+ db_user = User(**user.model_dump())
114
+ db.add(db_user)
115
+ await db.commit()
116
+ await db.refresh(db_user)
117
+ return db_user
118
+
119
+ @app.get("/users/{user_id}", response_model=UserResponse)
120
+ async def get_user(user_id: str, db: AsyncSession = Depends(get_db)):
121
+ result = await db.execute(select(User).where(User.id == user_id))
122
+ user = result.scalar_one_or_none()
123
+ if not user:
124
+ raise HTTPException(status_code=404, detail="User not found")
125
+ return user
126
+ ```
127
+
128
+ ## Async Queries
129
+
130
+ ```python
131
+ from sqlalchemy import select, update, delete
132
+
133
+ # Select all
134
+ result = await db.execute(select(User))
135
+ users = result.scalars().all()
136
+
137
+ # Select with filter
138
+ result = await db.execute(select(User).where(User.email == email))
139
+ user = result.scalar_one_or_none()
140
+
141
+ # Insert
142
+ new_user = User(email="test@example.com", name="Test")
143
+ db.add(new_user)
144
+ await db.commit()
145
+
146
+ # Update
147
+ await db.execute(
148
+ update(User).where(User.id == user_id).values(name="Updated")
149
+ )
150
+ await db.commit()
151
+
152
+ # Delete
153
+ await db.execute(delete(User).where(User.id == user_id))
154
+ await db.commit()
155
+ ```
156
+
157
+ ## Relations
158
+
159
+ ```python
160
+ from sqlalchemy import ForeignKey
161
+ from sqlalchemy.orm import relationship
162
+
163
+ class Post(Base):
164
+ __tablename__ = "posts"
165
+
166
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
167
+ title = Column(String, nullable=False)
168
+ author_id = Column(UUID(as_uuid=True), ForeignKey("users.id"))
169
+
170
+ author = relationship("User", back_populates="posts")
171
+
172
+ class User(Base):
173
+ # ... other fields
174
+ posts = relationship("Post", back_populates="author")
175
+ ```
176
+
177
+ ## Alembic Migrations
178
+
179
+ ```bash
180
+ # Initialize
181
+ alembic init alembic
182
+
183
+ # Generate migration
184
+ alembic revision --autogenerate -m "Add users table"
185
+
186
+ # Apply migrations
187
+ alembic upgrade head
188
+
189
+ # Rollback
190
+ alembic downgrade -1
191
+ ```
192
+
193
+ ### alembic/env.py (async)
194
+
195
+ ```python
196
+ from sqlalchemy.ext.asyncio import create_async_engine
197
+ from models import Base # Import your Base
198
+
199
+ target_metadata = Base.metadata
200
+
201
+ async def run_async_migrations():
202
+ connectable = create_async_engine(DATABASE_URL)
203
+ async with connectable.connect() as connection:
204
+ await connection.run_sync(do_run_migrations)
205
+ ```
@@ -0,0 +1,139 @@
1
+ ---
2
+ description: Tailwind CSS conventions with brutalist default style
3
+ globs: ["**/*.tsx", "**/*.jsx", "**/tailwind.config.*"]
4
+ ---
5
+
6
+ # Tailwind CSS Conventions
7
+
8
+ ## Default UI Style: Brutalist
9
+
10
+ Unless the user specifies a different design direction, use this style:
11
+
12
+ ### Core Palette:
13
+
14
+ ```tsx
15
+ // Background
16
+ className="bg-black" // or bg-zinc-950
17
+
18
+ // Text
19
+ className="text-white"
20
+
21
+ // Borders
22
+ className="border border-white"
23
+
24
+ // Corners - ALWAYS use rounded-none
25
+ className="rounded-none"
26
+
27
+ // NO gradients, NO soft shadows
28
+ ```
29
+
30
+ ### Brutalist Component Patterns:
31
+
32
+ ```tsx
33
+ // Button
34
+ <button className="bg-black text-white border border-white px-4 py-2 rounded-none hover:bg-white hover:text-black transition-colors">
35
+
36
+ // Card
37
+ <div className="bg-black border border-white p-6 rounded-none">
38
+
39
+ // Input
40
+ <input className="bg-black text-white border border-white px-3 py-2 rounded-none focus:outline-none focus:ring-1 focus:ring-white" />
41
+
42
+ // Link
43
+ <a className="text-white hover:underline">
44
+ ```
45
+
46
+ ### What to AVOID:
47
+
48
+ - `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-full` - use `rounded-none`
49
+ - `bg-gradient-*` - use flat colors
50
+ - `shadow-sm`, `shadow-md`, `shadow-lg` - minimal/no shadows
51
+ - Pastel colors - use high contrast black/white
52
+
53
+ ## Class Organization
54
+
55
+ Organize classes in **logical groups** (layout → spacing → sizing → colors → effects):
56
+
57
+ ```tsx
58
+ // Good: Logical grouping
59
+ <div className="
60
+ flex items-center justify-between
61
+ p-4 gap-2
62
+ w-full max-w-md
63
+ bg-black text-white
64
+ border border-white rounded-none
65
+ hover:bg-white hover:text-black transition-colors
66
+ ">
67
+ ```
68
+
69
+ ```tsx
70
+ // Single line for simple elements
71
+ <button className="px-4 py-2 bg-black text-white border border-white rounded-none">
72
+ Click me
73
+ </button>
74
+ ```
75
+
76
+ ## When to Extract Components
77
+
78
+ **Inline classes first**, extract when:
79
+ - Pattern repeats 3+ times
80
+ - Complex responsive/state logic
81
+ - Needs props for variants
82
+
83
+ ```tsx
84
+ // After extraction (component)
85
+ function Button({ children }: { children: React.ReactNode }) {
86
+ return (
87
+ <button className="px-4 py-2 bg-black text-white border border-white rounded-none hover:bg-white hover:text-black transition-colors">
88
+ {children}
89
+ </button>
90
+ );
91
+ }
92
+ ```
93
+
94
+ ## Conditional Classes
95
+
96
+ Use `cn()` utility (from `clsx` + `tailwind-merge`):
97
+
98
+ ```tsx
99
+ import { cn } from "@/lib/utils";
100
+
101
+ function Button({ variant = "primary", className, ...props }) {
102
+ return (
103
+ <button
104
+ className={cn(
105
+ "px-4 py-2 rounded-none font-medium transition-colors border",
106
+ variant === "primary" && "bg-black text-white border-white hover:bg-white hover:text-black",
107
+ variant === "outline" && "bg-transparent text-white border-white hover:bg-white hover:text-black",
108
+ className
109
+ )}
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+ ```
115
+
116
+ ## Responsive Design
117
+
118
+ Mobile-first approach:
119
+ ```tsx
120
+ <div className="
121
+ p-4 text-sm
122
+ md:p-6 md:text-base
123
+ lg:p-8 lg:text-lg
124
+ ">
125
+ ```
126
+
127
+ ## Dark Mode
128
+
129
+ For brutalist style, dark mode is the default. If supporting light mode:
130
+ ```tsx
131
+ <div className="bg-black dark:bg-black text-white dark:text-white">
132
+ ```
133
+
134
+ ## Avoid
135
+
136
+ - Don't use `@apply` excessively (defeats Tailwind's purpose)
137
+ - Don't create utility classes that duplicate Tailwind
138
+ - Don't use rounded corners (brutalist style)
139
+ - Don't use gradients (brutalist style)
@@ -0,0 +1,112 @@
1
+ ---
2
+ description: TypeScript-specific conventions
3
+ globs: ["**/*.ts", "**/*.tsx"]
4
+ ---
5
+
6
+ # TypeScript Conventions
7
+
8
+ ## Functions
9
+
10
+ Use **named functions** for exports:
11
+ ```typescript
12
+ // Good
13
+ export function handleRequest(req: Request): Response {
14
+ // ...
15
+ }
16
+
17
+ // Avoid
18
+ export const handleRequest = (req: Request): Response => {
19
+ // ...
20
+ };
21
+ ```
22
+
23
+ Arrow functions are fine for:
24
+ - Callbacks: `items.map((item) => item.id)`
25
+ - Simple helpers: `const clean = (s: string) => s.trim()`
26
+
27
+ ## If Statements
28
+
29
+ Always use braces:
30
+ ```typescript
31
+ // Good
32
+ if (condition) {
33
+ return value;
34
+ }
35
+
36
+ // Bad
37
+ if (condition) return value;
38
+ ```
39
+
40
+ ## Naming
41
+
42
+ - Variables/functions: `camelCase`
43
+ - Types/interfaces/classes: `PascalCase`
44
+ - Constants: `SCREAMING_SNAKE_CASE`
45
+
46
+ ## Type Validation with Zod
47
+
48
+ Use Zod for runtime validation of external data (API inputs, env vars, config):
49
+
50
+ ```typescript
51
+ import { z } from "zod";
52
+
53
+ // Define schema
54
+ const UserSchema = z.object({
55
+ name: z.string().min(1),
56
+ email: z.string().email(),
57
+ age: z.number().int().positive().optional(),
58
+ });
59
+
60
+ // Infer TypeScript type from schema
61
+ type User = z.infer<typeof UserSchema>;
62
+
63
+ // Validate
64
+ const result = UserSchema.safeParse(input);
65
+ if (!result.success) {
66
+ return res.status(400).json({ error: result.error.flatten() });
67
+ }
68
+ const user = result.data; // Fully typed
69
+ ```
70
+
71
+ ### When to Use Zod
72
+
73
+ - API request bodies
74
+ - Query parameters
75
+ - Environment variables
76
+ - External API responses
77
+ - Configuration files
78
+
79
+ ### When NOT to Use Zod
80
+
81
+ - Internal function calls (trust TypeScript)
82
+ - Database query results (trust your ORM)
83
+
84
+ ## Error Handling
85
+
86
+ ```typescript
87
+ try {
88
+ const result = await apiCall();
89
+ return res.json(result);
90
+ } catch (error) {
91
+ console.error("[module] Error:", error);
92
+ return res.status(500).json({ error: "Internal server error" });
93
+ }
94
+ ```
95
+
96
+ ## Project Structure
97
+
98
+ ```
99
+ src/
100
+ ├── index.ts # Entry point: server, middleware, routes
101
+ ├── config.ts # Environment variables, validation schemas
102
+ ├── utils.ts # Helpers, error mapping
103
+ ├── types.ts # Shared types
104
+ └── [feature].ts # Feature modules
105
+ ```
106
+
107
+ ## Linting
108
+
109
+ Run before committing:
110
+ ```bash
111
+ npm run check # biome check --write src/
112
+ ```