accessibility-patterns
WCAG 2.2 AA compliance, ARIA patterns, keyboard navigation, screen reader optimization
API design, versioning, testing, schema validation, and contract testing patterns for REST and GraphQL APIs.
$ npx -y skills add vibeeval/vibecosystem --skill api-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/api-patternsContext preview
The summary Claude sees to decide when to auto-load this skill.
API design, versioning, testing, schema validation, and contract testing patterns for REST and GraphQL APIs.
name: api-patterns description: API design, versioning, testing, schema validation, and contract testing patterns for REST and GraphQL APIs.
REST and GraphQL API design patterns for consistent, versioned, and well-tested interfaces.
// routes/v1/markets.ts
// routes/v2/markets.ts
// URL: /api/v1/markets, /api/v2/markets
// Express router setup
import { Router } from 'express'
const v1Router = Router()
const v2Router = Router()
app.use('/api/v1', v1Router)
app.use('/api/v2', v2Router)
// Deprecation header middleware
function deprecationWarning(version: string, sunsetDate: string) {
return (_req: Request, res: Response, next: NextFunction) => {
res.setHeader('Deprecation', 'true')
res.setHeader('Sunset', sunsetDate)
res.setHeader('Link', `</api/v${parseInt(version) + 1}>; rel="successor-version"`)
next()
}
}
v1Router.use(deprecationWarning('1', 'Sat, 01 Jan 2027 00:00:00 GMT'))// Accept: application/vnd.api+json;version=2
function versionMiddleware(req: Request, res: Response, next: NextFunction) {
const accept = req.headers['accept'] || ''
const match = accept.match(/version=(\d+)/)
req.apiVersion = match ? parseInt(match[1]) : 1
next()
}import { z } from 'zod'
// Request schema
const CreateMarketSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().max(2000).optional(),
category: z.enum(['sports', 'politics', 'crypto', 'tech']),
closeAt: z.string().datetime(),
initialLiquidity: z.number().positive().max(1_000_000)
})
// Response schema - strip internal fields
const MarketResponseSchema = z.object({
id: z.string().uuid(),
name: z.string(),
category: z.string(),
status: z.enum(['open', 'closed', 'resolved']),
volume: z.number(),
createdAt: z.string().datetime()
})
type CreateMarketDto = z.infer<typeof CreateMarketSchema>
type MarketResponse = z.infer<typeof MarketResponseSchema>
// Validation middleware
function validate<T>(schema: z.ZodSchema<T>) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body)
if (!result.success) {
return res.status(400).json({
success: false,
error: 'Validation failed',
code: 'VALIDATION_ERROR',
details: result.error.flatten()
})
}
req.validated = result.data
next()
}
}
// Usage
router.post('/markets', validate(CreateMarketSchema), async (req, res) => {
const dto = req.validated as CreateMarketDto
const market = await marketService.create(dto)
const response = MarketResponseSchema.parse(market)
res.status(201).json({ success: true, data: response })
})// Always: { success, error, code, details? }
interface ApiError {
success: false
error: string
code: string
details?: unknown
requestId?: string
}
interface ApiSuccess<T> {
success: true
data: T
meta?: { total?: number; page?: number; limit?: number }
}
const ERROR_CODES = {
VALIDATION_ERROR: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
CONFLICT: 409,
RATE_LIMITED: 429,
INTERNAL: 500
} as const
function apiError(
res: Response,
code: keyof typeof ERROR_CODES,
message: string,
details?: unknown
): Response {
return res.status(ERROR_CODES[code]).json({
success: false,
error: message,
code,
details,
requestId: res.locals.requestId
} satisfies ApiError)
}interface CursorPage<T> {
items: T[]
nextCursor: string | null
prevCursor: string | null
hasMore: boolean
}
async function paginateWithCursor<T extends { id: string; createdAt: Date }>(
query: (cursor: string | null, limit: number) => Promise<T[]>,
cursor: string | null,
limit = 20
): Promise<CursorPage<T>> {
// Fetch one extra to detect hasMore
const items = await query(cursor, limit + 1)
const hasMore = items.length > limit
const page = hasMore ? items.slice(0, limit) : items
return {
items: page,
nextCursor: hasMore ? Buffer.from(page[page.length - 1].id).toString('base64') : null,
prevCursor: cursor,
hasMore
}
}
// GET /api/markets?cursor=<base64>&limit=20
router.get('/markets', async (req, res) => {
const cursor = req.query.cursor as string | null
const limit = Math.min(parseInt(req.query.limit as string) || 20, 100)
const decoded = cursor ? Buffer.from(cursor, 'base64').toString() : null
const page = await paginateWithCursor(
(c, l) => db.market.findMany({
take: l,
skip: c ? 1 : 0,
cursor: c ? { id: c } : undefined,
orderBy: { createdAt: 'desc' }
}),
decoded,
limit
)
res.json({ success: true, ...page })
})interface OffsetPage<T> {
items: T[]
total: number
page: number
limit: number
totalPages: number
}
// GET /api/markets?page=2&limit=20import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL!)
async function tokenBucket(
key: string,
capacity: number,
refillRate: number // tokens per second
): Promise<{ allowed: boolean; remaining: number; resetIn: number }> {
const now = Date.now()
const bucketKey = `ratelimit:${key}`
const script = `
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local bucket = redis.call('HMGET', key, 'tokens', 'last_refill')
local tokens = tonumber(bucket[1]) or capacity
local last_refill = tonumber(bucket[2]) or now
local elapsed = (now - last_refill) / 1000
tokens = math.min(capacity, tokens + elapsed * reYour AI software team. Built on Claude Code. vibecosystem turns Claude Code into a full AI software team — 138 specialized agents that plan, build, review, test, and learn from every mistake. No configuration needed — just install and code.
Repo: vibeeval/vibecosystem
WCAG 2.2 AA compliance, ARIA patterns, keyboard navigation, screen reader optimization
axe-core integration, WCAG 2.2 AA checklist, keyboard navigation testing, screen reader testing, and ARIA pattern validation.
Steam-style achievement system with XP, levels, streaks, and skill trees. Gamifies the development workflow. 25 achievements across 5 categories.
Framework for measuring and tracking agent response quality over time. Detects regressions before they reach production. Use when evaluating agent changes,…
Agent ve skill dosyalarinin yapisal dogrulamasi. Frontmatter kontrol, naming convention, zorunlu bolum kontrolu, tutarlilik denetimi. Yeni agent/skill…