惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
Recent Announcements
Recent Announcements
V
Visual Studio Blog
博客园 - 叶小钗
H
Help Net Security
aimingoo的专栏
aimingoo的专栏
宝玉的分享
宝玉的分享
U
Unit 42
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Fortinet All Blogs
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
WordPress大学
WordPress大学
D
DataBreaches.Net
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
A
About on SuperTechFans
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
C
Check Point Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
M
MIT News - Artificial intelligence

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
JavaScript Console Tricks That Will Blow Your Mind
Alex Chen · 2026-05-16 · via DEV Community

Alex Chen

Designing REST APIs That Developers Actually Love Using

I've consumed hundreds of APIs. The good ones are a joy. The bad ones make you want to quit coding.

What Makes an API "Good"?

 Bad API:
GET /getUsers?userId=123
 { "data": { "user_info": { "name_str": "John" } }, "status": "success", "error": null }

 Good API:
GET /users/123
 { "id": 123, "name": "John Doe", "email": "john@example.com" }

Enter fullscreen mode Exit fullscreen mode

1. Resource Naming (The Foundation)

// ✅ Use nouns, not verbs
GET    /users          // List users
POST   /users          // Create user
GET    /users/123      // Get specific user
PUT    /users/123      // Update user (full)
PATCH  /users/123      // Partial update
DELETE /users/123      // Delete user

// ❌ Don't use verbs in URLs
GET /getAllUsers       // Wrong!
POST /createUser       // Wrong!
DELETE /deleteUser/123  // Wrong!

// ❌ Don't use URL query params for actions
POST /users?action=create        // Wrong!
PUT  /users?id=123&action=update // Wrong!

// Plural vs singular: Use PLURAL for collections
GET /users           // Collection → plural
GET /users/123/items // Sub-collection of user 123's items → plural

Enter fullscreen mode Exit fullscreen mode

2. Proper HTTP Methods & Status Codes

// Method semantics matter!
app.get('/users', listUsers);     // Read — safe, idempotent
app.post('/users', createUser);   // Create — not idempotent
app.put('/users/:id', replaceUser); // Full replace — idempotent
app.patch('/users/:id', updateUser); // Partial update — not idempotent
app.delete('/users/:id', deleteUser); // Remove — idempotent

// Status codes — use them correctly!
const StatusCodes = {
  // Success (2xx)
  OK: 200,                    // Standard success response
  Created: 201,               // POST created a resource
  NoContent: 204,             // DELETE succeeded (no body needed)

  // Redirection (3xx)
  NotModified: 304,           // Conditional GET, resource unchanged

  // Client Errors (4xx)
  BadRequest: 400,            // Malformed syntax
  Unauthorized: 401,         // Not authenticated
  Forbidden: 403,            // Authenticated but no permission
  NotFound: 404,             // Resource doesn't exist
  MethodNotAllowed: 405,     // Wrong HTTP method for this endpoint
  Conflict: 409,             // State conflict (e.g., duplicate email)
  UnprocessableEntity: 422,  // Valid request but semantic errors
  TooManyRequests: 429,      // Rate limited

  // Server Errors (5xx)
  InternalServerError: 500,  // Something went wrong
  NotImplemented: 501,       // Feature not implemented yet
};

Enter fullscreen mode Exit fullscreen mode

3. Consistent Response Format

// Option A: Simple (for public/read-heavy APIs)
// GET /users/123
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com",
  "role": "admin",
  "created_at": "2026-01-15T10:30:00Z",
  "_links": {
    "self": "/users/123",
    "posts": "/users/123/posts"
  }
}

// Option B: Enveloped (for APIs that need metadata)
// GET /users?page=1&per_page=20
{
  "data": [
    { "id": 1, "name": "Alice" },
    { "id": 2, "name": "Bob" }
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 150,
    "total_pages": 8
  }
}

// Error responses (ALWAYS consistent!)
// ❌ Inconsistent errors
{ "error": "not found" }              // Sometimes string
{ "message": "User doesn't exist" }   // Sometimes message
{ "errors": ["not found"] }           // Sometimes array

// ✅ Consistent error format
{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "User with ID 999 does not exist",
    "status": 404,
    "details": {
      "field": "id",
      "value": 999,
      "expected": "existing user ID"
    }
  },
  "request_id": "req_abc123"
}

Enter fullscreen mode Exit fullscreen mode

4. Pagination Done Right

// ❌ Bad pagination (no way to navigate!)
GET /users?offset=0&limit=20
 { data: [...] }

// ✅ Cursor-based (for infinite feeds, real-time data)
GET /users?cursor=eyJpZCI6MTAwfQ&limit=20
{
  "data": [...],
  "pagination": {
    "cursor": "eyJpZCI6MTIwfQ",  // Next page cursor
    "has_next": true,
    "has_prev": false
  }
}

// ✅ Offset-based (for admin panels, sortable tables)
GET /users?page=2&per_page=20&sort=name&order=asc
{
  "data": [...],
  "meta": {
    "page": 2,
    "per_page": 20,
    "total": 150,
    "total_pages": 8,
    "has_prev": true,
    "has_next": true
  },
  "links": {
    "first": "/users?page=1&per_page=20",
    "prev": "/users?page=1&per_page=20",
    "next": "/users?page=3&per_page=20",
    "last": "/users?page=8&per_page=20"
  }
}

Enter fullscreen mode Exit fullscreen mode

5. Filtering, Sorting & Field Selection

// Filtering
GET /posts?status=published&author_id=5&tag=javascript
GET /products?price_min=10&price_max=100&category=electronics
GET /users?role=admin&active=true

// Sorting
GET /users?sort=created_at&order=desc
GET /posts?sort=-published_at,title  // - prefix = descending
GET /products?sort=price&order=asc

// Field selection (reduce payload size!)
GET /users/123?fields=id,name,email
// Returns only requested fields instead of full object

// Search
GET /posts?q=react+hooks&search_fields=title,body
GET /users?search=john&search_fields=name,email

Enter fullscreen mode Exit fullscreen mode

6. Versioning

// Option A: URL path versioning (most common)
/api/v1/users
/api/v2/users  // Breaking changes go here

// Option B: Header versioning (cleaner URLs)
Accept: application/vnd.api.v1+json
GET /api/users

// My recommendation: Start with URL versioning.
// It's explicit and works everywhere.

// When to bump versions?
// v1 → v2: Breaking changes (removed fields, changed types, new required fields)
// Within same version: Additive changes are OK (new optional fields, new endpoints)

Enter fullscreen mode Exit fullscreen mode

7. Authentication & Security

// API Key (for server-to-server or simple cases)
GET /api/users
X-API-Key: ak_live_abc123

// Bearer Token (JWT/OAuth) — most common for user-facing APIs
GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

// Security headers (always include!)
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('Access-Control-Allow-Origin', 'https://your-app.com');

// Rate limiting headers
res.setHeader('X-RateLimit-Limit', '1000');
res.setHeader('X-RateLimit-Remaining', '999');
res.setHeader('X-RateLimit-Reset', '1715841200');

// Request ID for tracing
res.setHeader('X-Request-ID', generateRequestId());

Enter fullscreen mode Exit fullscreen mode

8. Documentation (Non-Negotiable!)

# OpenAPI/Swagger spec — the standard
openapi: 3.0.0
info:
  title: My API
  version: 1.0.0
  description: A well-designed REST API

paths:
  /users:
    get:
      summary: List all users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            maximum: 100
            default: 20
      responses:
        '200':
          description: List of users
          content:
            application/json:
              example:
                data:
                  - id: 1
                    name: Alice
                meta:
                  total: 150
                  page: 1
        '401':
          description: Unauthorized
        '429':
          description: Rate limited

Enter fullscreen mode Exit fullscreen mode

Quick Checklist

□ Nouns for resources, verbs for HTTP methods
□ Plural collection names
□ Correct status codes (2xx/4xx/5xx)
□ Consistent error format across ALL endpoints
□ Pagination on list endpoints
□ Filtering, sorting, field selection support
□ API versioning strategy
□ Auth headers documented
□ Rate limiting with informative headers
□ Request ID for debugging
□ OpenAPI/Swagger documentation
□ Example requests/responses for each endpoint

Enter fullscreen mode Exit fullscreen mode


What's the best/worst API you've used?

Follow @armorbreak for more backend content.