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.
- package/README.md +207 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +45 -0
- package/dist/commands/check.d.ts +8 -0
- package/dist/commands/check.js +96 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.js +1201 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.js +126 -0
- package/dist/types.d.ts +246 -0
- package/dist/types.js +4 -0
- package/dist/utils.d.ts +53 -0
- package/dist/utils.js +142 -0
- package/package.json +65 -0
- package/templates/LINTING_SETUP.md +205 -0
- package/templates/README_TEMPLATE.md +68 -0
- package/templates/STYLE_GUIDE.md +241 -0
- package/templates/assets/favicon.png +0 -0
- package/templates/assets/favicon.svg +17 -0
- package/templates/biome.json +43 -0
- package/templates/cursor/rules/drizzle.mdc +165 -0
- package/templates/cursor/rules/fastify.mdc +132 -0
- package/templates/cursor/rules/general.mdc +112 -0
- package/templates/cursor/rules/nextjs.mdc +89 -0
- package/templates/cursor/rules/python.mdc +89 -0
- package/templates/cursor/rules/react.mdc +200 -0
- package/templates/cursor/rules/sqlalchemy.mdc +205 -0
- package/templates/cursor/rules/tailwind.mdc +139 -0
- package/templates/cursor/rules/typescript.mdc +112 -0
- package/templates/cursor/rules/vite.mdc +169 -0
- package/templates/cursor/rules/workflows.mdc +349 -0
- package/templates/docker-compose.example.yml +55 -0
- package/templates/drizzle/db-index.ts +15 -0
- package/templates/drizzle/drizzle.config.ts +10 -0
- package/templates/drizzle/schema.ts +12 -0
- package/templates/env.example +15 -0
- package/templates/fastapi/app/__init__.py +1 -0
- package/templates/fastapi/app/config.py +12 -0
- package/templates/fastapi/app/database.py +16 -0
- package/templates/fastapi/app/models.py +13 -0
- package/templates/fastapi/main.py +22 -0
- package/templates/fastify/index.ts +40 -0
- package/templates/github/CODEOWNERS +10 -0
- package/templates/github/ISSUE_TEMPLATE/bug_report.md +39 -0
- package/templates/github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/templates/github/PULL_REQUEST_TEMPLATE.md +25 -0
- package/templates/gitignore/node.gitignore +41 -0
- package/templates/gitignore/python.gitignore +49 -0
- package/templates/multi-api/README.md +60 -0
- package/templates/multi-api/gitignore +28 -0
- package/templates/multi-api/node-api/drizzle.config.ts +10 -0
- package/templates/multi-api/node-api/package-simple.json +13 -0
- package/templates/multi-api/node-api/package.json +16 -0
- package/templates/multi-api/node-api/src/db/index.ts +13 -0
- package/templates/multi-api/node-api/src/db/schema.ts +9 -0
- package/templates/multi-api/node-api/src/index-simple.ts +36 -0
- package/templates/multi-api/node-api/src/index.ts +50 -0
- package/templates/multi-api/node-api/tsconfig.json +20 -0
- package/templates/multi-api/python-api/app/__init__.py +1 -0
- package/templates/multi-api/python-api/app/config.py +12 -0
- package/templates/multi-api/python-api/app/database.py +16 -0
- package/templates/multi-api/python-api/app/models.py +13 -0
- package/templates/multi-api/python-api/main-simple.py +25 -0
- package/templates/multi-api/python-api/main.py +44 -0
- package/templates/multi-api/python-api/requirements-simple.txt +3 -0
- package/templates/multi-api/python-api/requirements.txt +8 -0
- package/templates/next/globals.css +126 -0
- package/templates/next/layout.tsx +34 -0
- package/templates/next/next.config.static.ts +10 -0
- package/templates/next/page-fullstack.tsx +120 -0
- package/templates/next/page.tsx +72 -0
- package/templates/presets.json +581 -0
- package/templates/ruff.toml +30 -0
- package/templates/tsconfig.base.json +17 -0
- package/templates/vite/index.css +127 -0
- package/templates/vite/vite.config.ts +7 -0
- package/templates/worker/py/cron.py +53 -0
- package/templates/worker/py/worker.py +95 -0
- package/templates/worker/py/workflow.py +73 -0
- package/templates/worker/ts/cron.ts +49 -0
- package/templates/worker/ts/worker.ts +84 -0
- package/templates/worker/ts/workflow.ts +67 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Drizzle ORM conventions for PostgreSQL
|
|
3
|
+
globs: ["**/db/**/*.ts", "**/schema/**/*.ts", "**/drizzle/**/*.ts"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Drizzle ORM Conventions
|
|
7
|
+
|
|
8
|
+
## Project Structure
|
|
9
|
+
|
|
10
|
+
### Small projects: Single schema file
|
|
11
|
+
```
|
|
12
|
+
src/
|
|
13
|
+
├── db/
|
|
14
|
+
│ ├── index.ts # Database connection
|
|
15
|
+
│ ├── schema.ts # All tables
|
|
16
|
+
│ └── migrations/ # Generated migrations
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Larger projects: Split by domain
|
|
20
|
+
```
|
|
21
|
+
src/
|
|
22
|
+
├── db/
|
|
23
|
+
│ ├── index.ts
|
|
24
|
+
│ ├── schema/
|
|
25
|
+
│ │ ├── index.ts # Re-exports all schemas
|
|
26
|
+
│ │ ├── users.ts
|
|
27
|
+
│ │ ├── posts.ts
|
|
28
|
+
│ │ └── comments.ts
|
|
29
|
+
│ └── migrations/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Database Connection
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// db/index.ts
|
|
36
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
37
|
+
import { Pool } from "pg";
|
|
38
|
+
import * as schema from "./schema";
|
|
39
|
+
|
|
40
|
+
const pool = new Pool({
|
|
41
|
+
connectionString: process.env.DATABASE_URL,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const db = drizzle(pool, { schema });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Schema Definition
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// db/schema/users.ts
|
|
51
|
+
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
52
|
+
|
|
53
|
+
export const users = pgTable("users", {
|
|
54
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
55
|
+
email: text("email").notNull().unique(),
|
|
56
|
+
name: text("name").notNull(),
|
|
57
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
58
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Type inference
|
|
62
|
+
export type User = typeof users.$inferSelect;
|
|
63
|
+
export type NewUser = typeof users.$inferInsert;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Queries
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { db } from "./db";
|
|
70
|
+
import { users } from "./db/schema";
|
|
71
|
+
import { eq } from "drizzle-orm";
|
|
72
|
+
|
|
73
|
+
// Select all
|
|
74
|
+
const allUsers = await db.select().from(users);
|
|
75
|
+
|
|
76
|
+
// Select with filter
|
|
77
|
+
const user = await db
|
|
78
|
+
.select()
|
|
79
|
+
.from(users)
|
|
80
|
+
.where(eq(users.email, "test@example.com"));
|
|
81
|
+
|
|
82
|
+
// Insert
|
|
83
|
+
const newUser = await db
|
|
84
|
+
.insert(users)
|
|
85
|
+
.values({ email: "new@example.com", name: "New User" })
|
|
86
|
+
.returning();
|
|
87
|
+
|
|
88
|
+
// Update
|
|
89
|
+
await db
|
|
90
|
+
.update(users)
|
|
91
|
+
.set({ name: "Updated Name" })
|
|
92
|
+
.where(eq(users.id, userId));
|
|
93
|
+
|
|
94
|
+
// Delete
|
|
95
|
+
await db.delete(users).where(eq(users.id, userId));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Relations
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { relations } from "drizzle-orm";
|
|
102
|
+
|
|
103
|
+
export const usersRelations = relations(users, ({ many }) => ({
|
|
104
|
+
posts: many(posts),
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
export const postsRelations = relations(posts, ({ one }) => ({
|
|
108
|
+
author: one(users, {
|
|
109
|
+
fields: [posts.authorId],
|
|
110
|
+
references: [users.id],
|
|
111
|
+
}),
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
// Query with relations
|
|
115
|
+
const usersWithPosts = await db.query.users.findMany({
|
|
116
|
+
with: {
|
|
117
|
+
posts: true,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Zod Integration with drizzle-zod
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
|
126
|
+
import { users } from "./schema";
|
|
127
|
+
|
|
128
|
+
// Auto-generate Zod schemas from Drizzle tables
|
|
129
|
+
export const insertUserSchema = createInsertSchema(users, {
|
|
130
|
+
email: (schema) => schema.email.email(),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export const selectUserSchema = createSelectSchema(users);
|
|
134
|
+
|
|
135
|
+
// Use in API validation
|
|
136
|
+
const validated = insertUserSchema.parse(requestBody);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Migrations with drizzle-kit
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Generate migration
|
|
143
|
+
npx drizzle-kit generate
|
|
144
|
+
|
|
145
|
+
# Push directly to database (dev only)
|
|
146
|
+
npx drizzle-kit push
|
|
147
|
+
|
|
148
|
+
# Apply migrations
|
|
149
|
+
npx drizzle-kit migrate
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### drizzle.config.ts
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { defineConfig } from "drizzle-kit";
|
|
156
|
+
|
|
157
|
+
export default defineConfig({
|
|
158
|
+
schema: "./src/db/schema/index.ts",
|
|
159
|
+
out: "./src/db/migrations",
|
|
160
|
+
dialect: "postgresql",
|
|
161
|
+
dbCredentials: {
|
|
162
|
+
url: process.env.DATABASE_URL!,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Fastify conventions for TypeScript APIs
|
|
3
|
+
globs: ["**/fastify/**/*.ts", "**/server/**/*.ts", "**/api/**/*.ts"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fastify Conventions
|
|
7
|
+
|
|
8
|
+
## Project Structure
|
|
9
|
+
|
|
10
|
+
For simple APIs, use single-file structure:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
src/
|
|
14
|
+
├── index.ts # Server setup + all routes
|
|
15
|
+
├── config.ts # Environment variables
|
|
16
|
+
├── utils.ts # Helpers, error handling
|
|
17
|
+
└── types.ts # Zod schemas + types
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For larger APIs, organize by feature:
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
├── index.ts # Server setup
|
|
24
|
+
├── routes/
|
|
25
|
+
│ ├── users.ts
|
|
26
|
+
│ └── posts.ts
|
|
27
|
+
├── config.ts
|
|
28
|
+
└── utils.ts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Server Setup
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import Fastify from "fastify";
|
|
35
|
+
import cors from "@fastify/cors";
|
|
36
|
+
import {
|
|
37
|
+
serializerCompiler,
|
|
38
|
+
validatorCompiler,
|
|
39
|
+
ZodTypeProvider,
|
|
40
|
+
} from "fastify-type-provider-zod";
|
|
41
|
+
|
|
42
|
+
const app = Fastify({ logger: true }).withTypeProvider<ZodTypeProvider>();
|
|
43
|
+
|
|
44
|
+
// Zod validation
|
|
45
|
+
app.setValidatorCompiler(validatorCompiler);
|
|
46
|
+
app.setSerializerCompiler(serializerCompiler);
|
|
47
|
+
|
|
48
|
+
// Middleware
|
|
49
|
+
await app.register(cors, { origin: true });
|
|
50
|
+
|
|
51
|
+
// Routes go here...
|
|
52
|
+
|
|
53
|
+
// Start server
|
|
54
|
+
const port = Number(process.env.PORT) || 3000;
|
|
55
|
+
app.listen({ port, host: "0.0.0.0" }, (err) => {
|
|
56
|
+
if (err) {
|
|
57
|
+
app.log.error(err);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Route Definitions with Zod
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { z } from "zod";
|
|
67
|
+
|
|
68
|
+
// Define schemas
|
|
69
|
+
const CreateUserSchema = z.object({
|
|
70
|
+
name: z.string().min(1),
|
|
71
|
+
email: z.string().email(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const UserResponseSchema = z.object({
|
|
75
|
+
id: z.string(),
|
|
76
|
+
name: z.string(),
|
|
77
|
+
email: z.string(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Route with validation
|
|
81
|
+
app.post(
|
|
82
|
+
"/users",
|
|
83
|
+
{
|
|
84
|
+
schema: {
|
|
85
|
+
body: CreateUserSchema,
|
|
86
|
+
response: { 201: UserResponseSchema },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
async (request, reply) => {
|
|
90
|
+
const { name, email } = request.body; // Fully typed!
|
|
91
|
+
const user = await createUser({ name, email });
|
|
92
|
+
return reply.status(201).send(user);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Error Handling
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Custom error handler
|
|
101
|
+
app.setErrorHandler((error, request, reply) => {
|
|
102
|
+
app.log.error(error);
|
|
103
|
+
|
|
104
|
+
if (error.validation) {
|
|
105
|
+
return reply.status(400).send({
|
|
106
|
+
error: "Validation Error",
|
|
107
|
+
details: error.validation,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return reply.status(500).send({
|
|
112
|
+
error: "Internal Server Error",
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Logging
|
|
118
|
+
|
|
119
|
+
Use Fastify's built-in logger:
|
|
120
|
+
```typescript
|
|
121
|
+
app.log.info("[users] Creating user");
|
|
122
|
+
app.log.error("[users] Failed to create user:", error);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Health Check
|
|
126
|
+
|
|
127
|
+
Always include a health endpoint:
|
|
128
|
+
```typescript
|
|
129
|
+
app.get("/health", async () => {
|
|
130
|
+
return { status: "ok" };
|
|
131
|
+
});
|
|
132
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: General coding conventions
|
|
3
|
+
globs: ["**/*"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# General Conventions
|
|
7
|
+
|
|
8
|
+
## Dependencies & Libraries
|
|
9
|
+
|
|
10
|
+
ALWAYS use the latest stable versions of libraries. Never suggest outdated packages.
|
|
11
|
+
|
|
12
|
+
### When adding dependencies:
|
|
13
|
+
|
|
14
|
+
- Use `npm install <package>` or `pip install <package>` without pinning old versions
|
|
15
|
+
- If a package.json or requirements.txt exists, check current versions before suggesting additions
|
|
16
|
+
- Prefer actively maintained packages over abandoned ones
|
|
17
|
+
|
|
18
|
+
### Outdated patterns to AVOID:
|
|
19
|
+
|
|
20
|
+
- `create-react-app` → Use Next.js or Vite instead
|
|
21
|
+
- `moment.js` → Use `date-fns` or native `Intl`
|
|
22
|
+
- `request` → Use native `fetch` or `axios`
|
|
23
|
+
- Class components in React → Use functional components with hooks
|
|
24
|
+
- `var` in JavaScript → Use `const`/`let`
|
|
25
|
+
- Callback patterns → Use async/await
|
|
26
|
+
- Express.js for new projects → Use Fastify (faster, better TS support)
|
|
27
|
+
|
|
28
|
+
### When unsure about current best practices:
|
|
29
|
+
|
|
30
|
+
- Check the official documentation
|
|
31
|
+
- Verify the package's last publish date on npm/PyPI
|
|
32
|
+
- Prefer packages with recent activity (< 6 months since last update)
|
|
33
|
+
|
|
34
|
+
## Environment Variables
|
|
35
|
+
|
|
36
|
+
ALWAYS use dotenv for environment variable management:
|
|
37
|
+
|
|
38
|
+
### TypeScript/Node.js:
|
|
39
|
+
|
|
40
|
+
- Use `dotenv` package or framework built-in (Next.js, Vite have it)
|
|
41
|
+
- Create `.env.example` with all required vars (no secrets)
|
|
42
|
+
- Never commit `.env` files (ensure in .gitignore)
|
|
43
|
+
|
|
44
|
+
### Python:
|
|
45
|
+
|
|
46
|
+
- Use `python-dotenv` package
|
|
47
|
+
- Load with `load_dotenv()` at app entry point
|
|
48
|
+
- Create `.env.example` template
|
|
49
|
+
|
|
50
|
+
### Naming convention:
|
|
51
|
+
|
|
52
|
+
- UPPERCASE_WITH_UNDERSCORES
|
|
53
|
+
- Prefix with service name for clarity: `DATABASE_URL`, `REDIS_URL`, `API_SECRET_KEY`
|
|
54
|
+
|
|
55
|
+
## Multi-Service Applications
|
|
56
|
+
|
|
57
|
+
Use Docker Compose for complex apps with multiple services:
|
|
58
|
+
|
|
59
|
+
### When to use Docker Compose:
|
|
60
|
+
|
|
61
|
+
- 2+ services that communicate (API + DB, frontend + backend + cache)
|
|
62
|
+
- Local development environment setup
|
|
63
|
+
- Integration testing
|
|
64
|
+
|
|
65
|
+
### Best practices:
|
|
66
|
+
|
|
67
|
+
- Use `env_file` to load .env
|
|
68
|
+
- Define `depends_on` for service ordering
|
|
69
|
+
- Use named volumes for persistence
|
|
70
|
+
- Include health checks for production-like setups
|
|
71
|
+
|
|
72
|
+
## Tooling
|
|
73
|
+
|
|
74
|
+
Run linters before committing:
|
|
75
|
+
- TypeScript: `npm run check` (Biome)
|
|
76
|
+
- Python: `ruff check --fix . && ruff format .`
|
|
77
|
+
|
|
78
|
+
## Comments
|
|
79
|
+
|
|
80
|
+
- Add comments **liberally** to help future readers
|
|
81
|
+
- Explain WHY and provide context, not WHAT
|
|
82
|
+
- JSDoc/docstrings only when behavior is non-obvious
|
|
83
|
+
- File headers only on entry files (index.ts, app.py, main.py)
|
|
84
|
+
|
|
85
|
+
## Logging
|
|
86
|
+
|
|
87
|
+
Always prefix logs with module name in brackets:
|
|
88
|
+
```
|
|
89
|
+
console.log("[storage] Creating bucket");
|
|
90
|
+
print("[handler] Processing request")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Error Handling
|
|
94
|
+
|
|
95
|
+
- **Early return** for validation errors
|
|
96
|
+
- **Try-catch** for external operations (API calls, file I/O)
|
|
97
|
+
- Use SDK error mapping utilities when available
|
|
98
|
+
|
|
99
|
+
## Async
|
|
100
|
+
|
|
101
|
+
- Always use async/await
|
|
102
|
+
- Never use .then() chains
|
|
103
|
+
|
|
104
|
+
## Naming
|
|
105
|
+
|
|
106
|
+
- Use balanced verbosity: `accountStatus` not `s` or `userAccountStatusFromDatabase`
|
|
107
|
+
- Constants: `SCREAMING_SNAKE_CASE`
|
|
108
|
+
|
|
109
|
+
## References
|
|
110
|
+
|
|
111
|
+
- [STYLE_GUIDE.md](../../STYLE_GUIDE.md) - Full conventions
|
|
112
|
+
- [LINTING_SETUP.md](../../LINTING_SETUP.md) - Biome/Ruff setup
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Next.js conventions for App Router projects
|
|
3
|
+
globs: ["**/app/**/*.tsx", "**/app/**/*.ts", "**/components/**/*.tsx"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Next.js Conventions
|
|
7
|
+
|
|
8
|
+
## Project Structure
|
|
9
|
+
|
|
10
|
+
Use App Router (`app/` directory):
|
|
11
|
+
```
|
|
12
|
+
app/
|
|
13
|
+
├── layout.tsx # Root layout
|
|
14
|
+
├── page.tsx # Home page
|
|
15
|
+
├── globals.css # Global styles
|
|
16
|
+
├── api/ # Route handlers
|
|
17
|
+
│ └── [route]/route.ts
|
|
18
|
+
├── [feature]/ # Feature routes
|
|
19
|
+
│ ├── page.tsx
|
|
20
|
+
│ └── components/ # Route-specific components
|
|
21
|
+
└── components/ # Shared components (or use /components at root)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Server vs Client Components
|
|
25
|
+
|
|
26
|
+
Always be **explicit** about component type:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// Server Component (default) - add comment for clarity
|
|
30
|
+
// server component
|
|
31
|
+
export default async function UserList() {
|
|
32
|
+
const users = await fetchUsers(); // Can use async/await directly
|
|
33
|
+
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
// Client Component - must have 'use client' directive
|
|
39
|
+
"use client";
|
|
40
|
+
|
|
41
|
+
import { useState } from "react";
|
|
42
|
+
|
|
43
|
+
export function Counter() {
|
|
44
|
+
const [count, setCount] = useState(0);
|
|
45
|
+
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## When to Use Client Components
|
|
50
|
+
|
|
51
|
+
Add `"use client"` only when you need:
|
|
52
|
+
- React hooks (`useState`, `useEffect`, etc.)
|
|
53
|
+
- Browser APIs (`window`, `document`, etc.)
|
|
54
|
+
- Event handlers (`onClick`, `onChange`, etc.)
|
|
55
|
+
- Third-party client libraries
|
|
56
|
+
|
|
57
|
+
## Route Handlers (API Routes)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// app/api/users/route.ts
|
|
61
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
62
|
+
|
|
63
|
+
export async function GET(request: NextRequest) {
|
|
64
|
+
const users = await fetchUsers();
|
|
65
|
+
return NextResponse.json(users);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function POST(request: NextRequest) {
|
|
69
|
+
const body = await request.json();
|
|
70
|
+
// Validate with zod
|
|
71
|
+
const result = await createUser(body);
|
|
72
|
+
return NextResponse.json(result, { status: 201 });
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Data Fetching
|
|
77
|
+
|
|
78
|
+
- Use `fetch` in Server Components (auto-cached)
|
|
79
|
+
- Use Server Actions for mutations
|
|
80
|
+
- Use TanStack Query or SWR for client-side fetching only when necessary
|
|
81
|
+
|
|
82
|
+
## File Naming
|
|
83
|
+
|
|
84
|
+
- `page.tsx` - Route page
|
|
85
|
+
- `layout.tsx` - Shared layout
|
|
86
|
+
- `loading.tsx` - Loading UI
|
|
87
|
+
- `error.tsx` - Error boundary
|
|
88
|
+
- `not-found.tsx` - 404 page
|
|
89
|
+
- Components: `PascalCase.tsx` (e.g., `UserCard.tsx`)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Python-specific conventions
|
|
3
|
+
globs: ["**/*.py"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Conventions
|
|
7
|
+
|
|
8
|
+
## Functions
|
|
9
|
+
|
|
10
|
+
Use regular function definitions:
|
|
11
|
+
```python
|
|
12
|
+
# Good
|
|
13
|
+
def handle_request(request: Request) -> Response:
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
async def fetch_user_data(user_id: str) -> User:
|
|
17
|
+
...
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Docstrings only when behavior is non-obvious:
|
|
21
|
+
```python
|
|
22
|
+
def fetch_blog_title(url: str) -> str:
|
|
23
|
+
"""Fetch title, trying og:title, twitter:title, then <title> tag."""
|
|
24
|
+
...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Naming
|
|
28
|
+
|
|
29
|
+
- Variables/functions: `snake_case`
|
|
30
|
+
- Classes: `PascalCase`
|
|
31
|
+
- Constants: `SCREAMING_SNAKE_CASE`
|
|
32
|
+
|
|
33
|
+
## Type Validation with Pydantic
|
|
34
|
+
|
|
35
|
+
Use Pydantic for runtime validation of external data:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from pydantic import BaseModel, EmailStr
|
|
39
|
+
|
|
40
|
+
class CreateUserRequest(BaseModel):
|
|
41
|
+
name: str
|
|
42
|
+
email: EmailStr
|
|
43
|
+
age: int | None = None
|
|
44
|
+
|
|
45
|
+
# In FastAPI route
|
|
46
|
+
@app.post("/users")
|
|
47
|
+
async def create_user(request: CreateUserRequest):
|
|
48
|
+
# request is already validated and typed
|
|
49
|
+
return {"name": request.name, "email": request.email}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### When to Use Pydantic
|
|
53
|
+
|
|
54
|
+
- API request/response bodies
|
|
55
|
+
- Environment variables (pydantic-settings)
|
|
56
|
+
- Configuration files
|
|
57
|
+
- External API responses
|
|
58
|
+
|
|
59
|
+
## Error Handling
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from fastapi import HTTPException
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
result = await api_call()
|
|
66
|
+
return result
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"[module] Error: {e}")
|
|
69
|
+
raise HTTPException(status_code=500, detail="Internal server error")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Project Structure
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
project/
|
|
76
|
+
├── app.py # Entry point: FastAPI app, middleware, routes
|
|
77
|
+
├── config.py # Environment variables, Pydantic models
|
|
78
|
+
├── handlers.py # Route handlers (business logic)
|
|
79
|
+
├── utils.py # Helpers, error mapping
|
|
80
|
+
└── [feature].py # Feature modules
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Linting
|
|
84
|
+
|
|
85
|
+
Run before committing:
|
|
86
|
+
```bash
|
|
87
|
+
ruff check --fix .
|
|
88
|
+
ruff format .
|
|
89
|
+
```
|