vibes-plug 1.0.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/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/BLUEPRINT.md +125 -0
- package/CHANGELOG.md +195 -0
- package/CONTRIBUTING.md +199 -0
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/SECURITY.md +21 -0
- package/banner.png +0 -0
- package/package.json +25 -0
- package/plugin.json +8 -0
- package/scripts/update_skills.js +75 -0
- package/skills/ai-llm-integration-expert/SKILL.md +162 -0
- package/skills/api-design-expert/SKILL.md +310 -0
- package/skills/app-analyzer-optimizer/SKILL.md +189 -0
- package/skills/asisten_ramah/SKILL.md +41 -0
- package/skills/authentication-identity-expert/SKILL.md +45 -0
- package/skills/auto-doc-updater/SKILL.md +204 -0
- package/skills/bootstrap-to-modern/SKILL.md +87 -0
- package/skills/brainstorming/SKILL.md +353 -0
- package/skills/bun-runtime-expert/SKILL.md +211 -0
- package/skills/ci-cd-devops-architect/SKILL.md +45 -0
- package/skills/cloud-hosting-expert/SKILL.md +244 -0
- package/skills/coderabbit/SKILL.md +192 -0
- package/skills/data-telemetry-expert/SKILL.md +213 -0
- package/skills/database-orm-expert/SKILL.md +294 -0
- package/skills/design-system-architect/SKILL.md +243 -0
- package/skills/e2e-testing-expert/SKILL.md +315 -0
- package/skills/event-driven-architect/SKILL.md +81 -0
- package/skills/firebase-security-expert/SKILL.md +195 -0
- package/skills/fullstack-expert/SKILL.md +202 -0
- package/skills/fullstack-expert/references/api_design_guide.md +466 -0
- package/skills/fullstack-expert/references/devops_infrastructure.md +477 -0
- package/skills/fullstack-expert/references/multi_language_backend.md +528 -0
- package/skills/fullstack-expert/references/system_design_patterns.md +358 -0
- package/skills/fullstack-expert/scripts/api_contract_validator.py +253 -0
- package/skills/fullstack-expert/scripts/architecture_analyzer.py +326 -0
- package/skills/gemini-agent-booster/SKILL.md +135 -0
- package/skills/global-a11y-i18n-expert/SKILL.md +81 -0
- package/skills/go-programming-expert/SKILL.md +295 -0
- package/skills/hig/SKILL.md +188 -0
- package/skills/js-backend-expert/SKILL.md +192 -0
- package/skills/mcp-server-architect/SKILL.md +194 -0
- package/skills/mobile-expo-expert/SKILL.md +186 -0
- package/skills/monday-design-aesthetic/SKILL.md +67 -0
- package/skills/monorepo-architect/SKILL.md +227 -0
- package/skills/mpa-orchestrator/SKILL.md +101 -0
- package/skills/multi-agent-orchestration/SKILL.md +234 -0
- package/skills/multiple-entry-points/SKILL.md +55 -0
- package/skills/mvc-expert/SKILL.md +231 -0
- package/skills/payment-gateway-expert/SKILL.md +45 -0
- package/skills/performance-web-vitals/SKILL.md +332 -0
- package/skills/prd-architect/SKILL.md +191 -0
- package/skills/production-ready-hardener/SKILL.md +469 -0
- package/skills/production-ready-hardener/references/performance_optimization.md +441 -0
- package/skills/production-ready-hardener/references/production_checklist.md +161 -0
- package/skills/production-ready-hardener/references/security_hardening_guide.md +379 -0
- package/skills/production-ready-hardener/scripts/production_readiness_scanner.py +875 -0
- package/skills/python-programming-expert/SKILL.md +271 -0
- package/skills/realtime-collaboration-expert/SKILL.md +45 -0
- package/skills/rust-programming-expert/SKILL.md +235 -0
- package/skills/saas-billing/SKILL.md +377 -0
- package/skills/saas-multi-tenant/SKILL.md +237 -0
- package/skills/saas-mvp-launcher/SKILL.md +231 -0
- package/skills/saas-transformer/SKILL.md +446 -0
- package/skills/saas-transformer/references/billing_integration_guide.md +401 -0
- package/skills/saas-transformer/references/feature_gating_patterns.md +137 -0
- package/skills/saas-transformer/references/saas_transformation_checklist.md +121 -0
- package/skills/saas-transformer/scripts/saas_transformation_scanner.py +254 -0
- package/skills/scalability-clean-code/SKILL.md +229 -0
- package/skills/secure-fuzz-testing/SKILL.md +201 -0
- package/skills/senior-frontend/SKILL.md +161 -0
- package/skills/senior-frontend/references/frontend_best_practices.md +806 -0
- package/skills/senior-frontend/references/nextjs_optimization_guide.md +724 -0
- package/skills/senior-frontend/references/react_patterns.md +746 -0
- package/skills/senior-frontend/scripts/bundle_analyzer.py +407 -0
- package/skills/senior-frontend/scripts/component_generator.py +329 -0
- package/skills/senior-frontend/scripts/frontend_scaffolder.py +1005 -0
- package/skills/senior-fullstack/SKILL.md +167 -0
- package/skills/senior-fullstack/references/architecture_patterns.md +160 -0
- package/skills/senior-fullstack/references/development_workflows.md +222 -0
- package/skills/senior-fullstack/references/tech_stack_guide.md +190 -0
- package/skills/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/skills/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/skills/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/skills/seo/SKILL.md +225 -0
- package/skills/seo/references/cwv-thresholds.md +108 -0
- package/skills/seo/references/eeat-framework.md +214 -0
- package/skills/seo/references/quality-gates.md +155 -0
- package/skills/seo/references/schema-types.md +118 -0
- package/skills/seo-aeo-landing-page-writer/SKILL.md +97 -0
- package/skills/seo-geo/SKILL.md +188 -0
- package/skills/session-handoff-resume/SKILL.md +158 -0
- package/skills/skill_baru/SKILL.md +147 -0
- package/skills/spa-orchestrator/SKILL.md +288 -0
- package/skills/state-management-expert/SKILL.md +272 -0
- package/skills/supabase-migration/SKILL.md +45 -0
- package/skills/supabase-security-expert/SKILL.md +243 -0
- package/skills/tailwind-expert/SKILL.md +188 -0
- package/skills/tanstack-query-expert/SKILL.md +199 -0
- package/skills/tauri-expert/SKILL.md +97 -0
- package/skills/token-saver/SKILL.md +111 -0
- package/skills/typescript-expert/SKILL.md +279 -0
- package/skills/ui-components-expert/SKILL.md +63 -0
- package/skills/ui-ux-pro-max/SKILL.md +201 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +493 -0
- package/skills/ui-ux-pro-max/scripts/search.py +81 -0
- package/skills/ui_ux_expert/SKILL.md +114 -0
- package/skills/vibe-code-gardener/SKILL.md +173 -0
- package/skills/web-scraper/SKILL.md +205 -0
- package/skills/web-scraper/references/data-transforms.md +397 -0
- package/skills/web-scraper/references/extraction-patterns.md +475 -0
- package/skills/web-scraper/references/output-templates.md +481 -0
- package/skills/zero-to-prod-orchestrator/SKILL.md +180 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
# Multi-Language Backend Patterns
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This reference documents idiomatic backend patterns across TypeScript, Python, Go, and Rust. Each section shows the same core concepts (HTTP handlers, database access, error handling, middleware) implemented in each language's preferred framework.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. TypeScript (Hono + Drizzle ORM)
|
|
9
|
+
|
|
10
|
+
### Project Structure
|
|
11
|
+
```
|
|
12
|
+
src/
|
|
13
|
+
├── index.ts # Entry point
|
|
14
|
+
├── routes/
|
|
15
|
+
│ ├── products.ts # Product routes
|
|
16
|
+
│ └── users.ts # User routes
|
|
17
|
+
├── db/
|
|
18
|
+
│ ├── index.ts # Database client
|
|
19
|
+
│ └── schema.ts # Drizzle schema
|
|
20
|
+
├── middleware/
|
|
21
|
+
│ ├── auth.ts # JWT auth middleware
|
|
22
|
+
│ └── logger.ts # Request logger
|
|
23
|
+
├── services/
|
|
24
|
+
│ └── product.ts # Business logic
|
|
25
|
+
└── lib/
|
|
26
|
+
├── errors.ts # Custom error classes
|
|
27
|
+
└── validators.ts # Zod schemas
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### HTTP Handler + Validation
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// routes/products.ts
|
|
34
|
+
import { Hono } from 'hono';
|
|
35
|
+
import { zValidator } from '@hono/zod-validator';
|
|
36
|
+
import { z } from 'zod';
|
|
37
|
+
import { db } from '../db';
|
|
38
|
+
import { products } from '../db/schema';
|
|
39
|
+
import { eq } from 'drizzle-orm';
|
|
40
|
+
|
|
41
|
+
const CreateProductSchema = z.object({
|
|
42
|
+
name: z.string().min(1).max(255),
|
|
43
|
+
description: z.string().optional(),
|
|
44
|
+
price: z.number().positive(),
|
|
45
|
+
categoryId: z.string().uuid(),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const app = new Hono()
|
|
49
|
+
.get('/', async (c) => {
|
|
50
|
+
const limit = Number(c.req.query('limit') ?? '20');
|
|
51
|
+
const cursor = c.req.query('cursor');
|
|
52
|
+
|
|
53
|
+
const items = await db
|
|
54
|
+
.select()
|
|
55
|
+
.from(products)
|
|
56
|
+
.orderBy(products.createdAt)
|
|
57
|
+
.limit(limit + 1);
|
|
58
|
+
|
|
59
|
+
const hasMore = items.length > limit;
|
|
60
|
+
const data = hasMore ? items.slice(0, limit) : items;
|
|
61
|
+
|
|
62
|
+
return c.json({ data, hasMore });
|
|
63
|
+
})
|
|
64
|
+
.post('/', zValidator('json', CreateProductSchema), async (c) => {
|
|
65
|
+
const input = c.req.valid('json');
|
|
66
|
+
|
|
67
|
+
const [product] = await db
|
|
68
|
+
.insert(products)
|
|
69
|
+
.values(input)
|
|
70
|
+
.returning();
|
|
71
|
+
|
|
72
|
+
return c.json(product, 201);
|
|
73
|
+
})
|
|
74
|
+
.get('/:id', async (c) => {
|
|
75
|
+
const id = c.req.param('id');
|
|
76
|
+
const product = await db.query.products.findFirst({
|
|
77
|
+
where: eq(products.id, id),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (!product) {
|
|
81
|
+
return c.json({ error: 'Product not found' }, 404);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return c.json(product);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export default app;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 2. Python (FastAPI + SQLAlchemy 2.x)
|
|
93
|
+
|
|
94
|
+
### Project Structure
|
|
95
|
+
```
|
|
96
|
+
app/
|
|
97
|
+
├── main.py # Entry point
|
|
98
|
+
├── routes/
|
|
99
|
+
│ ├── products.py # Product routes
|
|
100
|
+
│ └── users.py # User routes
|
|
101
|
+
├── db/
|
|
102
|
+
│ ├── session.py # Database session
|
|
103
|
+
│ └── models.py # SQLAlchemy models
|
|
104
|
+
├── schemas/
|
|
105
|
+
│ └── product.py # Pydantic schemas
|
|
106
|
+
├── services/
|
|
107
|
+
│ └── product.py # Business logic
|
|
108
|
+
└── middleware/
|
|
109
|
+
├── auth.py # JWT auth dependency
|
|
110
|
+
└── logging.py # Request logging
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### HTTP Handler + Validation
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
# routes/products.py
|
|
117
|
+
from uuid import UUID
|
|
118
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
119
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
120
|
+
from sqlalchemy import select
|
|
121
|
+
|
|
122
|
+
from app.db.session import get_db
|
|
123
|
+
from app.db.models import Product
|
|
124
|
+
from app.schemas.product import (
|
|
125
|
+
ProductCreate,
|
|
126
|
+
ProductResponse,
|
|
127
|
+
ProductListResponse,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
router = APIRouter(prefix="/products", tags=["Products"])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@router.get("/", response_model=ProductListResponse)
|
|
134
|
+
async def list_products(
|
|
135
|
+
limit: int = Query(default=20, ge=1, le=100),
|
|
136
|
+
cursor: str | None = None,
|
|
137
|
+
category: str | None = None,
|
|
138
|
+
db: AsyncSession = Depends(get_db),
|
|
139
|
+
):
|
|
140
|
+
query = select(Product).order_by(Product.created_at).limit(limit + 1)
|
|
141
|
+
|
|
142
|
+
if category:
|
|
143
|
+
query = query.where(Product.category_id == category)
|
|
144
|
+
|
|
145
|
+
result = await db.execute(query)
|
|
146
|
+
items = list(result.scalars().all())
|
|
147
|
+
|
|
148
|
+
has_more = len(items) > limit
|
|
149
|
+
data = items[:limit] if has_more else items
|
|
150
|
+
|
|
151
|
+
return ProductListResponse(data=data, has_more=has_more)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@router.post("/", response_model=ProductResponse, status_code=201)
|
|
155
|
+
async def create_product(
|
|
156
|
+
payload: ProductCreate,
|
|
157
|
+
db: AsyncSession = Depends(get_db),
|
|
158
|
+
):
|
|
159
|
+
product = Product(**payload.model_dump())
|
|
160
|
+
db.add(product)
|
|
161
|
+
await db.commit()
|
|
162
|
+
await db.refresh(product)
|
|
163
|
+
return product
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@router.get("/{product_id}", response_model=ProductResponse)
|
|
167
|
+
async def get_product(
|
|
168
|
+
product_id: UUID,
|
|
169
|
+
db: AsyncSession = Depends(get_db),
|
|
170
|
+
):
|
|
171
|
+
result = await db.execute(
|
|
172
|
+
select(Product).where(Product.id == product_id)
|
|
173
|
+
)
|
|
174
|
+
product = result.scalar_one_or_none()
|
|
175
|
+
|
|
176
|
+
if not product:
|
|
177
|
+
raise HTTPException(status_code=404, detail="Product not found")
|
|
178
|
+
|
|
179
|
+
return product
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Pydantic Schema
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
# schemas/product.py
|
|
186
|
+
from datetime import datetime
|
|
187
|
+
from uuid import UUID
|
|
188
|
+
from pydantic import BaseModel, Field
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class ProductCreate(BaseModel):
|
|
192
|
+
name: str = Field(min_length=1, max_length=255)
|
|
193
|
+
description: str | None = None
|
|
194
|
+
price: float = Field(gt=0)
|
|
195
|
+
category_id: UUID
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ProductResponse(BaseModel):
|
|
199
|
+
id: UUID
|
|
200
|
+
name: str
|
|
201
|
+
description: str | None
|
|
202
|
+
price: float
|
|
203
|
+
category_id: UUID
|
|
204
|
+
created_at: datetime
|
|
205
|
+
updated_at: datetime
|
|
206
|
+
|
|
207
|
+
model_config = {"from_attributes": True}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class ProductListResponse(BaseModel):
|
|
211
|
+
data: list[ProductResponse]
|
|
212
|
+
has_more: bool
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 3. Go (Gin + sqlc)
|
|
218
|
+
|
|
219
|
+
### Project Structure
|
|
220
|
+
```
|
|
221
|
+
cmd/
|
|
222
|
+
└── server/
|
|
223
|
+
└── main.go # Entry point
|
|
224
|
+
internal/
|
|
225
|
+
├── handler/
|
|
226
|
+
│ ├── products.go # Product handlers
|
|
227
|
+
│ └── users.go # User handlers
|
|
228
|
+
├── db/
|
|
229
|
+
│ ├── db.go # Database connection
|
|
230
|
+
│ ├── queries.sql # SQL queries (for sqlc)
|
|
231
|
+
│ └── sqlc/ # Generated code
|
|
232
|
+
├── middleware/
|
|
233
|
+
│ ├── auth.go # JWT auth middleware
|
|
234
|
+
│ └── logger.go # Request logging
|
|
235
|
+
├── service/
|
|
236
|
+
│ └── product.go # Business logic
|
|
237
|
+
└── model/
|
|
238
|
+
└── product.go # Domain types
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### HTTP Handler
|
|
242
|
+
|
|
243
|
+
```go
|
|
244
|
+
// internal/handler/products.go
|
|
245
|
+
package handler
|
|
246
|
+
|
|
247
|
+
import (
|
|
248
|
+
"net/http"
|
|
249
|
+
|
|
250
|
+
"github.com/gin-gonic/gin"
|
|
251
|
+
"github.com/google/uuid"
|
|
252
|
+
"github.com/example/api/internal/db/sqlc"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
type ProductHandler struct {
|
|
256
|
+
queries *sqlc.Queries
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
func NewProductHandler(q *sqlc.Queries) *ProductHandler {
|
|
260
|
+
return &ProductHandler{queries: q}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
type CreateProductRequest struct {
|
|
264
|
+
Name string `json:"name" binding:"required,min=1,max=255"`
|
|
265
|
+
Description *string `json:"description"`
|
|
266
|
+
Price float64 `json:"price" binding:"required,gt=0"`
|
|
267
|
+
CategoryID string `json:"category_id" binding:"required,uuid"`
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
func (h *ProductHandler) ListProducts(c *gin.Context) {
|
|
271
|
+
limit := 20 // Default
|
|
272
|
+
products, err := h.queries.ListProducts(c, sqlc.ListProductsParams{
|
|
273
|
+
Limit: int32(limit + 1),
|
|
274
|
+
})
|
|
275
|
+
if err != nil {
|
|
276
|
+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch products"})
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
hasMore := len(products) > limit
|
|
281
|
+
if hasMore {
|
|
282
|
+
products = products[:limit]
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
c.JSON(http.StatusOK, gin.H{
|
|
286
|
+
"data": products,
|
|
287
|
+
"has_more": hasMore,
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
func (h *ProductHandler) CreateProduct(c *gin.Context) {
|
|
292
|
+
var req CreateProductRequest
|
|
293
|
+
if err := c.ShouldBindJSON(&req); err != nil {
|
|
294
|
+
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
categoryID, _ := uuid.Parse(req.CategoryID)
|
|
299
|
+
product, err := h.queries.CreateProduct(c, sqlc.CreateProductParams{
|
|
300
|
+
Name: req.Name,
|
|
301
|
+
Description: req.Description,
|
|
302
|
+
Price: req.Price,
|
|
303
|
+
CategoryID: categoryID,
|
|
304
|
+
})
|
|
305
|
+
if err != nil {
|
|
306
|
+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create product"})
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
c.JSON(http.StatusCreated, product)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
func (h *ProductHandler) GetProduct(c *gin.Context) {
|
|
314
|
+
id, err := uuid.Parse(c.Param("id"))
|
|
315
|
+
if err != nil {
|
|
316
|
+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid product ID"})
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
product, err := h.queries.GetProduct(c, id)
|
|
321
|
+
if err != nil {
|
|
322
|
+
c.JSON(http.StatusNotFound, gin.H{"error": "Product not found"})
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
c.JSON(http.StatusOK, product)
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 4. Rust (Axum + SQLx)
|
|
333
|
+
|
|
334
|
+
### Project Structure
|
|
335
|
+
```
|
|
336
|
+
src/
|
|
337
|
+
├── main.rs # Entry point
|
|
338
|
+
├── routes/
|
|
339
|
+
│ ├── mod.rs
|
|
340
|
+
│ ├── products.rs # Product routes
|
|
341
|
+
│ └── users.rs # User routes
|
|
342
|
+
├── db/
|
|
343
|
+
│ ├── mod.rs
|
|
344
|
+
│ └── models.rs # Database models
|
|
345
|
+
├── middleware/
|
|
346
|
+
│ ├── mod.rs
|
|
347
|
+
│ └── auth.rs # JWT auth layer
|
|
348
|
+
├── services/
|
|
349
|
+
│ ├── mod.rs
|
|
350
|
+
│ └── product.rs # Business logic
|
|
351
|
+
└── error.rs # Custom error types
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### HTTP Handler + Validation
|
|
355
|
+
|
|
356
|
+
```rust
|
|
357
|
+
// src/routes/products.rs
|
|
358
|
+
use axum::{
|
|
359
|
+
extract::{Path, Query, State},
|
|
360
|
+
http::StatusCode,
|
|
361
|
+
Json,
|
|
362
|
+
};
|
|
363
|
+
use serde::{Deserialize, Serialize};
|
|
364
|
+
use sqlx::PgPool;
|
|
365
|
+
use uuid::Uuid;
|
|
366
|
+
|
|
367
|
+
use crate::error::AppError;
|
|
368
|
+
|
|
369
|
+
#[derive(Debug, Deserialize)]
|
|
370
|
+
pub struct ListParams {
|
|
371
|
+
pub limit: Option<i64>,
|
|
372
|
+
pub cursor: Option<String>,
|
|
373
|
+
pub category: Option<String>,
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
#[derive(Debug, Deserialize)]
|
|
377
|
+
pub struct CreateProductRequest {
|
|
378
|
+
pub name: String,
|
|
379
|
+
pub description: Option<String>,
|
|
380
|
+
pub price: f64,
|
|
381
|
+
pub category_id: Uuid,
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
#[derive(Debug, Serialize, sqlx::FromRow)]
|
|
385
|
+
pub struct Product {
|
|
386
|
+
pub id: Uuid,
|
|
387
|
+
pub name: String,
|
|
388
|
+
pub description: Option<String>,
|
|
389
|
+
pub price: f64,
|
|
390
|
+
pub category_id: Uuid,
|
|
391
|
+
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
392
|
+
pub updated_at: chrono::DateTime<chrono::Utc>,
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
#[derive(Serialize)]
|
|
396
|
+
pub struct ProductListResponse {
|
|
397
|
+
pub data: Vec<Product>,
|
|
398
|
+
pub has_more: bool,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
pub async fn list_products(
|
|
402
|
+
State(pool): State<PgPool>,
|
|
403
|
+
Query(params): Query<ListParams>,
|
|
404
|
+
) -> Result<Json<ProductListResponse>, AppError> {
|
|
405
|
+
let limit = params.limit.unwrap_or(20).min(100);
|
|
406
|
+
|
|
407
|
+
let products = sqlx::query_as::<_, Product>(
|
|
408
|
+
"SELECT * FROM products ORDER BY created_at LIMIT $1"
|
|
409
|
+
)
|
|
410
|
+
.bind(limit + 1)
|
|
411
|
+
.fetch_all(&pool)
|
|
412
|
+
.await?;
|
|
413
|
+
|
|
414
|
+
let has_more = products.len() as i64 > limit;
|
|
415
|
+
let data = if has_more {
|
|
416
|
+
products[..limit as usize].to_vec()
|
|
417
|
+
} else {
|
|
418
|
+
products
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
Ok(Json(ProductListResponse { data, has_more }))
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
pub async fn create_product(
|
|
425
|
+
State(pool): State<PgPool>,
|
|
426
|
+
Json(input): Json<CreateProductRequest>,
|
|
427
|
+
) -> Result<(StatusCode, Json<Product>), AppError> {
|
|
428
|
+
if input.name.is_empty() || input.price <= 0.0 {
|
|
429
|
+
return Err(AppError::Validation("Invalid product data".into()));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
let product = sqlx::query_as::<_, Product>(
|
|
433
|
+
r#"
|
|
434
|
+
INSERT INTO products (name, description, price, category_id)
|
|
435
|
+
VALUES ($1, $2, $3, $4)
|
|
436
|
+
RETURNING *
|
|
437
|
+
"#
|
|
438
|
+
)
|
|
439
|
+
.bind(&input.name)
|
|
440
|
+
.bind(&input.description)
|
|
441
|
+
.bind(input.price)
|
|
442
|
+
.bind(input.category_id)
|
|
443
|
+
.fetch_one(&pool)
|
|
444
|
+
.await?;
|
|
445
|
+
|
|
446
|
+
Ok((StatusCode::CREATED, Json(product)))
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
pub async fn get_product(
|
|
450
|
+
State(pool): State<PgPool>,
|
|
451
|
+
Path(id): Path<Uuid>,
|
|
452
|
+
) -> Result<Json<Product>, AppError> {
|
|
453
|
+
let product = sqlx::query_as::<_, Product>(
|
|
454
|
+
"SELECT * FROM products WHERE id = $1"
|
|
455
|
+
)
|
|
456
|
+
.bind(id)
|
|
457
|
+
.fetch_optional(&pool)
|
|
458
|
+
.await?
|
|
459
|
+
.ok_or(AppError::NotFound)?;
|
|
460
|
+
|
|
461
|
+
Ok(Json(product))
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Custom Error Type
|
|
466
|
+
|
|
467
|
+
```rust
|
|
468
|
+
// src/error.rs
|
|
469
|
+
use axum::{
|
|
470
|
+
http::StatusCode,
|
|
471
|
+
response::{IntoResponse, Response},
|
|
472
|
+
Json,
|
|
473
|
+
};
|
|
474
|
+
use serde_json::json;
|
|
475
|
+
|
|
476
|
+
#[derive(Debug)]
|
|
477
|
+
pub enum AppError {
|
|
478
|
+
NotFound,
|
|
479
|
+
Validation(String),
|
|
480
|
+
Database(sqlx::Error),
|
|
481
|
+
Internal(String),
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
impl IntoResponse for AppError {
|
|
485
|
+
fn into_response(self) -> Response {
|
|
486
|
+
let (status, message) = match self {
|
|
487
|
+
AppError::NotFound => (StatusCode::NOT_FOUND, "Resource not found".to_string()),
|
|
488
|
+
AppError::Validation(msg) => (StatusCode::BAD_REQUEST, msg),
|
|
489
|
+
AppError::Database(e) => {
|
|
490
|
+
tracing::error!("Database error: {:?}", e);
|
|
491
|
+
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string())
|
|
492
|
+
}
|
|
493
|
+
AppError::Internal(msg) => {
|
|
494
|
+
tracing::error!("Internal error: {}", msg);
|
|
495
|
+
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string())
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
(status, Json(json!({ "error": message }))).into_response()
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
impl From<sqlx::Error> for AppError {
|
|
504
|
+
fn from(err: sqlx::Error) -> Self {
|
|
505
|
+
AppError::Database(err)
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Language Selection Guide
|
|
513
|
+
|
|
514
|
+
| Criteria | TypeScript | Python | Go | Rust |
|
|
515
|
+
|----------|-----------|--------|-----|------|
|
|
516
|
+
| **Best for** | Full-stack web apps | ML/AI, data pipelines | High-concurrency services | Systems, performance-critical |
|
|
517
|
+
| **Ecosystem** | Largest (npm) | Rich (ML/data) | Growing | Growing |
|
|
518
|
+
| **Performance** | Good (V8/Bun) | Moderate (async) | Excellent | Best |
|
|
519
|
+
| **Type Safety** | Good (strict TS) | Good (Pydantic) | Built-in | Best (compiler) |
|
|
520
|
+
| **Concurrency** | Event loop | asyncio/threading | Goroutines | async/tokio |
|
|
521
|
+
| **Learning Curve** | Low | Low | Medium | High |
|
|
522
|
+
| **Deploy Size** | Medium | Medium | Small (static) | Smallest (static) |
|
|
523
|
+
| **Startup Time** | Fast | Moderate | Very fast | Very fast |
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## Conclusion
|
|
528
|
+
Choose the right language for the right job. TypeScript for rapid full-stack development, Python for data-heavy workloads, Go for high-concurrency microservices, and Rust for performance-critical systems. The patterns (validation, error handling, middleware) remain consistent across all languages.
|