REST API Tutorial: Design, JSON, and Best Practices
A practical guide to building REST APIs with JSON payloads, status codes, and versioning.
What Is REST?
REST (Representational State Transfer) is an architectural style for designing networked applications, introduced by Roy Fielding in his 2000 doctoral dissertation. It is not a protocol or a standard — it is a set of constraints that, when followed, produce scalable, maintainable web APIs. The most important constraint for everyday development: resources are identified by URLs, and clients interact with them using a uniform set of HTTP methods.
A resource might be a user, an order, a blog post, or any entity your application exposes. Each resource has a canonical URL: `GET /users/42` retrieves user 42, `PUT /users/42` replaces it, `PATCH /users/42` partially updates it, and `DELETE /users/42` removes it. The server does not maintain client session state between requests — each request carries all information needed to process it.
JSON became the de facto payload format for REST APIs because it is lightweight, universally parsed, and maps cleanly to objects in every language. Use the JSON Formatter to inspect request and response bodies during development, and the JSON Validator to confirm payloads before sending them to production endpoints.
HTTP Methods and Idempotency
GET retrieves a resource without side effects. It should be safe and idempotent — calling it multiple times returns the same result without changing server state. POST creates a new resource or triggers an action; it is neither safe nor idempotent. PUT replaces a resource at a known URL and is idempotent. PATCH applies partial updates. DELETE removes a resource and is idempotent.
Idempotency matters for reliability. If a network timeout occurs after a PUT request, the client can safely retry knowing the end state will be the same. POST requests that create resources should use idempotency keys (a header like `Idempotency-Key: uuid`) to prevent duplicate creation on retry — a pattern popularized by Stripe and adopted across the industry.
Design your API so GET endpoints never modify data. Accidental caching of GET requests that change state causes subtle, hard-to-debug production issues. Use POST for actions that are not simple resource creation, such as `POST /orders/123/cancel`.
Status Codes and Error Responses
HTTP status codes communicate outcome at a glance. Use 200 OK for successful GET, PUT, and PATCH. Return 201 Created for POST that creates a resource, including a `Location` header pointing to the new resource. 204 No Content suits successful DELETE or updates with no response body.
Client errors use 4xx codes. 400 Bad Request indicates malformed JSON or invalid parameters. 401 Unauthorized means authentication is missing or invalid. 403 Forbidden means the authenticated user lacks permission. 404 Not Found means the resource does not exist. 409 Conflict signals a state conflict, such as a duplicate email. 422 Unprocessable Entity is popular for validation failures where the JSON is syntactically valid but semantically wrong.
Server errors use 5xx. Return 500 for unexpected failures, 502 for bad gateway responses from upstream services, and 503 when temporarily unavailable. Always include a JSON error body with a machine-readable code and a human-readable message: `{"error": {"code": "VALIDATION_FAILED", "message": "Email is required", "details": [...]}}`.
Designing JSON Request and Response Bodies
Consistency is the hallmark of a good API. Pick a naming convention — camelCase is standard for JSON APIs — and apply it everywhere. Wrap collections in a predictable envelope: `{"data": [...], "meta": {"total": 100, "page": 1}}`. Include pagination metadata for list endpoints so clients can iterate without guessing.
Avoid deeply nested responses that force clients to dig through layers of objects. Flatten when possible, or provide field selection via query parameters (`?fields=id,name,email`). Document every field in your OpenAPI specification, including which are required, their types, and example values.
Validate JSON on both sides. Client-side validation improves UX with immediate feedback. Server-side validation is mandatory for security — never trust client input. Paste sample payloads into the JSON Validator during design reviews and automate schema checks in CI with JSON Schema or OpenAPI validators.
Authentication and Security
REST APIs commonly authenticate via API keys, OAuth 2.0 bearer tokens, or JWTs. API keys are simple but should be transmitted over HTTPS only and rotated regularly. OAuth 2.0 delegates authorization to an identity provider. JWTs encode claims in a self-contained token that services can verify without a database lookup.
Use the JWT Decoder to inspect token headers and payloads during development. Remember: decoding reveals claims but does not verify the signature. Signature verification must happen server-side with the correct secret or public key. Read How JWT Works for a complete security overview.
Encode query parameters properly when building URLs. Special characters in search terms or redirect URLs must be percent-encoded. The URL Encode tool helps debug encoding issues, and URL Decode reverses encoded strings when analyzing request logs.
Versioning and Evolution
APIs change. Version them explicitly: URL path versioning (`/v1/users`), header versioning (`Accept: application/vnd.myapi.v1+json`), or query parameter versioning. URL path versioning is the most visible and easiest for developers to understand. Avoid breaking changes in existing versions — add fields rather than renaming or removing them.
Deprecation requires communication. Return a `Sunset` header or `Deprecation` header on old endpoints, document migration paths, and give consumers a reasonable timeline. Monitor usage of deprecated endpoints before removal.
Use HATEOAS (Hypermedia as the Engine of Application State) sparingly. Including links in responses (`"_links": {"self": "/users/42", "orders": "/users/42/orders"}`) helps discoverability but adds complexity. Most internal APIs skip HATEOAS and rely on documentation instead.
Testing and Debugging REST APIs
Test your API at every layer. Unit tests cover business logic. Integration tests verify database interactions. Contract tests (using tools like Pact) ensure API consumers and providers agree on request/response shapes. End-to-end tests validate full user flows.
During manual testing, capture JSON responses and format them with the JSON Formatter for readability. Use the JSON Viewer to explore nested response structures. When responses fail validation, the JSON Validator pinpoints syntax errors at exact line and column positions.
Log request and response metadata in development, but redact tokens, passwords, and personal data in production logs. Structured JSON logging (one JSON object per log line) integrates well with observability platforms and makes filtering by request ID straightforward.
Related Tools
Free online JSON formatter and validator. Beautify, minify, and validate JSON instantly in your browser.
Validate JSON syntax online with precise line and column error messages. Free, fast, and private.
Decode JWT tokens and inspect header, payload, and expiration. Free online JSON Web Token decoder.
Encode URLs and query parameters with percent-encoding. Free online URL encoder.
Related Articles
What is JSON? Definition, Syntax, and Examples
Understand what JSON is, how it works, and why it became the default format for APIs and configuration.
How JWT Works: Structure, Security, and Decoding
Understand JSON Web Tokens — header, payload, signature — and how to decode them safely.
How to Validate JSON: Tools and Techniques
Step-by-step guide to validating JSON syntax, catching errors, and fixing common mistakes.