[2026] C++ REST API Server Complete Guide | Routing, Middleware, JWT, Swagger [#50-2]

[2026] C++ REST API Server Complete Guide | Routing, Middleware, JWT, Swagger [#50-2]

이 글의 핵심

Build Express-style REST API servers in C++: routing, middleware, JWT auth, Swagger docs, and production patterns for high-performance backends.

Introduction: “Express-like ergonomics in C++“

REST API Server Essentials

Like Node.js Express or Python Flask, concise routing, middleware chains, and auto-documentation in C++ deliver both high performance and productivity. Goals:

  • Express-style routing (GET/POST/PUT/DELETE)
  • Middleware chain (logging, auth, CORS)
  • JSON request/response auto-parsing
  • Swagger/OpenAPI doc generation
  • Request validation and error handling Requirements: C++17+, Boost.Beast, nlohmann/json What you’ll learn:
  • Build Express-style REST API servers
  • Implement middleware patterns
  • Integrate JWT authentication
  • Auto-generate API documentation

Problem Scenarios: When C++ REST API is Needed

Scenario 1: High-Performance Backend API
Node.js/Python are convenient, but for game servers, financial trading APIs, real-time data feeds handling tens of thousands of requests per second, latency is the bottleneck. C++ REST APIs achieve microsecond response times. Scenario 2: Add HTTP API to Existing C++ Systems
Game engines, trading systems, IoT gateways already in C++ need web/mobile client integration. Separate Node.js server adds IPC overhead. Direct REST API in C++ process simplifies integration. Scenario 3: Resource-Constrained Environments
Embedded/edge servers have strict memory limits. Node.js runtime consumes tens of MB. Lightweight C++ REST server operates in megabytes with minimal dependencies. Scenario 4: Learning Routing/Middleware Patterns
To understand how Express’s app.get(), app.use(), next() work, implementing from scratch is most effective. C++ gives full control over memory and runtime behavior. Scenario 5: Swagger Auto-Generation
As APIs grow, manual docs fall behind. Code-synced OpenAPI docs ease frontend/mobile collaboration. Scenario 6: “CORS error in browser”
Frontend (React, Vue) on http://localhost:3000, API on http://localhost:8080browser blocks cross-origin requests. Access to fetch has been blocked by CORS policy error. Fix: CORS middleware sets Access-Control-Allow-Origin/Methods/Headers, answers OPTIONS preflight with 204. Scenario 7: “POST request body is empty”
req.json_body() returns empty {}. Cause: Missing Content-Type: application/json header or response sent before parsing. Fix: Check Content-Type, parse, return 400 on failure. Scenario 8: “Protected API returns 200 without auth”
/users endpoint has auth middleware, but requests without Authorization header return 200 OK. Cause: Middleware registration order error or route-specific middleware not applied. Fix: Bind auth middleware explicitly to route, return immediately on auth failure without calling next().

Conceptual Analogy

Sockets and async I/O are like mailbox addresses and delivery routes. With correct address (IP·port), data arrives, and Asio is like one post office with multiple carriers (threads·handlers) dividing work.

Table of Contents

  1. Router Design
  2. Middleware Chain
  3. Request/Response Handling
  4. Authentication Middleware
  5. Validation and Error Handling
  6. Swagger Documentation
  7. Complete REST API Example
  8. Common Errors and Solutions
  9. Best Practices
  10. Performance Optimization
  11. Production Patterns

1. Router Design

Architecture Diagram

다음은 mermaid를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

flowchart TB
    subgraph Client[Client]
        C1[HTTP Request]
    end
    subgraph Server[Server]
        subgraph Middleware[Middleware Chain]
            M1[Logging]
            M2[CORS]
            M3[Auth]
            M4[Error Handler]
        end
        subgraph Router[Router]
            R1["GET /users"]
            R2["POST /users"]
            R3["GET /users/:id"]
        end
        M1 --> M2 --> M3 --> M4 --> Router
    end
    C1 --> Client
    Client --> Server

Express-Style API

다음은 cpp를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Usage example
RestServer server;
server.get("/users", [](const Request& req, Response& res) {
    res.json({{"users", get_all_users()}});
});
server.post("/users", [](const Request& req, Response& res) {
    auto user = req.body<User>();
    auto id = create_user(user);
    res.status(201).json({{"id", id}});
});
server.get("/users/:id", [](const Request& req, Response& res) {
    auto id = req.params("id");
    auto user = get_user(id);
    if (!user) {
        res.status(404).json({{"error", "User not found"}});
        return;
    }
    res.json(*user);
});
server.listen(8080);

Router Implementation

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <functional>
#include <vector>
#include <regex>
#include <optional>
class Router {
public:
    using Handler = std::function<void(const Request&, Response&)>;
    using Middleware = std::function<void(const Request&, Response&, std::function<void()>)>;
    
private:
    struct Route {
        std::string method;
        std::regex path_regex;
        std::vector<std::string> param_names;
        Handler handler;
        std::vector<Middleware> middlewares;
    };
    
    std::vector<Route> routes_;
    std::vector<Middleware> global_middlewares_;
    
public:
    void get(const std::string& path, Handler handler) {
        add_route("GET", path, handler);
    }
    
    void post(const std::string& path, Handler handler) {
        add_route("POST", path, handler);
    }
    
    void put(const std::string& path, Handler handler) {
        add_route("PUT", path, handler);
    }
    
    void del(const std::string& path, Handler handler) {
        add_route("DELETE", path, handler);
    }
    
    void use(Middleware middleware) {
        global_middlewares_.push_back(middleware);
    }
    
private:
    void add_route(const std::string& method, const std::string& path, Handler handler) {
        Route route;
        route.method = method;
        route.handler = handler;
        
        // Parse path parameters: /users/:id -> /users/([^/]+)
        std::string regex_path = path;
        std::regex param_regex(R"(:([a-zA-Z_][a-zA-Z0-9_]*))");
        std::smatch match;
        
        while (std::regex_search(regex_path, match, param_regex)) {
            route.param_names.push_back(match[1]);
            regex_path = match.prefix().str() + "([^/]+)" + match.suffix().str();
        }
        
        route.path_regex = std::regex("^" + regex_path + "$");
        routes_.push_back(route);
    }
    
public:
    std::optional<Route> match(const std::string& method, const std::string& path) {
        for (auto& route : routes_) {
            if (route.method != method) continue;
            
            std::smatch match;
            if (std::regex_match(path, match, route.path_regex)) {
                // Extract parameters (match[1], match[2], ...)
                return route;
            }
        }
        return std::nullopt;
    }
};

Key: Path parameters (:id) are converted to regex capture groups for extraction.

2. Middleware Chain

Middleware Pattern

다음은 mermaid를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

sequenceDiagram
    participant C as Client
    participant M1 as Logging
    participant M2 as CORS
    participant M3 as Auth
    participant H as Handler
    C->>M1: Request
    M1->>M2: next()
    M2->>M3: next()
    M3->>H: next()
    H-->>M3: Response
    M3-->>M2: Return
    M2-->>M1: Return
    M1-->>C: Response

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 타입 정의
class MiddlewareChain {
    std::vector<Router::Middleware> middlewares_;
    Router::Handler final_handler_;
    
public:
    void add(Router::Middleware middleware) {
        middlewares_.push_back(middleware);
    }
    
    void execute(const Request& req, Response& res) {
        execute_at(0, req, res);
    }
    
private:
    void execute_at(size_t index, const Request& req, Response& res) {
        if (index >= middlewares_.size()) {
            final_handler_(req, res);
            return;
        }
        
        middlewares_[index](req, res, [this, index, &req, &res]() {
            execute_at(index + 1, req, res);
        });
    }
};

Key: Each middleware calls next() to continue chain or returns early to short-circuit.

Logging Middleware

다음은 cpp를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

auto logging_middleware = [](const Request& req, Response& res, auto next) {
    auto start = std::chrono::steady_clock::now();
    
    std::cout << req.method() << " " << req.path() << std::endl;
    
    next();  // Execute next middleware
    
    auto end = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    std::cout << "  -> " << res.status_code() 
              << " (" << duration.count() << "ms)" << std::endl;
};
server.use(logging_middleware);

Output:

GET /users
  -> 200 (15ms)

CORS Middleware

아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

auto cors_middleware = [](const Request& req, Response& res, auto next) {
    res.set_header("Access-Control-Allow-Origin", "*");
    res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization");
    
    if (req.method() == "OPTIONS") {
        res.status(204).send();
        return;  // Don't call next()
    }
    
    next();
};
server.use(cors_middleware);

Key: OPTIONS preflight requests return 204 immediately without calling next().

3. Request/Response Handling

Request Class

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <boost/beast.hpp>
#include <nlohmann/json.hpp>
#include <unordered_map>
namespace beast = boost::beast;
namespace http = beast::http;
using json = nlohmann::json;
class Request {
    http::request<http::string_body> req_;
    std::unordered_map<std::string, std::string> params_;
    std::unordered_map<std::string, std::string> query_;
    json body_json_;
    
public:
    Request(http::request<http::string_body> req) 
        : req_(std::move(req)) {
        parse_query();
        parse_body();
    }
    
    std::string method() const {
        return std::string(req_.method_string());
    }
    
    std::string path() const {
        auto target = req_.target();
        auto pos = target.find('?');
        return std::string(target.substr(0, pos));
    }
    
    std::string param(const std::string& name) const {
        auto it = params_.find(name);
        return it != params_.end() ? it->second : "";
    }
    
    std::string query(const std::string& name) const {
        auto it = query_.find(name);
        return it != query_.end() ? it->second : "";
    }
    
    template<typename T>
    T body() const {
        return body_json_.get<T>();
    }
    
    const json& json_body() const {
        return body_json_;
    }
    
    std::string header(const std::string& name) const {
        return std::string(req_[name]);
    }
    
    void set_param(const std::string& name, const std::string& value) {
        params_[name] = value;
    }
    
private:
    void parse_query() {
        auto target = req_.target();
        auto pos = target.find('?');
        if (pos == std::string_view::npos) return;
        
        auto query_string = target.substr(pos + 1);
        // Parse query_string: key1=value1&key2=value2
        std::string qs(query_string);
        size_t start = 0;
        while (start < qs.size()) {
            auto amp = qs.find('&', start);
            auto pair = qs.substr(start, amp - start);
            auto eq = pair.find('=');
            if (eq != std::string::npos) {
                query_[pair.substr(0, eq)] = pair.substr(eq + 1);
            }
            start = (amp == std::string::npos) ? qs.size() : amp + 1;
        }
    }
    
    void parse_body() {
        if (req_.body().empty()) return;
        
        try {
            body_json_ = json::parse(req_.body());
        } catch (const json::exception&) {
            // JSON parse failure
        }
    }
};

Response Class

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Response {
    http::response<http::string_body> res_;
    bool sent_ = false;
    
public:
    Response() {
        res_.version(11);  // HTTP/1.1
        res_.result(http::status::ok);
    }
    
    Response& status(unsigned code) {
        res_.result(static_cast<http::status>(code));
        return *this;
    }
    
    Response& set_header(const std::string& name, const std::string& value) {
        res_.set(name, value);
        return *this;
    }
    
    void json(const ::json& data) {
        res_.set(http::field::content_type, "application/json");
        res_.body() = data.dump();
        res_.prepare_payload();
        sent_ = true;
    }
    
    void send(const std::string& body = "") {
        res_.body() = body;
        res_.prepare_payload();
        sent_ = true;
    }
    
    void send_file(const std::string& path) {
        std::ifstream file(path, std::ios::binary);
        if (!file) {
            status(404).send("File not found");
            return;
        }
        
        std::string content((std::istreambuf_iterator<char>(file)),
                           std::istreambuf_iterator<char>());
        
        // Set MIME type
        auto ext = std::filesystem::path(path).extension().string();
        set_header("Content-Type", get_mime_type(ext));
        
        send(content);
    }
    
    unsigned status_code() const {
        return res_.result_int();
    }
    
    const auto& native() const { return res_; }
};

Key: Response provides fluent API (status().json()) for concise handler code.

4. Authentication Middleware

JWT Authentication

다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <jwt-cpp/jwt.h>
class JWTAuth {
    std::string secret_;
    
public:
    JWTAuth(const std::string& secret) : secret_(secret) {}
    
    std::string generate(const std::string& user_id) {
        return jwt::create()
            .set_issuer("my-api")
            .set_type("JWT")
            .set_payload_claim("user_id", jwt::claim(user_id))
            .set_expires_at(std::chrono::system_clock::now() + std::chrono::hours(24))
            .sign(jwt::algorithm::hs256{secret_});
    }
    
    std::optional<std::string> verify(const std::string& token) {
        try {
            auto decoded = jwt::decode(token);
            auto verifier = jwt::verify()
                .allow_algorithm(jwt::algorithm::hs256{secret_})
                .with_issuer("my-api");
            
            verifier.verify(decoded);
            return decoded.get_payload_claim("user_id").as_string();
        } catch (const std::exception&) {
            return std::nullopt;
        }
    }
};
// Auth middleware
auto auth_middleware(JWTAuth& jwt_auth) {
    return [&jwt_auth](const Request& req, Response& res, auto next) {
        auto auth_header = req.header("Authorization");
        if (auth_header.empty() || !auth_header.starts_with("Bearer ")) {
            res.status(401).json({{"error", "Unauthorized"}});
            return;  // Don't call next()
        }
        
        auto token = auth_header.substr(7);  // Remove "Bearer "
        auto user_id = jwt_auth.verify(token);
        
        if (!user_id) {
            res.status(401).json({{"error", "Invalid token"}});
            return;
        }
        
        // Store user_id in request (requires const_cast or mutable context)
        const_cast<Request&>(req).set_user_id(*user_id);
        
        next();  // Continue to handler
    };
}
// Usage
server.get("/profile", auth_middleware(jwt_auth), 
    [](const Request& req, Response& res) {
        auto user_id = req.user_id();
        auto profile = get_user_profile(user_id);
        res.json(profile);
    });

Key: Auth middleware validates JWT, stores user_id in request, and calls next() only on success.

5. Validation and Error Handling

Request Validation

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Validator {
public:
    struct Rule {
        std::string field;
        std::function<bool(const json&)> check;
        std::string message;
    };
    
    std::vector<Rule> rules_;
    
    Validator& required(const std::string& field) {
        rules_.push_back({
            field,
            [field](const json& data) { return data.contains(field); },
            field + " is required"
        });
        return *this;
    }
    
    Validator& string(const std::string& field) {
        rules_.push_back({
            field,
            [field](const json& data) { 
                return data.contains(field) && data[field].is_string(); 
            },
            field + " must be a string"
        });
        return *this;
    }
    
    Validator& min_length(const std::string& field, size_t len) {
        rules_.push_back({
            field,
            [field, len](const json& data) {
                return data.contains(field) && 
                       data[field].is_string() && 
                       data[field].get<std::string>().length() >= len;
            },
            field + " must be at least " + std::to_string(len) + " characters"
        });
        return *this;
    }
    
    Validator& email(const std::string& field) {
        rules_.push_back({
            field,
            [field](const json& data) {
                if (!data.contains(field) || !data[field].is_string()) return false;
                std::string val = data[field];
                std::regex email_regex(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
                return std::regex_match(val, email_regex);
            },
            field + " must be a valid email"
        });
        return *this;
    }
    
    std::optional<std::vector<std::string>> validate(const json& data) {
        std::vector<std::string> errors;
        for (const auto& rule : rules_) {
            if (!rule.check(data)) {
                errors.push_back(rule.message);
            }
        }
        return errors.empty() ? std::nullopt : std::make_optional(errors);
    }
};
// Usage
server.post("/users", [](const Request& req, Response& res) {
    Validator validator;
    validator.required("email")
             .string("email")
             .email("email")
             .required("password")
             .min_length("password", 8);
    
    auto errors = validator.validate(req.json_body());
    if (errors) {
        res.status(400).json({{"errors", *errors}});
        return;
    }
    
    // Validation passed
    auto user = create_user(req.body<User>());
    res.status(201).json(user);
});

Global Error Handler

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class ErrorHandler {
public:
    static void handle(const std::exception& e, Response& res) {
        if (auto* ve = dynamic_cast<const ValidationError*>(&e)) {
            res.status(400).json({
                {"error", "Validation failed"},
                {"details", ve->errors()}
            });
        }
        else if (auto* ne = dynamic_cast<const NotFoundException*>(&e)) {
            res.status(404).json({
                {"error", ne->what()}
            });
        }
        else if (auto* ae = dynamic_cast<const AuthError*>(&e)) {
            res.status(401).json({
                {"error", ae->what()}
            });
        }
        else {
            res.status(500).json({
                {"error", "Internal server error"},
                {"message", e.what()}
            });
        }
    }
};
// Error handling middleware
auto error_handler_middleware = [](const Request& req, Response& res, auto next) {
    try {
        next();
    } catch (const std::exception& e) {
        ErrorHandler::handle(e, res);
    }
};
server.use(error_handler_middleware);

Key: Catch exceptions from handlers and convert to appropriate HTTP error responses.

6. Swagger Documentation

API Documentation Definition

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

struct APIDoc {
    std::string path;
    std::string method;
    std::string summary;
    std::string description;
    json parameters;
    json request_body;
    json responses;
};
class SwaggerGenerator {
    std::vector<APIDoc> docs_;
    
public:
    void add_doc(const APIDoc& doc) {
        docs_.push_back(doc);
    }
    
    json generate() {
        json swagger = {
            {"openapi", "3.0.0"},
            {"info", {
                {"title", "My API"},
                {"version", "1.0.0"}
            }},
            {"paths", json::object()}
        };
        
        for (const auto& doc : docs_) {
            if (!swagger[paths].contains(doc.path)) {
                swagger[paths][doc.path] = json::object();
            }
            
            swagger[paths][doc.path][doc.method] = {
                {"summary", doc.summary},
                {"description", doc.description},
                {"parameters", doc.parameters},
                {"requestBody", doc.request_body},
                {"responses", doc.responses}
            };
        }
        
        return swagger;
    }
};
// Usage
SwaggerGenerator swagger;
swagger.add_doc({
    "/users",
    "post",
    "Create user",
    "Create a new user account",
    json::array(),
    {
        {"content", {
            {"application/json", {
                {"schema", {
                    {"type", "object"},
                    {"properties", {
                        {"email", {{"type", "string"}}},
                        {"password", {{"type", "string"}}}
                    }},
                    {"required", {"email", "password"}}
                }}
            }}
        }}
    },
    {
        {"201", {
            {"description", "User created"},
            {"content", {
                {"application/json", {
                    {"schema", {
                        {"type", "object"},
                        {"properties", {
                            {"id", {{"type", "string"}}},
                            {"email", {{"type", "string"}}}
                        }}
                    }}
                }}
            }}
        }},
        {"400", {{"description", "Validation error"}}}
    }
});
// Swagger UI endpoint
server.get("/api-docs", [&swagger](const Request& req, Response& res) {
    res.json(swagger.generate());
});

Key: OpenAPI schema can be generated from code annotations or maintained alongside routes.

7. Complete REST API Example

Request Flow (Middleware → Route)

다음은 mermaid를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

sequenceDiagram
    participant C as Client
    participant L as Logging
    participant CO as CORS
    participant A as Auth
    participant H as Handler
    C->>L: GET /users
    L->>CO: next()
    CO->>A: next()
    A->>A: JWT verify
    alt Auth success
        A->>H: next()
        H-->>C: 200 JSON
    else Auth failure
        A-->>C: 401 Unauthorized
    end

Complete User Management API

다음은 cpp를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// main.cpp - Complete user CRUD API
int main() {
    RestServer server;
    JWTAuth jwt_auth("my-secret-key-change-in-production");
    SwaggerGenerator swagger;
    
    // Global middlewares
    server.use(logging_middleware);
    server.use(cors_middleware);
    server.use(error_handler_middleware);
    
    // Public: login, register
    server.post("/auth/login", [&jwt_auth](const Request& req, Response& res) {
        auto body = req.json_body();
        auto user = authenticate(body[email], body[password]);
        if (!user) {
            res.status(401).json({{"error", "Invalid credentials"}});
            return;
        }
        res.json({
            {"token", jwt_auth.generate(user->id)},
            {"user", *user}
        });
    });
    
    server.post("/auth/register", [](const Request& req, Response& res) {
        Validator v;
        v.required("email").email("email")
         .required("password").min_length("password", 8);
        
        auto err = v.validate(req.json_body());
        if (err) {
            res.status(400).json({{"errors", *err}});
            return;
        }
        
        auto u = create_user(req.json_body());
        res.status(201).json({{"id", u.id}, {"email", u.email}});
    });
    
    // Protected: CRUD
    server.get("/users", auth_middleware(jwt_auth),
        [](const Request& req, Response& res) {
            auto page = req.query("page");
            auto limit = req.query("limit");
            res.json({{"users", get_users_paginated(page, limit)}});
        });
    
    server.get("/users/:id", auth_middleware(jwt_auth),
        [](const Request& req, Response& res) {
            auto u = get_user(req.param("id"));
            u ? res.json(*u) : res.status(404).json({{"error", "Not found"}});
        });
    
    server.put("/users/:id", auth_middleware(jwt_auth),
        [](const Request& req, Response& res) {
            if (req.user_id() != req.param("id")) {
                res.status(403).json({{"error", "Forbidden"}});
                return;
            }
            res.json(update_user(req.param("id"), req.json_body()));
        });
    
    server.del("/users/:id", auth_middleware(jwt_auth),
        [](const Request& req, Response& res) {
            if (req.user_id() != req.param("id")) {
                res.status(403).json({{"error", "Forbidden"}});
                return;
            }
            delete_user(req.param("id")) 
                ? res.status(204).send() 
                : res.status(404).json({{"error", "Not found"}});
        });
    
    // API docs
    server.get("/api-docs", [&swagger](const Request&, Response& res) {
        res.json(swagger.generate());
    });
    
    server.listen(8080);
    return 0;
}

cURL Testing

다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 1. Register
curl -X POST http://localhost:8080/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret1234","name":"John Doe"}'
# 2. Login
curl -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret1234"}'
# Response: {"token":"eyJ...", "user":{"id":"...","email":"..."}}
# 3. List users (auth required)
curl -X GET http://localhost:8080/users?page=1&limit=10 \
  -H "Authorization: Bearer eyJ..."
# 4. Get user
curl -X GET http://localhost:8080/users/123 \
  -H "Authorization: Bearer eyJ..."
# 5. Update user
curl -X PUT http://localhost:8080/users/123 \
  -H "Authorization: Bearer eyJ..." \
  -H "Content-Type: application/json" \
  -d '{"name":"John Doe 2","email":"new@example.com"}'
# 6. Delete user
curl -X DELETE http://localhost:8080/users/123 \
  -H "Authorization: Bearer eyJ..."

8. Common Errors and Solutions

Problem 1: “Connection reset” / Request body too large

Cause: Request body too large or client early termination. Solution: Set Beast parser.body_limit(1024*1024), return 413 Request Entity Too Large on exceed. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

http::request_parser<http::string_body> parser;
parser.body_limit(1024 * 1024);  // 1MB
if (/* body limit exceeded */) {
    res.status(413).json({{"error", "Request entity too large"}});
}

Problem 2: JSON Parsing Failure (400)

Cause: Missing Content-Type: application/json or malformed JSON. Solution: Check Content-Type, catch json::parse() exception, return 400. 아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void parse_body() {
    if (req_.body().empty()) return;
    
    auto content_type = req_[http::field::content_type];
    if (content_type != "application/json") {
        throw BadRequestException("Content-Type must be application/json");
    }
    
    try {
        body_json_ = json::parse(req_.body());
    } catch (const json::exception& e) {
        throw BadRequestException("Invalid JSON: " + std::string(e.what()));
    }
}

Problem 3: JWT Token Expired (401)

Cause: exp claim expired. Solution: Implement POST /auth/refresh endpoint for refresh token exchange. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

server.post("/auth/refresh", [&jwt_auth](const Request& req, Response& res) {
    auto refresh_token = req.json_body()[refresh_token];
    auto user_id = jwt_auth.verify_refresh(refresh_token);
    if (!user_id) {
        res.status(401).json({{"error", "Invalid refresh token"}});
        return;
    }
    res.json({{"token", jwt_auth.generate(*user_id)}});
});

Problem 4: CORS Preflight Failure

Cause: OPTIONS request not handled or missing Access-Control-Allow-* headers. Solution: 아래 코드는 cpp를 사용한 구현 예제입니다. 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

if (req.method() == "OPTIONS") {
    res.set_header("Access-Control-Allow-Origin", "*");
    res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization");
    res.status(204).send();
    return;  // Don't call next()!
}

Problem 5: Missing next() Call

Cause: Middleware doesn’t call next() on success. Solution: Always call next() after successful validation. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ✅ Correct pattern
if (!user_id) {
    res.status(401).json({{"error", "Unauthorized"}});
    return;  // Don't call next()
}
const_cast<Request&>(req).set_user_id(*user_id);
next();  // Required!

Problem 6: Path Parameter Matching Failure

Cause: Special character escaping missing in :id([^/]+) conversion. Solution: Use std::regex_replace to convert :param to ([^/]+), match with ^...$.

Problem 7: Data Race on Concurrent Requests

Cause: const_cast to store user_id in Request causes race when requests overlap. Solution: Use per-request RequestContext struct for independent storage. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

struct RequestContext {
    std::string user_id;
    std::string request_id;
};
// Store in thread-local or request-scoped shared_ptr

Problem 8: Missing 404 Handler

Cause: No route match, unhandled. Solution: Return 404 when router.match() returns nullopt. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

auto route = router.match(req.method(), req.path());
if (!route) {
    res.status(404).json({{"error", "Not found"}});
    return;
}

9. Best Practices

1. Middleware Registration Order

Middlewares execute in registration order. Register logging → CORS → error handler first, apply auth per-route. 다음은 간단한 cpp 코드 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

server.use(logging_middleware);
server.use(cors_middleware);
server.use(error_handler_middleware);
server.get("/users", auth_middleware(jwt_auth), handler);

2. Unified JSON Response Format

Standardize success/failure with data or error keys. Include request_id for debugging. 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Success
{"data": {...}, "request_id": "..."}
// Error
{"error": "...", "details": [...], "request_id": "..."}

3. HTTP Method and Status Code Compliance

OperationMethodSuccessFailure
CreatePOST201400, 409
ReadGET200404
UpdatePUT/PATCH200400, 404
DeleteDELETE204404

4. Environment Variables for Config

Never hardcode JWT secrets. Use std::getenv("JWT_SECRET"). 아래 코드는 cpp를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

auto secret = std::getenv("JWT_SECRET");
if (!secret) {
    throw std::runtime_error("JWT_SECRET not set");
}
JWTAuth jwt_auth(secret);

5. Async I/O and Thread Model

Run io_context on std::thread::hardware_concurrency() threads. 아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

net::io_context ioc;
std::vector<std::thread> threads;
for (int i = 0; i < std::thread::hardware_concurrency(); ++i) {
    threads.emplace_back([&ioc]() { ioc.run(); });
}
for (auto& t : threads) t.join();

6. API Versioning

Include version in URL path: /v1/users, /v2/users.

server.get("/v1/users", handler_v1);
server.get("/v2/users", handler_v2);

10. Performance Optimization

1. JSON Serialization Optimization

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Remove whitespace in production (smaller payload)
void json_response(const json& data) {
    res_.set(http::field::content_type, "application/json");
    res_.body() = data.dump(-1);  // -1: no indentation, minimal size
    res_.prepare_payload();
}

2. Connection Pooling (Keep-Alive)

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// HTTP Keep-Alive for connection reuse
void prepare_response() {
    res_.set(http::field::connection, "keep-alive");
    res_.set(http::field::keep_alive, "timeout=60");
}

3. Response Buffer Pre-Allocation

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Pre-allocate buffer for large responses
void json(const json& data) {
    std::string body = data.dump(-1);
    res_.body().reserve(body.size() + 256);  // Header overhead
    res_.body() = std::move(body);
    res_.prepare_payload();
}

4. Regex Caching

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Regex compilation is expensive
class Router {
    std::unordered_map<std::string, std::regex> path_cache_;
    
    std::regex get_or_compile(const std::string& path) {
        auto it = path_cache_.find(path);
        if (it != path_cache_.end()) return it->second;
        
        auto compiled = compile_path_to_regex(path);
        path_cache_[path] = compiled;
        return compiled;
    }
};

5. Async I/O Utilization

아래 코드는 cpp를 사용한 구현 예제입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Use Beast async instead of sync I/O
void handle_request() {
    http::async_read(socket_, buffer_, req_,
        [this](beast::error_code ec, std::size_t) {
            if (!ec) {
                process_request();
            }
        });
}

6. Performance Benchmarks

ConfigurationSync (1 thread)Async (1 thread)Async (4 threads)
1K req/s~800~1,500~5,000
Avg latency1.2ms0.6ms0.2ms
Memory50MB80MB120MB
Key: Async + multi-thread provides 6x throughput improvement.

11. Production Patterns

Pattern 1: Health Check Endpoint

다음은 cpp를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// For Kubernetes / load balancers
server.get("/health", [](const Request&, Response& res) {
    res.json({
        {"status", "ok"},
        {"version", "1.0.0"},
        {"timestamp", std::time(nullptr)}
    });
});
server.get("/health/ready", [](const Request&, Response& res) {
    if (!check_database_connection()) {
        res.status(503).json({{"status", "unhealthy"}});
    } else {
        res.json({{"status", "ready"}});
    }
});

Pattern 2: Request ID Tracking

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

auto request_id_middleware = [](const Request& req, Response& res, auto next) {
    auto req_id = req.header("X-Request-ID").empty() 
        ? generate_uuid() 
        : req.header("X-Request-ID");
    res.set_header("X-Request-ID", req_id);
    const_cast<Request&>(req).set_request_id(req_id);
    next();
};

Pattern 3: Rate Limiting

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Token bucket: 100 requests per minute per client
class RateLimiter {
    std::unordered_map<std::string, std::pair<int, std::chrono::steady_clock::time_point>> limits_;
    int max_ = 100;
    std::chrono::seconds window_{60};
    
public:
    bool allow(const std::string& client_id) {
        auto now = std::chrono::steady_clock::now();
        auto& [count, start] = limits_[client_id];
        if (now - start > window_) {
            count = 0;
            start = now;
        }
        return count++ < max_;
    }
};
// Return 429 Too Many Requests
auto rate_limit_middleware = [&limiter](const Request& req, Response& res, auto next) {
    auto client_id = req.header("X-Client-ID");
    if (!limiter.allow(client_id)) {
        res.status(429).json({{"error", "Too many requests"}});
        return;
    }
    next();
};

Pattern 4: Structured Logging

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// JSON logs for ELK/CloudWatch
void log_request(const Request& req, const Response& res, auto duration) {
    std::cerr << json{
        {"method", req.method()},
        {"path", req.path()},
        {"status", res.status_code()},
        {"duration_ms", duration.count()},
        {"request_id", req.request_id()}
    }.dump() << std::endl;
}

Pattern 5: Graceful Shutdown

아래 코드는 cpp를 사용한 구현 예제입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

std::atomic<bool> running{true};
std::signal(SIGTERM, [](int) { running = false; });
std::signal(SIGINT, [](int) { running = false; });
while (running) {
    server.poll();
}
server.stop();

Pattern 6: Production Checklist

아래 코드는 markdown를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

- [ ] JWT secret in environment variable (never hardcode)
- [ ] CORS Allow-Origin restricted to allowed domains (not "*")
- [ ] HTTPS only (HTTP redirect)
- [ ] Request body size limit (1MB-10MB)
- [ ] Rate limiting applied
- [ ] Health check endpoint exposed
- [ ] Structured logging (JSON)
- [ ] Graceful shutdown implemented
- [ ] Swagger UI only in dev environment
- [ ] Error responses don't leak stack traces

Summary

Key Points

  1. Routing: Regex + parameter extraction
  2. Middleware: Function chain + next() callback
  3. Authentication: JWT + Bearer token
  4. Validation: Rule-based Validator
  5. Documentation: Swagger/OpenAPI auto-generation
  6. Performance: Async I/O + multi-threading

Feature Comparison

FeatureImplementation
RoutingRegex + parameter extraction
MiddlewareFunction chain + next()
AuthJWT + Bearer token
ValidationRule-based Validator
DocsSwagger/OpenAPI generation

Core Principles

  1. Express-style API for productivity
  2. Middleware for cross-cutting concerns
  3. JSON auto-parsing for convenience
  4. Type safety maintained
  5. Auto-documentation for maintainability

Implementation Checklist

Before Writing Code

  • Is this the best approach for the problem?
  • Can team members understand and maintain this?
  • Does it meet performance requirements?

While Writing Code

  • All compiler warnings resolved?
  • Edge cases considered?
  • Error handling appropriate?

During Code Review

  • Intent clear?
  • Test cases sufficient?
  • Documentation present?

Keywords

C++ REST API, Boost.Beast, routing, middleware, JWT authentication, Swagger, OpenAPI, Express-style, HTTP server One-line summary: Build Express-style REST API servers in C++ with routing, middleware, JWT auth, and Swagger docs for high-performance backends with microsecond response times.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3