Geruest - A Modern C++ Web Framework
A comprehensive web framework built in C++ that simplifies both frontend and backend development. Geruest handles translations, asset management, routing, and more—letting you focus on building features.
View on GitHubJune 2026 - Modular Architecture & Stability (v0.14.3)
Release v0.14.3 builds on the v0.13 modular rewrite. The monolithic library split into optional CMake targets—GeruestCore, GeruestAssets, GeruestDatabase, GeruestEmail, GeruestObfuscation, and GeruestWebSocket—with umbrella <Geruest.hpp>. The HTTP pipeline refactored into focused units (HttpFraming, ResponseWriter, RouteDispatcher, StaticFileResolver, GzipResponse, GateEvaluation), and ServerData slimmed down with extracted registries for routes, gates, CORS, languages, metrics, and caches.
Example: optional CMake module linking
find_package(Geruest REQUIRED)
# Full stack (unchanged for existing apps)
# target_link_libraries(myapp PRIVATE Geruest::Geruest Boost::system Threads::Threads)
# Or link only modules installed on this machine:
set(_geruest_modules
Geruest::GeruestCore
Geruest::GeruestObfuscation
Geruest::GeruestAssets
)
if(TARGET Geruest::GeruestEmail)
list(APPEND _geruest_modules Geruest::GeruestEmail)
endif()
# Omit WebSocket / Database targets when Geruest was built without them
target_link_libraries(myapp PRIVATE
"$<LINK_GROUP:RESCAN,${_geruest_modules}>"
Boost::system
Threads::Threads)
# Minimal HTTP API — Core alone (routes, gates, static files):
# target_link_libraries(api PRIVATE Geruest::GeruestCore Boost::system Threads::Threads)
Routing & access control: RouteRegistry handles exact and wildcard paths; GateRegistry / GateEvaluation power gated pages, HTTP routes, and WebSocket upgrades. CORS is path-scoped via enableCors() with an origin allowlist and automatic OPTIONS preflight on matching routes.
WebSockets, assets & obfuscation: dedicated WebSocketUpgrade with coroutine and callback APIs wired into the same path matching as HTTP. Assets gained HTML-driven discovery (AssetHtmlDiscovery), a dev-mode cache (DevAssetCache), an improved merged-asset resolver, and better WebP conversion. Obfuscation picked up scoped rename fixes and a dedicated settings module.
v0.14.3 stability: fixes a stack overflow (exit 139) when serving synchronous routes whose handlers use large stack frames—notably repeated polls of GET /status. Sync handlers now run on the dispatcher coroutine stack instead of inside a nested coroutine frame in RouteDispatcher.
/status logic moved to Geruest::handleStatusRequest, and cgroup CPU sampling is guarded with a mutex so concurrent status polls stay thread-safe under load.
Configure-time creation of nested CMake object directories avoids parallel gmake -j races on missing folders. bindServerData runs after optional module registration so server data is bound once modules are registered.
June 2026 - Gated Pages & Routes (v0.11.7)
Geruest adds custom access checks for static pages and API routes via addGatedPage() and an optional gate on addRoute(). Supply a sync bool(const HTTPRequest&) handler—or an async variant with co_await for database or session lookups—and the framework enforces it before serving content or running the route handler.
Deny behavior: gated pages respond with 302 Found and a language-aware redirect (custom redirectTo or the language index). Matching API routes return 403 Forbidden without calling the handler. WebSocket upgrades use the same gate types and also deny with 403 before the handshake.
Protected merged assets: with mergeAssets=true, merged JS and CSS bundles for gated or Basic Auth pages inherit the same access rules as the HTML. Denied asset requests return 403—not a redirect—so clients cannot fetch bundles for pages they cannot open.
The database client exposes queryJsonAsync() alongside queryAsync() and executeAsync(). Async route handlers can co_await a JSONParser result shaped as {"rows":[...], "affectedRows":N} without manual row-to-JSON assembly.
Example: gated page and API route
server.addGatedPage("/admin/dashboard", [](const geruest::HTTPRequest& req) {
return req.getHeader("authorization") == "Bearer mytoken";
}, "/login");
server.addRoute("/v1/admin", handleAdmin, [](const geruest::HTTPRequest& req) {
return req.getHeader("authorization") == "Bearer mytoken";
});
Example: async route with queryJsonAsync
server.addRoute("/v1/users", [](const geruest::HTTPRequest& request)
-> geruest::AsyncResponse {
auto db = request.database();
if (!db) co_return geruest::responseInternalServerError();
auto json = co_await db->queryJsonAsync(
"SELECT id, name FROM users WHERE name = ?",
{std::string("alice")}
);
geruest::HTTPResponse response = geruest::responseOK();
response.setHeader("Content-Type", "application/json");
response.setBody(json.toString());
co_return response;
});
June 2026 - WebSocket Support (v0.10.7)
Geruest now supports RFC 6455 WebSockets alongside existing HTTP routes. Register persistent connections with addRouteWebSocket()—path matching works the same as addRoute(), including exact paths and * wildcards. Upgrade detection runs before redirects and regular HTTP routing on GET requests that include Upgrade: websocket.
Coroutine and callback APIs: The recommended coroutine API mirrors async addRoute()—handlers return boost::asio::awaitable<void> and use co_await on ws.send() and ws.recv(). For event-style code, the callback API exposes WebSocketRoute with onOpen, onMessage, and onClose hooks, plus sendNow() for fire-and-forget replies.
Configuration and behavior: Tune limits with setWebSocketMaxMessageBytes(), setWebSocketMaxFrameBytes(), setWebSocketIdleTimeout(), and setWebSocketPingInterval(). Optional subprotocol negotiation is available via addWebSocketSubprotocol(). After a successful 101 Switching Protocols, the connection stays in WebSocket mode until the handler returns. The server automatically replies to client ping frames with pong. Use a reverse proxy for WSS/TLS termination.
Example: WebSocket echo server (coroutine API)
server.addRouteWebSocket("/chat", [](geruest::WebSocketConnection& ws,
const geruest::HTTPRequest& req)
-> boost::asio::awaitable<void> {
co_await ws.send("welcome");
while (ws.isOpen()) {
geruest::WSMessage msg = co_await ws.recv();
if (msg.isClose()) break;
if (msg.isText()) {
co_await ws.send("echo: " + msg.text());
}
}
co_return;
});
Example: WebSocket echo server (callback API)
geruest::WebSocketRoute chat;
chat.onOpen = [](geruest::WebSocketConnection& ws, const geruest::HTTPRequest&) {
ws.sendNow("connected");
};
chat.onMessage = [](geruest::WebSocketConnection& ws, geruest::WSMessage msg) {
if (msg.isText()) ws.sendNow("echo: " + msg.text());
};
chat.onClose = [](geruest::WebSocketConnection&, uint16_t code, std::string_view reason) {
(void)code;
(void)reason;
};
server.addRouteWebSocket("/chat-cb", chat);
March 2026 - Async Optimisation (v0.9.26)
Geruest now supports asynchronous communication built on Boost.Asio and C++20 coroutines. The HTTP server uses non-blocking I/O under the hood—connections are accepted with async_accept, and each client session runs on a per-connection strand with co_await on socket reads and writes. This lets the framework handle many concurrent clients without blocking worker threads on slow operations.
Hybrid HTTP model: Not every request path is fully async by design. Synchronous routes via addRoute() remain the default for simple, fast handlers where coroutine overhead would add little benefit. The framework keeps the hot path lean so static responses and lightweight APIs stay as responsive as possible.
Async routes and database calls: For work that benefits from suspension—database queries, external APIs, or other waitable I/O—use the async addRoute() overload and return an AsyncResponse with co_await. PostgreSQL and SQLite integrations expose queryAsync(), so handlers can fetch rows without holding a thread for the entire query. Example: auto rows = co_await db->queryAsync("SELECT id FROM users", {});
Example: async route with a database query
server.addRoute("/users", [](const geruest::HTTPRequest& request)
-> geruest::AsyncResponse {
geruest::HTTPResponse response("200 OK");
response.setHeader("Content-Type", "application/json");
auto db = request.database();
if (!db) {
response.setBody("{\"error\":\"database not configured\"}");
co_return response;
}
auto result = co_await db->queryAsync(
"SELECT id, name FROM users WHERE name = ?",
{std::string("alice")}
);
std::string body = "[";
for (size_t i = 0; i < result.rows.size(); ++i) {
if (i > 0) body += ",";
body += "{\"id\":\"" + result.rows[i].columns[0] + "\",\"name\":\"" + result.rows[i].columns[1] + "\"}";
}
body += "]";
response.setBody(body);
co_return response;
});
February 2026 - New Features & Improvements (v0.7.0)
Since December, Geruest has evolved significantly with several powerful features that enhance both developer experience and application performance. These improvements reflect lessons learned from production deployments and developer feedback, focusing on making the framework more flexible and efficient.
Development Mode: To streamline the development process, Geruest now includes a dedicated development mode that can be activated using enableDevMode(). When enabled, the framework no longer saves generated files locally, eliminating the clutter of temporary files in your project directory. This makes it easier to work with the framework during development, as you don't need to constantly clean up generated assets or worry about version control conflicts with temporary files.
WebP Image Conversion: Modern web applications demand optimal image delivery, and Geruest now addresses this with automatic WebP conversion. By calling enableWebPConversion(), the framework automatically converts JPG and PNG images to the more efficient WebP format. This conversion happens transparently and can significantly reduce image file sizes, leading to faster page loads and reduced bandwidth consumption. The framework intelligently handles this conversion while maintaining image quality, making it a zero-effort performance boost.
Enhanced Logging System: Understanding what's happening in your application is crucial, which is why Geruest now features an improved logging system. The new implementation allows developers to choose between verbose and silent output modes depending on their needs. During development, verbose mode provides detailed insights into request processing, file operations, and framework decisions. In production, silent mode keeps logs minimal and focused on important events. This flexibility ensures you have the information you need without being overwhelmed by unnecessary output.
December 2025 - JS and CSS merge rework (v0.6.1)
Reworked the asset merging system to be more intuitive and maintainable. The previous approach required manually maintaining JSON map files that specified which files to merge for each page, which became cumbersome and error-prone as the project grew. The new system automatically reads HTML files and intelligently merges all referenced CSS and JavaScript assets, eliminating manual configuration. This new approach features automatic path normalization, support for nested directory structures, and intelligent file merging to reduce HTTP requests and improve page load performance.
Key Features:
- Automatic merging of CSS and JS files per page
- Path normalization with automatic leading slash addition
- Support for unlimited subdirectory nesting
- Cross-directory asset references
- Configurable via
setMergeAssets()method
December 2025 - Framework Overview (v0.5.4)
Frontend Capabilities:
Geruest excels at managing static resources with its intelligent file merging system. It can combine multiple CSS and JavaScript files into optimized bundles, significantly reducing the number of HTTP requests per page. This not only improves loading times but also allows developers to maintain clean, modular code structures while delivering efficient, production-ready assets to clients.
One of Geruest's standout features is its automatic translation system. The framework uses template pages with placeholder links that are dynamically filled with the appropriate language content. When a user requests a page in a specific language, Geruest generates the translated version and caches it for future requests. This approach ensures fast response times while minimizing resource usage—pages that are never requested in certain languages simply aren't created, saving both processing time and disk space.
Backend Architecture:
On the server side, Geruest provides a clean and intuitive API for defining routes and handling requests. Developers can easily map URL endpoints like 'api/users' or 'api/data' to callback functions. Each callback receives an HTTPRequest object containing all the request data—URL parameters, JSON body content, and cookies—consolidated into a single, easy-to-use parameter map.
Response handling is equally straightforward. Callback functions return an HTTPResponse object, which is a wrapper around standard HTTP responses. This abstraction simplifies common tasks like setting headers, status codes, and response bodies, while still providing the flexibility needed for complex API endpoints.
Design Philosophy:
Geruest was built with practicality in mind, drawing from real-world experiences in web server development. It aims to eliminate boilerplate code and common pain points while maintaining the performance and control that C++ developers expect. The framework handles the repetitive aspects of web development—file serving, request parsing, response formatting—allowing developers to focus on building features and business logic.