소스 정보
- 저장소
- tomevault-io/skills-registry
- 최근 소스 활동
- 2026년 5월 11일 15:30
- 감지된 SKILL.md 언어
- 영어
- 스타
- 0
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill api-design-skill명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | api-design-skill |
| description | Design APIs that developers love to use. Use when this capability is needed. |
| metadata | {"author":"fabioc-aloha"} |
Design APIs that developers love to use.
A good API is intuitive, consistent, and hard to misuse. Design for the consumer, not the implementation.
| Good | Bad | Why |
|---|---|---|
/users | /getUsers | Nouns, not verbs |
/users/123 | /user?id=123 | Path params for identity |
/users/123/orders | /getUserOrders | Hierarchical resources |
/search?q=term | /search/term | Query params for filters |
| Method | Purpose | Idempotent | Safe |
|---|---|---|---|
| GET | Read resource | ✅ | ✅ |
| POST | Create resource | ❌ | ❌ |
| PUT | Replace resource | ✅ | ❌ |
| PATCH | Partial update | ❌* | ❌ |
| DELETE | Remove resource | ✅ | ❌ |
*PATCH can be idempotent if designed carefully
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Validation error, malformed request |
| 401 | Unauthorized | Missing/invalid authentication |
| 403 | Forbidden | Authenticated but not allowed |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | State conflict (duplicate, version) |
| 422 | Unprocessable | Valid syntax, invalid semantics |
| 429 | Too Many Requests | Rate limited |
| 500 | Internal Error | Server bug (never expose details) |
openapi: 3.0.3
info:
title: My API
version: 1.0.0
paths:
/users:
get:
summary: List users
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
components:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: string
email:
type: string
format: email
| Strategy | Example | Pros | Cons |
|---|---|---|---|
| URL Path | /v1/users | Explicit, easy routing | URL pollution |
| Header | Accept: application/vnd.api.v1+json | Clean URLs | Hidden, harder to test |
| Query Param | /users?version=1 | Explicit, flexible | Looks like filter |
Recommendation: URL path for major versions. It's explicit and easy.
GET /users?offset=40&limit=20
{
"data": [...],
"pagination": {
"offset": 40,
"limit": 20,
"total": 150
}
}
GET /users?cursor=abc123&limit=20
{
"data": [...],
"pagination": {
"next_cursor": "def456",
"has_more": true
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "email",
"message": "Must be valid email format"
}
],
"request_id": "req_abc123"
}
}
2026-02-01T14:30:00Z{ "data": ..., "meta": ... }GET /users/123?fields=id,name,email
Reduces payload, improves performance.
POST /users/bulk
{
"operations": [
{ "method": "create", "data": {...} },
{ "method": "update", "id": "123", "data": {...} }
]
}
| Header | Purpose | Example |
|---|---|---|
Cache-Control | Caching directives | max-age=3600, private |
ETag | Content fingerprint | "abc123" |
Last-Modified | Timestamp of change | Sat, 01 Feb 2026 12:00:00 GMT |
Vary | Cache key factors | Accept, Authorization |
# Public, cacheable for 1 hour
Cache-Control: public, max-age=3600
# Private (user-specific), cacheable for 5 minutes
Cache-Control: private, max-age=300
# No caching at all
Cache-Control: no-store
# Cache but revalidate every time
Cache-Control: no-cache
# Stale content OK while revalidating
Cache-Control: max-age=60, stale-while-revalidate=30
# First request
GET /users/123
→ 200 OK
ETag: "v1-abc123"
Cache-Control: private, max-age=60
# Subsequent request (after cache expires)
GET /users/123
If-None-Match: "v1-abc123"
→ 304 Not Modified (cache still valid)
OR
→ 200 OK with new ETag (content changed)
| Resource Type | Cache Strategy | TTL |
|---|---|---|
| Static config | public, max-age | Hours/days |
| User profile | private, max-age | Minutes |
| Real-time data | no-store | None |
| Search results | private, max-age | Seconds |
| Public listings | public, s-maxage | Minutes |
// Cache key design
const cacheKey = `${resource}:${id}:${version}`;
// Cache-aside pattern
async function getUser(id: string) {
const cached = await cache.get(`user:${id}`);
if (cached) return cached;
const user = await db.users.findById(id);
await cache.set(`user:${id}`, user, { ttl: 300 });
return user;
}
// Cache invalidation on write
async function updateUser(id: string, data: UserUpdate) {
const user = await db.users.update(id, data);
await cache.delete(`user:${id}`);
await cache.delete(`users:list:*`); // Invalidate list caches
return user;
}
| Strategy | When to Use | Complexity |
|---|---|---|
| TTL expiry | Low-stakes data | Low |
| Event-driven | Critical consistency | Medium |
| Version tags | Immutable resources | Low |
| Cache busting | Static assets | Low |
| Write-through | Always current | High |
| Algorithm | Description | Best For |
|---|---|---|
| Fixed Window | X requests per minute | Simple APIs |
| Sliding Window | Rolling time window | Smoother limits |
| Token Bucket | Burst-friendly with refill | Flexible patterns |
| Leaky Bucket | Smooth output rate | Consistent throughput |
class TokenBucket {
private tokens: number;
private lastRefill: number;
constructor(
private capacity: number, // Max burst size
private refillRate: number, // Tokens per second
) {
this.tokens = capacity;
this.lastRefill = Date.now();
}
consume(tokens: number = 1): boolean {
this.refill();
if (this.tokens >= tokens) {
this.tokens -= tokens;
return true;
}
return false;
}
private refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = .(
.,
. + elapsed * .
);
. = now;
}
}
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1706792400
# When exceeded
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1706792400
| Tier | Limit | Use Case |
|---|---|---|
| Anonymous | 60/hour | Public exploration |
| Authenticated | 1000/hour | Normal usage |
| Premium | 10000/hour | Power users |
| Internal | Unlimited | Service-to-service |
// Per-user limiting
const userKey = `ratelimit:user:${userId}`;
// Per-IP limiting (for anonymous)
const ipKey = `ratelimit:ip:${clientIp}`;
// Per-endpoint limiting
const endpointKey = `ratelimit:${method}:${path}`;
// Combined (most flexible)
const combinedKey = `ratelimit:${userId}:${method}:${path}`;
async function apiCallWithRetry(request: Request): Promise<Response> {
const response = await fetch(request);
if (response.status === 429) {
const retryAfter = parseInt(
response.headers.get('Retry-After') || '60'
);
console.log(`Rate limited. Retrying in ${retryAfter}s`);
await sleep(retryAfter * 1000);
return apiCallWithRetry(request);
}
return response;
}
// Proactive rate limiting
class RateLimitedClient {
private remaining: number = Infinity;
private resetTime: number = 0;
async request(url: string): Promise<Response> {
// Wait if we know we're out of quota
if (this.remaining <= && .() < .) {
(. - .());
}
response = (url);
. = (
response..() ||
);
. = (
response..() ||
) * ;
response;
}
}
Most API gateways support rate limiting out of the box:
# AWS API Gateway
throttle:
burstLimit: 100
rateLimit: 50
# Azure API Management
<rate-limit calls="100" renewal-period="60" />
# Kong
plugins:
- name: rate-limiting
config:
minute: 100
policy: redis
Patterns from FishbowlGovernance multi-platform integrations (Feb 2026)
Key Limitations:
| Constraint | Impact | Workaround |
|---|---|---|
| Subscriptions expire after 3 days | Need timer-based renewal | Azure Function timer trigger |
| No native comments API | Cannot sync comments TO Planner | Use checklist items instead |
| 2400 tasks per plan limit | Monitor at threshold | Alert and archive old tasks |
| Task details require separate API call | Each has its own ETag | Batch carefully |
| Webhook requires public URL | Local dev challenge | Use ngrok or dev tunnels |
| User assignment needs Graph User ID | Must resolve from email/UPN | Cache user mappings |
| Tasks are flat (no hierarchy) | Can't mirror ADO parent/child | Map Features → Buckets |
ETag Pattern for Updates:
PATCH /planner/tasks/{task-id}/details
If-Match: {etag}
Content-Type: application/json
{"description": "Updated description"}
JSON Patch Pattern:
PATCH /_apis/wit/workitems/{id}?api-version=7.0
Content-Type: application/json-patch+json
[
{"op": "add", "path": "/fields/System.Title", "value": "New Title"},
{"op": "replace", "path": "/fields/System.State", "value": "Active"}
]
Key Patterns:
application/json-patch+json content typeWIQL Query for Duplicate Check:
POST /_apis/wit/wiql?api-version=7.0
Content-Type: application/json
{
"query": "SELECT [System.Id] FROM WorkItems WHERE [System.Title] = 'Exact Title'"
}
Rate Limits by Endpoint:
| Endpoint | Calls/Minute |
|---|---|
| Survey definitions | 100 |
| Distributions | 3000 |
| Responses | 2000 |
Integration Patterns:
Async Operation Pattern (202 Accepted):
# 1. POST triggers async operation
$response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Post
# 2. Get Location header for polling
if ($response.StatusCode -eq 202) {
$operationUrl = $response.Headers["Location"][0]
# 3. Poll until complete
do {
Start-Sleep -Seconds 2
$status = Invoke-RestMethod -Uri $operationUrl -Headers $headers
} while ($status.status -ne "Succeeded")
# 4. Fetch result
$result = Invoke-RestMethod -Uri "$operationUrl/result" -Headers $headers
}
Token Acquisition:
# Fabric API token
$fabricToken = az account get-access-token --resource https://api.fabric.microsoft.com --query accessToken -o tsv
# Storage token (for Unity Catalog/schema-enabled lakehouses)
$storageToken = az account get-access-token --resource https://storage.azure.com --query accessToken -o tsv
flowchart LR
subgraph Sources["Source Systems"]
ADO[Azure DevOps]
QX[Qualtrics]
end
subgraph Integration["Integration Layer"]
SB[Service Bus Queue]
FN[Azure Functions]
DB[(Cosmos DB<br/>Mappings)]
end
subgraph Targets["Target Systems"]
PL[Microsoft Planner]
FB[Microsoft Fabric]
end
ADO --> SB
QX --> SB
SB --> FN
FN --> DB
FN --> PL
FN --> FB
style Sources fill:#ddf4ff,color:#0550ae,stroke:#80ccff
style Integration fill:#d8b9ff,color:#6639ba,stroke:#bf8aff
style Targets fill:#d3f5db,color:#1a7f37,stroke:#6fdd8b
Key Design Principles:
□ Authentication on all non-public endpoints
□ Authorization checked for each resource
□ Rate limiting enabled
□ Input validation (size limits, type checking)
□ No sensitive data in URLs (tokens, passwords)
□ CORS configured appropriately
□ Security headers set
□ Audit logging for sensitive operations
Good API docs include:
Before shipping an API:
□ Resource names are nouns, plural
□ HTTP methods match semantics
□ Status codes are appropriate
□ Error responses are consistent
□ Pagination is implemented for lists
□ Versioning strategy is clear
□ Authentication is documented
□ Rate limits are defined
□ Breaking changes are versioned
□ OpenAPI spec is accurate
See synapses.json for connections.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.