Research CommonsResearch Commons
cppnet/Handlers & routing

Handlers & routing

Add endpoints to a cppnet server by defining handlers and registering them with the router.

Adding functionality to a cppnet server means writing a handler and registering it on the router. A handler owns the business logic for one endpoint; the router maps a method + path to it.

Step 1 — Define a handler

Subclass http::handlers::BaseHandler and implement handle. Pass shared state (a database connection, an in-memory store, …) through the constructor.

#include "http/handlers/base_handler.h"
#include "http/request.h"
#include <nlohmann/json.hpp>
#include <unordered_map>
 
using UserStore = std::unordered_map<std::string, nlohmann::json>;
 
class UserPostHandler : public http::handlers::BaseHandler {
public:
    UserPostHandler(UserStore &store) : users(store) {}
 
    std::string handle(const http::Request &req) const override {
        nlohmann::json resp;
        try {
            nlohmann::json posted = nlohmann::json::parse(req.body);
            std::string username = posted["username"].get<std::string>();
            users[username] = posted;       // store the user
            resp["success"] = true;
            resp["user"] = posted;
        } catch (const std::exception &e) {
            resp["success"] = false;
            resp["error"] = "Invalid JSON or missing username";
        }
        return resp.dump(2);
    }
 
private:
    UserStore &users;
};

Step 2 — Instantiate and register

Create the handler instance and register the method + path with the Router. A shared_ptr lets a single instance serve many requests.

#include "http/router.h"
#include <memory>
 
int main() {
    http::Router router;
    UserStore user_data_store;
 
    auto user_post_handler = std::make_shared<UserPostHandler>(user_data_store);
 
    router.add_route(
        http::Method::POST,
        "/user",
        [user_post_handler](const http::Request &req) {
            return user_post_handler->handle(req);
        }
    );
 
    // ... server startup (socket layer) ...
}

Step 3 — Repeat for every endpoint

The same pattern covers GET, PUT, PATCH, DELETE, and so on — register a handler per method/path. The repository's handler tests (post_put_test.cpp, post_patch_test.cpp, post_delete_test.cpp) demonstrate each verb.

Reading request data

Inside a handler, the Request gives you typed access to the incoming data:

  • req.method, req.path, req.body
  • req.get_header(name) for header values
  • req.get_query_param(name) for query parameters

Utility helpers (get_param, get_with_default) provide type-safe extraction of query parameters, using std::optional to handle missing values gracefully.

Next

Trace how these handlers get invoked in the Request lifecycle, or review the overall design in Architecture.