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.bodyreq.get_header(name)for header valuesreq.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.