Examples¶
This page provides working examples of Glaze HTTP functionality that you can use as templates for your own projects.
Basic REST API Server¶
Complete REST API server with CRUD operations:
#include "glaze/net/http_server.hpp"
#include "glaze/glaze.hpp"
#include <iostream>
#include <unordered_map>
#include <vector>
struct User {
int id{};
std::string name{};
std::string email{};
std::string created_at{};
};
struct CreateUserRequest {
std::string name{};
std::string email{};
};
struct UpdateUserRequest {
std::string name{};
std::string email{};
};
struct ErrorResponse {
std::string error{};
std::string message{};
};
class UserAPI {
std::unordered_map<int, User> users_;
int next_id_ = 1;
public:
UserAPI() {
// Add some initial data
users_[1] = {1, "Alice Johnson", "alice@example.com", "2024-01-01T10:00:00Z"};
users_[2] = {2, "Bob Smith", "bob@example.com", "2024-01-01T11:00:00Z"};
next_id_ = 3;
}
void setup_routes(glz::http_server& server) {
// GET /api/users - List all users
server.get("/api/users", [this](const glz::request& req, glz::response& res) {
std::vector<User> user_list;
for (const auto& [id, user] : users_) {
user_list.push_back(user);
}
res.json(user_list);
});
// GET /api/users/:id - Get user by ID
server.get("/api/users/:id", [this](const glz::request& req, glz::response& res) {
try {
int id = std::stoi(req.params.at("id"));
auto it = users_.find(id);
if (it != users_.end()) {
res.json(it->second);
} else {
res.status(404).json(ErrorResponse{"not_found", "User not found"});
}
} catch (const std::exception&) {
res.status(400).json(ErrorResponse{"invalid_id", "Invalid user ID"});
}
});
// POST /api/users - Create new user
server.post("/api/users", [this](const glz::request& req, glz::response& res) {
CreateUserRequest create_req;
auto ec = glz::read_json(create_req, req.body);
if (ec) {
res.status(400).json(ErrorResponse{"invalid_json", "Invalid request body"});
return;
}
if (create_req.name.empty() || create_req.email.empty()) {
res.status(400).json(ErrorResponse{"validation_error", "Name and email are required"});
return;
}
User new_user;
new_user.id = next_id_++;
new_user.name = create_req.name;
new_user.email = create_req.email;
new_user.created_at = get_current_timestamp();
users_[new_user.id] = new_user;
res.status(201).json(new_user);
});
// PUT /api/users/:id - Update user
server.put("/api/users/:id", [this](const glz::request& req, glz::response& res) {
try {
int id = std::stoi(req.params.at("id"));
auto it = users_.find(id);
if (it == users_.end()) {
res.status(404).json(ErrorResponse{"not_found", "User not found"});
return;
}
UpdateUserRequest update_req;
auto ec = glz::read_json(update_req, req.body);
if (ec) {
res.status(400).json(ErrorResponse{"invalid_json", "Invalid request body"});
return;
}
if (!update_req.name.empty()) {
it->second.name = update_req.name;
}
if (!update_req.email.empty()) {
it->second.email = update_req.email;
}
res.json(it->second);
} catch (const std::exception&) {
res.status(400).json(ErrorResponse{"invalid_id", "Invalid user ID"});
}
});
// DELETE /api/users/:id - Delete user
server.del("/api/users/:id", [this](const glz::request& req, glz::response& res) {
try {
int id = std::stoi(req.params.at("id"));
auto it = users_.find(id);
if (it != users_.end()) {
users_.erase(it);
res.status(204); // No content
} else {
res.status(404).json(ErrorResponse{"not_found", "User not found"});
}
} catch (const std::exception&) {
res.status(400).json(ErrorResponse{"invalid_id", "Invalid user ID"});
}
});
}
private:
std::string get_current_timestamp() {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%SZ");
return ss.str();
}
};
int main() {
glz::http_server server;
UserAPI user_api;
// Enable CORS for frontend development
server.enable_cors();
// Setup API routes
user_api.setup_routes(server);
// Health check endpoint
server.get("/health", [](const glz::request&, glz::response& res) {
res.json({{"status", "healthy"}, {"timestamp", std::time(nullptr)}});
});
std::cout << "Starting server on http://localhost:8080" << std::endl;
server.bind(8080);
server.start();
return 0;
}
Auto-Generated REST API¶
Using the REST registry to automatically generate endpoints:
#include "glaze/rpc/registry.hpp"
#include "glaze/net/http_server.hpp"
#include <vector>
#include <algorithm>
struct Task {
int id{};
std::string title{};
std::string description{};
bool completed{false};
std::string created_at{};
std::string due_date{};
};
struct CreateTaskRequest {
std::string title{};
std::string description{};
std::string due_date{};
};
struct UpdateTaskRequest {
int id{};
std::string title{};
std::string description{};
bool completed{};
std::string due_date{};
};
struct TaskSearchRequest {
std::string query{};
bool completed_only{false};
int limit{10};
};
class TaskService {
std::vector<Task> tasks_;
int next_id_ = 1;
public:
TaskService() {
// Add sample tasks
tasks_ = {
{1, "Learn Glaze", "Study the Glaze HTTP library", false, "2024-01-01T10:00:00Z", "2024-01-15T00:00:00Z"},
{2, "Write documentation", "Create API documentation", false, "2024-01-02T10:00:00Z", "2024-01-20T00:00:00Z"},
{3, "Deploy to production", "Deploy the new API", false, "2024-01-03T10:00:00Z", "2024-01-25T00:00:00Z"}
};
next_id_ = 4;
}
// Auto-generated endpoints:
// GET /api/getAllTasks
std::vector<Task> getAllTasks() {
return tasks_;
}
// POST /api/getTaskById (with JSON body: {"id": 123})
Task getTaskById(int id) {
auto it = std::find_if(tasks_.begin(), tasks_.end(),
[id](const Task& task) { return task.id == id; });
if (it != tasks_.end()) {
return *it;
}
throw std::runtime_error("Task not found");
}
// POST /api/createTask
Task createTask(const CreateTaskRequest& request) {
if (request.title.empty()) {
throw std::invalid_argument("Task title is required");
}
Task task;
task.id = next_id_++;
task.title = request.title;
task.description = request.description;
task.due_date = request.due_date;
task.completed = false;
task.created_at = get_current_timestamp();
tasks_.push_back(task);
return task;
}
// POST /api/updateTask
Task updateTask(const UpdateTaskRequest& request) {
auto it = std::find_if(tasks_.begin(), tasks_.end(),
[&request](const Task& task) { return task.id == request.id; });
if (it == tasks_.end()) {
throw std::runtime_error("Task not found");
}
if (!request.title.empty()) {
it->title = request.title;
}
if (!request.description.empty()) {
it->description = request.description;
}
if (!request.due_date.empty()) {
it->due_date = request.due_date;
}
it->completed = request.completed;
return *it;
}
// POST /api/deleteTask
void deleteTask(int id) {
auto it = std::find_if(tasks_.begin(), tasks_.end(),
[id](const Task& task) { return task.id == id; });
if (it != tasks_.end()) {
tasks_.erase(it);
} else {
throw std::runtime_error("Task not found");
}
}
// POST /api/searchTasks
std::vector<Task> searchTasks(const TaskSearchRequest& request) {
std::vector<Task> results;
for (const auto& task : tasks_) {
bool matches = true;
if (!request.query.empty()) {
matches = task.title.find(request.query) != std::string::npos ||
task.description.find(request.query) != std::string::npos;
}
if (request.completed_only && !task.completed) {
matches = false;
}
if (matches) {
results.push_back(task);
}
if (results.size() >= request.limit) {
break;
}
}
return results;
}
// GET /api/getCompletedTasks
std::vector<Task> getCompletedTasks() {
std::vector<Task> completed;
std::copy_if(tasks_.begin(), tasks_.end(), std::back_inserter(completed),
[](const Task& task) { return task.completed; });
return completed;
}
private:
std::string get_current_timestamp() {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%SZ");
return ss.str();
}
};
// Register the service with Glaze for reflection
template <>
struct glz::meta<TaskService> {
using T = TaskService;
static constexpr auto value = object(
&T::getAllTasks,
&T::getTaskById,
&T::createTask,
&T::updateTask,
&T::deleteTask,
&T::searchTasks,
&T::getCompletedTasks
);
};
int main() {
glz::http_server server;
TaskService taskService;
// Create REST registry
glz::registry<glz::opts{}, glz::REST> registry;
// Register the service - automatically generates all endpoints
registry.on(taskService);
// Mount the auto-generated endpoints
server.mount("/api", registry.endpoints);
// Enable CORS
server.enable_cors();
// Add some additional manual endpoints
server.get("/", [](const glz::request&, glz::response& res) {
res.content_type("text/html").body(R"(
<h1>Task API</h1>
<p>Auto-generated REST API for task management</p>
<h2>Endpoints:</h2>
<ul>
<li>GET /api/getAllTasks</li>
<li>POST /api/getTaskById</li>
<li>POST /api/createTask</li>
<li>POST /api/updateTask</li>
<li>POST /api/deleteTask</li>
<li>POST /api/searchTasks</li>
<li>GET /api/getCompletedTasks</li>
</ul>
)");
});
std::cout << "Task API server running on http://localhost:8080" << std::endl;
server.bind(8080);
server.start();
return 0;
}
WebSocket Chat Server¶
Real-time chat server with WebSocket support:
#include "glaze/net/http_server.hpp"
#include "glaze/net/websocket_connection.hpp"
#include <set>
#include <mutex>
#include <memory>
struct ChatMessage {
std::string username;
std::string message;
std::string timestamp;
std::string type = "message"; // "message", "join", "leave"
};
class ChatRoom {
std::set<std::shared_ptr<glz::websocket_connection>> connections_;
mutable std::mutex connections_mutex_;
std::vector<ChatMessage> message_history_;
std::mutex history_mutex_;
public:
void add_connection(std::shared_ptr<glz::websocket_connection> conn, const std::string& username) {
{
std::lock_guard<std::mutex> lock(connections_mutex_);
connections_.insert(conn);
}
// Store username in connection user data
conn->set_user_data(std::make_shared<std::string>(username));
// Send chat history to new user
{
std::lock_guard<std::mutex> lock(history_mutex_);
for (const auto& msg : message_history_) {
std::string json_msg;
glz::write_json(msg, json_msg);
conn->send_text(json_msg);
}
}
// Broadcast join message
ChatMessage join_msg;
join_msg.username = "System";
join_msg.message = username + " joined the chat";
join_msg.timestamp = get_current_timestamp();
join_msg.type = "join";
broadcast_message(join_msg);
}
void remove_connection(std::shared_ptr<glz::websocket_connection> conn) {
std::string username = "Unknown";
// Get username from user data
if (auto user_data = conn->get_user_data()) {
if (auto username_ptr = std::static_pointer_cast<std::string>(user_data)) {
username = *username_ptr;
}
}
{
std::lock_guard<std::mutex> lock(connections_mutex_);
connections_.erase(conn);
}
// Broadcast leave message
ChatMessage leave_msg;
leave_msg.username = "System";
leave_msg.message = username + " left the chat";
leave_msg.timestamp = get_current_timestamp();
leave_msg.type = "leave";
broadcast_message(leave_msg);
}
void handle_message(std::shared_ptr<glz::websocket_connection> conn, const std::string& message) {
std::string username = "Anonymous";
// Get username from connection
if (auto user_data = conn->get_user_data()) {
if (auto username_ptr = std::static_pointer_cast<std::string>(user_data)) {
username = *username_ptr;
}
}
// Create chat message
ChatMessage chat_msg;
chat_msg.username = username;
chat_msg.message = message;
chat_msg.timestamp = get_current_timestamp();
chat_msg.type = "message";
broadcast_message(chat_msg);
}
void broadcast_message(const ChatMessage& message) {
// Add to history
{
std::lock_guard<std::mutex> lock(history_mutex_);
message_history_.push_back(message);
// Keep only last 100 messages
if (message_history_.size() > 100) {
message_history_.erase(message_history_.begin());
}
}
// Serialize message
std::string json_msg;
glz::write_json(message, json_msg);
// Broadcast to all connections
std::lock_guard<std::mutex> lock(connections_mutex_);
auto it = connections_.begin();
while (it != connections_.end()) {
auto conn = *it;
try {
conn->send_text(json_msg);
++it;
} catch (...) {
// Remove broken connections
it = connections_.erase(it);
}
}
}
int get_user_count() const {
std::lock_guard<std::mutex> lock(connections_mutex_);
return connections_.size();
}
private:
std::string get_current_timestamp() {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::gmtime(&time_t), "%Y-%m-%dT%H:%M:%SZ");
return ss.str();
}
};
int main() {
glz::http_server server;
ChatRoom chat_room;
// Create WebSocket server
auto ws_server = std::make_shared<glz::websocket_server>();
// Handle new connections
ws_server->on_open([&chat_room](std::shared_ptr<glz::websocket_connection> conn, const glz::request& req) {
// Extract username from query parameters or headers
std::string username = "User" + std::to_string(std::rand() % 1000);
// In a real app, you'd extract this from authentication
auto auth_header = req.headers.find("x-username");
if (auth_header != req.headers.end()) {
username = auth_header->second;
}
std::cout << "WebSocket connection from " << conn->remote_address() << " (username: " << username << ")" << std::endl;
chat_room.add_connection(conn, username);
// Send welcome message
conn->send_text(R"({"username":"System","message":"Welcome to the chat!","type":"system"})");
});
// Handle incoming messages
ws_server->on_message([&chat_room](std::shared_ptr<glz::websocket_connection> conn, std::string_view message, glz::ws_opcode opcode) {
if (opcode == glz::ws_opcode::text) {
chat_room.handle_message(conn, std::string(message));
}
});
// Handle connection close
ws_server->on_close([&chat_room](std::shared_ptr<glz::websocket_connection> conn) {
chat_room.remove_connection(conn);
});
// Handle errors
ws_server->on_error([](std::shared_ptr<glz::websocket_connection> conn, std::error_code ec) {
std::cerr << "WebSocket error: " << ec.message() << std::endl;
});
// Mount WebSocket
server.websocket("/ws", ws_server);
// HTTP endpoints
server.get("/", [](const glz::request&, glz::response& res) {
res.content_type("text/html").body(R"(
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#messages { border: 1px solid #ccc; height: 400px; overflow-y: scroll; padding: 10px; margin-bottom: 10px; }
#messageInput { width: 70%; padding: 5px; }
#sendButton { padding: 5px 10px; }
.message { margin-bottom: 5px; }
.system { color: #666; font-style: italic; }
.join { color: #008000; }
.leave { color: #ff0000; }
</style>
</head>
<body>
<h1>WebSocket Chat</h1>
<div id="messages"></div>
<input type="text" id="messageInput" placeholder="Type your message...">
<button id="sendButton">Send</button>
<script>
const ws = new WebSocket('ws://localhost:8080/ws');
const messages = document.getElementById('messages');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
ws.onopen = function() {
addMessage('Connected to chat server', 'system');
};
ws.onmessage = function(event) {
const msg = JSON.parse(event.data);
addMessage(msg.username + ': ' + msg.message, msg.type);
};
ws.onclose = function() {
addMessage('Disconnected from chat server', 'system');
};
function addMessage(text, type) {
const div = document.createElement('div');
div.className = 'message ' + type;
div.textContent = new Date().toLocaleTimeString() + ' - ' + text;
messages.appendChild(div);
messages.scrollTop = messages.scrollHeight;
}
function sendMessage() {
const message = messageInput.value.trim();
if (message && ws.readyState === WebSocket.OPEN) {
ws.send(message);
messageInput.value = '';
}
}
sendButton.onclick = sendMessage;
messageInput.onkeypress = function(e) {
if (e.key === 'Enter') {
sendMessage();
}
};
</script>
</body>
</html>
)");
});
// Chat stats endpoint
server.get("/api/stats", [&chat_room](const glz::request&, glz::response& res) {
res.json({
{"users_online", chat_room.get_user_count()},
{"server_time", std::time(nullptr)}
});
});
server.enable_cors();
std::cout << "Chat server running on http://localhost:8080" << std::endl;
std::cout << "WebSocket endpoint: ws://localhost:8080/ws" << std::endl;
server.bind(8080);
server.start();
return 0;
}
Simple Authentication Server¶
Basic authentication server using simple token validation (no external dependencies):
#include "glaze/net/http_server.hpp"
#include <random>
struct LoginRequest {
std::string username;
std::string password;
};
struct LoginResponse {
std::string token;
std::string username;
int expires_in;
};
struct User {
int id;
std::string username;
std::string email;
std::string role;
};
class SimpleAuthService {
std::unordered_map<std::string, User> users_;
std::unordered_map<std::string, std::string> active_tokens_; // token -> username
public:
SimpleAuthService() {
// Add some test users
users_["admin"] = {1, "admin", "admin@example.com", "admin"};
users_["user"] = {2, "user", "user@example.com", "user"};
}
std::optional<LoginResponse> login(const LoginRequest& request) {
// Simple password validation (use proper hashing in production)
if ((request.username == "admin" && request.password == "admin123") ||
(request.username == "user" && request.password == "user123")) {
std::string token = generate_token();
active_tokens_[token] = request.username;
return LoginResponse{token, request.username, 3600}; // 1 hour
}
return std::nullopt;
}
std::optional<User> validate_token(const std::string& token) {
auto it = active_tokens_.find(token);
if (it != active_tokens_.end()) {
auto user_it = users_.find(it->second);
if (user_it != users_.end()) {
return user_it->second;
}
}
return std::nullopt;
}
void logout(const std::string& token) {
active_tokens_.erase(token);
}
private:
std::string generate_token() {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
std::string token = "tok_";
for (int i = 0; i < 32; ++i) {
token += "0123456789abcdef"[dis(gen)];
}
return token;
}
};
// Authentication middleware
auto create_simple_auth_middleware(SimpleAuthService& auth_service) {
return [&auth_service](const glz::request& req, glz::response& res) {
// Skip auth for login endpoint and public endpoints
if (req.target == "/api/login" || req.target == "/health" || req.target == "/") {
return;
}
auto auth_header = req.headers.find("authorization");
if (auth_header == req.headers.end()) {
res.status(401).json({{"error", "Authorization header required"}});
return;
}
std::string auth_value = auth_header->second;
if (!auth_value.starts_with("Bearer ")) {
res.status(401).json({{"error", "Bearer token required"}});
return;
}
std::string token = auth_value.substr(7); // Remove "Bearer "
auto user = auth_service.validate_token(token);
if (!user) {
res.status(401).json({{"error", "Invalid or expired token"}});
return;
}
// Store user info in response headers for use by handlers
res.header("X-User-ID", std::to_string(user->id));
res.header("X-Username", user->username);
res.header("X-User-Role", user->role);
};
}
int main() {
glz::http_server server;
SimpleAuthService auth_service;
// Add authentication middleware
server.use(create_simple_auth_middleware(auth_service));
// Enable CORS
server.enable_cors();
// Public endpoints
server.post("/api/login", [&auth_service](const glz::request& req, glz::response& res) {
LoginRequest login_req;
auto ec = glz::read_json(login_req, req.body);
if (ec) {
res.status(400).json({{"error", "Invalid request body"}});
return;
}
auto login_response = auth_service.login(login_req);
if (login_response) {
res.json(*login_response);
} else {
res.status(401).json({{"error", "Invalid credentials"}});
}
});
server.get("/health", [](const glz::request&, glz::response& res) {
res.json({{"status", "healthy"}});
});
// Protected endpoints
server.get("/api/profile", [](const glz::request& req, glz::response& res) {
// User info is available from auth middleware
std::string username = res.response_headers["X-Username"];
std::string role = res.response_headers["X-User-Role"];
res.json({
{"username", username},
{"role", role},
{"message", "This is your protected profile"}
});
});
server.get("/api/admin", [](const glz::request& req, glz::response& res) {
std::string role = res.response_headers["X-User-Role"];
if (role != "admin") {
res.status(403).json({{"error", "Admin access required"}});
return;
}
res.json({
{"message", "Admin-only data"},
{"users_count", 42},
{"server_stats", "All systems operational"}
});
});
std::cout << "Authentication server running on http://localhost:8080" << std::endl;
std::cout << "Test credentials: admin/admin123 or user/user123" << std::endl;
server.bind(8080);
server.start();
return 0;
}
Microservice Template¶
Production-ready microservice template with monitoring and health checks:
#include "glaze/net/http_server.hpp"
#include "glaze/rpc/registry.hpp"
#include <atomic>
#include <chrono>
#include <fstream>
#include <thread>
struct HealthStatus {
std::string status;
std::string version;
int uptime_seconds;
struct {
bool database;
bool external_api;
bool redis;
} dependencies;
struct {
int total_requests;
int error_rate;
double avg_response_time;
} metrics;
};
struct MetricsData {
std::atomic<int> total_requests{0};
std::atomic<int> error_count{0};
std::atomic<double> total_response_time{0.0};
std::chrono::steady_clock::time_point start_time;
MetricsData() : start_time(std::chrono::steady_clock::now()) {}
};
class ProductService {
struct Product {
int id;
std::string name;
std::string description;
double price;
std::string category;
bool available;
};
std::vector<Product> products_;
int next_id_ = 1;
public:
ProductService() {
// Load sample data
products_ = {
{1, "Laptop", "High-performance laptop", 999.99, "Electronics", true},
{2, "Mouse", "Wireless mouse", 29.99, "Electronics", true},
{3, "Keyboard", "Mechanical keyboard", 129.99, "Electronics", true}
};
next_id_ = 4;
}
std::vector<Product> getAllProducts() { return products_; }
Product getProductById(int id) {
auto it = std::find_if(products_.begin(), products_.end(),
[id](const Product& p) { return p.id == id; });
if (it != products_.end()) {
return *it;
}
throw std::runtime_error("Product not found");
}
std::vector<Product> getProductsByCategory(const std::string& category) {
std::vector<Product> result;
std::copy_if(products_.begin(), products_.end(), std::back_inserter(result),
[&category](const Product& p) { return p.category == category; });
return result;
}
};
template <>
struct glz::meta<ProductService> {
using T = ProductService;
static constexpr auto value = object(
&T::getAllProducts,
&T::getProductById,
&T::getProductsByCategory
);
};
// Middleware for metrics collection
auto create_metrics_middleware(MetricsData& metrics) {
return [&metrics](const glz::request& req, glz::response& res) {
auto start_time = std::chrono::high_resolution_clock::now();
metrics.total_requests++;
// Store start time for response time calculation
res.header("X-Request-Start", std::to_string(
std::chrono::duration_cast<std::chrono::microseconds>(
start_time.time_since_epoch()).count()));
};
}
// Health check implementation
class HealthChecker {
public:
HealthStatus get_health_status(const MetricsData& metrics) {
HealthStatus status;
status.version = "1.0.0";
status.status = "healthy";
auto now = std::chrono::steady_clock::now();
status.uptime_seconds = std::chrono::duration_cast<std::chrono::seconds>(
now - metrics.start_time).count();
// Check dependencies (simulate)
status.dependencies.database = check_database();
status.dependencies.external_api = check_external_api();
status.dependencies.redis = check_redis();
// Calculate metrics
int total_requests = metrics.total_requests.load();
status.metrics.total_requests = total_requests;
status.metrics.error_rate = total_requests > 0 ?
(metrics.error_count.load() * 100 / total_requests) : 0;
status.metrics.avg_response_time = total_requests > 0 ?
(metrics.total_response_time.load() / total_requests) : 0.0;
// Determine overall health
if (!status.dependencies.database ||
!status.dependencies.external_api ||
status.metrics.error_rate > 5) {
status.status = "unhealthy";
}
return status;
}
private:
bool check_database() {
// Simulate database health check
return true;
}
bool check_external_api() {
// Simulate external API health check
return std::rand() % 10 > 1; // 90% healthy
}
bool check_redis() {
// Simulate Redis health check
return true;
}
};
// Configuration loader
struct Config {
int port = 8080;
std::string log_level = "info";
bool enable_cors = true;
std::string database_url = "localhost:5432";
std::string redis_url = "localhost:6379";
static Config load_from_env() {
Config config;
if (const char* port_env = std::getenv("PORT")) {
config.port = std::atoi(port_env);
}
if (const char* log_level = std::getenv("LOG_LEVEL")) {
config.log_level = log_level;
}
if (const char* db_url = std::getenv("DATABASE_URL")) {
config.database_url = db_url;
}
return config;
}
};
int main() {
// Load configuration
Config config = Config::load_from_env();
std::cout << "Starting Product Microservice v1.0.0" << std::endl;
std::cout << "Port: " << config.port << std::endl;
std::cout << "Log Level: " << config.log_level << std::endl;
glz::http_server server;
ProductService productService;
MetricsData metrics;
HealthChecker health_checker;
// Add metrics middleware
server.use(create_metrics_middleware(metrics));
// Error handling middleware
server.use([&metrics](const glz::request& req, glz::response& res) {
if (res.status_code >= 400) {
metrics.error_count++;
}
});
// Create REST registry
glz::registry<glz::opts{}, glz::REST> registry;
registry.on(productService);
// Mount API endpoints
server.mount("/api/v1", registry.endpoints);
// Health endpoints
server.get("/health", [&health_checker, &metrics](const glz::request&, glz::response& res) {
auto health = health_checker.get_health_status(metrics);
if (health.status == "healthy") {
res.status(200);
} else {
res.status(503);
}
res.json(health);
});
server.get("/health/ready", [](const glz::request&, glz::response& res) {
// Readiness check - are we ready to receive traffic?
res.json({{"status", "ready"}});
});
server.get("/health/live", [](const glz::request&, glz::response& res) {
// Liveness check - is the service still running?
res.json({{"status", "alive"}});
});
// Metrics endpoint
server.get("/metrics", [&metrics](const glz::request&, glz::response& res) {
res.content_type("text/plain").body(
"# HELP http_requests_total Total HTTP requests\n"
"# TYPE http_requests_total counter\n"
"http_requests_total " + std::to_string(metrics.total_requests.load()) + "\n"
"# HELP http_errors_total Total HTTP errors\n"
"# TYPE http_errors_total counter\n"
"http_errors_total " + std::to_string(metrics.error_count.load()) + "\n"
);
});
// Info endpoint
server.get("/info", [&config](const glz::request&, glz::response& res) {
res.json({
{"service", "product-service"},
{"version", "1.0.0"},
{"environment", config.log_level},
{"build_time", __DATE__ " " __TIME__}
});
});
// Enable CORS if configured
if (config.enable_cors) {
server.enable_cors();
}
// Graceful shutdown handling
std::atomic<bool> running{true};
std::signal(SIGINT, [](int) {
std::cout << "\nReceived SIGINT, shutting down gracefully..." << std::endl;
});
std::signal(SIGTERM, [](int) {
std::cout << "\nReceived SIGTERM, shutting down gracefully..." << std::endl;
});
try {
server.bind(config.port);
std::cout << "Product Microservice listening on port " << config.port << std::endl;
std::cout << "Health check: http://localhost:" << config.port << "/health" << std::endl;
std::cout << "API docs: http://localhost:" << config.port << "/api/v1" << std::endl;
server.start();
} catch (const std::exception& e) {
std::cerr << "Failed to start server: " << e.what() << std::endl;
return 1;
}
return 0;
}