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,466 @@
|
|
|
1
|
+
# API Design Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This reference documents production-grade API design patterns covering REST, GraphQL, gRPC, and WebSocket APIs with concrete implementation examples.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. RESTful API Design (OpenAPI 3.1)
|
|
9
|
+
|
|
10
|
+
### Spec-First Design
|
|
11
|
+
|
|
12
|
+
Always define the API contract before implementing. Use OpenAPI 3.1 for REST APIs.
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
# openapi.yaml
|
|
16
|
+
openapi: 3.1.0
|
|
17
|
+
info:
|
|
18
|
+
title: Product Catalog API
|
|
19
|
+
version: 2.0.0
|
|
20
|
+
description: Product management and catalog service
|
|
21
|
+
|
|
22
|
+
servers:
|
|
23
|
+
- url: https://api.example.com/v2
|
|
24
|
+
description: Production
|
|
25
|
+
- url: https://staging-api.example.com/v2
|
|
26
|
+
description: Staging
|
|
27
|
+
|
|
28
|
+
paths:
|
|
29
|
+
/products:
|
|
30
|
+
get:
|
|
31
|
+
operationId: listProducts
|
|
32
|
+
summary: List products with filtering and pagination
|
|
33
|
+
tags: [Products]
|
|
34
|
+
parameters:
|
|
35
|
+
- name: cursor
|
|
36
|
+
in: query
|
|
37
|
+
schema:
|
|
38
|
+
type: string
|
|
39
|
+
description: Cursor for pagination (base64 encoded)
|
|
40
|
+
- name: limit
|
|
41
|
+
in: query
|
|
42
|
+
schema:
|
|
43
|
+
type: integer
|
|
44
|
+
minimum: 1
|
|
45
|
+
maximum: 100
|
|
46
|
+
default: 20
|
|
47
|
+
- name: category
|
|
48
|
+
in: query
|
|
49
|
+
schema:
|
|
50
|
+
type: string
|
|
51
|
+
- name: fields
|
|
52
|
+
in: query
|
|
53
|
+
schema:
|
|
54
|
+
type: string
|
|
55
|
+
description: Comma-separated list of fields to include
|
|
56
|
+
responses:
|
|
57
|
+
'200':
|
|
58
|
+
description: Paginated list of products
|
|
59
|
+
content:
|
|
60
|
+
application/json:
|
|
61
|
+
schema:
|
|
62
|
+
$ref: '#/components/schemas/ProductListResponse'
|
|
63
|
+
'429':
|
|
64
|
+
description: Rate limit exceeded
|
|
65
|
+
headers:
|
|
66
|
+
Retry-After:
|
|
67
|
+
schema:
|
|
68
|
+
type: integer
|
|
69
|
+
description: Seconds to wait before retrying
|
|
70
|
+
|
|
71
|
+
post:
|
|
72
|
+
operationId: createProduct
|
|
73
|
+
summary: Create a new product
|
|
74
|
+
tags: [Products]
|
|
75
|
+
parameters:
|
|
76
|
+
- name: Idempotency-Key
|
|
77
|
+
in: header
|
|
78
|
+
required: true
|
|
79
|
+
schema:
|
|
80
|
+
type: string
|
|
81
|
+
format: uuid
|
|
82
|
+
description: Unique key to ensure idempotent creation
|
|
83
|
+
requestBody:
|
|
84
|
+
required: true
|
|
85
|
+
content:
|
|
86
|
+
application/json:
|
|
87
|
+
schema:
|
|
88
|
+
$ref: '#/components/schemas/CreateProductRequest'
|
|
89
|
+
responses:
|
|
90
|
+
'201':
|
|
91
|
+
description: Product created successfully
|
|
92
|
+
'409':
|
|
93
|
+
description: Product already exists (idempotent replay)
|
|
94
|
+
|
|
95
|
+
components:
|
|
96
|
+
schemas:
|
|
97
|
+
ProductListResponse:
|
|
98
|
+
type: object
|
|
99
|
+
properties:
|
|
100
|
+
data:
|
|
101
|
+
type: array
|
|
102
|
+
items:
|
|
103
|
+
$ref: '#/components/schemas/Product'
|
|
104
|
+
pagination:
|
|
105
|
+
$ref: '#/components/schemas/CursorPagination'
|
|
106
|
+
|
|
107
|
+
CursorPagination:
|
|
108
|
+
type: object
|
|
109
|
+
properties:
|
|
110
|
+
next_cursor:
|
|
111
|
+
type: string
|
|
112
|
+
nullable: true
|
|
113
|
+
has_more:
|
|
114
|
+
type: boolean
|
|
115
|
+
total_count:
|
|
116
|
+
type: integer
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Cursor-Based Pagination (TypeScript)
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// lib/pagination.ts
|
|
123
|
+
interface CursorPaginationParams {
|
|
124
|
+
cursor?: string;
|
|
125
|
+
limit: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface PaginatedResult<T> {
|
|
129
|
+
data: T[];
|
|
130
|
+
pagination: {
|
|
131
|
+
next_cursor: string | null;
|
|
132
|
+
has_more: boolean;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function paginateWithCursor<T extends { id: string; createdAt: Date }>(
|
|
137
|
+
query: (params: { cursor?: { id: string; createdAt: Date }; limit: number }) => Promise<T[]>,
|
|
138
|
+
params: CursorPaginationParams
|
|
139
|
+
): Promise<PaginatedResult<T>> {
|
|
140
|
+
const { cursor, limit } = params;
|
|
141
|
+
const decodedCursor = cursor
|
|
142
|
+
? JSON.parse(Buffer.from(cursor, 'base64url').toString())
|
|
143
|
+
: undefined;
|
|
144
|
+
|
|
145
|
+
// Fetch one extra to determine if there are more results
|
|
146
|
+
const items = await query({ cursor: decodedCursor, limit: limit + 1 });
|
|
147
|
+
const hasMore = items.length > limit;
|
|
148
|
+
const data = hasMore ? items.slice(0, limit) : items;
|
|
149
|
+
|
|
150
|
+
const lastItem = data[data.length - 1];
|
|
151
|
+
const nextCursor = hasMore && lastItem
|
|
152
|
+
? Buffer.from(JSON.stringify({ id: lastItem.id, createdAt: lastItem.createdAt }))
|
|
153
|
+
.toString('base64url')
|
|
154
|
+
: null;
|
|
155
|
+
|
|
156
|
+
return { data, pagination: { next_cursor: nextCursor, has_more: hasMore } };
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Idempotency Key Middleware
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// middleware/idempotency.ts
|
|
164
|
+
import { Redis } from '@upstash/redis';
|
|
165
|
+
|
|
166
|
+
const redis = new Redis({
|
|
167
|
+
url: process.env.UPSTASH_REDIS_REST_URL!,
|
|
168
|
+
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export async function withIdempotency<T>(
|
|
172
|
+
key: string,
|
|
173
|
+
handler: () => Promise<T>,
|
|
174
|
+
ttlSeconds: number = 86400
|
|
175
|
+
): Promise<{ result: T; isReplay: boolean }> {
|
|
176
|
+
// Check if this key was already processed
|
|
177
|
+
const cached = await redis.get<string>(`idempotency:${key}`);
|
|
178
|
+
if (cached) {
|
|
179
|
+
return { result: JSON.parse(cached) as T, isReplay: true };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Execute the handler
|
|
183
|
+
const result = await handler();
|
|
184
|
+
|
|
185
|
+
// Store the result for replay
|
|
186
|
+
await redis.set(`idempotency:${key}`, JSON.stringify(result), { ex: ttlSeconds });
|
|
187
|
+
|
|
188
|
+
return { result, isReplay: false };
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 2. GraphQL API Design
|
|
195
|
+
|
|
196
|
+
### Schema-First with Code Generation
|
|
197
|
+
|
|
198
|
+
```graphql
|
|
199
|
+
# schema.graphql
|
|
200
|
+
type Query {
|
|
201
|
+
product(id: ID!): Product
|
|
202
|
+
products(
|
|
203
|
+
filter: ProductFilter
|
|
204
|
+
pagination: PaginationInput
|
|
205
|
+
sort: ProductSort
|
|
206
|
+
): ProductConnection!
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
type Mutation {
|
|
210
|
+
createProduct(input: CreateProductInput!): CreateProductPayload!
|
|
211
|
+
updateProduct(id: ID!, input: UpdateProductInput!): UpdateProductPayload!
|
|
212
|
+
deleteProduct(id: ID!): DeleteProductPayload!
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
type Product {
|
|
216
|
+
id: ID!
|
|
217
|
+
name: String!
|
|
218
|
+
description: String
|
|
219
|
+
price: Float!
|
|
220
|
+
category: Category!
|
|
221
|
+
variants: [ProductVariant!]!
|
|
222
|
+
reviews(first: Int, after: String): ReviewConnection!
|
|
223
|
+
createdAt: DateTime!
|
|
224
|
+
updatedAt: DateTime!
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Relay-style connection for cursor pagination
|
|
228
|
+
type ProductConnection {
|
|
229
|
+
edges: [ProductEdge!]!
|
|
230
|
+
pageInfo: PageInfo!
|
|
231
|
+
totalCount: Int!
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
type ProductEdge {
|
|
235
|
+
cursor: String!
|
|
236
|
+
node: Product!
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
type PageInfo {
|
|
240
|
+
hasNextPage: Boolean!
|
|
241
|
+
hasPreviousPage: Boolean!
|
|
242
|
+
startCursor: String
|
|
243
|
+
endCursor: String
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
input ProductFilter {
|
|
247
|
+
categoryId: ID
|
|
248
|
+
minPrice: Float
|
|
249
|
+
maxPrice: Float
|
|
250
|
+
search: String
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
input PaginationInput {
|
|
254
|
+
first: Int
|
|
255
|
+
after: String
|
|
256
|
+
last: Int
|
|
257
|
+
before: String
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### DataLoader for N+1 Prevention
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// loaders/product-loader.ts
|
|
265
|
+
import DataLoader from 'dataloader';
|
|
266
|
+
import { db } from '@/lib/db';
|
|
267
|
+
import { products } from '@/lib/db/schema';
|
|
268
|
+
import { inArray } from 'drizzle-orm';
|
|
269
|
+
|
|
270
|
+
export function createProductLoader() {
|
|
271
|
+
return new DataLoader<string, typeof products.$inferSelect | null>(
|
|
272
|
+
async (ids) => {
|
|
273
|
+
const results = await db
|
|
274
|
+
.select()
|
|
275
|
+
.from(products)
|
|
276
|
+
.where(inArray(products.id, [...ids]));
|
|
277
|
+
|
|
278
|
+
// Map results back to input order (DataLoader requirement)
|
|
279
|
+
const resultMap = new Map(results.map((r) => [r.id, r]));
|
|
280
|
+
return ids.map((id) => resultMap.get(id) ?? null);
|
|
281
|
+
},
|
|
282
|
+
{ cache: true, maxBatchSize: 100 }
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 3. gRPC API Design
|
|
290
|
+
|
|
291
|
+
### Protocol Buffer Definition
|
|
292
|
+
|
|
293
|
+
```protobuf
|
|
294
|
+
// proto/product/v1/product.proto
|
|
295
|
+
syntax = "proto3";
|
|
296
|
+
|
|
297
|
+
package product.v1;
|
|
298
|
+
|
|
299
|
+
option go_package = "github.com/example/api/gen/product/v1";
|
|
300
|
+
|
|
301
|
+
import "google/protobuf/timestamp.proto";
|
|
302
|
+
import "google/protobuf/field_mask.proto";
|
|
303
|
+
|
|
304
|
+
service ProductService {
|
|
305
|
+
// Unary RPCs
|
|
306
|
+
rpc GetProduct(GetProductRequest) returns (GetProductResponse);
|
|
307
|
+
rpc CreateProduct(CreateProductRequest) returns (CreateProductResponse);
|
|
308
|
+
rpc UpdateProduct(UpdateProductRequest) returns (UpdateProductResponse);
|
|
309
|
+
rpc DeleteProduct(DeleteProductRequest) returns (DeleteProductResponse);
|
|
310
|
+
|
|
311
|
+
// Server streaming for real-time updates
|
|
312
|
+
rpc WatchProductChanges(WatchProductChangesRequest) returns (stream ProductChange);
|
|
313
|
+
|
|
314
|
+
// Bulk operations
|
|
315
|
+
rpc BatchGetProducts(BatchGetProductsRequest) returns (BatchGetProductsResponse);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
message Product {
|
|
319
|
+
string id = 1;
|
|
320
|
+
string name = 2;
|
|
321
|
+
string description = 3;
|
|
322
|
+
int64 price_cents = 4; // Use integer cents to avoid floating point issues
|
|
323
|
+
string category_id = 5;
|
|
324
|
+
ProductStatus status = 6;
|
|
325
|
+
google.protobuf.Timestamp created_at = 7;
|
|
326
|
+
google.protobuf.Timestamp updated_at = 8;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
enum ProductStatus {
|
|
330
|
+
PRODUCT_STATUS_UNSPECIFIED = 0;
|
|
331
|
+
PRODUCT_STATUS_DRAFT = 1;
|
|
332
|
+
PRODUCT_STATUS_ACTIVE = 2;
|
|
333
|
+
PRODUCT_STATUS_ARCHIVED = 3;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
message UpdateProductRequest {
|
|
337
|
+
string id = 1;
|
|
338
|
+
Product product = 2;
|
|
339
|
+
google.protobuf.FieldMask update_mask = 3; // Partial updates
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
message GetProductRequest {
|
|
343
|
+
string id = 1;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
message GetProductResponse {
|
|
347
|
+
Product product = 1;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 4. WebSocket & Real-Time APIs
|
|
354
|
+
|
|
355
|
+
### Type-Safe WebSocket with Zod Validation
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// lib/ws/types.ts
|
|
359
|
+
import { z } from 'zod';
|
|
360
|
+
|
|
361
|
+
// Define all possible client → server messages
|
|
362
|
+
export const ClientMessageSchema = z.discriminatedUnion('type', [
|
|
363
|
+
z.object({
|
|
364
|
+
type: z.literal('subscribe'),
|
|
365
|
+
channel: z.string(),
|
|
366
|
+
}),
|
|
367
|
+
z.object({
|
|
368
|
+
type: z.literal('unsubscribe'),
|
|
369
|
+
channel: z.string(),
|
|
370
|
+
}),
|
|
371
|
+
z.object({
|
|
372
|
+
type: z.literal('message'),
|
|
373
|
+
channel: z.string(),
|
|
374
|
+
payload: z.unknown(),
|
|
375
|
+
}),
|
|
376
|
+
z.object({
|
|
377
|
+
type: z.literal('ping'),
|
|
378
|
+
}),
|
|
379
|
+
]);
|
|
380
|
+
|
|
381
|
+
// Define all possible server → client messages
|
|
382
|
+
export type ServerMessage =
|
|
383
|
+
| { type: 'subscribed'; channel: string }
|
|
384
|
+
| { type: 'unsubscribed'; channel: string }
|
|
385
|
+
| { type: 'message'; channel: string; payload: unknown; sender: string }
|
|
386
|
+
| { type: 'pong' }
|
|
387
|
+
| { type: 'error'; code: string; message: string };
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## 5. Error Handling Standards
|
|
393
|
+
|
|
394
|
+
### Structured Error Response (RFC 7807 - Problem Details)
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// lib/errors.ts
|
|
398
|
+
interface ProblemDetails {
|
|
399
|
+
type: string; // URI identifying the error type
|
|
400
|
+
title: string; // Human-readable summary
|
|
401
|
+
status: number; // HTTP status code
|
|
402
|
+
detail?: string; // Human-readable explanation specific to this occurrence
|
|
403
|
+
instance?: string; // URI identifying the specific occurrence
|
|
404
|
+
errors?: FieldError[]; // Validation errors
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
interface FieldError {
|
|
408
|
+
field: string;
|
|
409
|
+
message: string;
|
|
410
|
+
code: string;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export class AppError extends Error {
|
|
414
|
+
constructor(
|
|
415
|
+
public readonly status: number,
|
|
416
|
+
public readonly type: string,
|
|
417
|
+
public readonly title: string,
|
|
418
|
+
public readonly detail?: string,
|
|
419
|
+
public readonly fieldErrors?: FieldError[],
|
|
420
|
+
) {
|
|
421
|
+
super(title);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
toProblemDetails(): ProblemDetails {
|
|
425
|
+
return {
|
|
426
|
+
type: `https://api.example.com/errors/${this.type}`,
|
|
427
|
+
title: this.title,
|
|
428
|
+
status: this.status,
|
|
429
|
+
detail: this.detail,
|
|
430
|
+
errors: this.fieldErrors,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Usage
|
|
436
|
+
throw new AppError(
|
|
437
|
+
422,
|
|
438
|
+
'validation-failed',
|
|
439
|
+
'Validation Failed',
|
|
440
|
+
'One or more fields failed validation',
|
|
441
|
+
[
|
|
442
|
+
{ field: 'email', message: 'Invalid email format', code: 'INVALID_FORMAT' },
|
|
443
|
+
{ field: 'price', message: 'Price must be positive', code: 'INVALID_RANGE' },
|
|
444
|
+
]
|
|
445
|
+
);
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Best Practices Summary
|
|
451
|
+
|
|
452
|
+
| Practice | Description |
|
|
453
|
+
|----------|-------------|
|
|
454
|
+
| Spec-first design | Define API contract before implementation |
|
|
455
|
+
| Cursor pagination | Use cursor-based pagination for large datasets |
|
|
456
|
+
| Idempotency keys | Make all mutations idempotent with unique keys |
|
|
457
|
+
| Rate limiting | Protect APIs with token bucket / sliding window |
|
|
458
|
+
| Versioning | URL path versioning (`/v1/`) for breaking changes |
|
|
459
|
+
| Error format | Use RFC 7807 Problem Details for structured errors |
|
|
460
|
+
| Field masking | Allow clients to select specific fields |
|
|
461
|
+
| HATEOAS | Include actionable links in responses |
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Conclusion
|
|
466
|
+
Good API design is the foundation of a scalable system. Invest in spec-first design, strong typing, and consistent error handling to create APIs that are easy to consume, evolve, and maintain.
|