Skip to content

# REST Registry

The Glaze REST Registry automatically generates REST API endpoints from C++ classes using reflection. This eliminates boilerplate code and ensures consistency between your C++ domain models and REST APIs.

## Overview

The REST Registry: - Auto-generates REST endpoints from C++ classes - Maps methods to appropriate HTTP verbs (GET/POST/PUT/DELETE)
- Handles JSON serialization/deserialization automatically - Validates input using Glaze's JSON validation - Supports complex nested objects and collections - Provides type-safe API generation

> [!NOTE]

> Glaze's REST registry is actually format agnostic and can also be used with binary formats other than JSON.

## Quick Start

### Basic Service Registration

    #include "glaze/rpc/registry.hpp"
    #include "glaze/net/http_server.hpp"

    struct User {
        uint64_t id{};
        std::string name{};
        float weight{};
    };

    struct UserService {
        std::vector<User> getAllUsers() {
            return users;
        }

        User getUserById(size_t id) const {
            return users.at(id);
        }

        User createUser(const User& user) {
            users.push_back(user);
            return users.back();
        }

    private:
        std::vector<User> users{};
    };

    template <>
    struct glz::meta<UserService> {
        using T = UserService;
        static constexpr auto value = glz::object(
            &T::getAllUsers,
            &T::getUserById,
            &T::createUser
        );
    };

    int main() {
        glz::http_server server;
        UserService userService;

        // Create REST registry
        glz::registry<glz::opts{}, glz::REST> registry;

        // Register service - auto-generates endpoints
        registry.on(userService);

        // Mount generated endpoints
        server.mount("/api", registry.endpoints);

        server.bind(8080).with_signals();
        server.start();
        server.wait_for_signal();
    }

Service types must define `glz::meta<...>::value` with the methods you want to expose.

This automatically creates: - `GET /api/getAllUsers` → `getAllUsers()` - `POST /api/getUserById` → `getUserById(size_t)`
- `POST /api/createUser` → `createUser(const User&)`

> [!IMPORTANT]

> `registry.on(service)` requires Glaze reflection metadata. For service methods, define `glz::meta<Service>::value = glz::object(...)` and list the methods you want to expose.

## Service Definition

### Method Types

The registry supports different method signatures:

    struct BookService {
        // No parameters - GET endpoint
        std::vector<Book> getAllBooks() {
            return books;
        }

        // Single parameter - POST endpoint with JSON body
        Book getBookById(int id) {
            return find_book(id);
        }

        // Object parameter - POST endpoint with object body
        Book createBook(const Book& book) {
            books.push_back(book);
            return book;
        }

        // Update method - PUT endpoint
        Book updateBook(const BookUpdate& update) {
            auto& book = find_book(update.id);
            book.title = update.title;
            book.author = update.author;
            return book;
        }

        // Void return - POST endpoint, 204 No Content response
        void deleteBook(int id) {
            remove_book(id);
        }

    private:
        std::vector<Book> books;
    };

Expose methods from `BookService` by listing them in `glz::meta<BookService>::value = glz::object(...)`.

### Request/Response Objects

    // Domain objects for API
    struct Book {
        int id;
        std::string title;
        std::string author;
        std::string isbn;
        int year;
    };

    struct BookUpdate {
        int id;
        std::string title;
        std::string author;
    };

    struct BookSearchRequest {
        std::string query;
        int limit = 10;
        int offset = 0;
    };

    struct BookSearchResponse {
        std::vector<Book> books;
        int total_count;
        bool has_more;
    };

## HTTP Mapping

### Automatic Method Mapping

The registry maps C++ methods to HTTP verbs based on patterns:

Method Pattern | HTTP Verb | Description
--- | --- | ---
`get*()` | GET | Read operations, no parameters
`find*()` | GET | Read operations, no parameters
`list*()` | GET | Read operations, no parameters
`*()` with params | POST | Operations with input data
`create*()` | POST | Create operations
`update*()` | PUT | Update operations
`delete*()` | DELETE | Delete operations

### Custom Mapping

    struct CustomService {
        // These all become POST endpoints due to parameters
        UserStats getUserStats(const StatsRequest& req) { /*...*/ }
        SearchResults searchUsers(const SearchQuery& query) { /*...*/ }
        ValidationResult validateUser(const User& user) { /*...*/ }

        // These become GET endpoints (no parameters)
        std::vector<User> getAllActiveUsers() { /*...*/ }
        ServerStatus getServerStatus() { /*...*/ }
        std::string getVersion() { /*...*/ }
    };

## Advanced Features

### Error Handling

    struct SafeUserService {
        User getUserById(int id) {
            if (id <= 0) {
                throw std::invalid_argument("Invalid user ID");
            }

            auto it = users.find(id);
            if (it == users.end()) {
                throw std::runtime_error("User not found");
            }

            return it->second;
        }

        // Registry automatically converts exceptions to HTTP errors:
        // std::invalid_argument -> 400 Bad Request
        // std::runtime_error -> 500 Internal Server Error

    private:
        std::unordered_map<int, User> users;
    };

## Multiple Services

### Service Composition

Multiple services can be registered on a single registry. Each service's member endpoints are registered under the mount path, while the root endpoint reflects the last registered service.

    int main() {
        glz::http_server server;

        // Multiple service instances
        UserService userService;
        ProductService productService;
        OrderService orderService;

        // Single registry for all services
        glz::registry<glz::opts{}, glz::REST> registry;

        // Register all services
        registry.on(userService);
        registry.on(productService);
        registry.on(orderService);

        // All endpoints available under /api
        server.mount("/api", registry.endpoints);

        server.bind(8080).with_signals();
        server.start();
        server.wait_for_signal();
    }

Each registered service in this setup (`UserService`, `ProductService`, `OrderService`) needs its own `glz::meta<...>::value = glz::object(...)` declaration.

> [!NOTE]

> When composing multiple services, the root endpoint (e.g. `GET /api`) returns only the last registered service's data. If you need a combined root endpoint that merges all services into a single view, use `glz::merge` instead:

>     auto merged = glz::merge{userService, productService, orderService};
>     registry.on(merged);

## Customization

### Custom Serialization Options

    // Pretty JSON formatting
    constexpr auto pretty_opts = glz::opts{.prettify = true};
    glz::registry<pretty_opts, glz::REST> registry;

## API Documentation Generation

### Endpoint Discovery

    // Add endpoint listing for API discovery
    server.get("/api", [&registry](const glz::request&, glz::response& res) {
        std::vector<std::string> endpoints;

        for (const auto& [path, methods] : registry.endpoints.normal_routes.routes) {
            for (const auto& [method, handler] : methods) {
                endpoints.push_back(glz::to_string(method) + " " + path);
            }
        }

        res.json({{"endpoints", endpoints}});
    });