404 Not Found

404 Not Found


nginx

REST API Quick Start Guide

REST APIs are the universal language of microservices—they use HTTP verbs to manipulate resources and JSON to transfer data, making them simple, standardized, and efficient.

1. What You'll Learn


2. A True Story from an API Developer

(1) Pain Point: Inconsistent API Specifications

Alice joined an existing project team and discovered that the REST API design was chaotic: some methods used GET to modify data, URL naming was inconsistent (/getOrder, /order/list, /deleteOrderById), and the response formats were not standardized—some returned JSON, some returned XML, and error responses were plain text. Bob, a front-end developer, had to consult the back-end team every time he integrated a new API, wasting a significant amount of time on communication.

(2) RESTful Approach

REST uses a set of uniform conventions to define API design:

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    @GetMapping("/{id}")      // GET    /api/orders/123
    @PostMapping              // POST   /api/orders
    @PutMapping("/{id}")      // PUT    /api/orders/123
    @DeleteMapping("/{id}")   // DELETE /api/orders/123
}

(3) Revenue

After Alice refactored the OrderFlow API to follow a RESTful style—with consistent URL naming conventions and clearly defined HTTP verb semantics—Bob, a front-end developer, remarked, "You can tell how to use it just by looking at the URL." As a result, the time required to integrate with the API was reduced from an average of 2 hours to 15 minutes.


3. Core Concepts of REST

(1) The Six Principles of REST

REST (Representational State Transfer) defines the core constraints of this architectural style:

100%
graph TD
    A[REST Constraints] --> B[Client-Server<br/>Separation of Concerns]
    A --> C[Stateless<br/>Each Request Contains<br/>All Needed Info]
    A --> D[Cacheable<br/>Responses Define<br/>Cacheability]
    A --> E[Uniform Interface<br/>Consistent API Design]
    A --> F[Layered System<br/>Client Cannot Tell<br/>If Connected Directly]
    A --> G[Code on Demand<br/>Optional: Server Can<br/>Send Executable Code]

(2) Mapping HTTP Verbs to CRUD Operations

HTTP Verb Operation Idempotency Security Typical URL
GET Query Yes Yes /api/orders
POST Created No No /api/orders
PUT Full Update Yes No /api/orders/123
PATCH Partial Update No No /api/orders/123
DELETE Delete Yes No /api/orders/123
📌 Key Point: Idempotence means that executing an operation multiple times yields the same result. PUT is idempotent (it replaces the entire resource), while POST is not idempotent (it may create a new resource each time).


4. @RestController and Request Mapping

(1) How @RestController Works

@RestController = @Controller + @ResponseBody indicates that the return values of all methods in this class are written directly into the HTTP response body (JSON by default).

(1) ▶ Example: Basic OrderController

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final List<Map<String, Object>> orders = new ArrayList<>();

    @GetMapping
    public List<Map<String, Object>> listOrders() {
        return orders;
    }

    @PostMapping
    public Map<String, Object> createOrder(
            @RequestBody Map<String, Object> order) {
        order.put("id", (long) (orders.size() + 1));
        order.put("status", "PENDING");
        orders.add(order);
        return order;
    }
}

Output:

TEXT
// Execution Successful

(2) Request Parameter Binding

(2) ▶ Example: @PathVariable and @RequestParam

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @GetMapping("/{id}")
    public Map<String, Object> getOrder(@PathVariable Long id) {
        return Map.of("id", id, "status", "PENDING");
    }

    @GetMapping
    public List<Map<String, Object>> searchOrders(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String status) {
        return List.of(Map.of(
            "page", page, "size", size, "status", status
        ));
    }
}

Output:

TEXT
// Execution Successful
Comment Purpose Example URL
@PathVariable Extract variables from the path @PathVariable Long id /api/orders/123
@RequestParam Extract Query Parameters @RequestParam String status /api/orders?status=PENDING
@RequestBody Extract JSON from request body @RequestBody OrderRequest req POST body
@RequestHeader Extract Request Headers @RequestHeader String auth Header: Authorization

(3) ▶ Example: Using a DTO to Receive the Request Body

JAVA
public record CreateOrderRequest(
    Long productId,
    Integer quantity,
    String shippingAddress
) {}

@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @PostMapping
    public ResponseEntity<Map<String, Object>> createOrder(
            @RequestBody CreateOrderRequest request) {
        Map<String, Object> order = Map.of(
            "productId", request.productId(),
            "quantity", request.quantity(),
            "shippingAddress", request.shippingAddress(),
            "status", "PENDING"
        );
        return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(order);
    }
}

Output:

TEXT
// Execution Successful

5. ResponseEntity and HTTP Status Codes

(1) Using ResponseEntity

ResponseEntity Gives you full control over HTTP responses: status codes, response headers, and response bodies.

Method Applicable Scenarios Flexibility
Returns an object directly Simple success response Low (fixed at 200)
ResponseEntity.ok(body) Must be set to 200 Medium
ResponseEntity.status(CREATED).body(body) Resource creation returned 201 High
ResponseEntity.notFound().build() Resource not found—returns 404 High

(1) ▶ Example: Complete CRUD Operations

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final Map<Long, Map<String, Object>> orderStore = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Map<String, Object>> create(
            @RequestBody CreateOrderRequest request) {
        Long id = idGenerator.getAndIncrement();
        Map<String, Object> order = new HashMap<>();
        order.put("id", id);
        order.put("productId", request.productId());
        order.put("quantity", request.quantity());
        order.put("status", "PENDING");
        orderStore.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Map<String, Object>> getOne(@PathVariable Long id) {
        Map<String, Object> order = orderStore.get(id);
        if (order == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(order);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Map<String, Object>> update(
            @PathVariable Long id,
            @RequestBody CreateOrderRequest request) {
        if (!orderStore.containsKey(id)) {
            return ResponseEntity.notFound().build();
        }
        Map<String, Object> order = new HashMap<>();
        order.put("id", id);
        order.put("productId", request.productId());
        order.put("quantity", request.quantity());
        order.put("status", "CONFIRMED");
        orderStore.put(id, order);
        return ResponseEntity.ok(order);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        if (orderStore.remove(id) == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.noContent().build();
    }
}

Output:

TEXT
// Execution Successful

6. API Testing Methods

(1) curl test command

(1) ▶ Example: Testing CRUD APIs with curl

BASH
# Create order
curl -X POST http://localhost:8080/api/orders \
  -H "Content-Type: application/json" \
  -d '{"productId":1, "quantity":3, "shippingAddress":"123 Main St"}'

# Get order
curl http://localhost:8080/api/orders/1

# List orders with pagination
curl "http://localhost:8080/api/orders?page=0&size=10"

# Update order
curl -X PUT http://localhost:8080/api/orders/1 \
  -H "Content-Type: application/json" \
  -d '{"productId":2, "quantity":5, "shippingAddress":"456 Oak Ave"}'

# Delete order
curl -X DELETE http://localhost:8080/api/orders/1

Output:

TEXT
{"status":"ok","data":{}}
HTTP Status Code Meaning When Returned
200 OK Success GET / PUT Success
201 Created Created POST Resource created successfully
204 No Content No content DELETE successful
400 Bad Request Request Error Parameter Validation Failed
404 Not Found Not Found Resource Does Not Exist

7. RESTful API Design Specifications

(1) URL Naming Conventions

Rule Incorrect Form Correct Form
Use of Plural Nouns /getOrder /api/orders
Nested Resource Relationships /orderItems?orderId=1 /api/orders/1/items
Use Paths Instead of Queries /api/orders?id=1 /api/orders/1
Version Control None /api/v1/orders
Query Parameters for Filtering /api/pendingOrders /api/orders?status=PENDING

8. Comprehensive Example: OrderFlow Order CRUD API

JAVA
package com.orderflow.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public record CreateOrderRequest(
    Long productId, Integer quantity, String shippingAddress
) {}

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final Map<Long, Map<String, Object>> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Map<String, Object>> create(
            @RequestBody CreateOrderRequest req) {
        Long id = seq.getAndIncrement();
        Map<String, Object> order = new LinkedHashMap<>();
        order.put("id", id);
        order.put("productId", req.productId());
        order.put("quantity", req.quantity());
        order.put("shippingAddress", req.shippingAddress());
        order.put("status", "PENDING");
        order.put("createdAt", java.time.Instant.now());
        store.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping
    public List<Map<String, Object>> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        return store.values().stream()
            .skip((long) page * size).limit(size).toList();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Map<String, Object>> get(@PathVariable Long id) {
        Map<String, Object> order = store.get(id);
        return order != null ? ResponseEntity.ok(order)
                             : ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        return store.remove(id) != null
            ? ResponseEntity.noContent().build()
            : ResponseEntity.notFound().build();
    }
}

❓ FAQ

Q What is the difference between @Controller and @RestController?
A @Controller returns a view name (for use with a template engine), while @RestController = @Controller + @ResponseBody, which serializes the return value directly to JSON. Always use @RestController when writing REST APIs.
Q What is the difference between PUT and PATCH?
A PUT performs a full replacement, so you must pass the entire object; PATCH performs a partial update, so you only need to pass the fields that need to be modified. Spring Boot supports both, but PATCH requires custom merge logic.
Q When should I use @PathVariable and @RequestParam?
A @PathVariable is used to identify a resource's unique identifier (such as an order ID), while @RequestParam is used for filtering and pagination parameters (such as status and page). Simple rule: Use @PathVariable for paths and @RequestParam for query parameters.
Q Should I return a ResponseEntity or just return the object directly?
A For simple queries, you can simply return the object (which automatically returns a 200 status code). Use ResponseEntity when you need to set custom status codes (such as 201 or 404) or response headers.
Q How should date and time formats be handled?
A By default, they are serialized as timestamps. You can configure this in application.yml or use @JsonFormat(pattern = "yyyy-MM-dd") on the field.
Q Does a REST API require version control?
A We recommend using URL-based version control (/api/v1/orders), which is simple and intuitive. You can also use header-based version control (Accept: application/vnd.orderflow.v1+json), which is more RESTful but more complex to implement.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty ⭐): Implement CRUD APIs for the Product resource in OrderFlow, including GET /api/v1/products, POST /api/v1/products, GET /api/v1/products/{id}, and DELETE /api/v1/products/{id}.

  2. Advanced Problem (Difficulty: ⭐⭐): Add the PATCH /api/v1/orders/{id}/status endpoint to the Order API that updates only the order status field. Consider the difference between PATCH and PUT.

  3. Challenge (Difficulty: ⭐⭐⭐): Implement the GET /api/v1/orders/{id}/items nested resource interface to return all order items under a specified order, with support for pagination parameters.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏