GraphQL vs REST API: When to Use Which

GraphQL and REST are both popular API design approaches. Here’s when to use each. REST API Overview REST (Representational State Transfer) uses HTTP methods to interact with resources. Characteristics Resource-based: URLs represent resources HTTP methods: GET, POST, PUT, DELETE Stateless: Each request is independent Multiple endpoints: Different URLs for different resources Example GET /api/users GET /api/users/123 POST /api/users PUT /api/users/123 DELETE /api/users/123 GraphQL Overview GraphQL is a query language and runtime for APIs. ...

December 10, 2025 · DevCraft Studio · 3748 views

API Design Best Practices: Building Developer-Friendly APIs

Good API design is crucial for developer experience and system success. Here are best practices for designing RESTful APIs. 1. Use RESTful Conventions Resource-Based URLs # Good: Resource-based GET /api/users GET /api/users/123 POST /api/users PUT /api/users/123 DELETE /api/users/123 # Bad: Action-based GET /api/getUsers POST /api/createUser POST /api/deleteUser HTTP Methods GET: Retrieve resources POST: Create resources PUT: Update entire resource PATCH: Partial update DELETE: Remove resource 2. Consistent Naming Use Plural Nouns # Good GET /api/users GET /api/orders GET /api/products # Bad GET /api/user GET /api/order GET /api/product Use kebab-case or camelCase # Good: Consistent GET /api/user-profiles GET /api/orderItems # Bad: Mixed GET /api/user_profiles GET /api/order-items 3. Version Your API URL Versioning GET /api/v1/users GET /api/v2/users Header Versioning GET /api/users Accept: application/vnd.api+json;version=2 4. Use Proper HTTP Status Codes Success Codes 200 OK # Successful GET, PUT, PATCH 201 Created # Successful POST 204 No Content # Successful DELETE Client Error Codes 400 Bad Request # Invalid request 401 Unauthorized # Authentication required 403 Forbidden # Not authorized 404 Not Found # Resource doesn't exist 409 Conflict # Resource conflict 422 Unprocessable # Validation errors Server Error Codes 500 Internal Server Error 502 Bad Gateway 503 Service Unavailable 5. Consistent Response Format Standard Response Structure { "data": { "id": "123", "type": "user", "attributes": { "name": "John Doe", "email": "[email protected]" } }, "meta": { "timestamp": "2025-12-10T10:00:00Z" } } Error Response Format { "error": { "code": "VALIDATION_ERROR", "message": "Invalid input", "details": [ { "field": "email", "message": "Invalid email format" } ] } } 6. Pagination Cursor-Based Pagination GET /api/users?cursor=eyJpZCI6IjEyMyJ9&limit=20 Response: { "data": [...], "pagination": { "cursor": "eyJpZCI6IjE0MyJ9", "has_more": true } } Offset-Based Pagination GET /api/users?page=1&limit=20 Response: { "data": [...], "pagination": { "page": 1, "limit": 20, "total": 100, "total_pages": 5 } } 7. Filtering and Sorting Filtering GET /api/users?status=active&role=admin GET /api/orders?created_after=2024-01-01&created_before=2024-12-31 Sorting GET /api/users?sort=name,email&order=asc,desc GET /api/users?sort=-created_at # Descending 8. Field Selection Sparse Fieldsets GET /api/users?fields=id,name,email GET /api/users/123?fields=id,name Include Related Resources GET /api/users/123?include=orders,profile 9. Rate Limiting Headers X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 999 X-RateLimit-Reset: 1609459200 Response HTTP/1.1 429 Too Many Requests Retry-After: 60 10. Authentication and Authorization Use Standard Methods # Bearer token Authorization: Bearer <token> # API key X-API-Key: <key> Return Clear Errors { "error": { "code": "UNAUTHORIZED", "message": "Invalid or expired token" } } 11. Documentation OpenAPI/Swagger openapi: 3.0.0 info: title: User API version: 1.0.0 paths: /users: get: summary: List users responses: '200': description: Success Interactive Documentation Swagger UI: Visual API documentation Postman: API testing and docs Redoc: Beautiful API docs 12. Error Handling Consistent Error Format { "error": { "code": "RESOURCE_NOT_FOUND", "message": "User with ID 123 not found", "request_id": "req_abc123" } } Validation Errors { "error": { "code": "VALIDATION_ERROR", "message": "Validation failed", "errors": [ { "field": "email", "message": "Invalid email format", "code": "INVALID_FORMAT" } ] } } 13. Caching Cache Headers Cache-Control: public, max-age=3600 ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT Conditional Requests GET /api/users/123 If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4" # 304 Not Modified if unchanged 14. Security Best Practices Use HTTPS Always use HTTPS in production. ...

December 10, 2025 · DevCraft Studio · 4185 views

Go HTTP Timeouts & Resilience Defaults

Client defaults Set Timeout on http.Client; set Transport with DialContext timeout (e.g., 3s), TLSHandshakeTimeout (3s), ResponseHeaderTimeout (5s), IdleConnTimeout (90s), MaxIdleConns/MaxIdleConnsPerHost. Retry only idempotent methods with backoff + jitter; cap attempts. Use context.WithTimeout per request; cancel on exit. Server defaults ReadHeaderTimeout (e.g., 5s) to mitigate slowloris. ReadTimeout/WriteTimeout to bound handler time (align with business SLAs). IdleTimeout to recycle idle connections; prefer HTTP/2 when available. Patterns Wrap handlers with middleware for deadline + logging when timeouts hit. For upstreams, expose metrics: connect latency, TLS handshake, TTFB, retries. Prefer connection re-use; avoid per-request clients. Checklist Timeouts set on both client and server. Retries limited to idempotent verbs with jitter. Connection pooling tuned; idle conns reused. Metrics for latency stages and timeouts.

May 12, 2025 · DevCraft Studio · 4125 views

Benchmarking Go REST APIs with k6

Test design Define goals: latency budgets (p95/p99), error ceilings, throughput targets. Scenarios: ramping arrival rate, soak tests, spike tests; match production payloads. Include auth headers and realistic think time. k6 script skeleton import http from "k6/http"; import { check, sleep } from "k6"; export const options = { thresholds: { http_req_duration: ["p(95)<300"] }, scenarios: { api: { executor: "ramping-arrival-rate", startRate: 10, timeUnit: "1s", preAllocatedVUs: 50, maxVUs: 200, stages: [ { target: 100, duration: "3m" }, { target: 100, duration: "10m" }, { target: 0, duration: "2m" }, ]}, }, }; export default function () { const res = http.get("https://api.example.com/resource"); check(res, { "status 200": (r) => r.status === 200 }); sleep(1); } Run & observe Capture k6 summary + JSON output; feed to Grafana for trends. Correlate with Go metrics (pprof, Prometheus) to find CPU/alloc hot paths. Record build SHA; compare runs release over release. Checklist Scenarios match prod traffic shape. Thresholds tied to SLOs; tests fail on regressions. Service metrics/pprof captured during runs.

April 2, 2025 · DevCraft Studio · 4041 views

Node.js Worker Threads for CPU Tasks

When to use CPU-bound tasks (crypto, compression, image/video processing) that would block event loop. Avoid for short, tiny tasks—overhead may outweigh benefit. Patterns Use a pool (e.g., piscina/workerpool) with bounded size; queue tasks. Pass data via Transferable when large buffers; avoid heavy serialization. Propagate cancellation/timeouts; surface errors to main thread. Observability Track queue length, task duration, worker utilization, crashes. Monitor event loop lag to confirm offload benefits. Safety Validate inputs in main thread; avoid untrusted code in workers. Cleanly shut down pool on SIGTERM; drain and close workers. Checklist CPU tasks isolated to workers; pool sized to cores. Transferables used for big buffers; timeouts set. Metrics for queue/utilization/errors in place.

March 18, 2025 · DevCraft Studio · 4284 views
Abstract message queue illustration

Kafka Reliability Playbook

Producers acks=all, min.insync.replicas>=2, idempotent producer on; enable transactions for exactly-once pipelines. Tune batch.size/linger.ms for throughput; cap max.in.flight.requests.per.connection for ordering. Handle retries with backoff; surface delivery errors. Consumers enable.auto.commit=false; commit after processing; DLT for poison messages. Size max.poll.interval.ms to work time; bound max.poll.records. Isolate heavy work in worker pool; keep poll loop fast. Topics & brokers RF ≥ 3; clean-up policy fit (delete vs compact); segment/retention sized to storage. Monitor ISR, under-replicated partitions, controller changes, request latency, disk usage. Throttle large produce/fetch; use quotas per client if needed. Checklist Producers idempotent, acks=all, MISR set. Consumers manual commit + DLT. RF/retention sized; ISR/URP monitored. Alerts on broker latency/disk/replication health.

March 5, 2025 · DevCraft Studio · 4060 views

Scaling Node.js: Fastify vs Express

Performance notes Fastify: schema-driven, AJV validation, low-overhead routing; typically better RPS and lower p99s. Express: mature ecosystem; middleware can add overhead; great for quick prototypes. Bench considerations Compare with same validation/parsing; disable unnecessary middleware in Express. Test under keep-alive and HTTP/1.1 & HTTP/2 where applicable. Measure event loop lag and heap; not just RPS. Migration tips Start new services with Fastify; for existing Express apps, migrate edge routes first. Replace middleware with hooks/plugins; map Express middlewares to Fastify equivalents. Validate payloads with JSON schema for speed and safety. Operational tips Use pino (Fastify default) for structured logs; avoid console log overhead. Keep plugin count minimal; watch async hooks cost. Load test with realistic payload sizes; profile hotspots before/after migration. Takeaway: Fastify wins on raw throughput and structured DX; Express still fine for smaller apps, but performance-critical services benefit from Fastify’s lower overhead.

March 2, 2025 · DevCraft Studio · 3857 views

Rate Limiting Java REST APIs

Approaches Token bucket for burst+steady control; sliding window for fairness. Enforce at edge (gateway/ingress) plus app-level for per-tenant safety. Spring implementation Use filters/interceptors with Redis/Lua for atomic buckets. Key by tenant/user/IP; return 429 with Retry-After. Expose metrics per key and rule; alert on near-capacity. Considerations Separate auth failures from rate limits; avoid blocking login endpoints too aggressively. Keep rule configs dynamic; hot-reload from config store. Combine with circuit breakers/timeouts for upstream dependencies. Checklist Edge and app-level limits defined. Redis-based atomic counters/buckets with TTL. Metrics + logs for limit decisions; alerts in place.

February 15, 2025 · DevCraft Studio · 4360 views

Go High-Performance Concurrency Playbook

Principles Keep goroutine count bounded; size pools to CPU/core and downstream QPS. Apply backpressure: bounded channels + select with default to shed load early. Context everywhere: cancel on timeouts/parent cancellation; close resources. Prefer immutability; minimize shared state. Use channels for coordination. Patterns Worker pool with buffered jobs; fan-out/fan-in via contexts. Rate limit with time.Ticker or golang.org/x/time/rate. Semaphore via buffered channel for limited resources (DB, disk, external API). Sync cheatsheet sync.Mutex for critical sections; avoid long hold times. sync.WaitGroup for bounded concurrent tasks; errgroup for cancellation on first error. sync.Map only for high-concurrency, write-light cases; prefer map+mutex otherwise. Instrument & guard pprof: net/http/pprof + CPU/mem profiles in staging under load. Trace blocking: GODEBUG=schedtrace=1000,scavtrace=1 when diagnosing. Metrics: goroutines, GC pause, allocations, queue depth, worker utilization. Checklist Context per request; timeouts set at ingress. Bounded goroutines; pools sized and observable. Backpressure on queues; drop/timeout strategy defined. pprof/metrics enabled in non-prod and behind auth in prod. Load tests for saturation behavior and graceful degradation.

February 10, 2025 · DevCraft Studio · 4228 views

Laravel Queues with Horizon: Reliable Setup

Workers & balancing Define queue priorities; dedicate workers per queue (emails, webhooks, default). Use balance strategies (simple, auto) and cap max processes per supervisor. Reliability Set retry/backoff per job; push non-idempotent tasks carefully. Configure timeout and retry_after (keep retry_after > max job time). Use Redis with persistence; enable horizon:supervisors monitors. Observability Horizon dashboard: throughput, runtime, failures, retries. Alert on rising failures and long runtimes; log payload/context for failed jobs. Prune failed jobs with retention policy; send to DLQ when needed. Deployment Restart Horizon on deploy to pick up code; use horizon:terminate. Ensure supervisor/systemd restarts Horizon if it dies. Checklist Queues prioritized; supervisors sized. Retries/backoff and timeouts set; DLQ plan. Monitoring/alerts configured; failed job retention in place.

January 20, 2025 · DevCraft Studio · 4050 views