📦 deps(thirdparty): update snapshots
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "antigravity-bundle-aas-api-platform-builder",
|
||||
"version": "13.0.0",
|
||||
"description": "Editorial \"AAS API Platform Builder\" bundle for Claude Code from Antigravity Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
"url": "https://github.com/sickn33/antigravity-awesome-skills"
|
||||
},
|
||||
"homepage": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"repository": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"claude-code",
|
||||
"skills",
|
||||
"bundle",
|
||||
"aas-api-platform-builder",
|
||||
"antigravity-awesome-skills"
|
||||
]
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "agyb-aas-api-platform-builder",
|
||||
"version": "13.0.0",
|
||||
"description": "Install the \"AAS API Platform Builder\" workflow plugin from Antigravity Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
"url": "https://github.com/sickn33/antigravity-awesome-skills"
|
||||
},
|
||||
"homepage": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"repository": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"codex",
|
||||
"skills",
|
||||
"bundle",
|
||||
"aas-api-platform-builder",
|
||||
"productivity"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"interface": {
|
||||
"displayName": "AAS API Platform Builder",
|
||||
"shortDescription": "Design language-agnostic API platforms with contracts, docs, endpoints, auth, security, load testing, and observability.",
|
||||
"longDescription": "Design language-agnostic API platforms with contracts, docs, endpoints, auth, security, load testing, and observability. Complements Python API Builder with a language-agnostic API platform workflow from design and OpenAPI contracts to auth, security, documentation, load testing, and observability. Recommended for: Backend teams, Platform teams, Full-stack teams designing shared APIs. Not for: Python-only implementation guidance, Frontend UI polish. Covers API Design Principles, API Patterns, and 8 more skills.",
|
||||
"developerName": "sickn33 and contributors",
|
||||
"category": "Specialized Product Plugins",
|
||||
"capabilities": [
|
||||
"Interactive",
|
||||
"Write"
|
||||
],
|
||||
"websiteURL": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"brandColor": "#111827",
|
||||
"defaultPrompt": [
|
||||
"Use this plugin to design an API platform with OpenAPI contracts, auth, docs, and observability.",
|
||||
"Use this plugin to review this API for versioning, endpoint design, security, and load-test gaps.",
|
||||
"Use this plugin to turn these product requirements into API endpoints and a contract-first implementation plan."
|
||||
]
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: api-design-principles
|
||||
description: "Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers and stand the test of time."
|
||||
risk: safe
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# API Design Principles
|
||||
|
||||
Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers and stand the test of time.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Designing new REST or GraphQL APIs
|
||||
- Refactoring existing APIs for better usability
|
||||
- Establishing API design standards for your team
|
||||
- Reviewing API specifications before implementation
|
||||
- Migrating between API paradigms (REST to GraphQL, etc.)
|
||||
- Creating developer-friendly API documentation
|
||||
- Optimizing APIs for specific use cases (mobile, third-party integrations)
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need implementation guidance for a specific framework
|
||||
- You are doing infrastructure-only work without API contracts
|
||||
- You cannot change or version public interfaces
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Define consumers, use cases, and constraints.
|
||||
2. Choose API style and model resources or types.
|
||||
3. Specify errors, versioning, pagination, and auth strategy.
|
||||
4. Validate with examples and review for consistency.
|
||||
|
||||
Refer to `resources/implementation-playbook.md` for detailed patterns, checklists, and templates.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed patterns, checklists, and templates.
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
# API Design Checklist
|
||||
|
||||
## Pre-Implementation Review
|
||||
|
||||
### Resource Design
|
||||
|
||||
- [ ] Resources are nouns, not verbs
|
||||
- [ ] Plural names for collections
|
||||
- [ ] Consistent naming across all endpoints
|
||||
- [ ] Clear resource hierarchy (avoid deep nesting >2 levels)
|
||||
- [ ] All CRUD operations properly mapped to HTTP methods
|
||||
|
||||
### HTTP Methods
|
||||
|
||||
- [ ] GET for retrieval (safe, idempotent)
|
||||
- [ ] POST for creation
|
||||
- [ ] PUT for full replacement (idempotent)
|
||||
- [ ] PATCH for partial updates
|
||||
- [ ] DELETE for removal (idempotent)
|
||||
|
||||
### Status Codes
|
||||
|
||||
- [ ] 200 OK for successful GET/PATCH/PUT
|
||||
- [ ] 201 Created for POST
|
||||
- [ ] 204 No Content for DELETE
|
||||
- [ ] 400 Bad Request for malformed requests
|
||||
- [ ] 401 Unauthorized for missing auth
|
||||
- [ ] 403 Forbidden for insufficient permissions
|
||||
- [ ] 404 Not Found for missing resources
|
||||
- [ ] 422 Unprocessable Entity for validation errors
|
||||
- [ ] 429 Too Many Requests for rate limiting
|
||||
- [ ] 500 Internal Server Error for server issues
|
||||
|
||||
### Pagination
|
||||
|
||||
- [ ] All collection endpoints paginated
|
||||
- [ ] Default page size defined (e.g., 20)
|
||||
- [ ] Maximum page size enforced (e.g., 100)
|
||||
- [ ] Pagination metadata included (total, pages, etc.)
|
||||
- [ ] Cursor-based or offset-based pattern chosen
|
||||
|
||||
### Filtering & Sorting
|
||||
|
||||
- [ ] Query parameters for filtering
|
||||
- [ ] Sort parameter supported
|
||||
- [ ] Search parameter for full-text search
|
||||
- [ ] Field selection supported (sparse fieldsets)
|
||||
|
||||
### Versioning
|
||||
|
||||
- [ ] Versioning strategy defined (URL/header/query)
|
||||
- [ ] Version included in all endpoints
|
||||
- [ ] Deprecation policy documented
|
||||
|
||||
### Error Handling
|
||||
|
||||
- [ ] Consistent error response format
|
||||
- [ ] Detailed error messages
|
||||
- [ ] Field-level validation errors
|
||||
- [ ] Error codes for client handling
|
||||
- [ ] Timestamps in error responses
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
- [ ] Authentication method defined (Bearer token, API key)
|
||||
- [ ] Authorization checks on all endpoints
|
||||
- [ ] 401 vs 403 used correctly
|
||||
- [ ] Token expiration handled
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
- [ ] Rate limits defined per endpoint/user
|
||||
- [ ] Rate limit headers included
|
||||
- [ ] 429 status code for exceeded limits
|
||||
- [ ] Retry-After header provided
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] OpenAPI/Swagger spec generated
|
||||
- [ ] All endpoints documented
|
||||
- [ ] Request/response examples provided
|
||||
- [ ] Error responses documented
|
||||
- [ ] Authentication flow documented
|
||||
|
||||
### Testing
|
||||
|
||||
- [ ] Unit tests for business logic
|
||||
- [ ] Integration tests for endpoints
|
||||
- [ ] Error scenarios tested
|
||||
- [ ] Edge cases covered
|
||||
- [ ] Performance tests for heavy endpoints
|
||||
|
||||
### Security
|
||||
|
||||
- [ ] Input validation on all fields
|
||||
- [ ] SQL injection prevention
|
||||
- [ ] XSS prevention
|
||||
- [ ] CORS configured correctly
|
||||
- [ ] HTTPS enforced
|
||||
- [ ] Sensitive data not in URLs
|
||||
- [ ] No secrets in responses
|
||||
|
||||
### Performance
|
||||
|
||||
- [ ] Database queries optimized
|
||||
- [ ] N+1 queries prevented
|
||||
- [ ] Caching strategy defined
|
||||
- [ ] Cache headers set appropriately
|
||||
- [ ] Large responses paginated
|
||||
|
||||
### Monitoring
|
||||
|
||||
- [ ] Logging implemented
|
||||
- [ ] Error tracking configured
|
||||
- [ ] Performance metrics collected
|
||||
- [ ] Health check endpoint available
|
||||
- [ ] Alerts configured for errors
|
||||
|
||||
## GraphQL-Specific Checks
|
||||
|
||||
### Schema Design
|
||||
|
||||
- [ ] Schema-first approach used
|
||||
- [ ] Types properly defined
|
||||
- [ ] Non-null vs nullable decided
|
||||
- [ ] Interfaces/unions used appropriately
|
||||
- [ ] Custom scalars defined
|
||||
|
||||
### Queries
|
||||
|
||||
- [ ] Query depth limiting
|
||||
- [ ] Query complexity analysis
|
||||
- [ ] DataLoaders prevent N+1
|
||||
- [ ] Pagination pattern chosen (Relay/offset)
|
||||
|
||||
### Mutations
|
||||
|
||||
- [ ] Input types defined
|
||||
- [ ] Payload types with errors
|
||||
- [ ] Optimistic response support
|
||||
- [ ] Idempotency considered
|
||||
|
||||
### Performance
|
||||
|
||||
- [ ] DataLoader for all relationships
|
||||
- [ ] Query batching enabled
|
||||
- [ ] Persisted queries considered
|
||||
- [ ] Response caching implemented
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] All fields documented
|
||||
- [ ] Deprecations marked
|
||||
- [ ] Examples provided
|
||||
- [ ] Schema introspection enabled
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
"""
|
||||
Production-ready REST API template using FastAPI.
|
||||
Includes pagination, filtering, error handling, and best practices.
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Query, Path, Depends, status
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, Field, EmailStr, ConfigDict
|
||||
from typing import Optional, List, Any
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
app = FastAPI(
|
||||
title="API Template",
|
||||
version="1.0.0",
|
||||
docs_url="/api/docs"
|
||||
)
|
||||
|
||||
# Security Middleware
|
||||
# Trusted Host: Prevents HTTP Host Header attacks
|
||||
app.add_middleware(
|
||||
TrustedHostMiddleware,
|
||||
allowed_hosts=["*"] # TODO: Configure this in production, e.g. ["api.example.com"]
|
||||
)
|
||||
|
||||
# CORS: Configures Cross-Origin Resource Sharing
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # TODO: Update this with specific origins in production
|
||||
allow_credentials=False, # TODO: Set to True if you need cookies/auth headers, but restrict origins
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Models
|
||||
class UserStatus(str, Enum):
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
SUSPENDED = "suspended"
|
||||
|
||||
class UserBase(BaseModel):
|
||||
email: EmailStr
|
||||
name: str = Field(..., min_length=1, max_length=100)
|
||||
status: UserStatus = UserStatus.ACTIVE
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str = Field(..., min_length=8)
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
email: Optional[EmailStr] = None
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
status: Optional[UserStatus] = None
|
||||
|
||||
class User(UserBase):
|
||||
id: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
# Pagination
|
||||
class PaginationParams(BaseModel):
|
||||
page: int = Field(1, ge=1)
|
||||
page_size: int = Field(20, ge=1, le=100)
|
||||
|
||||
class PaginatedResponse(BaseModel):
|
||||
items: List[Any]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
pages: int
|
||||
|
||||
# Error handling
|
||||
class ErrorDetail(BaseModel):
|
||||
field: Optional[str] = None
|
||||
message: str
|
||||
code: str
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
error: str
|
||||
message: str
|
||||
details: Optional[List[ErrorDetail]] = None
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(request, exc):
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=ErrorResponse(
|
||||
error=exc.__class__.__name__,
|
||||
message=exc.detail if isinstance(exc.detail, str) else exc.detail.get("message", "Error"),
|
||||
details=exc.detail.get("details") if isinstance(exc.detail, dict) else None
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
# Endpoints
|
||||
@app.get("/api/users", response_model=PaginatedResponse, tags=["Users"])
|
||||
async def list_users(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
status: Optional[UserStatus] = Query(None),
|
||||
search: Optional[str] = Query(None)
|
||||
):
|
||||
"""List users with pagination and filtering."""
|
||||
# Mock implementation
|
||||
total = 100
|
||||
items = [
|
||||
User(
|
||||
id=str(i),
|
||||
email=f"user{i}@example.com",
|
||||
name=f"User {i}",
|
||||
status=UserStatus.ACTIVE,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
).model_dump()
|
||||
for i in range((page-1)*page_size, min(page*page_size, total))
|
||||
]
|
||||
|
||||
return PaginatedResponse(
|
||||
items=items,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
pages=(total + page_size - 1) // page_size
|
||||
)
|
||||
|
||||
@app.post("/api/users", response_model=User, status_code=status.HTTP_201_CREATED, tags=["Users"])
|
||||
async def create_user(user: UserCreate):
|
||||
"""Create a new user."""
|
||||
# Mock implementation
|
||||
return User(
|
||||
id="123",
|
||||
email=user.email,
|
||||
name=user.name,
|
||||
status=user.status,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
@app.get("/api/users/{user_id}", response_model=User, tags=["Users"])
|
||||
async def get_user(user_id: str = Path(..., description="User ID")):
|
||||
"""Get user by ID."""
|
||||
# Mock: Check if exists
|
||||
if user_id == "999":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail={"message": "User not found", "details": {"id": user_id}}
|
||||
)
|
||||
|
||||
return User(
|
||||
id=user_id,
|
||||
email="user@example.com",
|
||||
name="User Name",
|
||||
status=UserStatus.ACTIVE,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
@app.patch("/api/users/{user_id}", response_model=User, tags=["Users"])
|
||||
async def update_user(user_id: str, update: UserUpdate):
|
||||
"""Partially update user."""
|
||||
# Validate user exists
|
||||
existing = await get_user(user_id)
|
||||
|
||||
# Apply updates
|
||||
update_data = update.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(existing, field, value)
|
||||
|
||||
existing.updated_at = datetime.now()
|
||||
return existing
|
||||
|
||||
@app.delete("/api/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT, tags=["Users"])
|
||||
async def delete_user(user_id: str):
|
||||
"""Delete user."""
|
||||
await get_user(user_id) # Verify exists
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
+583
@@ -0,0 +1,583 @@
|
||||
# GraphQL Schema Design Patterns
|
||||
|
||||
## Schema Organization
|
||||
|
||||
### Modular Schema Structure
|
||||
|
||||
```graphql
|
||||
# user.graphql
|
||||
type User {
|
||||
id: ID!
|
||||
email: String!
|
||||
name: String!
|
||||
posts: [Post!]!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
user(id: ID!): User
|
||||
users(first: Int, after: String): UserConnection!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createUser(input: CreateUserInput!): CreateUserPayload!
|
||||
}
|
||||
|
||||
# post.graphql
|
||||
type Post {
|
||||
id: ID!
|
||||
title: String!
|
||||
content: String!
|
||||
author: User!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
post(id: ID!): Post
|
||||
}
|
||||
```
|
||||
|
||||
## Type Design Patterns
|
||||
|
||||
### 1. Non-Null Types
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
id: ID! # Always required
|
||||
email: String! # Required
|
||||
phone: String # Optional (nullable)
|
||||
posts: [Post!]! # Non-null array of non-null posts
|
||||
tags: [String!] # Nullable array of non-null strings
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Interfaces for Polymorphism
|
||||
|
||||
```graphql
|
||||
interface Node {
|
||||
id: ID!
|
||||
createdAt: DateTime!
|
||||
}
|
||||
|
||||
type User implements Node {
|
||||
id: ID!
|
||||
createdAt: DateTime!
|
||||
email: String!
|
||||
}
|
||||
|
||||
type Post implements Node {
|
||||
id: ID!
|
||||
createdAt: DateTime!
|
||||
title: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
node(id: ID!): Node
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Unions for Heterogeneous Results
|
||||
|
||||
```graphql
|
||||
union SearchResult = User | Post | Comment
|
||||
|
||||
type Query {
|
||||
search(query: String!): [SearchResult!]!
|
||||
}
|
||||
|
||||
# Query example
|
||||
{
|
||||
search(query: "graphql") {
|
||||
... on User {
|
||||
name
|
||||
email
|
||||
}
|
||||
... on Post {
|
||||
title
|
||||
content
|
||||
}
|
||||
... on Comment {
|
||||
text
|
||||
author {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Input Types
|
||||
|
||||
```graphql
|
||||
input CreateUserInput {
|
||||
email: String!
|
||||
name: String!
|
||||
password: String!
|
||||
profileInput: ProfileInput
|
||||
}
|
||||
|
||||
input ProfileInput {
|
||||
bio: String
|
||||
avatar: String
|
||||
website: String
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
id: ID!
|
||||
email: String
|
||||
name: String
|
||||
profileInput: ProfileInput
|
||||
}
|
||||
```
|
||||
|
||||
## Pagination Patterns
|
||||
|
||||
### Relay Cursor Pagination (Recommended)
|
||||
|
||||
```graphql
|
||||
type UserConnection {
|
||||
edges: [UserEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type UserEdge {
|
||||
node: User!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: String
|
||||
endCursor: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
users(first: Int, after: String, last: Int, before: String): UserConnection!
|
||||
}
|
||||
|
||||
# Usage
|
||||
{
|
||||
users(first: 10, after: "cursor123") {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Offset Pagination (Simpler)
|
||||
|
||||
```graphql
|
||||
type UserList {
|
||||
items: [User!]!
|
||||
total: Int!
|
||||
page: Int!
|
||||
pageSize: Int!
|
||||
}
|
||||
|
||||
type Query {
|
||||
users(page: Int = 1, pageSize: Int = 20): UserList!
|
||||
}
|
||||
```
|
||||
|
||||
## Mutation Design Patterns
|
||||
|
||||
### 1. Input/Payload Pattern
|
||||
|
||||
```graphql
|
||||
input CreatePostInput {
|
||||
title: String!
|
||||
content: String!
|
||||
tags: [String!]
|
||||
}
|
||||
|
||||
type CreatePostPayload {
|
||||
post: Post
|
||||
errors: [Error!]
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type Error {
|
||||
field: String
|
||||
message: String!
|
||||
code: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createPost(input: CreatePostInput!): CreatePostPayload!
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Optimistic Response Support
|
||||
|
||||
```graphql
|
||||
type UpdateUserPayload {
|
||||
user: User
|
||||
clientMutationId: String
|
||||
errors: [Error!]
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
id: ID!
|
||||
name: String
|
||||
clientMutationId: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
updateUser(input: UpdateUserInput!): UpdateUserPayload!
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Batch Mutations
|
||||
|
||||
```graphql
|
||||
input BatchCreateUserInput {
|
||||
users: [CreateUserInput!]!
|
||||
}
|
||||
|
||||
type BatchCreateUserPayload {
|
||||
results: [CreateUserResult!]!
|
||||
successCount: Int!
|
||||
errorCount: Int!
|
||||
}
|
||||
|
||||
type CreateUserResult {
|
||||
user: User
|
||||
errors: [Error!]
|
||||
index: Int!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
batchCreateUsers(input: BatchCreateUserInput!): BatchCreateUserPayload!
|
||||
}
|
||||
```
|
||||
|
||||
## Field Design
|
||||
|
||||
### Arguments and Filtering
|
||||
|
||||
```graphql
|
||||
type Query {
|
||||
posts(
|
||||
# Pagination
|
||||
first: Int = 20
|
||||
after: String
|
||||
|
||||
# Filtering
|
||||
status: PostStatus
|
||||
authorId: ID
|
||||
tag: String
|
||||
|
||||
# Sorting
|
||||
orderBy: PostOrderBy = CREATED_AT
|
||||
orderDirection: OrderDirection = DESC
|
||||
|
||||
# Searching
|
||||
search: String
|
||||
): PostConnection!
|
||||
}
|
||||
|
||||
enum PostStatus {
|
||||
DRAFT
|
||||
PUBLISHED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
enum PostOrderBy {
|
||||
CREATED_AT
|
||||
UPDATED_AT
|
||||
TITLE
|
||||
}
|
||||
|
||||
enum OrderDirection {
|
||||
ASC
|
||||
DESC
|
||||
}
|
||||
```
|
||||
|
||||
### Computed Fields
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
fullName: String! # Computed in resolver
|
||||
posts: [Post!]!
|
||||
postCount: Int! # Computed, doesn't load all posts
|
||||
}
|
||||
|
||||
type Post {
|
||||
likeCount: Int!
|
||||
commentCount: Int!
|
||||
isLikedByViewer: Boolean! # Context-dependent
|
||||
}
|
||||
```
|
||||
|
||||
## Subscriptions
|
||||
|
||||
```graphql
|
||||
type Subscription {
|
||||
postAdded: Post!
|
||||
|
||||
postUpdated(postId: ID!): Post!
|
||||
|
||||
userStatusChanged(userId: ID!): UserStatus!
|
||||
}
|
||||
|
||||
type UserStatus {
|
||||
userId: ID!
|
||||
online: Boolean!
|
||||
lastSeen: DateTime!
|
||||
}
|
||||
|
||||
# Client usage
|
||||
subscription {
|
||||
postAdded {
|
||||
id
|
||||
title
|
||||
author {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Scalars
|
||||
|
||||
```graphql
|
||||
scalar DateTime
|
||||
scalar Email
|
||||
scalar URL
|
||||
scalar JSON
|
||||
scalar Money
|
||||
|
||||
type User {
|
||||
email: Email!
|
||||
website: URL
|
||||
createdAt: DateTime!
|
||||
metadata: JSON
|
||||
}
|
||||
|
||||
type Product {
|
||||
price: Money!
|
||||
}
|
||||
```
|
||||
|
||||
## Directives
|
||||
|
||||
### Built-in Directives
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
name: String!
|
||||
email: String! @deprecated(reason: "Use emails field instead")
|
||||
emails: [String!]!
|
||||
|
||||
# Conditional inclusion
|
||||
privateData: PrivateData @include(if: $isOwner)
|
||||
}
|
||||
|
||||
# Query
|
||||
query GetUser($isOwner: Boolean!) {
|
||||
user(id: "123") {
|
||||
name
|
||||
privateData @include(if: $isOwner) {
|
||||
ssn
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Directives
|
||||
|
||||
```graphql
|
||||
directive @auth(requires: Role = USER) on FIELD_DEFINITION
|
||||
|
||||
enum Role {
|
||||
USER
|
||||
ADMIN
|
||||
MODERATOR
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
deleteUser(id: ID!): Boolean! @auth(requires: ADMIN)
|
||||
updateProfile(input: ProfileInput!): User! @auth
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Union Error Pattern
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
id: ID!
|
||||
email: String!
|
||||
}
|
||||
|
||||
type ValidationError {
|
||||
field: String!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type NotFoundError {
|
||||
message: String!
|
||||
resourceType: String!
|
||||
resourceId: ID!
|
||||
}
|
||||
|
||||
type AuthorizationError {
|
||||
message: String!
|
||||
}
|
||||
|
||||
union UserResult = User | ValidationError | NotFoundError | AuthorizationError
|
||||
|
||||
type Query {
|
||||
user(id: ID!): UserResult!
|
||||
}
|
||||
|
||||
# Usage
|
||||
{
|
||||
user(id: "123") {
|
||||
... on User {
|
||||
id
|
||||
email
|
||||
}
|
||||
... on NotFoundError {
|
||||
message
|
||||
resourceType
|
||||
}
|
||||
... on AuthorizationError {
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Errors in Payload
|
||||
|
||||
```graphql
|
||||
type CreateUserPayload {
|
||||
user: User
|
||||
errors: [Error!]
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type Error {
|
||||
field: String
|
||||
message: String!
|
||||
code: ErrorCode!
|
||||
}
|
||||
|
||||
enum ErrorCode {
|
||||
VALIDATION_ERROR
|
||||
UNAUTHORIZED
|
||||
NOT_FOUND
|
||||
INTERNAL_ERROR
|
||||
}
|
||||
```
|
||||
|
||||
## N+1 Query Problem Solutions
|
||||
|
||||
### DataLoader Pattern
|
||||
|
||||
```python
|
||||
from aiodataloader import DataLoader
|
||||
|
||||
class PostLoader(DataLoader):
|
||||
async def batch_load_fn(self, post_ids):
|
||||
posts = await db.posts.find({"id": {"$in": post_ids}})
|
||||
post_map = {post["id"]: post for post in posts}
|
||||
return [post_map.get(pid) for pid in post_ids]
|
||||
|
||||
# Resolver
|
||||
@user_type.field("posts")
|
||||
async def resolve_posts(user, info):
|
||||
loader = info.context["loaders"]["post"]
|
||||
return await loader.load_many(user["post_ids"])
|
||||
```
|
||||
|
||||
### Query Depth Limiting
|
||||
|
||||
```python
|
||||
from graphql import GraphQLError
|
||||
|
||||
def depth_limit_validator(max_depth: int):
|
||||
def validate(context, node, ancestors):
|
||||
depth = len(ancestors)
|
||||
if depth > max_depth:
|
||||
raise GraphQLError(
|
||||
f"Query depth {depth} exceeds maximum {max_depth}"
|
||||
)
|
||||
return validate
|
||||
```
|
||||
|
||||
### Query Complexity Analysis
|
||||
|
||||
```python
|
||||
def complexity_limit_validator(max_complexity: int):
|
||||
def calculate_complexity(node):
|
||||
# Each field = 1, lists multiply
|
||||
complexity = 1
|
||||
if is_list_field(node):
|
||||
complexity *= get_list_size_arg(node)
|
||||
return complexity
|
||||
|
||||
return validate_complexity
|
||||
```
|
||||
|
||||
## Schema Versioning
|
||||
|
||||
### Field Deprecation
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
name: String! @deprecated(reason: "Use firstName and lastName")
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
}
|
||||
```
|
||||
|
||||
### Schema Evolution
|
||||
|
||||
```graphql
|
||||
# v1 - Initial
|
||||
type User {
|
||||
name: String!
|
||||
}
|
||||
|
||||
# v2 - Add optional field (backward compatible)
|
||||
type User {
|
||||
name: String!
|
||||
email: String
|
||||
}
|
||||
|
||||
# v3 - Deprecate and add new field
|
||||
type User {
|
||||
name: String! @deprecated(reason: "Use firstName/lastName")
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
email: String
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Nullable vs Non-Null**: Start nullable, make non-null when guaranteed
|
||||
2. **Input Types**: Always use input types for mutations
|
||||
3. **Payload Pattern**: Return errors in mutation payloads
|
||||
4. **Pagination**: Use cursor-based for infinite scroll, offset for simple cases
|
||||
5. **Naming**: Use camelCase for fields, PascalCase for types
|
||||
6. **Deprecation**: Use `@deprecated` instead of removing fields
|
||||
7. **DataLoaders**: Always use for relationships to prevent N+1
|
||||
8. **Complexity Limits**: Protect against expensive queries
|
||||
9. **Custom Scalars**: Use for domain-specific types (Email, DateTime)
|
||||
10. **Documentation**: Document all fields with descriptions
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
# REST API Best Practices
|
||||
|
||||
## URL Structure
|
||||
|
||||
### Resource Naming
|
||||
|
||||
```
|
||||
# Good - Plural nouns
|
||||
GET /api/users
|
||||
GET /api/orders
|
||||
GET /api/products
|
||||
|
||||
# Bad - Verbs or mixed conventions
|
||||
GET /api/getUser
|
||||
GET /api/user (inconsistent singular)
|
||||
POST /api/createOrder
|
||||
```
|
||||
|
||||
### Nested Resources
|
||||
|
||||
```
|
||||
# Shallow nesting (preferred)
|
||||
GET /api/users/{id}/orders
|
||||
GET /api/orders/{id}
|
||||
|
||||
# Deep nesting (avoid)
|
||||
GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews
|
||||
# Better:
|
||||
GET /api/order-items/{id}/reviews
|
||||
```
|
||||
|
||||
## HTTP Methods and Status Codes
|
||||
|
||||
### GET - Retrieve Resources
|
||||
|
||||
```
|
||||
GET /api/users → 200 OK (with list)
|
||||
GET /api/users/{id} → 200 OK or 404 Not Found
|
||||
GET /api/users?page=2 → 200 OK (paginated)
|
||||
```
|
||||
|
||||
### POST - Create Resources
|
||||
|
||||
```
|
||||
POST /api/users
|
||||
Body: {"name": "John", "email": "john@example.com"}
|
||||
→ 201 Created
|
||||
Location: /api/users/123
|
||||
Body: {"id": "123", "name": "John", ...}
|
||||
|
||||
POST /api/users (validation error)
|
||||
→ 422 Unprocessable Entity
|
||||
Body: {"errors": [...]}
|
||||
```
|
||||
|
||||
### PUT - Replace Resources
|
||||
|
||||
```
|
||||
PUT /api/users/{id}
|
||||
Body: {complete user object}
|
||||
→ 200 OK (updated)
|
||||
→ 404 Not Found (doesn't exist)
|
||||
|
||||
# Must include ALL fields
|
||||
```
|
||||
|
||||
### PATCH - Partial Update
|
||||
|
||||
```
|
||||
PATCH /api/users/{id}
|
||||
Body: {"name": "Jane"} (only changed fields)
|
||||
→ 200 OK
|
||||
→ 404 Not Found
|
||||
```
|
||||
|
||||
### DELETE - Remove Resources
|
||||
|
||||
```
|
||||
DELETE /api/users/{id}
|
||||
→ 204 No Content (deleted)
|
||||
→ 404 Not Found
|
||||
→ 409 Conflict (can't delete due to references)
|
||||
```
|
||||
|
||||
## Filtering, Sorting, and Searching
|
||||
|
||||
### Query Parameters
|
||||
|
||||
```
|
||||
# Filtering
|
||||
GET /api/users?status=active
|
||||
GET /api/users?role=admin&status=active
|
||||
|
||||
# Sorting
|
||||
GET /api/users?sort=created_at
|
||||
GET /api/users?sort=-created_at (descending)
|
||||
GET /api/users?sort=name,created_at
|
||||
|
||||
# Searching
|
||||
GET /api/users?search=john
|
||||
GET /api/users?q=john
|
||||
|
||||
# Field selection (sparse fieldsets)
|
||||
GET /api/users?fields=id,name,email
|
||||
```
|
||||
|
||||
## Pagination Patterns
|
||||
|
||||
### Offset-Based Pagination
|
||||
|
||||
```python
|
||||
GET /api/users?page=2&page_size=20
|
||||
|
||||
Response:
|
||||
{
|
||||
"items": [...],
|
||||
"page": 2,
|
||||
"page_size": 20,
|
||||
"total": 150,
|
||||
"pages": 8
|
||||
}
|
||||
```
|
||||
|
||||
### Cursor-Based Pagination (for large datasets)
|
||||
|
||||
```python
|
||||
GET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ
|
||||
|
||||
Response:
|
||||
{
|
||||
"items": [...],
|
||||
"next_cursor": "eyJpZCI6MTQzfQ",
|
||||
"has_more": true
|
||||
}
|
||||
```
|
||||
|
||||
### Link Header Pagination (RESTful)
|
||||
|
||||
```
|
||||
GET /api/users?page=2
|
||||
|
||||
Response Headers:
|
||||
Link: <https://api.example.com/users?page=3>; rel="next",
|
||||
<https://api.example.com/users?page=1>; rel="prev",
|
||||
<https://api.example.com/users?page=1>; rel="first",
|
||||
<https://api.example.com/users?page=8>; rel="last"
|
||||
```
|
||||
|
||||
## Versioning Strategies
|
||||
|
||||
### URL Versioning (Recommended)
|
||||
|
||||
```
|
||||
/api/v1/users
|
||||
/api/v2/users
|
||||
|
||||
Pros: Clear, easy to route
|
||||
Cons: Multiple URLs for same resource
|
||||
```
|
||||
|
||||
### Header Versioning
|
||||
|
||||
```
|
||||
GET /api/users
|
||||
Accept: application/vnd.api+json; version=2
|
||||
|
||||
Pros: Clean URLs
|
||||
Cons: Less visible, harder to test
|
||||
```
|
||||
|
||||
### Query Parameter
|
||||
|
||||
```
|
||||
GET /api/users?version=2
|
||||
|
||||
Pros: Easy to test
|
||||
Cons: Optional parameter can be forgotten
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
### Headers
|
||||
|
||||
```
|
||||
X-RateLimit-Limit: 1000
|
||||
X-RateLimit-Remaining: 742
|
||||
X-RateLimit-Reset: 1640000000
|
||||
|
||||
Response when limited:
|
||||
429 Too Many Requests
|
||||
Retry-After: 3600
|
||||
```
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
```python
|
||||
from fastapi import HTTPException, Request
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class RateLimiter:
|
||||
def __init__(self, calls: int, period: int):
|
||||
self.calls = calls
|
||||
self.period = period
|
||||
self.cache = {}
|
||||
|
||||
def check(self, key: str) -> bool:
|
||||
now = datetime.now()
|
||||
if key not in self.cache:
|
||||
self.cache[key] = []
|
||||
|
||||
# Remove old requests
|
||||
self.cache[key] = [
|
||||
ts for ts in self.cache[key]
|
||||
if now - ts < timedelta(seconds=self.period)
|
||||
]
|
||||
|
||||
if len(self.cache[key]) >= self.calls:
|
||||
return False
|
||||
|
||||
self.cache[key].append(now)
|
||||
return True
|
||||
|
||||
limiter = RateLimiter(calls=100, period=60)
|
||||
|
||||
@app.get("/api/users")
|
||||
async def get_users(request: Request):
|
||||
if not limiter.check(request.client.host):
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
headers={"Retry-After": "60"}
|
||||
)
|
||||
return {"users": [...]}
|
||||
```
|
||||
|
||||
## Authentication and Authorization
|
||||
|
||||
### Bearer Token
|
||||
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
|
||||
|
||||
401 Unauthorized - Missing/invalid token
|
||||
403 Forbidden - Valid token, insufficient permissions
|
||||
```
|
||||
|
||||
### API Keys
|
||||
|
||||
```
|
||||
X-API-Key: your-api-key-here
|
||||
```
|
||||
|
||||
## Error Response Format
|
||||
|
||||
### Consistent Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Request validation failed",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"message": "Invalid email format",
|
||||
"value": "not-an-email"
|
||||
}
|
||||
],
|
||||
"timestamp": "2025-10-16T12:00:00Z",
|
||||
"path": "/api/users"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Status Code Guidelines
|
||||
|
||||
- `200 OK`: Successful GET, PATCH, PUT
|
||||
- `201 Created`: Successful POST
|
||||
- `204 No Content`: Successful DELETE
|
||||
- `400 Bad Request`: Malformed request
|
||||
- `401 Unauthorized`: Authentication required
|
||||
- `403 Forbidden`: Authenticated but not authorized
|
||||
- `404 Not Found`: Resource doesn't exist
|
||||
- `409 Conflict`: State conflict (duplicate email, etc.)
|
||||
- `422 Unprocessable Entity`: Validation errors
|
||||
- `429 Too Many Requests`: Rate limited
|
||||
- `500 Internal Server Error`: Server error
|
||||
- `503 Service Unavailable`: Temporary downtime
|
||||
|
||||
## Caching
|
||||
|
||||
### Cache Headers
|
||||
|
||||
```
|
||||
# Client caching
|
||||
Cache-Control: public, max-age=3600
|
||||
|
||||
# No caching
|
||||
Cache-Control: no-cache, no-store, must-revalidate
|
||||
|
||||
# Conditional requests
|
||||
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
|
||||
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
|
||||
→ 304 Not Modified
|
||||
```
|
||||
|
||||
## Bulk Operations
|
||||
|
||||
### Batch Endpoints
|
||||
|
||||
```python
|
||||
POST /api/users/batch
|
||||
{
|
||||
"items": [
|
||||
{"name": "User1", "email": "user1@example.com"},
|
||||
{"name": "User2", "email": "user2@example.com"}
|
||||
]
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"results": [
|
||||
{"id": "1", "status": "created"},
|
||||
{"id": null, "status": "failed", "error": "Email already exists"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Idempotency
|
||||
|
||||
### Idempotency Keys
|
||||
|
||||
```
|
||||
POST /api/orders
|
||||
Idempotency-Key: unique-key-123
|
||||
|
||||
If duplicate request:
|
||||
→ 200 OK (return cached response)
|
||||
```
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
```python
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["https://example.com"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
```
|
||||
|
||||
## Documentation with OpenAPI
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(
|
||||
title="My API",
|
||||
description="API for managing users",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc"
|
||||
)
|
||||
|
||||
@app.get(
|
||||
"/api/users/{user_id}",
|
||||
summary="Get user by ID",
|
||||
response_description="User details",
|
||||
tags=["Users"]
|
||||
)
|
||||
async def get_user(
|
||||
user_id: str = Path(..., description="The user ID")
|
||||
):
|
||||
"""
|
||||
Retrieve user by ID.
|
||||
|
||||
Returns full user profile including:
|
||||
- Basic information
|
||||
- Contact details
|
||||
- Account status
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
## Health and Monitoring Endpoints
|
||||
|
||||
```python
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"version": "1.0.0",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@app.get("/health/detailed")
|
||||
async def detailed_health():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"checks": {
|
||||
"database": await check_database(),
|
||||
"redis": await check_redis(),
|
||||
"external_api": await check_external_api()
|
||||
}
|
||||
}
|
||||
```
|
||||
+513
@@ -0,0 +1,513 @@
|
||||
# API Design Principles Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. RESTful Design Principles
|
||||
|
||||
**Resource-Oriented Architecture**
|
||||
|
||||
- Resources are nouns (users, orders, products), not verbs
|
||||
- Use HTTP methods for actions (GET, POST, PUT, PATCH, DELETE)
|
||||
- URLs represent resource hierarchies
|
||||
- Consistent naming conventions
|
||||
|
||||
**HTTP Methods Semantics:**
|
||||
|
||||
- `GET`: Retrieve resources (idempotent, safe)
|
||||
- `POST`: Create new resources
|
||||
- `PUT`: Replace entire resource (idempotent)
|
||||
- `PATCH`: Partial resource updates
|
||||
- `DELETE`: Remove resources (idempotent)
|
||||
|
||||
### 2. GraphQL Design Principles
|
||||
|
||||
**Schema-First Development**
|
||||
|
||||
- Types define your domain model
|
||||
- Queries for reading data
|
||||
- Mutations for modifying data
|
||||
- Subscriptions for real-time updates
|
||||
|
||||
**Query Structure:**
|
||||
|
||||
- Clients request exactly what they need
|
||||
- Single endpoint, multiple operations
|
||||
- Strongly typed schema
|
||||
- Introspection built-in
|
||||
|
||||
### 3. API Versioning Strategies
|
||||
|
||||
**URL Versioning:**
|
||||
|
||||
```
|
||||
/api/v1/users
|
||||
/api/v2/users
|
||||
```
|
||||
|
||||
**Header Versioning:**
|
||||
|
||||
```
|
||||
Accept: application/vnd.api+json; version=1
|
||||
```
|
||||
|
||||
**Query Parameter Versioning:**
|
||||
|
||||
```
|
||||
/api/users?version=1
|
||||
```
|
||||
|
||||
## REST API Design Patterns
|
||||
|
||||
### Pattern 1: Resource Collection Design
|
||||
|
||||
```python
|
||||
# Good: Resource-oriented endpoints
|
||||
GET /api/users # List users (with pagination)
|
||||
POST /api/users # Create user
|
||||
GET /api/users/{id} # Get specific user
|
||||
PUT /api/users/{id} # Replace user
|
||||
PATCH /api/users/{id} # Update user fields
|
||||
DELETE /api/users/{id} # Delete user
|
||||
|
||||
# Nested resources
|
||||
GET /api/users/{id}/orders # Get user's orders
|
||||
POST /api/users/{id}/orders # Create order for user
|
||||
|
||||
# Bad: Action-oriented endpoints (avoid)
|
||||
POST /api/createUser
|
||||
POST /api/getUserById
|
||||
POST /api/deleteUser
|
||||
```
|
||||
|
||||
### Pattern 2: Pagination and Filtering
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class PaginationParams(BaseModel):
|
||||
page: int = Field(1, ge=1, description="Page number")
|
||||
page_size: int = Field(20, ge=1, le=100, description="Items per page")
|
||||
|
||||
class FilterParams(BaseModel):
|
||||
status: Optional[str] = None
|
||||
created_after: Optional[str] = None
|
||||
search: Optional[str] = None
|
||||
|
||||
class PaginatedResponse(BaseModel):
|
||||
items: List[dict]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
pages: int
|
||||
|
||||
@property
|
||||
def has_next(self) -> bool:
|
||||
return self.page < self.pages
|
||||
|
||||
@property
|
||||
def has_prev(self) -> bool:
|
||||
return self.page > 1
|
||||
|
||||
# FastAPI endpoint example
|
||||
from fastapi import FastAPI, Query, Depends
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/api/users", response_model=PaginatedResponse)
|
||||
async def list_users(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
status: Optional[str] = Query(None),
|
||||
search: Optional[str] = Query(None)
|
||||
):
|
||||
# Apply filters
|
||||
query = build_query(status=status, search=search)
|
||||
|
||||
# Count total
|
||||
total = await count_users(query)
|
||||
|
||||
# Fetch page
|
||||
offset = (page - 1) * page_size
|
||||
users = await fetch_users(query, limit=page_size, offset=offset)
|
||||
|
||||
return PaginatedResponse(
|
||||
items=users,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
pages=(total + page_size - 1) // page_size
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 3: Error Handling and Status Codes
|
||||
|
||||
```python
|
||||
from fastapi import HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
error: str
|
||||
message: str
|
||||
details: Optional[dict] = None
|
||||
timestamp: str
|
||||
path: str
|
||||
|
||||
class ValidationErrorDetail(BaseModel):
|
||||
field: str
|
||||
message: str
|
||||
value: Any
|
||||
|
||||
# Consistent error responses
|
||||
STATUS_CODES = {
|
||||
"success": 200,
|
||||
"created": 201,
|
||||
"no_content": 204,
|
||||
"bad_request": 400,
|
||||
"unauthorized": 401,
|
||||
"forbidden": 403,
|
||||
"not_found": 404,
|
||||
"conflict": 409,
|
||||
"unprocessable": 422,
|
||||
"internal_error": 500
|
||||
}
|
||||
|
||||
def raise_not_found(resource: str, id: str):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail={
|
||||
"error": "NotFound",
|
||||
"message": f"{resource} not found",
|
||||
"details": {"id": id}
|
||||
}
|
||||
)
|
||||
|
||||
def raise_validation_error(errors: List[ValidationErrorDetail]):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail={
|
||||
"error": "ValidationError",
|
||||
"message": "Request validation failed",
|
||||
"details": {"errors": [e.dict() for e in errors]}
|
||||
}
|
||||
)
|
||||
|
||||
# Example usage
|
||||
@app.get("/api/users/{user_id}")
|
||||
async def get_user(user_id: str):
|
||||
user = await fetch_user(user_id)
|
||||
if not user:
|
||||
raise_not_found("User", user_id)
|
||||
return user
|
||||
```
|
||||
|
||||
### Pattern 4: HATEOAS (Hypermedia as the Engine of Application State)
|
||||
|
||||
```python
|
||||
class UserResponse(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
email: str
|
||||
_links: dict
|
||||
|
||||
@classmethod
|
||||
def from_user(cls, user: User, base_url: str):
|
||||
return cls(
|
||||
id=user.id,
|
||||
name=user.name,
|
||||
email=user.email,
|
||||
_links={
|
||||
"self": {"href": f"{base_url}/api/users/{user.id}"},
|
||||
"orders": {"href": f"{base_url}/api/users/{user.id}/orders"},
|
||||
"update": {
|
||||
"href": f"{base_url}/api/users/{user.id}",
|
||||
"method": "PATCH"
|
||||
},
|
||||
"delete": {
|
||||
"href": f"{base_url}/api/users/{user.id}",
|
||||
"method": "DELETE"
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## GraphQL Design Patterns
|
||||
|
||||
### Pattern 1: Schema Design
|
||||
|
||||
```graphql
|
||||
# schema.graphql
|
||||
|
||||
# Clear type definitions
|
||||
type User {
|
||||
id: ID!
|
||||
email: String!
|
||||
name: String!
|
||||
createdAt: DateTime!
|
||||
|
||||
# Relationships
|
||||
orders(first: Int = 20, after: String, status: OrderStatus): OrderConnection!
|
||||
|
||||
profile: UserProfile
|
||||
}
|
||||
|
||||
type Order {
|
||||
id: ID!
|
||||
status: OrderStatus!
|
||||
total: Money!
|
||||
items: [OrderItem!]!
|
||||
createdAt: DateTime!
|
||||
|
||||
# Back-reference
|
||||
user: User!
|
||||
}
|
||||
|
||||
# Pagination pattern (Relay-style)
|
||||
type OrderConnection {
|
||||
edges: [OrderEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type OrderEdge {
|
||||
node: Order!
|
||||
cursor: String!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: String
|
||||
endCursor: String
|
||||
}
|
||||
|
||||
# Enums for type safety
|
||||
enum OrderStatus {
|
||||
PENDING
|
||||
CONFIRMED
|
||||
SHIPPED
|
||||
DELIVERED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
# Custom scalars
|
||||
scalar DateTime
|
||||
scalar Money
|
||||
|
||||
# Query root
|
||||
type Query {
|
||||
user(id: ID!): User
|
||||
users(first: Int = 20, after: String, search: String): UserConnection!
|
||||
|
||||
order(id: ID!): Order
|
||||
}
|
||||
|
||||
# Mutation root
|
||||
type Mutation {
|
||||
createUser(input: CreateUserInput!): CreateUserPayload!
|
||||
updateUser(input: UpdateUserInput!): UpdateUserPayload!
|
||||
deleteUser(id: ID!): DeleteUserPayload!
|
||||
|
||||
createOrder(input: CreateOrderInput!): CreateOrderPayload!
|
||||
}
|
||||
|
||||
# Input types for mutations
|
||||
input CreateUserInput {
|
||||
email: String!
|
||||
name: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
# Payload types for mutations
|
||||
type CreateUserPayload {
|
||||
user: User
|
||||
errors: [Error!]
|
||||
}
|
||||
|
||||
type Error {
|
||||
field: String
|
||||
message: String!
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Resolver Design
|
||||
|
||||
```python
|
||||
from typing import Optional, List
|
||||
from ariadne import QueryType, MutationType, ObjectType
|
||||
from dataclasses import dataclass
|
||||
|
||||
query = QueryType()
|
||||
mutation = MutationType()
|
||||
user_type = ObjectType("User")
|
||||
|
||||
@query.field("user")
|
||||
async def resolve_user(obj, info, id: str) -> Optional[dict]:
|
||||
"""Resolve single user by ID."""
|
||||
return await fetch_user_by_id(id)
|
||||
|
||||
@query.field("users")
|
||||
async def resolve_users(
|
||||
obj,
|
||||
info,
|
||||
first: int = 20,
|
||||
after: Optional[str] = None,
|
||||
search: Optional[str] = None
|
||||
) -> dict:
|
||||
"""Resolve paginated user list."""
|
||||
# Decode cursor
|
||||
offset = decode_cursor(after) if after else 0
|
||||
|
||||
# Fetch users
|
||||
users = await fetch_users(
|
||||
limit=first + 1, # Fetch one extra to check hasNextPage
|
||||
offset=offset,
|
||||
search=search
|
||||
)
|
||||
|
||||
# Pagination
|
||||
has_next = len(users) > first
|
||||
if has_next:
|
||||
users = users[:first]
|
||||
|
||||
edges = [
|
||||
{
|
||||
"node": user,
|
||||
"cursor": encode_cursor(offset + i)
|
||||
}
|
||||
for i, user in enumerate(users)
|
||||
]
|
||||
|
||||
return {
|
||||
"edges": edges,
|
||||
"pageInfo": {
|
||||
"hasNextPage": has_next,
|
||||
"hasPreviousPage": offset > 0,
|
||||
"startCursor": edges[0]["cursor"] if edges else None,
|
||||
"endCursor": edges[-1]["cursor"] if edges else None
|
||||
},
|
||||
"totalCount": await count_users(search=search)
|
||||
}
|
||||
|
||||
@user_type.field("orders")
|
||||
async def resolve_user_orders(user: dict, info, first: int = 20) -> dict:
|
||||
"""Resolve user's orders (N+1 prevention with DataLoader)."""
|
||||
# Use DataLoader to batch requests
|
||||
loader = info.context["loaders"]["orders_by_user"]
|
||||
orders = await loader.load(user["id"])
|
||||
|
||||
return paginate_orders(orders, first)
|
||||
|
||||
@mutation.field("createUser")
|
||||
async def resolve_create_user(obj, info, input: dict) -> dict:
|
||||
"""Create new user."""
|
||||
try:
|
||||
# Validate input
|
||||
validate_user_input(input)
|
||||
|
||||
# Create user
|
||||
user = await create_user(
|
||||
email=input["email"],
|
||||
name=input["name"],
|
||||
password=hash_password(input["password"])
|
||||
)
|
||||
|
||||
return {
|
||||
"user": user,
|
||||
"errors": []
|
||||
}
|
||||
except ValidationError as e:
|
||||
return {
|
||||
"user": None,
|
||||
"errors": [{"field": e.field, "message": e.message}]
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: DataLoader (N+1 Problem Prevention)
|
||||
|
||||
```python
|
||||
from aiodataloader import DataLoader
|
||||
from typing import List, Optional
|
||||
|
||||
class UserLoader(DataLoader):
|
||||
"""Batch load users by ID."""
|
||||
|
||||
async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:
|
||||
"""Load multiple users in single query."""
|
||||
users = await fetch_users_by_ids(user_ids)
|
||||
|
||||
# Map results back to input order
|
||||
user_map = {user["id"]: user for user in users}
|
||||
return [user_map.get(user_id) for user_id in user_ids]
|
||||
|
||||
class OrdersByUserLoader(DataLoader):
|
||||
"""Batch load orders by user ID."""
|
||||
|
||||
async def batch_load_fn(self, user_ids: List[str]) -> List[List[dict]]:
|
||||
"""Load orders for multiple users in single query."""
|
||||
orders = await fetch_orders_by_user_ids(user_ids)
|
||||
|
||||
# Group orders by user_id
|
||||
orders_by_user = {}
|
||||
for order in orders:
|
||||
user_id = order["user_id"]
|
||||
if user_id not in orders_by_user:
|
||||
orders_by_user[user_id] = []
|
||||
orders_by_user[user_id].append(order)
|
||||
|
||||
# Return in input order
|
||||
return [orders_by_user.get(user_id, []) for user_id in user_ids]
|
||||
|
||||
# Context setup
|
||||
def create_context():
|
||||
return {
|
||||
"loaders": {
|
||||
"user": UserLoader(),
|
||||
"orders_by_user": OrdersByUserLoader()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### REST APIs
|
||||
|
||||
1. **Consistent Naming**: Use plural nouns for collections (`/users`, not `/user`)
|
||||
2. **Stateless**: Each request contains all necessary information
|
||||
3. **Use HTTP Status Codes Correctly**: 2xx success, 4xx client errors, 5xx server errors
|
||||
4. **Version Your API**: Plan for breaking changes from day one
|
||||
5. **Pagination**: Always paginate large collections
|
||||
6. **Rate Limiting**: Protect your API with rate limits
|
||||
7. **Documentation**: Use OpenAPI/Swagger for interactive docs
|
||||
|
||||
### GraphQL APIs
|
||||
|
||||
1. **Schema First**: Design schema before writing resolvers
|
||||
2. **Avoid N+1**: Use DataLoaders for efficient data fetching
|
||||
3. **Input Validation**: Validate at schema and resolver levels
|
||||
4. **Error Handling**: Return structured errors in mutation payloads
|
||||
5. **Pagination**: Use cursor-based pagination (Relay spec)
|
||||
6. **Deprecation**: Use `@deprecated` directive for gradual migration
|
||||
7. **Monitoring**: Track query complexity and execution time
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Over-fetching/Under-fetching (REST)**: Fixed in GraphQL but requires DataLoaders
|
||||
- **Breaking Changes**: Version APIs or use deprecation strategies
|
||||
- **Inconsistent Error Formats**: Standardize error responses
|
||||
- **Missing Rate Limits**: APIs without limits are vulnerable to abuse
|
||||
- **Poor Documentation**: Undocumented APIs frustrate developers
|
||||
- **Ignoring HTTP Semantics**: POST for idempotent operations breaks expectations
|
||||
- **Tight Coupling**: API structure shouldn't mirror database schema
|
||||
|
||||
## Resources
|
||||
|
||||
- **references/rest-best-practices.md**: Comprehensive REST API design guide
|
||||
- **references/graphql-schema-design.md**: GraphQL schema patterns and anti-patterns
|
||||
- **references/api-versioning-strategies.md**: Versioning approaches and migration paths
|
||||
- **assets/rest-api-template.py**: FastAPI REST API template
|
||||
- **assets/graphql-schema-template.graphql**: Complete GraphQL schema example
|
||||
- **assets/api-design-checklist.md**: Pre-implementation review checklist
|
||||
- **scripts/openapi-generator.py**: Generate OpenAPI specs from code
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
---
|
||||
name: api-documentation
|
||||
description: "API documentation workflow for generating OpenAPI specs, creating developer guides, and maintaining comprehensive API documentation."
|
||||
category: granular-workflow-bundle
|
||||
risk: safe
|
||||
source: personal
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# API Documentation Workflow
|
||||
|
||||
## Overview
|
||||
|
||||
Specialized workflow for creating comprehensive API documentation including OpenAPI/Swagger specs, developer guides, code examples, and interactive documentation.
|
||||
|
||||
## When to Use This Workflow
|
||||
|
||||
Use this workflow when:
|
||||
- Creating API documentation
|
||||
- Generating OpenAPI specs
|
||||
- Writing developer guides
|
||||
- Adding code examples
|
||||
- Setting up API portals
|
||||
|
||||
## Workflow Phases
|
||||
|
||||
### Phase 1: API Discovery
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - API documentation
|
||||
- `api-design-principles` - API design
|
||||
|
||||
#### Actions
|
||||
1. Inventory endpoints
|
||||
2. Document request/response
|
||||
3. Identify authentication
|
||||
4. Map error codes
|
||||
5. Note rate limits
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to discover and document API endpoints
|
||||
```
|
||||
|
||||
### Phase 2: OpenAPI Specification
|
||||
|
||||
#### Skills to Invoke
|
||||
- `openapi-spec-generation` - OpenAPI
|
||||
- `api-documenter` - API specs
|
||||
|
||||
#### Actions
|
||||
1. Create OpenAPI schema
|
||||
2. Define paths
|
||||
3. Add schemas
|
||||
4. Configure security
|
||||
5. Add examples
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @openapi-spec-generation to create OpenAPI specification
|
||||
```
|
||||
|
||||
### Phase 3: Developer Guide
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documentation-generator` - Documentation
|
||||
- `documentation-templates` - Templates
|
||||
|
||||
#### Actions
|
||||
1. Create getting started
|
||||
2. Write authentication guide
|
||||
3. Document common patterns
|
||||
4. Add troubleshooting
|
||||
5. Create FAQ
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documentation-generator to create developer guide
|
||||
```
|
||||
|
||||
### Phase 4: Code Examples
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - Code examples
|
||||
- `tutorial-engineer` - Tutorials
|
||||
|
||||
#### Actions
|
||||
1. Create example requests
|
||||
2. Write SDK examples
|
||||
3. Add curl examples
|
||||
4. Create tutorials
|
||||
5. Test examples
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to generate code examples
|
||||
```
|
||||
|
||||
### Phase 5: Interactive Docs
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - Interactive docs
|
||||
|
||||
#### Actions
|
||||
1. Set up Swagger UI
|
||||
2. Configure Redoc
|
||||
3. Add try-it functionality
|
||||
4. Test interactivity
|
||||
5. Deploy docs
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to set up interactive documentation
|
||||
```
|
||||
|
||||
### Phase 6: Documentation Site
|
||||
|
||||
#### Skills to Invoke
|
||||
- `docs-architect` - Documentation architecture
|
||||
- `wiki-page-writer` - Documentation
|
||||
|
||||
#### Actions
|
||||
1. Choose platform
|
||||
2. Design structure
|
||||
3. Create pages
|
||||
4. Add navigation
|
||||
5. Configure search
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @docs-architect to design API documentation site
|
||||
```
|
||||
|
||||
### Phase 7: Maintenance
|
||||
|
||||
#### Skills to Invoke
|
||||
- `api-documenter` - Doc maintenance
|
||||
|
||||
#### Actions
|
||||
1. Set up auto-generation
|
||||
2. Configure validation
|
||||
3. Add review process
|
||||
4. Schedule updates
|
||||
5. Monitor feedback
|
||||
|
||||
#### Copy-Paste Prompts
|
||||
```
|
||||
Use @api-documenter to set up automated doc generation
|
||||
```
|
||||
|
||||
## Quality Gates
|
||||
|
||||
- [ ] OpenAPI spec complete
|
||||
- [ ] Developer guide written
|
||||
- [ ] Code examples working
|
||||
- [ ] Interactive docs functional
|
||||
- [ ] Documentation deployed
|
||||
|
||||
## Related Workflow Bundles
|
||||
|
||||
- `documentation` - Documentation
|
||||
- `api-development` - API development
|
||||
- `development` - Development
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# API Endpoint Builder
|
||||
|
||||
Creates a complete and production-ready REST API endpoint with validation, error handling, authentication, and documentation.
|
||||
|
||||
## What It Does
|
||||
|
||||
This skill creates a complete and production-ready REST API endpoint that includes all the necessary components, such as a route handler with the correct HTTP methods, validation, authentication, error handling, response formatting, and documentation.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Use @api-endpoint-builder to create a user registration endpoint
|
||||
```
|
||||
|
||||
This skill creates a complete and production-ready REST API endpoint that includes all the necessary components, such as a route handler with the correct HTTP methods, validation, authentication, error handling, response formatting, and documentation.
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
---
|
||||
name: api-endpoint-builder
|
||||
description: "Builds production-ready REST API endpoints with validation, error handling, authentication, and documentation. Follows best practices for security and scalability."
|
||||
category: development
|
||||
risk: safe
|
||||
source: community
|
||||
date_added: "2026-03-05"
|
||||
---
|
||||
|
||||
# API Endpoint Builder
|
||||
|
||||
Build complete, production-ready REST API endpoints with proper validation, error handling, authentication, and documentation.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User asks to "create an API endpoint" or "build a REST API"
|
||||
- Building new backend features
|
||||
- Adding endpoints to existing APIs
|
||||
- User mentions "API", "endpoint", "route", or "REST"
|
||||
- Creating CRUD operations
|
||||
|
||||
## What You'll Build
|
||||
|
||||
For each endpoint, you create:
|
||||
- Route handler with proper HTTP method
|
||||
- Input validation (request body, params, query)
|
||||
- Authentication/authorization checks
|
||||
- Business logic
|
||||
- Error handling
|
||||
- Response formatting
|
||||
- API documentation
|
||||
- Tests (if requested)
|
||||
|
||||
## Endpoint Structure
|
||||
|
||||
### 1. Route Definition
|
||||
|
||||
```javascript
|
||||
// Express example
|
||||
router.post('/api/users', authenticate, validateUser, createUser);
|
||||
|
||||
// Fastify example
|
||||
fastify.post('/api/users', {
|
||||
preHandler: [authenticate],
|
||||
schema: userSchema
|
||||
}, createUser);
|
||||
```
|
||||
|
||||
### 2. Input Validation
|
||||
|
||||
Always validate before processing:
|
||||
|
||||
```javascript
|
||||
const validateUser = (req, res, next) => {
|
||||
const { email, name, password } = req.body;
|
||||
|
||||
if (!email || !email.includes('@')) {
|
||||
return res.status(400).json({ error: 'Valid email required' });
|
||||
}
|
||||
|
||||
if (!name || name.length < 2) {
|
||||
return res.status(400).json({ error: 'Name must be at least 2 characters' });
|
||||
}
|
||||
|
||||
if (!password || password.length < 8) {
|
||||
return res.status(400).json({ error: 'Password must be at least 8 characters' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Handler Implementation
|
||||
|
||||
```javascript
|
||||
const createUser = async (req, res) => {
|
||||
try {
|
||||
const { email, name, password } = req.body;
|
||||
|
||||
// Check if user exists
|
||||
const existing = await db.users.findOne({ email });
|
||||
if (existing) {
|
||||
return res.status(409).json({ error: 'User already exists' });
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user
|
||||
const user = await db.users.create({
|
||||
email,
|
||||
name,
|
||||
password: hashedPassword,
|
||||
createdAt: new Date()
|
||||
});
|
||||
|
||||
// Don't return password
|
||||
const { password: _, ...userWithoutPassword } = user;
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: userWithoutPassword
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Create user error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### HTTP Status Codes
|
||||
- `200` - Success (GET, PUT, PATCH)
|
||||
- `201` - Created (POST)
|
||||
- `204` - No Content (DELETE)
|
||||
- `400` - Bad Request (validation failed)
|
||||
- `401` - Unauthorized (not authenticated)
|
||||
- `403` - Forbidden (not authorized)
|
||||
- `404` - Not Found
|
||||
- `409` - Conflict (duplicate)
|
||||
- `500` - Internal Server Error
|
||||
|
||||
### Response Format
|
||||
|
||||
Consistent structure:
|
||||
|
||||
```javascript
|
||||
// Success
|
||||
{
|
||||
"success": true,
|
||||
"data": { ... }
|
||||
}
|
||||
|
||||
// Error
|
||||
{
|
||||
"error": "Error message",
|
||||
"details": { ... } // optional
|
||||
}
|
||||
|
||||
// List with pagination
|
||||
{
|
||||
"success": true,
|
||||
"data": [...],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 20,
|
||||
"total": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security Checklist
|
||||
|
||||
- [ ] Authentication required for protected routes
|
||||
- [ ] Authorization checks (user owns resource)
|
||||
- [ ] Input validation on all fields
|
||||
- [ ] SQL injection prevention (use parameterized queries)
|
||||
- [ ] Rate limiting on public endpoints
|
||||
- [ ] No sensitive data in responses (passwords, tokens)
|
||||
- [ ] CORS configured properly
|
||||
- [ ] Request size limits set
|
||||
|
||||
### Error Handling
|
||||
|
||||
```javascript
|
||||
// Centralized error handler
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
|
||||
// Don't leak error details in production
|
||||
const message = process.env.NODE_ENV === 'production'
|
||||
? 'Internal server error'
|
||||
: err.message;
|
||||
|
||||
res.status(err.status || 500).json({ error: message });
|
||||
});
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### CRUD Operations
|
||||
|
||||
```javascript
|
||||
// Create
|
||||
POST /api/resources
|
||||
Body: { name, description }
|
||||
|
||||
// Read (list)
|
||||
GET /api/resources?page=1&limit=20
|
||||
|
||||
// Read (single)
|
||||
GET /api/resources/:id
|
||||
|
||||
// Update
|
||||
PUT /api/resources/:id
|
||||
Body: { name, description }
|
||||
|
||||
// Delete
|
||||
DELETE /api/resources/:id
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
```javascript
|
||||
const getResources = async (req, res) => {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 20;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [resources, total] = await Promise.all([
|
||||
db.resources.find().skip(skip).limit(limit),
|
||||
db.resources.countDocuments()
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: resources,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
pages: Math.ceil(total / limit)
|
||||
}
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Filtering & Sorting
|
||||
|
||||
```javascript
|
||||
const getResources = async (req, res) => {
|
||||
const { status, sort = '-createdAt' } = req.query;
|
||||
|
||||
const filter = {};
|
||||
if (status) filter.status = status;
|
||||
|
||||
const resources = await db.resources
|
||||
.find(filter)
|
||||
.sort(sort)
|
||||
.limit(20);
|
||||
|
||||
res.json({ success: true, data: resources });
|
||||
};
|
||||
```
|
||||
|
||||
## Documentation Template
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* @route POST /api/users
|
||||
* @desc Create a new user
|
||||
* @access Public
|
||||
*
|
||||
* @body {string} email - User email (required)
|
||||
* @body {string} name - User name (required)
|
||||
* @body {string} password - Password, min 8 chars (required)
|
||||
*
|
||||
* @returns {201} User created successfully
|
||||
* @returns {400} Validation error
|
||||
* @returns {409} User already exists
|
||||
* @returns {500} Server error
|
||||
*
|
||||
* @example
|
||||
* POST /api/users
|
||||
* {
|
||||
* "email": "user@example.com",
|
||||
* "name": "John Doe",
|
||||
* "password": "securepass123"
|
||||
* }
|
||||
*/
|
||||
```
|
||||
|
||||
## Testing Example
|
||||
|
||||
```javascript
|
||||
describe('POST /api/users', () => {
|
||||
it('should create a new user', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/users')
|
||||
.send({
|
||||
email: 'test@example.com',
|
||||
name: 'Test User',
|
||||
password: 'password123'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.email).toBe('test@example.com');
|
||||
expect(response.body.data.password).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should reject invalid email', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/users')
|
||||
.send({
|
||||
email: 'invalid',
|
||||
name: 'Test User',
|
||||
password: 'password123'
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.error).toContain('email');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Key Principles
|
||||
|
||||
- Validate all inputs before processing
|
||||
- Use proper HTTP status codes
|
||||
- Handle errors gracefully
|
||||
- Never expose sensitive data
|
||||
- Keep responses consistent
|
||||
- Add authentication where needed
|
||||
- Document your endpoints
|
||||
- Write tests for critical paths
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `@security-auditor` - Security review
|
||||
- `@test-driven-development` - Testing
|
||||
- `@database-design` - Data modeling
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: api-patterns
|
||||
description: "API design principles and decision-making. REST vs GraphQL vs tRPC selection, response formats, versioning, pagination."
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# API Patterns
|
||||
|
||||
> API design principles and decision-making for 2025.
|
||||
> **Learn to THINK, not copy fixed patterns.**
|
||||
|
||||
## 🎯 Selective Reading Rule
|
||||
|
||||
**Read ONLY files relevant to the request!** Check the content map, find what you need.
|
||||
|
||||
---
|
||||
|
||||
## 📑 Content Map
|
||||
|
||||
| File | Description | When to Read |
|
||||
|------|-------------|--------------|
|
||||
| `api-style.md` | REST vs GraphQL vs tRPC decision tree | Choosing API type |
|
||||
| `rest.md` | Resource naming, HTTP methods, status codes | Designing REST API |
|
||||
| `response.md` | Envelope pattern, error format, pagination | Response structure |
|
||||
| `graphql.md` | Schema design, when to use, security | Considering GraphQL |
|
||||
| `trpc.md` | TypeScript monorepo, type safety | TS fullstack projects |
|
||||
| `versioning.md` | URI/Header/Query versioning | API evolution planning |
|
||||
| `auth.md` | JWT, OAuth, Passkey, API Keys | Auth pattern selection |
|
||||
| `rate-limiting.md` | Token bucket, sliding window | API protection |
|
||||
| `documentation.md` | OpenAPI/Swagger best practices | Documentation |
|
||||
| `security-testing.md` | OWASP API Top 10, auth/authz testing | Security audits |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Skills
|
||||
|
||||
| Need | Skill |
|
||||
|------|-------|
|
||||
| API implementation | `@[skills/backend-development]` |
|
||||
| Data structure | `@[skills/database-design]` |
|
||||
| Security details | `@[skills/security-hardening]` |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Decision Checklist
|
||||
|
||||
Before designing an API:
|
||||
|
||||
- [ ] **Asked user about API consumers?**
|
||||
- [ ] **Chosen API style for THIS context?** (REST/GraphQL/tRPC)
|
||||
- [ ] **Defined consistent response format?**
|
||||
- [ ] **Planned versioning strategy?**
|
||||
- [ ] **Considered authentication needs?**
|
||||
- [ ] **Planned rate limiting?**
|
||||
- [ ] **Documentation approach defined?**
|
||||
|
||||
---
|
||||
|
||||
## ❌ Anti-Patterns
|
||||
|
||||
**DON'T:**
|
||||
- Default to REST for everything
|
||||
- Use verbs in REST endpoints (/getUsers)
|
||||
- Return inconsistent response formats
|
||||
- Expose internal errors to clients
|
||||
- Skip rate limiting
|
||||
|
||||
**DO:**
|
||||
- Choose API style based on context
|
||||
- Ask about client requirements
|
||||
- Document thoroughly
|
||||
- Use appropriate status codes
|
||||
|
||||
---
|
||||
|
||||
## Script
|
||||
|
||||
| Script | Purpose | Command |
|
||||
|--------|---------|---------|
|
||||
| `scripts/api_validator.py` | API endpoint validation | `python scripts/api_validator.py <project_path>` |
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# API Style Selection (2025)
|
||||
|
||||
> REST vs GraphQL vs tRPC - Hangi durumda hangisi?
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
Who are the API consumers?
|
||||
│
|
||||
├── Public API / Multiple platforms
|
||||
│ └── REST + OpenAPI (widest compatibility)
|
||||
│
|
||||
├── Complex data needs / Multiple frontends
|
||||
│ └── GraphQL (flexible queries)
|
||||
│
|
||||
├── TypeScript frontend + backend (monorepo)
|
||||
│ └── tRPC (end-to-end type safety)
|
||||
│
|
||||
├── Real-time / Event-driven
|
||||
│ └── WebSocket + AsyncAPI
|
||||
│
|
||||
└── Internal microservices
|
||||
└── gRPC (performance) or REST (simplicity)
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
| Factor | REST | GraphQL | tRPC |
|
||||
|--------|------|---------|------|
|
||||
| **Best for** | Public APIs | Complex apps | TS monorepos |
|
||||
| **Learning curve** | Low | Medium | Low (if TS) |
|
||||
| **Over/under fetching** | Common | Solved | Solved |
|
||||
| **Type safety** | Manual (OpenAPI) | Schema-based | Automatic |
|
||||
| **Caching** | HTTP native | Complex | Client-based |
|
||||
|
||||
## Selection Questions
|
||||
|
||||
1. Who are the API consumers?
|
||||
2. Is the frontend TypeScript?
|
||||
3. How complex are the data relationships?
|
||||
4. Is caching critical?
|
||||
5. Public or internal API?
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Authentication Patterns
|
||||
|
||||
> Choose auth pattern based on use case.
|
||||
|
||||
## Selection Guide
|
||||
|
||||
| Pattern | Best For |
|
||||
|---------|----------|
|
||||
| **JWT** | Stateless, microservices |
|
||||
| **Session** | Traditional web, simple |
|
||||
| **OAuth 2.0** | Third-party integration |
|
||||
| **API Keys** | Server-to-server, public APIs |
|
||||
| **Passkey** | Modern passwordless (2025+) |
|
||||
|
||||
## JWT Principles
|
||||
|
||||
```
|
||||
Important:
|
||||
├── Always verify signature
|
||||
├── Check expiration
|
||||
├── Include minimal claims
|
||||
├── Use short expiry + refresh tokens
|
||||
└── Never store sensitive data in JWT
|
||||
```
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# API Documentation Principles
|
||||
|
||||
> Good docs = happy developers = API adoption.
|
||||
|
||||
## OpenAPI/Swagger Essentials
|
||||
|
||||
```
|
||||
Include:
|
||||
├── All endpoints with examples
|
||||
├── Request/response schemas
|
||||
├── Authentication requirements
|
||||
├── Error response formats
|
||||
└── Rate limiting info
|
||||
```
|
||||
|
||||
## Good Documentation Has
|
||||
|
||||
```
|
||||
Essentials:
|
||||
├── Quick start / Getting started
|
||||
├── Authentication guide
|
||||
├── Complete API reference
|
||||
├── Error handling guide
|
||||
├── Code examples (multiple languages)
|
||||
└── Changelog
|
||||
```
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# GraphQL Principles
|
||||
|
||||
> Flexible queries for complex, interconnected data.
|
||||
|
||||
## When to Use
|
||||
|
||||
```
|
||||
✅ Good fit:
|
||||
├── Complex, interconnected data
|
||||
├── Multiple frontend platforms
|
||||
├── Clients need flexible queries
|
||||
├── Evolving data requirements
|
||||
└── Reducing over-fetching matters
|
||||
|
||||
❌ Poor fit:
|
||||
├── Simple CRUD operations
|
||||
├── File upload heavy
|
||||
├── HTTP caching important
|
||||
└── Team unfamiliar with GraphQL
|
||||
```
|
||||
|
||||
## Schema Design Principles
|
||||
|
||||
```
|
||||
Principles:
|
||||
├── Think in graphs, not endpoints
|
||||
├── Design for evolvability (no versions)
|
||||
├── Use connections for pagination
|
||||
├── Be specific with types (not generic "data")
|
||||
└── Handle nullability thoughtfully
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
```
|
||||
Protect against:
|
||||
├── Query depth attacks → Set max depth
|
||||
├── Query complexity → Calculate cost
|
||||
├── Batching abuse → Limit batch size
|
||||
├── Introspection → Disable in production
|
||||
```
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Rate Limiting Principles
|
||||
|
||||
> Protect your API from abuse and overload.
|
||||
|
||||
## Why Rate Limit
|
||||
|
||||
```
|
||||
Protect against:
|
||||
├── Brute force attacks
|
||||
├── Resource exhaustion
|
||||
├── Cost overruns (if pay-per-use)
|
||||
└── Unfair usage
|
||||
```
|
||||
|
||||
## Strategy Selection
|
||||
|
||||
| Type | How | When |
|
||||
|------|-----|------|
|
||||
| **Token bucket** | Burst allowed, refills over time | Most APIs |
|
||||
| **Sliding window** | Smooth distribution | Strict limits |
|
||||
| **Fixed window** | Simple counters per window | Basic needs |
|
||||
|
||||
## Response Headers
|
||||
|
||||
```
|
||||
Include in headers:
|
||||
├── X-RateLimit-Limit (max requests)
|
||||
├── X-RateLimit-Remaining (requests left)
|
||||
├── X-RateLimit-Reset (when limit resets)
|
||||
└── Return 429 when exceeded
|
||||
```
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# Response Format Principles
|
||||
|
||||
> Consistency is key - choose a format and stick to it.
|
||||
|
||||
## Common Patterns
|
||||
|
||||
```
|
||||
Choose one:
|
||||
├── Envelope pattern ({ success, data, error })
|
||||
├── Direct data (just return the resource)
|
||||
└── HAL/JSON:API (hypermedia)
|
||||
```
|
||||
|
||||
## Error Response
|
||||
|
||||
```
|
||||
Include:
|
||||
├── Error code (for programmatic handling)
|
||||
├── User message (for display)
|
||||
├── Details (for debugging, field-level errors)
|
||||
├── Request ID (for support)
|
||||
└── NOT internal details (security!)
|
||||
```
|
||||
|
||||
## Pagination Types
|
||||
|
||||
| Type | Best For | Trade-offs |
|
||||
|------|----------|------------|
|
||||
| **Offset** | Simple, jumpable | Performance on large datasets |
|
||||
| **Cursor** | Large datasets | Can't jump to page |
|
||||
| **Keyset** | Performance critical | Requires sortable key |
|
||||
|
||||
### Selection Questions
|
||||
|
||||
1. How large is the dataset?
|
||||
2. Do users need to jump to specific pages?
|
||||
3. Is data frequently changing?
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# REST Principles
|
||||
|
||||
> Resource-based API design - nouns not verbs.
|
||||
|
||||
## Resource Naming Rules
|
||||
|
||||
```
|
||||
Principles:
|
||||
├── Use NOUNS, not verbs (resources, not actions)
|
||||
├── Use PLURAL forms (/users not /user)
|
||||
├── Use lowercase with hyphens (/user-profiles)
|
||||
├── Nest for relationships (/users/123/posts)
|
||||
└── Keep shallow (max 3 levels deep)
|
||||
```
|
||||
|
||||
## HTTP Method Selection
|
||||
|
||||
| Method | Purpose | Idempotent? | Body? |
|
||||
|--------|---------|-------------|-------|
|
||||
| **GET** | Read resource(s) | Yes | No |
|
||||
| **POST** | Create new resource | No | Yes |
|
||||
| **PUT** | Replace entire resource | Yes | Yes |
|
||||
| **PATCH** | Partial update | No | Yes |
|
||||
| **DELETE** | Remove resource | Yes | No |
|
||||
|
||||
## Status Code Selection
|
||||
|
||||
| Situation | Code | Why |
|
||||
|-----------|------|-----|
|
||||
| Success (read) | 200 | Standard success |
|
||||
| Created | 201 | New resource created |
|
||||
| No content | 204 | Success, nothing to return |
|
||||
| Bad request | 400 | Malformed request |
|
||||
| Unauthorized | 401 | Missing/invalid auth |
|
||||
| Forbidden | 403 | Valid auth, no permission |
|
||||
| Not found | 404 | Resource doesn't exist |
|
||||
| Conflict | 409 | State conflict (duplicate) |
|
||||
| Validation error | 422 | Valid syntax, invalid data |
|
||||
| Rate limited | 429 | Too many requests |
|
||||
| Server error | 500 | Our fault |
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
API Validator - Checks API endpoints for best practices.
|
||||
Validates OpenAPI specs, response formats, and common issues.
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
# Fix Windows console encoding for Unicode output
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
||||
except AttributeError:
|
||||
pass # Python < 3.7
|
||||
|
||||
def find_api_files(project_path: Path) -> list:
|
||||
"""Find API-related files."""
|
||||
patterns = [
|
||||
"**/*api*.ts", "**/*api*.js", "**/*api*.py",
|
||||
"**/routes/*.ts", "**/routes/*.js", "**/routes/*.py",
|
||||
"**/controllers/*.ts", "**/controllers/*.js",
|
||||
"**/endpoints/*.ts", "**/endpoints/*.py",
|
||||
"**/*.openapi.json", "**/*.openapi.yaml",
|
||||
"**/swagger.json", "**/swagger.yaml",
|
||||
"**/openapi.json", "**/openapi.yaml"
|
||||
]
|
||||
|
||||
files = []
|
||||
for pattern in patterns:
|
||||
files.extend(project_path.glob(pattern))
|
||||
|
||||
# Exclude node_modules, etc.
|
||||
return [f for f in files if not any(x in str(f) for x in ['node_modules', '.git', 'dist', 'build', '__pycache__'])]
|
||||
|
||||
def check_openapi_spec(file_path: Path) -> dict:
|
||||
"""Check OpenAPI/Swagger specification."""
|
||||
issues = []
|
||||
passed = []
|
||||
|
||||
try:
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
if file_path.suffix == '.json':
|
||||
spec = json.loads(content)
|
||||
else:
|
||||
# Basic YAML check
|
||||
if 'openapi:' in content or 'swagger:' in content:
|
||||
passed.append("[OK] OpenAPI/Swagger version defined")
|
||||
else:
|
||||
issues.append("[X] No OpenAPI version found")
|
||||
|
||||
if 'paths:' in content:
|
||||
passed.append("[OK] Paths section exists")
|
||||
else:
|
||||
issues.append("[X] No paths defined")
|
||||
|
||||
if 'components:' in content or 'definitions:' in content:
|
||||
passed.append("[OK] Schema components defined")
|
||||
|
||||
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'openapi'}
|
||||
|
||||
# JSON OpenAPI checks
|
||||
if 'openapi' in spec or 'swagger' in spec:
|
||||
passed.append("[OK] OpenAPI version defined")
|
||||
|
||||
if 'info' in spec:
|
||||
if 'title' in spec['info']:
|
||||
passed.append("[OK] API title defined")
|
||||
if 'version' in spec['info']:
|
||||
passed.append("[OK] API version defined")
|
||||
if 'description' not in spec['info']:
|
||||
issues.append("[!] API description missing")
|
||||
|
||||
if 'paths' in spec:
|
||||
path_count = len(spec['paths'])
|
||||
passed.append(f"[OK] {path_count} endpoints defined")
|
||||
|
||||
# Check each path
|
||||
for path, methods in spec['paths'].items():
|
||||
for method, details in methods.items():
|
||||
if method in ['get', 'post', 'put', 'patch', 'delete']:
|
||||
if 'responses' not in details:
|
||||
issues.append(f"[X] {method.upper()} {path}: No responses defined")
|
||||
if 'summary' not in details and 'description' not in details:
|
||||
issues.append(f"[!] {method.upper()} {path}: No description")
|
||||
|
||||
except Exception as e:
|
||||
issues.append(f"[X] Parse error: {e}")
|
||||
|
||||
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'openapi'}
|
||||
|
||||
def check_api_code(file_path: Path) -> dict:
|
||||
"""Check API code for common issues."""
|
||||
issues = []
|
||||
passed = []
|
||||
|
||||
try:
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
# Check for error handling
|
||||
error_patterns = [
|
||||
r'try\s*{', r'try:', r'\.catch\(',
|
||||
r'except\s+', r'catch\s*\('
|
||||
]
|
||||
has_error_handling = any(re.search(p, content) for p in error_patterns)
|
||||
if has_error_handling:
|
||||
passed.append("[OK] Error handling present")
|
||||
else:
|
||||
issues.append("[X] No error handling found")
|
||||
|
||||
# Check for status codes
|
||||
status_patterns = [
|
||||
r'status\s*\(\s*\d{3}\s*\)', r'statusCode\s*[=:]\s*\d{3}',
|
||||
r'HttpStatus\.', r'status_code\s*=\s*\d{3}',
|
||||
r'\.status\(\d{3}\)', r'res\.status\('
|
||||
]
|
||||
has_status = any(re.search(p, content) for p in status_patterns)
|
||||
if has_status:
|
||||
passed.append("[OK] HTTP status codes used")
|
||||
else:
|
||||
issues.append("[!] No explicit HTTP status codes")
|
||||
|
||||
# Check for validation
|
||||
validation_patterns = [
|
||||
r'validate', r'schema', r'zod', r'joi', r'yup',
|
||||
r'pydantic', r'@Body\(', r'@Query\('
|
||||
]
|
||||
has_validation = any(re.search(p, content, re.I) for p in validation_patterns)
|
||||
if has_validation:
|
||||
passed.append("[OK] Input validation present")
|
||||
else:
|
||||
issues.append("[!] No input validation detected")
|
||||
|
||||
# Check for auth middleware
|
||||
auth_patterns = [
|
||||
r'auth', r'jwt', r'bearer', r'token',
|
||||
r'middleware', r'guard', r'@Authenticated'
|
||||
]
|
||||
has_auth = any(re.search(p, content, re.I) for p in auth_patterns)
|
||||
if has_auth:
|
||||
passed.append("[OK] Authentication/authorization detected")
|
||||
|
||||
# Check for rate limiting
|
||||
rate_patterns = [r'rateLimit', r'throttle', r'rate.?limit']
|
||||
has_rate = any(re.search(p, content, re.I) for p in rate_patterns)
|
||||
if has_rate:
|
||||
passed.append("[OK] Rate limiting present")
|
||||
|
||||
# Check for logging
|
||||
log_patterns = [r'console\.log', r'logger\.', r'logging\.', r'log\.']
|
||||
has_logging = any(re.search(p, content) for p in log_patterns)
|
||||
if has_logging:
|
||||
passed.append("[OK] Logging present")
|
||||
|
||||
except Exception as e:
|
||||
issues.append(f"[X] Read error: {e}")
|
||||
|
||||
return {'file': str(file_path), 'passed': passed, 'issues': issues, 'type': 'code'}
|
||||
|
||||
def main():
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else "."
|
||||
project_path = Path(target)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(" API VALIDATOR - Endpoint Best Practices Check")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
api_files = find_api_files(project_path)
|
||||
|
||||
if not api_files:
|
||||
print("[!] No API files found.")
|
||||
print(" Looking for: routes/, controllers/, api/, openapi.json/yaml")
|
||||
sys.exit(0)
|
||||
|
||||
results = []
|
||||
for file_path in api_files[:15]: # Limit
|
||||
if 'openapi' in file_path.name.lower() or 'swagger' in file_path.name.lower():
|
||||
result = check_openapi_spec(file_path)
|
||||
else:
|
||||
result = check_api_code(file_path)
|
||||
results.append(result)
|
||||
|
||||
# Print results
|
||||
total_issues = 0
|
||||
total_passed = 0
|
||||
|
||||
for result in results:
|
||||
print(f"\n[FILE] {result['file']} [{result['type']}]")
|
||||
for item in result['passed']:
|
||||
print(f" {item}")
|
||||
total_passed += 1
|
||||
for item in result['issues']:
|
||||
print(f" {item}")
|
||||
if item.startswith("[X]"):
|
||||
total_issues += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"[RESULTS] {total_passed} passed, {total_issues} critical issues")
|
||||
print("=" * 60)
|
||||
|
||||
if total_issues == 0:
|
||||
print("[OK] API validation passed")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("[X] Fix critical issues before deployment")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
# API Security Testing
|
||||
|
||||
> Principles for testing API security. OWASP API Top 10, authentication, authorization testing.
|
||||
|
||||
---
|
||||
|
||||
## OWASP API Security Top 10
|
||||
|
||||
| Vulnerability | Test Focus |
|
||||
|---------------|------------|
|
||||
| **API1: BOLA** | Access other users' resources |
|
||||
| **API2: Broken Auth** | JWT, session, credentials |
|
||||
| **API3: Property Auth** | Mass assignment, data exposure |
|
||||
| **API4: Resource Consumption** | Rate limiting, DoS |
|
||||
| **API5: Function Auth** | Admin endpoints, role bypass |
|
||||
| **API6: Business Flow** | Logic abuse, automation |
|
||||
| **API7: SSRF** | Internal network access |
|
||||
| **API8: Misconfiguration** | Debug endpoints, CORS |
|
||||
| **API9: Inventory** | Shadow APIs, old versions |
|
||||
| **API10: Unsafe Consumption** | Third-party API trust |
|
||||
|
||||
---
|
||||
|
||||
## Authentication Testing
|
||||
|
||||
### JWT Testing
|
||||
|
||||
| Check | What to Test |
|
||||
|-------|--------------|
|
||||
| Algorithm | None, algorithm confusion |
|
||||
| Secret | Weak secrets, brute force |
|
||||
| Claims | Expiration, issuer, audience |
|
||||
| Signature | Manipulation, key injection |
|
||||
|
||||
### Session Testing
|
||||
|
||||
| Check | What to Test |
|
||||
|-------|--------------|
|
||||
| Generation | Predictability |
|
||||
| Storage | Client-side security |
|
||||
| Expiration | Timeout enforcement |
|
||||
| Invalidation | Logout effectiveness |
|
||||
|
||||
---
|
||||
|
||||
## Authorization Testing
|
||||
|
||||
| Test Type | Approach |
|
||||
|-----------|----------|
|
||||
| **Horizontal** | Access peer users' data |
|
||||
| **Vertical** | Access higher privilege functions |
|
||||
| **Context** | Access outside allowed scope |
|
||||
|
||||
### BOLA/IDOR Testing
|
||||
|
||||
1. Identify resource IDs in requests
|
||||
2. Capture request with user A's session
|
||||
3. Replay with user B's session
|
||||
4. Check for unauthorized access
|
||||
|
||||
---
|
||||
|
||||
## Input Validation Testing
|
||||
|
||||
| Injection Type | Test Focus |
|
||||
|----------------|------------|
|
||||
| SQL | Query manipulation |
|
||||
| NoSQL | Document queries |
|
||||
| Command | System commands |
|
||||
| LDAP | Directory queries |
|
||||
|
||||
**Approach:** Test all parameters, try type coercion, test boundaries, check error messages.
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting Testing
|
||||
|
||||
| Aspect | Check |
|
||||
|--------|-------|
|
||||
| Existence | Is there any limit? |
|
||||
| Bypass | Headers, IP rotation |
|
||||
| Scope | Per-user, per-IP, global |
|
||||
|
||||
**Bypass techniques:** X-Forwarded-For, different HTTP methods, case variations, API versioning.
|
||||
|
||||
---
|
||||
|
||||
## GraphQL Security
|
||||
|
||||
| Test | Focus |
|
||||
|------|-------|
|
||||
| Introspection | Schema disclosure |
|
||||
| Batching | Query DoS |
|
||||
| Nesting | Depth-based DoS |
|
||||
| Authorization | Field-level access |
|
||||
|
||||
---
|
||||
|
||||
## Security Testing Checklist
|
||||
|
||||
**Authentication:**
|
||||
- [ ] Test for bypass
|
||||
- [ ] Check credential strength
|
||||
- [ ] Verify token security
|
||||
|
||||
**Authorization:**
|
||||
- [ ] Test BOLA/IDOR
|
||||
- [ ] Check privilege escalation
|
||||
- [ ] Verify function access
|
||||
|
||||
**Input:**
|
||||
- [ ] Test all parameters
|
||||
- [ ] Check for injection
|
||||
|
||||
**Config:**
|
||||
- [ ] Check CORS
|
||||
- [ ] Verify headers
|
||||
- [ ] Test error handling
|
||||
|
||||
---
|
||||
|
||||
> **Remember:** APIs are the backbone of modern apps. Test them like attackers will.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# tRPC Principles
|
||||
|
||||
> End-to-end type safety for TypeScript monorepos.
|
||||
|
||||
## When to Use
|
||||
|
||||
```
|
||||
✅ Perfect fit:
|
||||
├── TypeScript on both ends
|
||||
├── Monorepo structure
|
||||
├── Internal tools
|
||||
├── Rapid development
|
||||
└── Type safety critical
|
||||
|
||||
❌ Poor fit:
|
||||
├── Non-TypeScript clients
|
||||
├── Public API
|
||||
├── Need REST conventions
|
||||
└── Multiple language backends
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
```
|
||||
Why tRPC:
|
||||
├── Zero schema maintenance
|
||||
├── End-to-end type inference
|
||||
├── IDE autocomplete across stack
|
||||
├── Instant API changes reflected
|
||||
└── No code generation step
|
||||
```
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
```
|
||||
Common setups:
|
||||
├── Next.js + tRPC (most common)
|
||||
├── Monorepo with shared types
|
||||
├── Remix + tRPC
|
||||
└── Any TS frontend + backend
|
||||
```
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# Versioning Strategies
|
||||
|
||||
> Plan for API evolution from day one.
|
||||
|
||||
## Decision Factors
|
||||
|
||||
| Strategy | Implementation | Trade-offs |
|
||||
|----------|---------------|------------|
|
||||
| **URI** | /v1/users | Clear, easy caching |
|
||||
| **Header** | Accept-Version: 1 | Cleaner URLs, harder discovery |
|
||||
| **Query** | ?version=1 | Easy to add, messy |
|
||||
| **None** | Evolve carefully | Best for internal, risky for public |
|
||||
|
||||
## Versioning Philosophy
|
||||
|
||||
```
|
||||
Consider:
|
||||
├── Public API? → Version in URI
|
||||
├── Internal only? → May not need versioning
|
||||
├── GraphQL? → Typically no versions (evolve schema)
|
||||
├── tRPC? → Types enforce compatibility
|
||||
```
|
||||
+915
@@ -0,0 +1,915 @@
|
||||
---
|
||||
name: api-security-best-practices
|
||||
description: "Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities"
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# API Security Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
Guide developers in building secure APIs by implementing authentication, authorization, input validation, rate limiting, and protection against common vulnerabilities. This skill covers security patterns for REST, GraphQL, and WebSocket APIs.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Use when designing new API endpoints
|
||||
- Use when securing existing APIs
|
||||
- Use when implementing authentication and authorization
|
||||
- Use when protecting against API attacks (injection, DDoS, etc.)
|
||||
- Use when conducting API security reviews
|
||||
- Use when preparing for security audits
|
||||
- Use when implementing rate limiting and throttling
|
||||
- Use when handling sensitive data in APIs
|
||||
|
||||
## How It Works
|
||||
|
||||
### Step 1: Authentication & Authorization
|
||||
|
||||
I'll help you implement secure authentication:
|
||||
- Choose authentication method (JWT, OAuth 2.0, API keys)
|
||||
- Implement token-based authentication
|
||||
- Set up role-based access control (RBAC)
|
||||
- Secure session management
|
||||
- Implement multi-factor authentication (MFA)
|
||||
|
||||
### Step 2: Input Validation & Sanitization
|
||||
|
||||
Protect against injection attacks:
|
||||
- Validate all input data
|
||||
- Sanitize user inputs
|
||||
- Use parameterized queries
|
||||
- Implement request schema validation
|
||||
- Prevent SQL injection, XSS, and command injection
|
||||
|
||||
### Step 3: Rate Limiting & Throttling
|
||||
|
||||
Prevent abuse and DDoS attacks:
|
||||
- Implement rate limiting per user/IP
|
||||
- Set up API throttling
|
||||
- Configure request quotas
|
||||
- Handle rate limit errors gracefully
|
||||
- Monitor for suspicious activity
|
||||
|
||||
### Step 4: Data Protection
|
||||
|
||||
Secure sensitive data:
|
||||
- Encrypt data in transit (HTTPS/TLS)
|
||||
- Encrypt sensitive data at rest
|
||||
- Implement proper error handling (no data leaks)
|
||||
- Sanitize error messages
|
||||
- Use secure headers
|
||||
|
||||
### Step 5: API Security Testing
|
||||
|
||||
Verify security implementation:
|
||||
- Test authentication and authorization
|
||||
- Perform penetration testing
|
||||
- Check for common vulnerabilities (OWASP API Top 10)
|
||||
- Validate input handling
|
||||
- Test rate limiting
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Implementing JWT Authentication
|
||||
|
||||
```markdown
|
||||
## Secure JWT Authentication Implementation
|
||||
|
||||
### Authentication Flow
|
||||
|
||||
1. User logs in with credentials
|
||||
2. Server validates credentials
|
||||
3. Server generates JWT token
|
||||
4. Client stores token securely
|
||||
5. Client sends token with each request
|
||||
6. Server validates token
|
||||
|
||||
### Implementation
|
||||
|
||||
#### 1. Generate Secure JWT Tokens
|
||||
|
||||
\`\`\`javascript
|
||||
// auth.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
// Login endpoint
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Validate input
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({
|
||||
error: 'Email and password are required'
|
||||
});
|
||||
}
|
||||
|
||||
// Find user
|
||||
const user = await db.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
// Don't reveal if user exists
|
||||
return res.status(401).json({
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const validPassword = await bcrypt.compare(
|
||||
password,
|
||||
user.passwordHash
|
||||
);
|
||||
|
||||
if (!validPassword) {
|
||||
return res.status(401).json({
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
expiresIn: '1h',
|
||||
issuer: 'your-app',
|
||||
audience: 'your-app-users'
|
||||
}
|
||||
);
|
||||
|
||||
// Generate refresh token
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId: user.id },
|
||||
process.env.JWT_REFRESH_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
// Store refresh token in database
|
||||
await db.refreshToken.create({
|
||||
data: {
|
||||
token: refreshToken,
|
||||
userId: user.id,
|
||||
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
token,
|
||||
refreshToken,
|
||||
expiresIn: 3600
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
res.status(500).json({
|
||||
error: 'An error occurred during login'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 2. Verify JWT Tokens (Middleware)
|
||||
|
||||
\`\`\`javascript
|
||||
// middleware/auth.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
function authenticateToken(req, res, next) {
|
||||
// Get token from header
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
error: 'Access token required'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify token
|
||||
jwt.verify(
|
||||
token,
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
issuer: 'your-app',
|
||||
audience: 'your-app-users'
|
||||
},
|
||||
(err, user) => {
|
||||
if (err) {
|
||||
if (err.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
error: 'Token expired'
|
||||
});
|
||||
}
|
||||
return res.status(403).json({
|
||||
error: 'Invalid token'
|
||||
});
|
||||
}
|
||||
|
||||
// Attach user to request
|
||||
req.user = user;
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { authenticateToken };
|
||||
\`\`\`
|
||||
|
||||
#### 3. Protect Routes
|
||||
|
||||
\`\`\`javascript
|
||||
const { authenticateToken } = require('./middleware/auth');
|
||||
|
||||
// Protected route
|
||||
app.get('/api/user/profile', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: req.user.userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
// Don't return passwordHash
|
||||
}
|
||||
});
|
||||
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 4. Implement Token Refresh
|
||||
|
||||
\`\`\`javascript
|
||||
app.post('/api/auth/refresh', async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(401).json({
|
||||
error: 'Refresh token required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify refresh token
|
||||
const decoded = jwt.verify(
|
||||
refreshToken,
|
||||
process.env.JWT_REFRESH_SECRET
|
||||
);
|
||||
|
||||
// Check if refresh token exists in database
|
||||
const storedToken = await db.refreshToken.findFirst({
|
||||
where: {
|
||||
token: refreshToken,
|
||||
userId: decoded.userId,
|
||||
expiresAt: { gt: new Date() }
|
||||
}
|
||||
});
|
||||
|
||||
if (!storedToken) {
|
||||
return res.status(403).json({
|
||||
error: 'Invalid refresh token'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: decoded.userId }
|
||||
});
|
||||
|
||||
const newToken = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
res.json({
|
||||
token: newToken,
|
||||
expiresIn: 3600
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.status(403).json({
|
||||
error: 'Invalid refresh token'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
- ✅ Use strong JWT secrets (256-bit minimum)
|
||||
- ✅ Set short expiration times (1 hour for access tokens)
|
||||
- ✅ Implement refresh tokens for long-lived sessions
|
||||
- ✅ Store refresh tokens in database (can be revoked)
|
||||
- ✅ Use HTTPS only
|
||||
- ✅ Don't store sensitive data in JWT payload
|
||||
- ✅ Validate token issuer and audience
|
||||
- ✅ Implement token blacklisting for logout
|
||||
```
|
||||
|
||||
|
||||
### Example 2: Input Validation and SQL Injection Prevention
|
||||
|
||||
```markdown
|
||||
## Preventing SQL Injection and Input Validation
|
||||
|
||||
### The Problem
|
||||
|
||||
**❌ Vulnerable Code:**
|
||||
\`\`\`javascript
|
||||
// NEVER DO THIS - SQL Injection vulnerability
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
// Dangerous: User input directly in query
|
||||
const query = \`SELECT * FROM users WHERE id = '\${userId}'\`;
|
||||
const user = await db.query(query);
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
|
||||
// Attack example:
|
||||
// GET /api/users/1' OR '1'='1
|
||||
// Returns all users!
|
||||
\`\`\`
|
||||
|
||||
### The Solution
|
||||
|
||||
#### 1. Use Parameterized Queries
|
||||
|
||||
\`\`\`javascript
|
||||
// ✅ Safe: Parameterized query
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
// Validate input first
|
||||
if (!userId || !/^\d+$/.test(userId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid user ID'
|
||||
});
|
||||
}
|
||||
|
||||
// Use parameterized query
|
||||
const user = await db.query(
|
||||
'SELECT id, email, name FROM users WHERE id = $1',
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: 'User not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 2. Use ORM with Proper Escaping
|
||||
|
||||
\`\`\`javascript
|
||||
// ✅ Safe: Using Prisma ORM
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(userId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid user ID'
|
||||
});
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
// Don't select sensitive fields
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: 'User not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 3. Implement Request Validation with Zod
|
||||
|
||||
\`\`\`javascript
|
||||
const { z } = require('zod');
|
||||
|
||||
// Define validation schema
|
||||
const createUserSchema = z.object({
|
||||
email: z.string().email('Invalid email format'),
|
||||
password: z.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Password must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Password must contain number'),
|
||||
name: z.string()
|
||||
.min(2, 'Name must be at least 2 characters')
|
||||
.max(100, 'Name too long'),
|
||||
age: z.number()
|
||||
.int('Age must be an integer')
|
||||
.min(18, 'Must be 18 or older')
|
||||
.max(120, 'Invalid age')
|
||||
.optional()
|
||||
});
|
||||
|
||||
// Validation middleware
|
||||
function validateRequest(schema) {
|
||||
return (req, res, next) => {
|
||||
try {
|
||||
schema.parse(req.body);
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: error.errors
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Use validation
|
||||
app.post('/api/users',
|
||||
validateRequest(createUserSchema),
|
||||
async (req, res) => {
|
||||
// Input is validated at this point
|
||||
const { email, password, name, age } = req.body;
|
||||
|
||||
// Hash password
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
age
|
||||
}
|
||||
});
|
||||
|
||||
// Don't return password hash
|
||||
const { passwordHash: _, ...userWithoutPassword } = user;
|
||||
res.status(201).json(userWithoutPassword);
|
||||
}
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
#### 4. Sanitize Output to Prevent XSS
|
||||
|
||||
\`\`\`javascript
|
||||
const DOMPurify = require('isomorphic-dompurify');
|
||||
|
||||
app.post('/api/comments', authenticateToken, async (req, res) => {
|
||||
const { content } = req.body;
|
||||
|
||||
// Validate
|
||||
if (!content || content.length > 1000) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid comment content'
|
||||
});
|
||||
}
|
||||
|
||||
// Sanitize HTML to prevent XSS
|
||||
const sanitizedContent = DOMPurify.sanitize(content, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
|
||||
ALLOWED_ATTR: ['href']
|
||||
});
|
||||
|
||||
const comment = await prisma.comment.create({
|
||||
data: {
|
||||
content: sanitizedContent,
|
||||
userId: req.user.userId
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(comment);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Validation Checklist
|
||||
|
||||
- [ ] Validate all user inputs
|
||||
- [ ] Use parameterized queries or ORM
|
||||
- [ ] Validate data types (string, number, email, etc.)
|
||||
- [ ] Validate data ranges (min/max length, value ranges)
|
||||
- [ ] Sanitize HTML content
|
||||
- [ ] Escape special characters
|
||||
- [ ] Validate file uploads (type, size, content)
|
||||
- [ ] Use allowlists, not blocklists
|
||||
```
|
||||
|
||||
|
||||
### Example 3: Rate Limiting and DDoS Protection
|
||||
|
||||
```markdown
|
||||
## Implementing Rate Limiting
|
||||
|
||||
### Why Rate Limiting?
|
||||
|
||||
- Prevent brute force attacks
|
||||
- Protect against DDoS
|
||||
- Prevent API abuse
|
||||
- Ensure fair usage
|
||||
- Reduce server costs
|
||||
|
||||
### Implementation with Express Rate Limit
|
||||
|
||||
\`\`\`javascript
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const RedisStore = require('rate-limit-redis');
|
||||
const Redis = require('ioredis');
|
||||
|
||||
// Create Redis client
|
||||
const redis = new Redis({
|
||||
host: process.env.REDIS_HOST,
|
||||
port: process.env.REDIS_PORT
|
||||
});
|
||||
|
||||
// General API rate limit
|
||||
const apiLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
client: redis,
|
||||
prefix: 'rl:api:'
|
||||
}),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // 100 requests per window
|
||||
message: {
|
||||
error: 'Too many requests, please try again later',
|
||||
retryAfter: 900 // seconds
|
||||
},
|
||||
standardHeaders: true, // Return rate limit info in headers
|
||||
legacyHeaders: false,
|
||||
// Custom key generator (by user ID or IP)
|
||||
keyGenerator: (req) => {
|
||||
return req.user?.userId || req.ip;
|
||||
}
|
||||
});
|
||||
|
||||
// Strict rate limit for authentication endpoints
|
||||
const authLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
client: redis,
|
||||
prefix: 'rl:auth:'
|
||||
}),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // Only 5 login attempts per 15 minutes
|
||||
skipSuccessfulRequests: true, // Don't count successful logins
|
||||
message: {
|
||||
error: 'Too many login attempts, please try again later',
|
||||
retryAfter: 900
|
||||
}
|
||||
});
|
||||
|
||||
// Apply rate limiters
|
||||
app.use('/api/', apiLimiter);
|
||||
app.use('/api/auth/login', authLimiter);
|
||||
app.use('/api/auth/register', authLimiter);
|
||||
|
||||
// Custom rate limiter for expensive operations
|
||||
const expensiveLimiter = rateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1 hour
|
||||
max: 10, // 10 requests per hour
|
||||
message: {
|
||||
error: 'Rate limit exceeded for this operation'
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/reports/generate',
|
||||
authenticateToken,
|
||||
expensiveLimiter,
|
||||
async (req, res) => {
|
||||
// Expensive operation
|
||||
}
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
### Advanced: Per-User Rate Limiting
|
||||
|
||||
\`\`\`javascript
|
||||
// Different limits based on user tier
|
||||
function createTieredRateLimiter() {
|
||||
const limits = {
|
||||
free: { windowMs: 60 * 60 * 1000, max: 100 },
|
||||
pro: { windowMs: 60 * 60 * 1000, max: 1000 },
|
||||
enterprise: { windowMs: 60 * 60 * 1000, max: 10000 }
|
||||
};
|
||||
|
||||
return async (req, res, next) => {
|
||||
const user = req.user;
|
||||
const tier = user?.tier || 'free';
|
||||
const limit = limits[tier];
|
||||
|
||||
const key = \`rl:user:\${user.userId}\`;
|
||||
const current = await redis.incr(key);
|
||||
|
||||
if (current === 1) {
|
||||
await redis.expire(key, limit.windowMs / 1000);
|
||||
}
|
||||
|
||||
if (current > limit.max) {
|
||||
return res.status(429).json({
|
||||
error: 'Rate limit exceeded',
|
||||
limit: limit.max,
|
||||
remaining: 0,
|
||||
reset: await redis.ttl(key)
|
||||
});
|
||||
}
|
||||
|
||||
// Set rate limit headers
|
||||
res.set({
|
||||
'X-RateLimit-Limit': limit.max,
|
||||
'X-RateLimit-Remaining': limit.max - current,
|
||||
'X-RateLimit-Reset': await redis.ttl(key)
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
app.use('/api/', authenticateToken, createTieredRateLimiter());
|
||||
\`\`\`
|
||||
|
||||
### DDoS Protection with Helmet
|
||||
|
||||
\`\`\`javascript
|
||||
const helmet = require('helmet');
|
||||
|
||||
app.use(helmet({
|
||||
// Content Security Policy
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
scriptSrc: ["'self'"],
|
||||
imgSrc: ["'self'", 'data:', 'https:']
|
||||
}
|
||||
},
|
||||
// Prevent clickjacking
|
||||
frameguard: { action: 'deny' },
|
||||
// Hide X-Powered-By header
|
||||
hidePoweredBy: true,
|
||||
// Prevent MIME type sniffing
|
||||
noSniff: true,
|
||||
// Enable HSTS
|
||||
hsts: {
|
||||
maxAge: 31536000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
}
|
||||
}));
|
||||
\`\`\`
|
||||
|
||||
### Rate Limit Response Headers
|
||||
|
||||
\`\`\`
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 87
|
||||
X-RateLimit-Reset: 1640000000
|
||||
Retry-After: 900
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ Do This
|
||||
|
||||
- **Use HTTPS Everywhere** - Never send sensitive data over HTTP
|
||||
- **Implement Authentication** - Require authentication for protected endpoints
|
||||
- **Validate All Inputs** - Never trust user input
|
||||
- **Use Parameterized Queries** - Prevent SQL injection
|
||||
- **Implement Rate Limiting** - Protect against brute force and DDoS
|
||||
- **Hash Passwords** - Use bcrypt with salt rounds >= 10
|
||||
- **Use Short-Lived Tokens** - JWT access tokens should expire quickly
|
||||
- **Implement CORS Properly** - Only allow trusted origins
|
||||
- **Log Security Events** - Monitor for suspicious activity
|
||||
- **Keep Dependencies Updated** - Regularly update packages
|
||||
- **Use Security Headers** - Implement Helmet.js
|
||||
- **Sanitize Error Messages** - Don't leak sensitive information
|
||||
|
||||
### ❌ Don't Do This
|
||||
|
||||
- **Don't Store Passwords in Plain Text** - Always hash passwords
|
||||
- **Don't Use Weak Secrets** - Use strong, random JWT secrets
|
||||
- **Don't Trust User Input** - Always validate and sanitize
|
||||
- **Don't Expose Stack Traces** - Hide error details in production
|
||||
- **Don't Use String Concatenation for SQL** - Use parameterized queries
|
||||
- **Don't Store Sensitive Data in JWT** - JWTs are not encrypted
|
||||
- **Don't Ignore Security Updates** - Update dependencies regularly
|
||||
- **Don't Use Default Credentials** - Change all default passwords
|
||||
- **Don't Disable CORS Completely** - Configure it properly instead
|
||||
- **Don't Log Sensitive Data** - Sanitize logs
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Problem: JWT Secret Exposed in Code
|
||||
**Symptoms:** JWT secret hardcoded or committed to Git
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad
|
||||
const JWT_SECRET = 'my-secret-key';
|
||||
|
||||
// ✅ Good
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
if (!JWT_SECRET) {
|
||||
throw new Error('JWT_SECRET environment variable is required');
|
||||
}
|
||||
|
||||
// Generate strong secret
|
||||
// node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
|
||||
\`\`\`
|
||||
|
||||
### Problem: Weak Password Requirements
|
||||
**Symptoms:** Users can set weak passwords like "password123"
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
const passwordSchema = z.string()
|
||||
.min(12, 'Password must be at least 12 characters')
|
||||
.regex(/[A-Z]/, 'Must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Must contain number')
|
||||
.regex(/[^A-Za-z0-9]/, 'Must contain special character');
|
||||
|
||||
// Or use a password strength library
|
||||
const zxcvbn = require('zxcvbn');
|
||||
const result = zxcvbn(password);
|
||||
if (result.score < 3) {
|
||||
return res.status(400).json({
|
||||
error: 'Password too weak',
|
||||
suggestions: result.feedback.suggestions
|
||||
});
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Problem: Missing Authorization Checks
|
||||
**Symptoms:** Users can access resources they shouldn't
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad: Only checks authentication
|
||||
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
|
||||
await prisma.post.delete({ where: { id: req.params.id } });
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// ✅ Good: Checks both authentication and authorization
|
||||
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
|
||||
const post = await prisma.post.findUnique({
|
||||
where: { id: req.params.id }
|
||||
});
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: 'Post not found' });
|
||||
}
|
||||
|
||||
// Check if user owns the post or is admin
|
||||
if (post.userId !== req.user.userId && req.user.role !== 'admin') {
|
||||
return res.status(403).json({
|
||||
error: 'Not authorized to delete this post'
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.post.delete({ where: { id: req.params.id } });
|
||||
res.json({ success: true });
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Problem: Verbose Error Messages
|
||||
**Symptoms:** Error messages reveal system details
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad: Exposes database details
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const user = await prisma.user.create({ data: req.body });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
// Error: "Unique constraint failed on the fields: (`email`)"
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ Good: Generic error message
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const user = await prisma.user.create({ data: req.body });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
console.error('User creation error:', error); // Log full error
|
||||
|
||||
if (error.code === 'P2002') {
|
||||
return res.status(400).json({
|
||||
error: 'Email already exists'
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'An error occurred while creating user'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
## Security Checklist
|
||||
|
||||
### Authentication & Authorization
|
||||
- [ ] Implement strong authentication (JWT, OAuth 2.0)
|
||||
- [ ] Use HTTPS for all endpoints
|
||||
- [ ] Hash passwords with bcrypt (salt rounds >= 10)
|
||||
- [ ] Implement token expiration
|
||||
- [ ] Add refresh token mechanism
|
||||
- [ ] Verify user authorization for each request
|
||||
- [ ] Implement role-based access control (RBAC)
|
||||
|
||||
### Input Validation
|
||||
- [ ] Validate all user inputs
|
||||
- [ ] Use parameterized queries or ORM
|
||||
- [ ] Sanitize HTML content
|
||||
- [ ] Validate file uploads
|
||||
- [ ] Implement request schema validation
|
||||
- [ ] Use allowlists, not blocklists
|
||||
|
||||
### Rate Limiting & DDoS Protection
|
||||
- [ ] Implement rate limiting per user/IP
|
||||
- [ ] Add stricter limits for auth endpoints
|
||||
- [ ] Use Redis for distributed rate limiting
|
||||
- [ ] Return proper rate limit headers
|
||||
- [ ] Implement request throttling
|
||||
|
||||
### Data Protection
|
||||
- [ ] Use HTTPS/TLS for all traffic
|
||||
- [ ] Encrypt sensitive data at rest
|
||||
- [ ] Don't store sensitive data in JWT
|
||||
- [ ] Sanitize error messages
|
||||
- [ ] Implement proper CORS configuration
|
||||
- [ ] Use security headers (Helmet.js)
|
||||
|
||||
### Monitoring & Logging
|
||||
- [ ] Log security events
|
||||
- [ ] Monitor for suspicious activity
|
||||
- [ ] Set up alerts for failed auth attempts
|
||||
- [ ] Track API usage patterns
|
||||
- [ ] Don't log sensitive data
|
||||
|
||||
## OWASP API Security Top 10
|
||||
|
||||
1. **Broken Object Level Authorization** - Always verify user can access resource
|
||||
2. **Broken Authentication** - Implement strong authentication mechanisms
|
||||
3. **Broken Object Property Level Authorization** - Validate which properties user can access
|
||||
4. **Unrestricted Resource Consumption** - Implement rate limiting and quotas
|
||||
5. **Broken Function Level Authorization** - Verify user role for each function
|
||||
6. **Unrestricted Access to Sensitive Business Flows** - Protect critical workflows
|
||||
7. **Server Side Request Forgery (SSRF)** - Validate and sanitize URLs
|
||||
8. **Security Misconfiguration** - Use security best practices and headers
|
||||
9. **Improper Inventory Management** - Document and secure all API endpoints
|
||||
10. **Unsafe Consumption of APIs** - Validate data from third-party APIs
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `@ethical-hacking-methodology` - Security testing perspective
|
||||
- `@sql-injection-testing` - Testing for SQL injection
|
||||
- `@xss-html-injection` - Testing for XSS vulnerabilities
|
||||
- `@broken-authentication` - Authentication vulnerabilities
|
||||
- `@backend-dev-guidelines` - Backend development standards
|
||||
- `@systematic-debugging` - Debug security issues
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
|
||||
- [JWT Best Practices](https://tools.ietf.org/html/rfc8725)
|
||||
- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)
|
||||
- [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/)
|
||||
- [API Security Checklist](https://github.com/shieldfy/API-Security-Checklist)
|
||||
|
||||
---
|
||||
|
||||
**Pro Tip:** Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities!
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: auth-implementation-patterns
|
||||
description: "Build secure, scalable authentication and authorization systems using industry-standard patterns and modern best practices."
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# Authentication & Authorization Implementation Patterns
|
||||
|
||||
Build secure, scalable authentication and authorization systems using industry-standard patterns and modern best practices.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Implementing user authentication systems
|
||||
- Securing REST or GraphQL APIs
|
||||
- Adding OAuth2/social login or SSO
|
||||
- Designing session management or RBAC
|
||||
- Debugging authentication or authorization issues
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need UI copy or login page styling
|
||||
- The task is infrastructure-only without identity concerns
|
||||
- You cannot change auth policies or credential storage
|
||||
|
||||
## Instructions
|
||||
|
||||
- Define users, tenants, flows, and threat model constraints.
|
||||
- Choose auth strategy (session, JWT, OIDC) and token lifecycle.
|
||||
- Design authorization model and policy enforcement points.
|
||||
- Plan secrets storage, rotation, logging, and audit requirements.
|
||||
- If detailed examples are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
## Safety
|
||||
|
||||
- Never log secrets, tokens, or credentials.
|
||||
- Enforce least privilege and secure storage for keys.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed patterns and examples.
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+618
@@ -0,0 +1,618 @@
|
||||
# Authentication and Authorization Implementation Patterns Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. Authentication vs Authorization
|
||||
|
||||
**Authentication (AuthN)**: Who are you?
|
||||
- Verifying identity (username/password, OAuth, biometrics)
|
||||
- Issuing credentials (sessions, tokens)
|
||||
- Managing login/logout
|
||||
|
||||
**Authorization (AuthZ)**: What can you do?
|
||||
- Permission checking
|
||||
- Role-based access control (RBAC)
|
||||
- Resource ownership validation
|
||||
- Policy enforcement
|
||||
|
||||
### 2. Authentication Strategies
|
||||
|
||||
**Session-Based:**
|
||||
- Server stores session state
|
||||
- Session ID in cookie
|
||||
- Traditional, simple, stateful
|
||||
|
||||
**Token-Based (JWT):**
|
||||
- Stateless, self-contained
|
||||
- Scales horizontally
|
||||
- Can store claims
|
||||
|
||||
**OAuth2/OpenID Connect:**
|
||||
- Delegate authentication
|
||||
- Social login (Google, GitHub)
|
||||
- Enterprise SSO
|
||||
|
||||
## JWT Authentication
|
||||
|
||||
### Pattern 1: JWT Implementation
|
||||
|
||||
```typescript
|
||||
// JWT structure: header.payload.signature
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
interface JWTPayload {
|
||||
userId: string;
|
||||
email: string;
|
||||
role: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
// Generate JWT
|
||||
function generateTokens(userId: string, email: string, role: string) {
|
||||
const accessToken = jwt.sign(
|
||||
{ userId, email, role },
|
||||
process.env.JWT_SECRET!,
|
||||
{ expiresIn: '15m' } // Short-lived
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId },
|
||||
process.env.JWT_REFRESH_SECRET!,
|
||||
{ expiresIn: '7d' } // Long-lived
|
||||
);
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
}
|
||||
|
||||
// Verify JWT
|
||||
function verifyToken(token: string): JWTPayload {
|
||||
try {
|
||||
return jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
|
||||
} catch (error) {
|
||||
if (error instanceof jwt.TokenExpiredError) {
|
||||
throw new Error('Token expired');
|
||||
}
|
||||
if (error instanceof jwt.JsonWebTokenError) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware
|
||||
function authenticate(req: Request, res: Response, next: NextFunction) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader?.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ error: 'No token provided' });
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
try {
|
||||
const payload = verifyToken(token);
|
||||
req.user = payload; // Attach user to request
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json({ error: 'Invalid token' });
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.get('/api/profile', authenticate, (req, res) => {
|
||||
res.json({ user: req.user });
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 2: Refresh Token Flow
|
||||
|
||||
```typescript
|
||||
interface StoredRefreshToken {
|
||||
token: string;
|
||||
userId: string;
|
||||
expiresAt: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
class RefreshTokenService {
|
||||
// Store refresh token in database
|
||||
async storeRefreshToken(userId: string, refreshToken: string) {
|
||||
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
|
||||
await db.refreshTokens.create({
|
||||
token: await hash(refreshToken), // Hash before storing
|
||||
userId,
|
||||
expiresAt,
|
||||
});
|
||||
}
|
||||
|
||||
// Refresh access token
|
||||
async refreshAccessToken(refreshToken: string) {
|
||||
// Verify refresh token
|
||||
let payload;
|
||||
try {
|
||||
payload = jwt.verify(
|
||||
refreshToken,
|
||||
process.env.JWT_REFRESH_SECRET!
|
||||
) as { userId: string };
|
||||
} catch {
|
||||
throw new Error('Invalid refresh token');
|
||||
}
|
||||
|
||||
// Check if token exists in database
|
||||
const storedToken = await db.refreshTokens.findOne({
|
||||
where: {
|
||||
token: await hash(refreshToken),
|
||||
userId: payload.userId,
|
||||
expiresAt: { $gt: new Date() },
|
||||
},
|
||||
});
|
||||
|
||||
if (!storedToken) {
|
||||
throw new Error('Refresh token not found or expired');
|
||||
}
|
||||
|
||||
// Get user
|
||||
const user = await db.users.findById(payload.userId);
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
const accessToken = jwt.sign(
|
||||
{ userId: user.id, email: user.email, role: user.role },
|
||||
process.env.JWT_SECRET!,
|
||||
{ expiresIn: '15m' }
|
||||
);
|
||||
|
||||
return { accessToken };
|
||||
}
|
||||
|
||||
// Revoke refresh token (logout)
|
||||
async revokeRefreshToken(refreshToken: string) {
|
||||
await db.refreshTokens.deleteOne({
|
||||
token: await hash(refreshToken),
|
||||
});
|
||||
}
|
||||
|
||||
// Revoke all user tokens (logout all devices)
|
||||
async revokeAllUserTokens(userId: string) {
|
||||
await db.refreshTokens.deleteMany({ userId });
|
||||
}
|
||||
}
|
||||
|
||||
// API endpoints
|
||||
app.post('/api/auth/refresh', async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
try {
|
||||
const { accessToken } = await refreshTokenService
|
||||
.refreshAccessToken(refreshToken);
|
||||
res.json({ accessToken });
|
||||
} catch (error) {
|
||||
res.status(401).json({ error: 'Invalid refresh token' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/auth/logout', authenticate, async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
await refreshTokenService.revokeRefreshToken(refreshToken);
|
||||
res.json({ message: 'Logged out successfully' });
|
||||
});
|
||||
```
|
||||
|
||||
## Session-Based Authentication
|
||||
|
||||
### Pattern 1: Express Session
|
||||
|
||||
```typescript
|
||||
import session from 'express-session';
|
||||
import RedisStore from 'connect-redis';
|
||||
import { createClient } from 'redis';
|
||||
|
||||
// Setup Redis for session storage
|
||||
const redisClient = createClient({
|
||||
url: process.env.REDIS_URL,
|
||||
});
|
||||
await redisClient.connect();
|
||||
|
||||
app.use(
|
||||
session({
|
||||
store: new RedisStore({ client: redisClient }),
|
||||
secret: process.env.SESSION_SECRET!,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === 'production', // HTTPS only
|
||||
httpOnly: true, // No JavaScript access
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
sameSite: 'strict', // CSRF protection
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Login
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
|
||||
const user = await db.users.findOne({ email });
|
||||
if (!user || !(await verifyPassword(password, user.passwordHash))) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
// Store user in session
|
||||
req.session.userId = user.id;
|
||||
req.session.role = user.role;
|
||||
|
||||
res.json({ user: { id: user.id, email: user.email, role: user.role } });
|
||||
});
|
||||
|
||||
// Session middleware
|
||||
function requireAuth(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.session.userId) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// Protected route
|
||||
app.get('/api/profile', requireAuth, async (req, res) => {
|
||||
const user = await db.users.findById(req.session.userId);
|
||||
res.json({ user });
|
||||
});
|
||||
|
||||
// Logout
|
||||
app.post('/api/auth/logout', (req, res) => {
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Logout failed' });
|
||||
}
|
||||
res.clearCookie('connect.sid');
|
||||
res.json({ message: 'Logged out successfully' });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## OAuth2 / Social Login
|
||||
|
||||
### Pattern 1: OAuth2 with Passport.js
|
||||
|
||||
```typescript
|
||||
import passport from 'passport';
|
||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||
import { Strategy as GitHubStrategy } from 'passport-github2';
|
||||
|
||||
// Google OAuth
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
callbackURL: '/api/auth/google/callback',
|
||||
},
|
||||
async (accessToken, refreshToken, profile, done) => {
|
||||
try {
|
||||
// Find or create user
|
||||
let user = await db.users.findOne({
|
||||
googleId: profile.id,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
user = await db.users.create({
|
||||
googleId: profile.id,
|
||||
email: profile.emails?.[0]?.value,
|
||||
name: profile.displayName,
|
||||
avatar: profile.photos?.[0]?.value,
|
||||
});
|
||||
}
|
||||
|
||||
return done(null, user);
|
||||
} catch (error) {
|
||||
return done(error, undefined);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Routes
|
||||
app.get('/api/auth/google', passport.authenticate('google', {
|
||||
scope: ['profile', 'email'],
|
||||
}));
|
||||
|
||||
app.get(
|
||||
'/api/auth/google/callback',
|
||||
passport.authenticate('google', { session: false }),
|
||||
(req, res) => {
|
||||
// Generate JWT
|
||||
const tokens = generateTokens(req.user.id, req.user.email, req.user.role);
|
||||
// Redirect to frontend with token
|
||||
res.redirect(`${process.env.FRONTEND_URL}/auth/callback?token=${tokens.accessToken}`);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Authorization Patterns
|
||||
|
||||
### Pattern 1: Role-Based Access Control (RBAC)
|
||||
|
||||
```typescript
|
||||
enum Role {
|
||||
USER = 'user',
|
||||
MODERATOR = 'moderator',
|
||||
ADMIN = 'admin',
|
||||
}
|
||||
|
||||
const roleHierarchy: Record<Role, Role[]> = {
|
||||
[Role.ADMIN]: [Role.ADMIN, Role.MODERATOR, Role.USER],
|
||||
[Role.MODERATOR]: [Role.MODERATOR, Role.USER],
|
||||
[Role.USER]: [Role.USER],
|
||||
};
|
||||
|
||||
function hasRole(userRole: Role, requiredRole: Role): boolean {
|
||||
return roleHierarchy[userRole].includes(requiredRole);
|
||||
}
|
||||
|
||||
// Middleware
|
||||
function requireRole(...roles: Role[]) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
if (!roles.some(role => hasRole(req.user.role, role))) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.delete('/api/users/:id',
|
||||
authenticate,
|
||||
requireRole(Role.ADMIN),
|
||||
async (req, res) => {
|
||||
// Only admins can delete users
|
||||
await db.users.delete(req.params.id);
|
||||
res.json({ message: 'User deleted' });
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Pattern 2: Permission-Based Access Control
|
||||
|
||||
```typescript
|
||||
enum Permission {
|
||||
READ_USERS = 'read:users',
|
||||
WRITE_USERS = 'write:users',
|
||||
DELETE_USERS = 'delete:users',
|
||||
READ_POSTS = 'read:posts',
|
||||
WRITE_POSTS = 'write:posts',
|
||||
}
|
||||
|
||||
const rolePermissions: Record<Role, Permission[]> = {
|
||||
[Role.USER]: [Permission.READ_POSTS, Permission.WRITE_POSTS],
|
||||
[Role.MODERATOR]: [
|
||||
Permission.READ_POSTS,
|
||||
Permission.WRITE_POSTS,
|
||||
Permission.READ_USERS,
|
||||
],
|
||||
[Role.ADMIN]: Object.values(Permission),
|
||||
};
|
||||
|
||||
function hasPermission(userRole: Role, permission: Permission): boolean {
|
||||
return rolePermissions[userRole]?.includes(permission) ?? false;
|
||||
}
|
||||
|
||||
function requirePermission(...permissions: Permission[]) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
const hasAllPermissions = permissions.every(permission =>
|
||||
hasPermission(req.user.role, permission)
|
||||
);
|
||||
|
||||
if (!hasAllPermissions) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.get('/api/users',
|
||||
authenticate,
|
||||
requirePermission(Permission.READ_USERS),
|
||||
async (req, res) => {
|
||||
const users = await db.users.findAll();
|
||||
res.json({ users });
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Pattern 3: Resource Ownership
|
||||
|
||||
```typescript
|
||||
// Check if user owns resource
|
||||
async function requireOwnership(
|
||||
resourceType: 'post' | 'comment',
|
||||
resourceIdParam: string = 'id'
|
||||
) {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
const resourceId = req.params[resourceIdParam];
|
||||
|
||||
// Admins can access anything
|
||||
if (req.user.role === Role.ADMIN) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
let resource;
|
||||
if (resourceType === 'post') {
|
||||
resource = await db.posts.findById(resourceId);
|
||||
} else if (resourceType === 'comment') {
|
||||
resource = await db.comments.findById(resourceId);
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
return res.status(404).json({ error: 'Resource not found' });
|
||||
}
|
||||
|
||||
if (resource.userId !== req.user.userId) {
|
||||
return res.status(403).json({ error: 'Not authorized' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.put('/api/posts/:id',
|
||||
authenticate,
|
||||
requireOwnership('post'),
|
||||
async (req, res) => {
|
||||
// User can only update their own posts
|
||||
const post = await db.posts.update(req.params.id, req.body);
|
||||
res.json({ post });
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Pattern 1: Password Security
|
||||
|
||||
```typescript
|
||||
import bcrypt from 'bcrypt';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Password validation schema
|
||||
const passwordSchema = z.string()
|
||||
.min(12, 'Password must be at least 12 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Password must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Password must contain number')
|
||||
.regex(/[^A-Za-z0-9]/, 'Password must contain special character');
|
||||
|
||||
// Hash password
|
||||
async function hashPassword(password: string): Promise<string> {
|
||||
const saltRounds = 12; // 2^12 iterations
|
||||
return bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
|
||||
// Verify password
|
||||
async function verifyPassword(
|
||||
password: string,
|
||||
hash: string
|
||||
): Promise<boolean> {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
// Registration with password validation
|
||||
app.post('/api/auth/register', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Validate password
|
||||
passwordSchema.parse(password);
|
||||
|
||||
// Check if user exists
|
||||
const existingUser = await db.users.findOne({ email });
|
||||
if (existingUser) {
|
||||
return res.status(400).json({ error: 'Email already registered' });
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const passwordHash = await hashPassword(password);
|
||||
|
||||
// Create user
|
||||
const user = await db.users.create({
|
||||
email,
|
||||
passwordHash,
|
||||
});
|
||||
|
||||
// Generate tokens
|
||||
const tokens = generateTokens(user.id, user.email, user.role);
|
||||
|
||||
res.status(201).json({
|
||||
user: { id: user.id, email: user.email },
|
||||
...tokens,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return res.status(400).json({ error: error.errors[0].message });
|
||||
}
|
||||
res.status(500).json({ error: 'Registration failed' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 2: Rate Limiting
|
||||
|
||||
```typescript
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import RedisStore from 'rate-limit-redis';
|
||||
|
||||
// Login rate limiter
|
||||
const loginLimiter = rateLimit({
|
||||
store: new RedisStore({ client: redisClient }),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // 5 attempts
|
||||
message: 'Too many login attempts, please try again later',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
// API rate limiter
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 100, // 100 requests per minute
|
||||
standardHeaders: true,
|
||||
});
|
||||
|
||||
// Apply to routes
|
||||
app.post('/api/auth/login', loginLimiter, async (req, res) => {
|
||||
// Login logic
|
||||
});
|
||||
|
||||
app.use('/api/', apiLimiter);
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Never Store Plain Passwords**: Always hash with bcrypt/argon2
|
||||
2. **Use HTTPS**: Encrypt data in transit
|
||||
3. **Short-Lived Access Tokens**: 15-30 minutes max
|
||||
4. **Secure Cookies**: httpOnly, secure, sameSite flags
|
||||
5. **Validate All Input**: Email format, password strength
|
||||
6. **Rate Limit Auth Endpoints**: Prevent brute force attacks
|
||||
7. **Implement CSRF Protection**: For session-based auth
|
||||
8. **Rotate Secrets Regularly**: JWT secrets, session secrets
|
||||
9. **Log Security Events**: Login attempts, failed auth
|
||||
10. **Use MFA When Possible**: Extra security layer
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Weak Passwords**: Enforce strong password policies
|
||||
- **JWT in localStorage**: Vulnerable to XSS, use httpOnly cookies
|
||||
- **No Token Expiration**: Tokens should expire
|
||||
- **Client-Side Auth Checks Only**: Always validate server-side
|
||||
- **Insecure Password Reset**: Use secure tokens with expiration
|
||||
- **No Rate Limiting**: Vulnerable to brute force
|
||||
- **Trusting Client Data**: Always validate on server
|
||||
|
||||
## Resources
|
||||
|
||||
- **references/jwt-best-practices.md**: JWT implementation guide
|
||||
- **references/oauth2-flows.md**: OAuth2 flow diagrams and examples
|
||||
- **references/session-security.md**: Secure session management
|
||||
- **assets/auth-security-checklist.md**: Security review checklist
|
||||
- **assets/password-policy-template.md**: Password requirements template
|
||||
- **scripts/token-validator.ts**: JWT validation utility
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
---
|
||||
name: backend-architect
|
||||
description: Expert backend architect specializing in scalable API design, microservices architecture, and distributed systems.
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: '2026-02-27'
|
||||
---
|
||||
You are a backend system architect specializing in scalable, resilient, and maintainable backend systems and APIs.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Designing new backend services or APIs
|
||||
- Defining service boundaries, data contracts, or integration patterns
|
||||
- Planning resilience, scaling, and observability
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need a code-level bug fix
|
||||
- You are working on small scripts without architectural concerns
|
||||
- You need frontend or UX guidance instead of backend architecture
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Capture domain context, use cases, and non-functional requirements.
|
||||
2. Define service boundaries and API contracts.
|
||||
3. Choose architecture patterns and integration mechanisms.
|
||||
4. Identify risks, observability needs, and rollout plan.
|
||||
|
||||
## Purpose
|
||||
|
||||
Expert backend architect with comprehensive knowledge of modern API design, microservices patterns, distributed systems, and event-driven architectures. Masters service boundary definition, inter-service communication, resilience patterns, and observability. Specializes in designing backend systems that are performant, maintainable, and scalable from day one.
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
Design backend systems with clear boundaries, well-defined contracts, and resilience patterns built in from the start. Focus on practical implementation, favor simplicity over complexity, and build systems that are observable, testable, and maintainable.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### API Design & Patterns
|
||||
|
||||
- **RESTful APIs**: Resource modeling, HTTP methods, status codes, versioning strategies
|
||||
- **GraphQL APIs**: Schema design, resolvers, mutations, subscriptions, DataLoader patterns
|
||||
- **gRPC Services**: Protocol Buffers, streaming (unary, server, client, bidirectional), service definition
|
||||
- **WebSocket APIs**: Real-time communication, connection management, scaling patterns
|
||||
- **Server-Sent Events**: One-way streaming, event formats, reconnection strategies
|
||||
- **Webhook patterns**: Event delivery, retry logic, signature verification, idempotency
|
||||
- **API versioning**: URL versioning, header versioning, content negotiation, deprecation strategies
|
||||
- **Pagination strategies**: Offset, cursor-based, keyset pagination, infinite scroll
|
||||
- **Filtering & sorting**: Query parameters, GraphQL arguments, search capabilities
|
||||
- **Batch operations**: Bulk endpoints, batch mutations, transaction handling
|
||||
- **HATEOAS**: Hypermedia controls, discoverable APIs, link relations
|
||||
|
||||
### API Contract & Documentation
|
||||
|
||||
- **OpenAPI/Swagger**: Schema definition, code generation, documentation generation
|
||||
- **GraphQL Schema**: Schema-first design, type system, directives, federation
|
||||
- **API-First design**: Contract-first development, consumer-driven contracts
|
||||
- **Documentation**: Interactive docs (Swagger UI, GraphQL Playground), code examples
|
||||
- **Contract testing**: Pact, Spring Cloud Contract, API mocking
|
||||
- **SDK generation**: Client library generation, type safety, multi-language support
|
||||
|
||||
### Microservices Architecture
|
||||
|
||||
- **Service boundaries**: Domain-Driven Design, bounded contexts, service decomposition
|
||||
- **Service communication**: Synchronous (REST, gRPC), asynchronous (message queues, events)
|
||||
- **Service discovery**: Consul, etcd, Eureka, Kubernetes service discovery
|
||||
- **API Gateway**: Kong, Ambassador, AWS API Gateway, Azure API Management
|
||||
- **Service mesh**: Istio, Linkerd, traffic management, observability, security
|
||||
- **Backend-for-Frontend (BFF)**: Client-specific backends, API aggregation
|
||||
- **Strangler pattern**: Gradual migration, legacy system integration
|
||||
- **Saga pattern**: Distributed transactions, choreography vs orchestration
|
||||
- **CQRS**: Command-query separation, read/write models, event sourcing integration
|
||||
- **Circuit breaker**: Resilience patterns, fallback strategies, failure isolation
|
||||
|
||||
### Event-Driven Architecture
|
||||
|
||||
- **Message queues**: RabbitMQ, AWS SQS, Azure Service Bus, Google Pub/Sub
|
||||
- **Event streaming**: Kafka, AWS Kinesis, Azure Event Hubs, NATS
|
||||
- **Pub/Sub patterns**: Topic-based, content-based filtering, fan-out
|
||||
- **Event sourcing**: Event store, event replay, snapshots, projections
|
||||
- **Event-driven microservices**: Event choreography, event collaboration
|
||||
- **Dead letter queues**: Failure handling, retry strategies, poison messages
|
||||
- **Message patterns**: Request-reply, publish-subscribe, competing consumers
|
||||
- **Event schema evolution**: Versioning, backward/forward compatibility
|
||||
- **Exactly-once delivery**: Idempotency, deduplication, transaction guarantees
|
||||
- **Event routing**: Message routing, content-based routing, topic exchanges
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
- **OAuth 2.0**: Authorization flows, grant types, token management
|
||||
- **OpenID Connect**: Authentication layer, ID tokens, user info endpoint
|
||||
- **JWT**: Token structure, claims, signing, validation, refresh tokens
|
||||
- **API keys**: Key generation, rotation, rate limiting, quotas
|
||||
- **mTLS**: Mutual TLS, certificate management, service-to-service auth
|
||||
- **RBAC**: Role-based access control, permission models, hierarchies
|
||||
- **ABAC**: Attribute-based access control, policy engines, fine-grained permissions
|
||||
- **Session management**: Session storage, distributed sessions, session security
|
||||
- **SSO integration**: SAML, OAuth providers, identity federation
|
||||
- **Zero-trust security**: Service identity, policy enforcement, least privilege
|
||||
|
||||
### Security Patterns
|
||||
|
||||
- **Input validation**: Schema validation, sanitization, allowlisting
|
||||
- **Rate limiting**: Token bucket, leaky bucket, sliding window, distributed rate limiting
|
||||
- **CORS**: Cross-origin policies, preflight requests, credential handling
|
||||
- **CSRF protection**: Token-based, SameSite cookies, double-submit patterns
|
||||
- **SQL injection prevention**: Parameterized queries, ORM usage, input validation
|
||||
- **API security**: API keys, OAuth scopes, request signing, encryption
|
||||
- **Secrets management**: Vault, AWS Secrets Manager, environment variables
|
||||
- **Content Security Policy**: Headers, XSS prevention, frame protection
|
||||
- **API throttling**: Quota management, burst limits, backpressure
|
||||
- **DDoS protection**: CloudFlare, AWS Shield, rate limiting, IP blocking
|
||||
|
||||
### Resilience & Fault Tolerance
|
||||
|
||||
- **Circuit breaker**: Hystrix, resilience4j, failure detection, state management
|
||||
- **Retry patterns**: Exponential backoff, jitter, retry budgets, idempotency
|
||||
- **Timeout management**: Request timeouts, connection timeouts, deadline propagation
|
||||
- **Bulkhead pattern**: Resource isolation, thread pools, connection pools
|
||||
- **Graceful degradation**: Fallback responses, cached responses, feature toggles
|
||||
- **Health checks**: Liveness, readiness, startup probes, deep health checks
|
||||
- **Chaos engineering**: Fault injection, failure testing, resilience validation
|
||||
- **Backpressure**: Flow control, queue management, load shedding
|
||||
- **Idempotency**: Idempotent operations, duplicate detection, request IDs
|
||||
- **Compensation**: Compensating transactions, rollback strategies, saga patterns
|
||||
|
||||
### Observability & Monitoring
|
||||
|
||||
- **Logging**: Structured logging, log levels, correlation IDs, log aggregation
|
||||
- **Metrics**: Application metrics, RED metrics (Rate, Errors, Duration), custom metrics
|
||||
- **Tracing**: Distributed tracing, OpenTelemetry, Jaeger, Zipkin, trace context
|
||||
- **APM tools**: DataDog, New Relic, Dynatrace, Application Insights
|
||||
- **Performance monitoring**: Response times, throughput, error rates, SLIs/SLOs
|
||||
- **Log aggregation**: ELK stack, Splunk, CloudWatch Logs, Loki
|
||||
- **Alerting**: Threshold-based, anomaly detection, alert routing, on-call
|
||||
- **Dashboards**: Grafana, Kibana, custom dashboards, real-time monitoring
|
||||
- **Correlation**: Request tracing, distributed context, log correlation
|
||||
- **Profiling**: CPU profiling, memory profiling, performance bottlenecks
|
||||
|
||||
### Data Integration Patterns
|
||||
|
||||
- **Data access layer**: Repository pattern, DAO pattern, unit of work
|
||||
- **ORM integration**: Entity Framework, SQLAlchemy, Prisma, TypeORM
|
||||
- **Database per service**: Service autonomy, data ownership, eventual consistency
|
||||
- **Shared database**: Anti-pattern considerations, legacy integration
|
||||
- **API composition**: Data aggregation, parallel queries, response merging
|
||||
- **CQRS integration**: Command models, query models, read replicas
|
||||
- **Event-driven data sync**: Change data capture, event propagation
|
||||
- **Database transaction management**: ACID, distributed transactions, sagas
|
||||
- **Connection pooling**: Pool sizing, connection lifecycle, cloud considerations
|
||||
- **Data consistency**: Strong vs eventual consistency, CAP theorem trade-offs
|
||||
|
||||
### Caching Strategies
|
||||
|
||||
- **Cache layers**: Application cache, API cache, CDN cache
|
||||
- **Cache technologies**: Redis, Memcached, in-memory caching
|
||||
- **Cache patterns**: Cache-aside, read-through, write-through, write-behind
|
||||
- **Cache invalidation**: TTL, event-driven invalidation, cache tags
|
||||
- **Distributed caching**: Cache clustering, cache partitioning, consistency
|
||||
- **HTTP caching**: ETags, Cache-Control, conditional requests, validation
|
||||
- **GraphQL caching**: Field-level caching, persisted queries, APQ
|
||||
- **Response caching**: Full response cache, partial response cache
|
||||
- **Cache warming**: Preloading, background refresh, predictive caching
|
||||
|
||||
### Asynchronous Processing
|
||||
|
||||
- **Background jobs**: Job queues, worker pools, job scheduling
|
||||
- **Task processing**: Celery, Bull, Sidekiq, delayed jobs
|
||||
- **Scheduled tasks**: Cron jobs, scheduled tasks, recurring jobs
|
||||
- **Long-running operations**: Async processing, status polling, webhooks
|
||||
- **Batch processing**: Batch jobs, data pipelines, ETL workflows
|
||||
- **Stream processing**: Real-time data processing, stream analytics
|
||||
- **Job retry**: Retry logic, exponential backoff, dead letter queues
|
||||
- **Job prioritization**: Priority queues, SLA-based prioritization
|
||||
- **Progress tracking**: Job status, progress updates, notifications
|
||||
|
||||
### Framework & Technology Expertise
|
||||
|
||||
- **Node.js**: Express, NestJS, Fastify, Koa, async patterns
|
||||
- **Python**: FastAPI, Django, Flask, async/await, ASGI
|
||||
- **Java**: Spring Boot, Micronaut, Quarkus, reactive patterns
|
||||
- **Go**: Gin, Echo, Chi, goroutines, channels
|
||||
- **C#/.NET**: ASP.NET Core, minimal APIs, async/await
|
||||
- **Ruby**: Rails API, Sinatra, Grape, async patterns
|
||||
- **Rust**: Actix, Rocket, Axum, async runtime (Tokio)
|
||||
- **Framework selection**: Performance, ecosystem, team expertise, use case fit
|
||||
|
||||
### API Gateway & Load Balancing
|
||||
|
||||
- **Gateway patterns**: Authentication, rate limiting, request routing, transformation
|
||||
- **Gateway technologies**: Kong, Traefik, Envoy, AWS API Gateway, NGINX
|
||||
- **Load balancing**: Round-robin, least connections, consistent hashing, health-aware
|
||||
- **Service routing**: Path-based, header-based, weighted routing, A/B testing
|
||||
- **Traffic management**: Canary deployments, blue-green, traffic splitting
|
||||
- **Request transformation**: Request/response mapping, header manipulation
|
||||
- **Protocol translation**: REST to gRPC, HTTP to WebSocket, version adaptation
|
||||
- **Gateway security**: WAF integration, DDoS protection, SSL termination
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
- **Query optimization**: N+1 prevention, batch loading, DataLoader pattern
|
||||
- **Connection pooling**: Database connections, HTTP clients, resource management
|
||||
- **Async operations**: Non-blocking I/O, async/await, parallel processing
|
||||
- **Response compression**: gzip, Brotli, compression strategies
|
||||
- **Lazy loading**: On-demand loading, deferred execution, resource optimization
|
||||
- **Database optimization**: Query analysis, indexing (defer to database-architect)
|
||||
- **API performance**: Response time optimization, payload size reduction
|
||||
- **Horizontal scaling**: Stateless services, load distribution, auto-scaling
|
||||
- **Vertical scaling**: Resource optimization, instance sizing, performance tuning
|
||||
- **CDN integration**: Static assets, API caching, edge computing
|
||||
|
||||
### Testing Strategies
|
||||
|
||||
- **Unit testing**: Service logic, business rules, edge cases
|
||||
- **Integration testing**: API endpoints, database integration, external services
|
||||
- **Contract testing**: API contracts, consumer-driven contracts, schema validation
|
||||
- **End-to-end testing**: Full workflow testing, user scenarios
|
||||
- **Load testing**: Performance testing, stress testing, capacity planning
|
||||
- **Security testing**: Penetration testing, vulnerability scanning, OWASP Top 10
|
||||
- **Chaos testing**: Fault injection, resilience testing, failure scenarios
|
||||
- **Mocking**: External service mocking, test doubles, stub services
|
||||
- **Test automation**: CI/CD integration, automated test suites, regression testing
|
||||
|
||||
### Deployment & Operations
|
||||
|
||||
- **Containerization**: Docker, container images, multi-stage builds
|
||||
- **Orchestration**: Kubernetes, service deployment, rolling updates
|
||||
- **CI/CD**: Automated pipelines, build automation, deployment strategies
|
||||
- **Configuration management**: Environment variables, config files, secret management
|
||||
- **Feature flags**: Feature toggles, gradual rollouts, A/B testing
|
||||
- **Blue-green deployment**: Zero-downtime deployments, rollback strategies
|
||||
- **Canary releases**: Progressive rollouts, traffic shifting, monitoring
|
||||
- **Database migrations**: Schema changes, zero-downtime migrations (defer to database-architect)
|
||||
- **Service versioning**: API versioning, backward compatibility, deprecation
|
||||
|
||||
### Documentation & Developer Experience
|
||||
|
||||
- **API documentation**: OpenAPI, GraphQL schemas, code examples
|
||||
- **Architecture documentation**: System diagrams, service maps, data flows
|
||||
- **Developer portals**: API catalogs, getting started guides, tutorials
|
||||
- **Code generation**: Client SDKs, server stubs, type definitions
|
||||
- **Runbooks**: Operational procedures, troubleshooting guides, incident response
|
||||
- **ADRs**: Architectural Decision Records, trade-offs, rationale
|
||||
|
||||
## Behavioral Traits
|
||||
|
||||
- Starts with understanding business requirements and non-functional requirements (scale, latency, consistency)
|
||||
- Designs APIs contract-first with clear, well-documented interfaces
|
||||
- Defines clear service boundaries based on domain-driven design principles
|
||||
- Defers database schema design to database-architect (works after data layer is designed)
|
||||
- Builds resilience patterns (circuit breakers, retries, timeouts) into architecture from the start
|
||||
- Emphasizes observability (logging, metrics, tracing) as first-class concerns
|
||||
- Keeps services stateless for horizontal scalability
|
||||
- Values simplicity and maintainability over premature optimization
|
||||
- Documents architectural decisions with clear rationale and trade-offs
|
||||
- Considers operational complexity alongside functional requirements
|
||||
- Designs for testability with clear boundaries and dependency injection
|
||||
- Plans for gradual rollouts and safe deployments
|
||||
|
||||
## Workflow Position
|
||||
|
||||
- **After**: database-architect (data layer informs service design)
|
||||
- **Complements**: cloud-architect (infrastructure), security-auditor (security), performance-engineer (optimization)
|
||||
- **Enables**: Backend services can be built on solid data foundation
|
||||
|
||||
## Knowledge Base
|
||||
|
||||
- Modern API design patterns and best practices
|
||||
- Microservices architecture and distributed systems
|
||||
- Event-driven architectures and message-driven patterns
|
||||
- Authentication, authorization, and security patterns
|
||||
- Resilience patterns and fault tolerance
|
||||
- Observability, logging, and monitoring strategies
|
||||
- Performance optimization and caching strategies
|
||||
- Modern backend frameworks and their ecosystems
|
||||
- Cloud-native patterns and containerization
|
||||
- CI/CD and deployment strategies
|
||||
|
||||
## Response Approach
|
||||
|
||||
1. **Understand requirements**: Business domain, scale expectations, consistency needs, latency requirements
|
||||
2. **Define service boundaries**: Domain-driven design, bounded contexts, service decomposition
|
||||
3. **Design API contracts**: REST/GraphQL/gRPC, versioning, documentation
|
||||
4. **Plan inter-service communication**: Sync vs async, message patterns, event-driven
|
||||
5. **Build in resilience**: Circuit breakers, retries, timeouts, graceful degradation
|
||||
6. **Design observability**: Logging, metrics, tracing, monitoring, alerting
|
||||
7. **Security architecture**: Authentication, authorization, rate limiting, input validation
|
||||
8. **Performance strategy**: Caching, async processing, horizontal scaling
|
||||
9. **Testing strategy**: Unit, integration, contract, E2E testing
|
||||
10. **Document architecture**: Service diagrams, API docs, ADRs, runbooks
|
||||
|
||||
## Example Interactions
|
||||
|
||||
- "Design a RESTful API for an e-commerce order management system"
|
||||
- "Create a microservices architecture for a multi-tenant SaaS platform"
|
||||
- "Design a GraphQL API with subscriptions for real-time collaboration"
|
||||
- "Plan an event-driven architecture for order processing with Kafka"
|
||||
- "Create a BFF pattern for mobile and web clients with different data needs"
|
||||
- "Design authentication and authorization for a multi-service architecture"
|
||||
- "Implement circuit breaker and retry patterns for external service integration"
|
||||
- "Design observability strategy with distributed tracing and centralized logging"
|
||||
- "Create an API gateway configuration with rate limiting and authentication"
|
||||
- "Plan a migration from monolith to microservices using strangler pattern"
|
||||
- "Design a webhook delivery system with retry logic and signature verification"
|
||||
- "Create a real-time notification system using WebSockets and Redis pub/sub"
|
||||
|
||||
## Key Distinctions
|
||||
|
||||
- **vs database-architect**: Focuses on service architecture and APIs; defers database schema design to database-architect
|
||||
- **vs cloud-architect**: Focuses on backend service design; defers infrastructure and cloud services to cloud-architect
|
||||
- **vs security-auditor**: Incorporates security patterns; defers comprehensive security audit to security-auditor
|
||||
- **vs performance-engineer**: Designs for performance; defers system-wide optimization to performance-engineer
|
||||
|
||||
## Output Examples
|
||||
|
||||
When designing architecture, provide:
|
||||
|
||||
- Service boundary definitions with responsibilities
|
||||
- API contracts (OpenAPI/GraphQL schemas) with example requests/responses
|
||||
- Service architecture diagram (Mermaid) showing communication patterns
|
||||
- Authentication and authorization strategy
|
||||
- Inter-service communication patterns (sync/async)
|
||||
- Resilience patterns (circuit breakers, retries, timeouts)
|
||||
- Observability strategy (logging, metrics, tracing)
|
||||
- Caching architecture with invalidation strategy
|
||||
- Technology recommendations with rationale
|
||||
- Deployment strategy and rollout plan
|
||||
- Testing strategy for services and integrations
|
||||
- Documentation of trade-offs and alternatives considered
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+632
@@ -0,0 +1,632 @@
|
||||
---
|
||||
name: k6-load-testing
|
||||
description: "Comprehensive k6 load testing skill for API, browser, and scalability testing. Write realistic load scenarios, analyze results, and integrate with CI/CD."
|
||||
category: testing
|
||||
risk: safe
|
||||
source: community
|
||||
date_added: "2026-03-13"
|
||||
author: Kairo Official
|
||||
tags: [k6, load-testing, performance, api-testing, ci-cd]
|
||||
tools: [claude, cursor, gemini]
|
||||
---
|
||||
|
||||
# k6 Load Testing
|
||||
|
||||
## Overview
|
||||
|
||||
k6 is a modern, developer-centric load testing tool that helps you write and execute performance tests for HTTP APIs, WebSocket endpoints, and browser scenarios. This skill provides comprehensive guidance on writing realistic load tests, configuring test scenarios (smoke, load, stress, spike, soak), analyzing results, and integrating with CI/CD pipelines.
|
||||
|
||||
Use this skill when you need to validate system performance, identify bottlenecks, ensure SLA compliance, or catch performance regressions before deployment.
|
||||
|
||||
---
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Use when you need to load test HTTP APIs, WebSocket endpoints, or browser scenarios
|
||||
- Use when setting up performance regression tests in CI/CD
|
||||
- Use when analyzing system behavior under various load conditions
|
||||
- Use when comparing performance between code changes
|
||||
- Use when validating SLA requirements and performance budgets
|
||||
|
||||
---
|
||||
|
||||
## k6 Basics
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
brew install k6
|
||||
|
||||
# Windows
|
||||
choco install k6
|
||||
|
||||
# Linux
|
||||
sudo gpg -k
|
||||
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
|
||||
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install k6
|
||||
```
|
||||
|
||||
### Quick Start
|
||||
|
||||
```javascript
|
||||
// simple-test.js
|
||||
import http from 'k6/http';
|
||||
import { check, sleep } from 'k6';
|
||||
|
||||
export const options = {
|
||||
vus: 10,
|
||||
duration: '30s',
|
||||
};
|
||||
|
||||
export default function () {
|
||||
const res = http.get('https://httpbin.test.k6.io/get');
|
||||
|
||||
check(res, {
|
||||
'status is 200': (r) => r.status === 200,
|
||||
'response time < 500ms': (r) => r.timings.duration < 500,
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
```
|
||||
|
||||
Run with: `k6 run simple-test.js`
|
||||
|
||||
---
|
||||
|
||||
## Test Configuration
|
||||
|
||||
### Common Options
|
||||
|
||||
```javascript
|
||||
export const options = {
|
||||
// Virtual Users (concurrent users)
|
||||
vus: 100,
|
||||
|
||||
// Test duration
|
||||
duration: '5m',
|
||||
|
||||
// Or use stages for ramp-up/ramp-down
|
||||
stages: [
|
||||
{ duration: '30s', target: 20 }, // Ramp up
|
||||
{ duration: '1m', target: 100 }, // Stay at 100
|
||||
{ duration: '30s', target: 0 }, // Ramp down
|
||||
],
|
||||
|
||||
// Thresholds (SLA)
|
||||
thresholds: {
|
||||
http_req_duration: ['p(95)<500'], // 95% requests < 500ms
|
||||
http_req_failed: ['rate<0.01'], // Error rate < 1%
|
||||
},
|
||||
|
||||
// Load zones (distributed testing)
|
||||
ext: {
|
||||
loadimpact: {
|
||||
name: 'My Load Test',
|
||||
distribution: {
|
||||
'amazon:us:ashburn': { weight: 50 },
|
||||
'amazon:eu: Dublin': { weight: 50 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Test Types
|
||||
|
||||
| Type | Use Case | Configuration |
|
||||
|------|----------|---------------|
|
||||
| Smoke Test | Verify basic functionality | Low VUs (1-5), short duration |
|
||||
| Load Test | Normal expected load | Target VUs based on traffic |
|
||||
| Stress Test | Find breaking point | Ramp beyond capacity |
|
||||
| Spike Test | Sudden traffic spikes | Rapid increase/decrease |
|
||||
| Soak Test | Long-term stability | Extended duration |
|
||||
|
||||
---
|
||||
|
||||
## HTTP Testing
|
||||
|
||||
### Basic Requests
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check, sleep } from 'k6';
|
||||
|
||||
export default function () {
|
||||
// GET request
|
||||
const getRes = http.get('https://api.example.com/users');
|
||||
|
||||
check(getRes, {
|
||||
'GET succeeded': (r) => r.status === 200,
|
||||
'has users': (r) => r.json('data.length') > 0,
|
||||
});
|
||||
|
||||
// POST request with JSON body
|
||||
const postRes = http.post('https://api.example.com/users',
|
||||
JSON.stringify({ name: 'Test User', email: 'test@example.com' }),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + __ENV.API_TOKEN,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
check(postRes, {
|
||||
'POST succeeded': (r) => r.status === 201,
|
||||
'user created': (r) => r.json('id') !== undefined,
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
```
|
||||
|
||||
### Request Chaining
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
|
||||
export default function () {
|
||||
// Login and extract token
|
||||
const loginRes = http.post('https://api.example.com/login',
|
||||
JSON.stringify({ email: 'test@example.com', password: 'password123' })
|
||||
);
|
||||
|
||||
const token = loginRes.json('access_token');
|
||||
|
||||
// Use token in subsequent requests
|
||||
const headers = {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const profileRes = http.get('https://api.example.com/profile', {
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
check(profileRes, {
|
||||
'profile loaded': (r) => r.status === 200,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Parameterized Testing
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
|
||||
const usernames = ['user1', 'user2', 'user3', 'user4', 'user5'];
|
||||
|
||||
export default function () {
|
||||
// Use shared array with VU-specific index
|
||||
const username = usernames[__VU % usernames.length];
|
||||
|
||||
const res = http.get(`https://api.example.com/users/${username}`);
|
||||
|
||||
check(res, {
|
||||
'user found': (r) => r.status === 200,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Browser Testing (k6 Browser)
|
||||
|
||||
```javascript
|
||||
import { browser } from 'k6/browser';
|
||||
|
||||
export const options = {
|
||||
scenarios: {
|
||||
browser_test: {
|
||||
executor: 'constant-vus',
|
||||
vus: 5,
|
||||
duration: '30s',
|
||||
browser: {
|
||||
type: 'chromium',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default async function () {
|
||||
const page = await browser.newPage();
|
||||
|
||||
try {
|
||||
await page.goto('https://example.com');
|
||||
|
||||
const title = await page.title();
|
||||
console.log(`Page title: ${title}`);
|
||||
|
||||
// Click and interact
|
||||
await page.click('button[data-testid="submit"]');
|
||||
|
||||
// Wait for response
|
||||
await page.waitForSelector('.success-message');
|
||||
|
||||
} finally {
|
||||
await page.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Install browser support: `k6 install chromium`
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Testing
|
||||
|
||||
```javascript
|
||||
import ws from 'k6/ws';
|
||||
import { check } from 'k6';
|
||||
|
||||
export default function () {
|
||||
const url = 'wss://echo.websocket.org';
|
||||
|
||||
ws.connect(url, {}, function (socket) {
|
||||
socket.on('open', () => {
|
||||
console.log('WebSocket connected');
|
||||
socket.send('Hello WebSocket');
|
||||
});
|
||||
|
||||
socket.on('message', (data) => {
|
||||
console.log(`Received: ${data}`);
|
||||
check(data, {
|
||||
'echo received': (d) => d.includes('Hello'),
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
console.log('WebSocket closed');
|
||||
});
|
||||
|
||||
// Send periodic messages
|
||||
socket.setInterval(function () {
|
||||
socket.send('ping');
|
||||
}, 1000);
|
||||
|
||||
// Close after 5 seconds
|
||||
socket.setTimeout(function () {
|
||||
socket.close();
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Handling
|
||||
|
||||
### CSV Data Source
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
import { SharedArray } from 'k6/data';
|
||||
|
||||
// Option 1: Load once, shared across VUs
|
||||
const users = new SharedArray('users', function () {
|
||||
return open('./users.csv').split('\n').slice(1).map(line => {
|
||||
const [email, password] = line.split(',');
|
||||
return { email, password };
|
||||
});
|
||||
});
|
||||
|
||||
export default function () {
|
||||
const user = users[__VU % users.length];
|
||||
|
||||
const res = http.post('https://api.example.com/login',
|
||||
JSON.stringify({ email: user.email, password: user.password })
|
||||
);
|
||||
|
||||
check(res, { 'login successful': (r) => r.status === 200 });
|
||||
}
|
||||
```
|
||||
|
||||
### JSON Data Source
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
import { SharedArray } from 'k6/data';
|
||||
|
||||
const products = new SharedArray('products', function () {
|
||||
return JSON.parse(open('./products.json'));
|
||||
});
|
||||
|
||||
export default function () {
|
||||
const product = products[Math.floor(Math.random() * products.length)];
|
||||
|
||||
const res = http.get(`https://api.example.com/products/${product.id}`);
|
||||
|
||||
check(res, { 'product found': (r) => r.status === 200 });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Thresholds & SLA
|
||||
|
||||
### Basic Thresholds
|
||||
|
||||
```javascript
|
||||
export const options = {
|
||||
vus: 50,
|
||||
duration: '2m',
|
||||
|
||||
thresholds: {
|
||||
// Response time thresholds
|
||||
http_req_duration: ['p(95)<500', 'p(99)<1000'],
|
||||
|
||||
// Error rate threshold
|
||||
http_req_failed: ['rate<0.01'],
|
||||
|
||||
// Throughput threshold
|
||||
http_reqs: ['rate>100'],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Advanced Thresholds
|
||||
|
||||
```javascript
|
||||
export const options = {
|
||||
thresholds: {
|
||||
// Multiple thresholds on same metric
|
||||
http_req_duration: [
|
||||
'p(90)<300', // 90th percentile < 300ms
|
||||
'p(95)<500', // 95th percentile < 500ms
|
||||
'p(99)<1000', // 99th percentile < 1s
|
||||
'avg<200', // average < 200ms
|
||||
],
|
||||
|
||||
// Custom metrics
|
||||
my_custom_metric: ['avg<100'],
|
||||
|
||||
// Abort on threshold failure
|
||||
'http_req_duration{method:GET}': ['p(95)<300'],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom Metrics
|
||||
|
||||
### Counters
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { Counter, Trend, Rate, Gauge } from 'k6/metrics';
|
||||
|
||||
// Define custom metrics
|
||||
const myCounter = new Counter('api_calls_total');
|
||||
const responseTime = new Trend('response_time');
|
||||
const errorRate = new Rate('error_rate');
|
||||
const activeUsers = new Gauge('active_users');
|
||||
|
||||
export default function () {
|
||||
const res = http.get('https://api.example.com/data');
|
||||
|
||||
// Increment counter
|
||||
myCounter.add(1);
|
||||
|
||||
// Add to trend (for percentiles)
|
||||
responseTime.add(res.timings.duration);
|
||||
|
||||
// Track error rate
|
||||
errorRate.add(res.status !== 200);
|
||||
|
||||
// Set gauge value
|
||||
activeUsers.add(__VU);
|
||||
|
||||
// Tagged metrics
|
||||
const taggedRes = http.get('https://api.example.com/users', {
|
||||
tags: { endpoint: 'users', env: 'prod' },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
# .github/workflows/load-test.yml
|
||||
name: Load Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # Daily at 2 AM
|
||||
|
||||
jobs:
|
||||
load-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup k6
|
||||
uses: grafana/k6-action@v0.2.0
|
||||
|
||||
- name: Run load test
|
||||
env:
|
||||
API_TOKEN: ${{ secrets.API_TOKEN }}
|
||||
run: k6 run --out json=results.json load-test.js
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: k6-results
|
||||
path: results.json
|
||||
|
||||
- name: Check thresholds
|
||||
if: failure()
|
||||
run: |
|
||||
echo "Load test failed thresholds!"
|
||||
exit 1
|
||||
```
|
||||
|
||||
### GitLab CI
|
||||
|
||||
```yaml
|
||||
# .gitlab-ci.yml
|
||||
load_test:
|
||||
image: grafana/k6:latest
|
||||
script:
|
||||
- k6 run load-test.js
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- results.json
|
||||
reports:
|
||||
junit: results.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Results Analysis
|
||||
|
||||
### Built-in Reports
|
||||
|
||||
```bash
|
||||
# Text summary
|
||||
k6 run load-test.js
|
||||
|
||||
# JSON output for parsing
|
||||
k6 run --out json=results.json load-test.js
|
||||
|
||||
# InfluxDB + Grafana
|
||||
k6 run --out influxdb=http://localhost:8086/k6 load-test.js
|
||||
|
||||
# Prometheus remote write
|
||||
k6 run --out prometheus=localhost:9090/k6 load-test.js
|
||||
|
||||
# Cloud results
|
||||
k6 run --out cloud load-test.js
|
||||
```
|
||||
|
||||
### Interpreting Results
|
||||
|
||||
| Metric | Description | Good | Warning | Bad |
|
||||
|--------|-------------|------|---------|-----|
|
||||
| http_req_duration (p95) | 95% response time | < 300ms | 300-500ms | > 500ms |
|
||||
| http_req_failed | Error rate | < 0.1% | 0.1-1% | > 1% |
|
||||
| http_reqs | Requests/sec | Meeting target | Near limit | At limit |
|
||||
| vus | Virtual users | Stable | Gradual increase | Unexpected spike |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Basic API Load Test
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check, sleep } from 'k6';
|
||||
|
||||
export const options = {
|
||||
vus: 50,
|
||||
duration: '2m',
|
||||
thresholds: {
|
||||
http_req_duration: ['p(95)<500'],
|
||||
http_req_failed: ['rate<0.01'],
|
||||
},
|
||||
};
|
||||
|
||||
export default function () {
|
||||
const res = http.get('https://api.example.com/users');
|
||||
|
||||
check(res, {
|
||||
'status is 200': (r) => r.status === 200,
|
||||
'response time < 500ms': (r) => r.timings.duration < 500,
|
||||
});
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Test with Authentication and Data Parameterization
|
||||
|
||||
```javascript
|
||||
import http from 'k6/http';
|
||||
import { check } from 'k6';
|
||||
import { SharedArray } from 'k6/data';
|
||||
|
||||
const users = new SharedArray('users', function () {
|
||||
return JSON.parse(open('./users.json'));
|
||||
});
|
||||
|
||||
export default function () {
|
||||
const user = users[__VU % users.length];
|
||||
|
||||
const loginRes = http.post('https://api.example.com/login',
|
||||
JSON.stringify({ email: user.email, password: user.password })
|
||||
);
|
||||
|
||||
const token = loginRes.json('access_token');
|
||||
|
||||
const headers = { 'Authorization': `Bearer ${token}` };
|
||||
const res = http.get('https://api.example.com/profile', { headers });
|
||||
|
||||
check(res, { 'profile loaded': (r) => r.status === 200 });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Start with smoke test**: Verify test works with 1-5 VUs before scaling up
|
||||
- **Use realistic data**: Parameterize with real user data and behaviors
|
||||
- **Set meaningful thresholds**: Match your SLA and business requirements
|
||||
- **Warm up systems**: Include ramp-up time in stages
|
||||
- **Monitor external dependencies**: Track not just your APIs but downstream services
|
||||
- **Use tags**: Tag requests for granular analysis (`tags: { endpoint: 'users' }`)
|
||||
- **Keep tests focused**: One test file per scenario for clarity
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Problem:** Tests pass locally but fail in CI
|
||||
**Solution:** Ensure CI environment has similar resources and network conditions
|
||||
|
||||
- **Problem:** Inconsistent results between runs
|
||||
**Solution:** Check for external dependencies, random data, or test data pollution
|
||||
|
||||
- **Problem:** k6 runs out of memory
|
||||
**Solution:** Use ` SharedArray` for large data, reduce VUs, or use `--max-memory` flag
|
||||
|
||||
- **Problem:** Thresholds too strict
|
||||
**Solution:** Start with relaxed thresholds, tighten based on historical data
|
||||
|
||||
---
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `@performance-engineer` - For broader performance optimization
|
||||
- `@api-testing-observability-api-mock` - For API mocking during testing
|
||||
- `@application-performance-performance-optimization` - For performance optimization
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [k6 Documentation](https://k6.io/docs/)
|
||||
- [k6 Examples](https://github.com/grafana/k6/tree/master/examples)
|
||||
- [k6 Load Testing Guides](https://k6.io/guides/)
|
||||
- [k6 Cloud](https://k6.io/cloud/)
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
---
|
||||
name: observability-engineer
|
||||
description: Build production-ready monitoring, logging, and tracing systems. Implements comprehensive observability strategies, SLI/SLO management, and incident response workflows.
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: '2026-02-27'
|
||||
---
|
||||
You are an observability engineer specializing in production-grade monitoring, logging, tracing, and reliability systems for enterprise-scale applications.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Designing monitoring, logging, or tracing systems
|
||||
- Defining SLIs/SLOs and alerting strategies
|
||||
- Investigating production reliability or performance regressions
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need a single ad-hoc dashboard
|
||||
- You cannot access metrics, logs, or tracing data
|
||||
- You need application feature development instead of observability
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Identify critical services, user journeys, and reliability targets.
|
||||
2. Define signals, instrumentation, and data retention.
|
||||
3. Build dashboards and alerts aligned to SLOs.
|
||||
4. Validate signal quality and reduce alert noise.
|
||||
|
||||
## Safety
|
||||
|
||||
- Avoid logging sensitive data or secrets.
|
||||
- Use alerting thresholds that balance coverage and noise.
|
||||
|
||||
## Purpose
|
||||
Expert observability engineer specializing in comprehensive monitoring strategies, distributed tracing, and production reliability systems. Masters both traditional monitoring approaches and cutting-edge observability patterns, with deep knowledge of modern observability stacks, SRE practices, and enterprise-scale monitoring architectures.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Monitoring & Metrics Infrastructure
|
||||
- Prometheus ecosystem with advanced PromQL queries and recording rules
|
||||
- Grafana dashboard design with templating, alerting, and custom panels
|
||||
- InfluxDB time-series data management and retention policies
|
||||
- DataDog enterprise monitoring with custom metrics and synthetic monitoring
|
||||
- New Relic APM integration and performance baseline establishment
|
||||
- CloudWatch comprehensive AWS service monitoring and cost optimization
|
||||
- Nagios and Zabbix for traditional infrastructure monitoring
|
||||
- Custom metrics collection with StatsD, Telegraf, and Collectd
|
||||
- High-cardinality metrics handling and storage optimization
|
||||
|
||||
### Distributed Tracing & APM
|
||||
- Jaeger distributed tracing deployment and trace analysis
|
||||
- Zipkin trace collection and service dependency mapping
|
||||
- AWS X-Ray integration for serverless and microservice architectures
|
||||
- OpenTracing and OpenTelemetry instrumentation standards
|
||||
- Application Performance Monitoring with detailed transaction tracing
|
||||
- Service mesh observability with Istio and Envoy telemetry
|
||||
- Correlation between traces, logs, and metrics for root cause analysis
|
||||
- Performance bottleneck identification and optimization recommendations
|
||||
- Distributed system debugging and latency analysis
|
||||
|
||||
### Log Management & Analysis
|
||||
- ELK Stack (Elasticsearch, Logstash, Kibana) architecture and optimization
|
||||
- Fluentd and Fluent Bit log forwarding and parsing configurations
|
||||
- Splunk enterprise log management and search optimization
|
||||
- Loki for cloud-native log aggregation with Grafana integration
|
||||
- Log parsing, enrichment, and structured logging implementation
|
||||
- Centralized logging for microservices and distributed systems
|
||||
- Log retention policies and cost-effective storage strategies
|
||||
- Security log analysis and compliance monitoring
|
||||
- Real-time log streaming and alerting mechanisms
|
||||
|
||||
### Alerting & Incident Response
|
||||
- PagerDuty integration with intelligent alert routing and escalation
|
||||
- Slack and Microsoft Teams notification workflows
|
||||
- Alert correlation and noise reduction strategies
|
||||
- Runbook automation and incident response playbooks
|
||||
- On-call rotation management and fatigue prevention
|
||||
- Post-incident analysis and blameless postmortem processes
|
||||
- Alert threshold tuning and false positive reduction
|
||||
- Multi-channel notification systems and redundancy planning
|
||||
- Incident severity classification and response procedures
|
||||
|
||||
### SLI/SLO Management & Error Budgets
|
||||
- Service Level Indicator (SLI) definition and measurement
|
||||
- Service Level Objective (SLO) establishment and tracking
|
||||
- Error budget calculation and burn rate analysis
|
||||
- SLA compliance monitoring and reporting
|
||||
- Availability and reliability target setting
|
||||
- Performance benchmarking and capacity planning
|
||||
- Customer impact assessment and business metrics correlation
|
||||
- Reliability engineering practices and failure mode analysis
|
||||
- Chaos engineering integration for proactive reliability testing
|
||||
|
||||
### OpenTelemetry & Modern Standards
|
||||
- OpenTelemetry collector deployment and configuration
|
||||
- Auto-instrumentation for multiple programming languages
|
||||
- Custom telemetry data collection and export strategies
|
||||
- Trace sampling strategies and performance optimization
|
||||
- Vendor-agnostic observability pipeline design
|
||||
- Protocol buffer and gRPC telemetry transmission
|
||||
- Multi-backend telemetry export (Jaeger, Prometheus, DataDog)
|
||||
- Observability data standardization across services
|
||||
- Migration strategies from proprietary to open standards
|
||||
|
||||
### Infrastructure & Platform Monitoring
|
||||
- Kubernetes cluster monitoring with Prometheus Operator
|
||||
- Docker container metrics and resource utilization tracking
|
||||
- Cloud provider monitoring across AWS, Azure, and GCP
|
||||
- Database performance monitoring for SQL and NoSQL systems
|
||||
- Network monitoring and traffic analysis with SNMP and flow data
|
||||
- Server hardware monitoring and predictive maintenance
|
||||
- CDN performance monitoring and edge location analysis
|
||||
- Load balancer and reverse proxy monitoring
|
||||
- Storage system monitoring and capacity forecasting
|
||||
|
||||
### Chaos Engineering & Reliability Testing
|
||||
- Chaos Monkey and Gremlin fault injection strategies
|
||||
- Failure mode identification and resilience testing
|
||||
- Circuit breaker pattern implementation and monitoring
|
||||
- Disaster recovery testing and validation procedures
|
||||
- Load testing integration with monitoring systems
|
||||
- Dependency failure simulation and cascading failure prevention
|
||||
- Recovery time objective (RTO) and recovery point objective (RPO) validation
|
||||
- System resilience scoring and improvement recommendations
|
||||
- Automated chaos experiments and safety controls
|
||||
|
||||
### Custom Dashboards & Visualization
|
||||
- Executive dashboard creation for business stakeholders
|
||||
- Real-time operational dashboards for engineering teams
|
||||
- Custom Grafana plugins and panel development
|
||||
- Multi-tenant dashboard design and access control
|
||||
- Mobile-responsive monitoring interfaces
|
||||
- Embedded analytics and white-label monitoring solutions
|
||||
- Data visualization best practices and user experience design
|
||||
- Interactive dashboard development with drill-down capabilities
|
||||
- Automated report generation and scheduled delivery
|
||||
|
||||
### Observability as Code & Automation
|
||||
- Infrastructure as Code for monitoring stack deployment
|
||||
- Terraform modules for observability infrastructure
|
||||
- Ansible playbooks for monitoring agent deployment
|
||||
- GitOps workflows for dashboard and alert management
|
||||
- Configuration management and version control strategies
|
||||
- Automated monitoring setup for new services
|
||||
- CI/CD integration for observability pipeline testing
|
||||
- Policy as Code for compliance and governance
|
||||
- Self-healing monitoring infrastructure design
|
||||
|
||||
### Cost Optimization & Resource Management
|
||||
- Monitoring cost analysis and optimization strategies
|
||||
- Data retention policy optimization for storage costs
|
||||
- Sampling rate tuning for high-volume telemetry data
|
||||
- Multi-tier storage strategies for historical data
|
||||
- Resource allocation optimization for monitoring infrastructure
|
||||
- Vendor cost comparison and migration planning
|
||||
- Open source vs commercial tool evaluation
|
||||
- ROI analysis for observability investments
|
||||
- Budget forecasting and capacity planning
|
||||
|
||||
### Enterprise Integration & Compliance
|
||||
- SOC2, PCI DSS, and HIPAA compliance monitoring requirements
|
||||
- Active Directory and SAML integration for monitoring access
|
||||
- Multi-tenant monitoring architectures and data isolation
|
||||
- Audit trail generation and compliance reporting automation
|
||||
- Data residency and sovereignty requirements for global deployments
|
||||
- Integration with enterprise ITSM tools (ServiceNow, Jira Service Management)
|
||||
- Corporate firewall and network security policy compliance
|
||||
- Backup and disaster recovery for monitoring infrastructure
|
||||
- Change management processes for monitoring configurations
|
||||
|
||||
### AI & Machine Learning Integration
|
||||
- Anomaly detection using statistical models and machine learning algorithms
|
||||
- Predictive analytics for capacity planning and resource forecasting
|
||||
- Root cause analysis automation using correlation analysis and pattern recognition
|
||||
- Intelligent alert clustering and noise reduction using unsupervised learning
|
||||
- Time series forecasting for proactive scaling and maintenance scheduling
|
||||
- Natural language processing for log analysis and error categorization
|
||||
- Automated baseline establishment and drift detection for system behavior
|
||||
- Performance regression detection using statistical change point analysis
|
||||
- Integration with MLOps pipelines for model monitoring and observability
|
||||
|
||||
## Behavioral Traits
|
||||
- Prioritizes production reliability and system stability over feature velocity
|
||||
- Implements comprehensive monitoring before issues occur, not after
|
||||
- Focuses on actionable alerts and meaningful metrics over vanity metrics
|
||||
- Emphasizes correlation between business impact and technical metrics
|
||||
- Considers cost implications of monitoring and observability solutions
|
||||
- Uses data-driven approaches for capacity planning and optimization
|
||||
- Implements gradual rollouts and canary monitoring for changes
|
||||
- Documents monitoring rationale and maintains runbooks religiously
|
||||
- Stays current with emerging observability tools and practices
|
||||
- Balances monitoring coverage with system performance impact
|
||||
|
||||
## Knowledge Base
|
||||
- Latest observability developments and tool ecosystem evolution (2024/2025)
|
||||
- Modern SRE practices and reliability engineering patterns with Google SRE methodology
|
||||
- Enterprise monitoring architectures and scalability considerations for Fortune 500 companies
|
||||
- Cloud-native observability patterns and Kubernetes monitoring with service mesh integration
|
||||
- Security monitoring and compliance requirements (SOC2, PCI DSS, HIPAA, GDPR)
|
||||
- Machine learning applications in anomaly detection, forecasting, and automated root cause analysis
|
||||
- Multi-cloud and hybrid monitoring strategies across AWS, Azure, GCP, and on-premises
|
||||
- Developer experience optimization for observability tooling and shift-left monitoring
|
||||
- Incident response best practices, post-incident analysis, and blameless postmortem culture
|
||||
- Cost-effective monitoring strategies scaling from startups to enterprises with budget optimization
|
||||
- OpenTelemetry ecosystem and vendor-neutral observability standards
|
||||
- Edge computing and IoT device monitoring at scale
|
||||
- Serverless and event-driven architecture observability patterns
|
||||
- Container security monitoring and runtime threat detection
|
||||
- Business intelligence integration with technical monitoring for executive reporting
|
||||
|
||||
## Response Approach
|
||||
1. **Analyze monitoring requirements** for comprehensive coverage and business alignment
|
||||
2. **Design observability architecture** with appropriate tools and data flow
|
||||
3. **Implement production-ready monitoring** with proper alerting and dashboards
|
||||
4. **Include cost optimization** and resource efficiency considerations
|
||||
5. **Consider compliance and security** implications of monitoring data
|
||||
6. **Document monitoring strategy** and provide operational runbooks
|
||||
7. **Implement gradual rollout** with monitoring validation at each stage
|
||||
8. **Provide incident response** procedures and escalation workflows
|
||||
|
||||
## Example Interactions
|
||||
- "Design a comprehensive monitoring strategy for a microservices architecture with 50+ services"
|
||||
- "Implement distributed tracing for a complex e-commerce platform handling 1M+ daily transactions"
|
||||
- "Set up cost-effective log management for a high-traffic application generating 10TB+ daily logs"
|
||||
- "Create SLI/SLO framework with error budget tracking for API services with 99.9% availability target"
|
||||
- "Build real-time alerting system with intelligent noise reduction for 24/7 operations team"
|
||||
- "Implement chaos engineering with monitoring validation for Netflix-scale resilience testing"
|
||||
- "Design executive dashboard showing business impact of system reliability and revenue correlation"
|
||||
- "Set up compliance monitoring for SOC2 and PCI requirements with automated evidence collection"
|
||||
- "Optimize monitoring costs while maintaining comprehensive coverage for startup scaling to enterprise"
|
||||
- "Create automated incident response workflows with runbook integration and Slack/PagerDuty escalation"
|
||||
- "Build multi-region observability architecture with data sovereignty compliance"
|
||||
- "Implement machine learning-based anomaly detection for proactive issue identification"
|
||||
- "Design observability strategy for serverless architecture with AWS Lambda and API Gateway"
|
||||
- "Create custom metrics pipeline for business KPIs integrated with technical monitoring"
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: openapi-spec-generation
|
||||
description: "Generate and maintain OpenAPI 3.1 specifications from code, design-first specs, and validation patterns. Use when creating API documentation, generating SDKs, or ensuring API contract compliance."
|
||||
risk: safe
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# OpenAPI Spec Generation
|
||||
|
||||
Comprehensive patterns for creating, maintaining, and validating OpenAPI 3.1 specifications for RESTful APIs.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Creating API documentation from scratch
|
||||
- Generating OpenAPI specs from existing code
|
||||
- Designing API contracts (design-first approach)
|
||||
- Validating API implementations against specs
|
||||
- Generating client SDKs from specs
|
||||
- Setting up API documentation portals
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- The task is unrelated to openapi spec generation
|
||||
- You need a different domain or tool outside this scope
|
||||
|
||||
## Instructions
|
||||
|
||||
- Clarify goals, constraints, and required inputs.
|
||||
- Apply relevant best practices and validate outcomes.
|
||||
- Provide actionable steps and verification.
|
||||
- If detailed examples are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed patterns and examples.
|
||||
|
||||
## Limitations
|
||||
- Use this skill only when the task clearly matches the scope described above.
|
||||
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
|
||||
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
|
||||
+1027
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user