API Mastery: REST & Security#

Introduction#

In modern software development, APIs (Application Programming Interfaces) are the bridges that connect different software components. Whether you are building a mobile app, a web frontend, or a microservice architecture, the API is the backbone of your system.

While creating an API that “works” is relatively easy, designing one that is scalable, secure, and easy to use is a significant challenge. This guide covers the essential pillars of professional API development: RESTful design principles, solid security with JWT, robust Rate Limiting, and clear Documentation.

RESTful API Design#

REST (Representational State Transfer) is the most popular architectural style for web APIs. It relies on standard HTTP methods and status codes to manage resources.

REST Principles#

To thrive in the API world, you must understand these core principles:

  1. Resource-Based: APIs should expose “resources” (nouns), not actions (verbs).

    • Bad: /getAllUsers, /createUser

    • Good: /users (GET for list, POST for creation)

  2. Statelessness: The server should not store any client context between requests. Every request must contain all the information needed to process it (e.g., authentication tokens).

  3. Uniform Interface: Resources should be uniquely identified (URIs) and manipulated through standard representations (JSON).

API Design Best Practices#

  • Use Standard HTTP Methods:

    • GET: Retrieve resources.

    • POST: Create a new resource.

    • PUT: Update a resource (full replacement).

    • PATCH: Update a resource (partial update).

    • DELETE: Remove a resource.

  • Return Correct Status Codes:

    • 200 OK: Success (GET, PUT, PATCH).

    • 201 Created: Resource successfully created (POST).

    • 400 Bad Request: Invalid input.

    • 401 Unauthorized: Missing or invalid authentication.

    • 403 Forbidden: Authenticated but not allowed (permission issue).

    • 404 Not Found: Resource does not exist.

    • 500 Internal Server Error: Server blew up.

  • Versioning: Always version your API to prevent breaking changes for clients.

    • Example: /api/v1/products

Authentication & Authorization#

Security is paramount. The modern standard for stateless API authentication is JWT (JSON Web Tokens).

JWT (JSON Web Tokens)#

A JWT is a compact, URL-safe means of representing claims to be transferred between two parties. It consists of three parts separated by dots (.):

  1. Header: Algorithm & Token Type (e.g., {"alg": "HS256", "typ": "JWT"}).

  2. Payload: Data (Claims) about the user (e.g., {"id": 123, "role": "admin"}).

  3. Signature: Verifies the token hasn’t been tampered with.

The Flow:

  1. User logs in with credentials.

  2. Server verifies credentials and returns a signed JWT.

  3. Client stores the JWT (e.g., in cookies).

  4. Client sends the JWT in the Authorization header (Bearer <token>) for subsequent requests.

  5. Server validates the signature and grants access.

        graph TD
    H["Header\n{alg: HS256, typ: JWT}"]
    P["Payload\n{sub, name, iat, ...}"]
    S["Signature\nHMAC-SHA256(base64(header) + '.' + base64(payload), secret)"]

    H -- Base64Url encode --> HE[Encoded Header]
    P -- Base64Url encode --> PE[Encoded Payload]
    S -- Generate via algorithm --> SE[Encoded Signature]

    HE --> JWT["JWT\nheader.payload.signature"]
    PE --> JWT
    SE --> JWT
    

JWT components.

Refresh Tokens & Security Best Practices#

Why use a Refresh Token? In a real-world application, keeping an Access Token valid for a long time (e.g., 1 week) is a security risk. If it’s stolen, the attacker has access for that entire week. However, asking the user to log in every 15 minutes is a bad user experience.

The Solution: Access Token + Refresh Token

  1. Access Token: Short-lived (e.g., 15 minutes). Used for API requests.

  2. Refresh Token: Long-lived (e.g., 7 days). Stored securely (e.g., HTTPOnly cookie). Used ONLY to get a new Access Token.

The Flow with Refresh Tokens:

  1. Client makes an API request with Access Token.

  2. If server returns 401 Unauthorized (Token Expired), client sends the Refresh Token to a special endpoint (e.g., /auth/refresh).

  3. Server validates the Refresh Token and issues a new Access Token.

  4. Client retries the original request with the new Access Token.

Security Tip NEVER store sensitive data (like passwords or credit card numbers) in a JWT. JWTs are encoded (Base64), not encrypted. Anyone can paste your token into jwt.io and read the payload. Only store non-sensitive identifiers like user_id or role.

Implementation Example#

Here is a simplified example using Python logic (conceptual):

import jwt
import datetime

SECRET_KEY = "my_super_secret_key"

def create_token(user_id):
    payload = {
        "user_id": user_id,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
    }
    token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
    return token

def verify_token(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload["user_id"]
    except jwt.ExpiredSignatureError:
        return "Token expired"
    except jwt.InvalidTokenError:
        return "Invalid token"

Rate Limiting#

Why Rate Limiting?#

Imagine a user (or a bot) sending 10,000 requests per second to your login endpoint. This could crash your database or slow down the service for everyone else.

Rate Limiting controls the number of requests a client can make within a specific timeframe (e.g., 100 requests per minute). It protects against:

  • DDoS Attacks: Overwhelming the server.

  • Brute Force Attacks: Guessing passwords.

  • Resource Starvation: Ensuring fair usage for all users.

Implementation Strategies#

  1. Fixed Window: “100 requests per hour”. The counter resets at the top of the hour. Simple but can have “burst” issues at the window boundary.

  2. Sliding Window: Smoother approach, calculating the rate based on the last X minutes.

  3. Token Bucket: Analogy of a bucket filled with tokens at a constant rate. Each request costs a token. If the bucket is empty, the request is rejected (429 Too Many Requests).

API Documentation#

Good code is useless if no one knows how to use it. Documentation should be comprehensive and up-to-date.

OpenAPI / Swagger#

OpenAPI Specification (OAS) is the industry standard for describing REST APIs. Swagger UI turns that specification into an interactive webpage where developers can test endpoints directly.

Benefits:

  • Automation: Generate clients (SDKs) and server stubs automatically.

  • Interactivity: Try out the API without writing code.

  • Clarity: Defines exactly what request body is expected and what response will be returned.

Example Specification (YAML)#

openapi: 3.0.0
info:
  title: Sample API
  version: 1.0.0
paths:
  /users:
    get:
      summary: Returns a list of users
      responses:
        "200":
          description: A JSON array of user names
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string

API Spec Example

API Spec Swagger Example.

Summary#

Mastering APIs is about more than just moving data. It’s about designing systems that are predictable (REST), secure (JWT), reliable (Rate Limiting), and usable (Documentation). Focus on these pillars, and you will build APIs that other developers love to use.

References#