Skip to content

Goeries/qnetbox

Repository files navigation

QNetBox

A type-safe, async C++23 client library for the NetBox REST API (v4.5+), built on Qt 6 and C++20 coroutines.

Features

  • Coroutine-based async API using co_await — no callbacks, no signal chains
  • Type-safe domain models for all 10 NetBox app namespaces (DCIM, IPAM, Tenancy, Users, Circuits, VPN, Virtualization, Wireless, Extras, Core)
  • ~108 fully typed models with fromJson(), toJson(), typed filters, and fluent builders
  • Full CRUDget, list, create, update, remove on every endpoint
  • Bulk operationsbulkCreate, bulkUpdate, bulkDelete
  • Auto-paginationlistAll() follows NetBox next links with optional page limit
  • Structured error handling — field-level validation errors via std::expected<T, ApiError>
  • Custom endpointsDevicesEndpoint::renderConfig(), RacksEndpoint::elevation()
  • Header-only — no compilation step, just add to your include path
  • Qt-style includes#include <QNetbox> or #include <QNetbox/Client>

Requirements

  • C++23 compiler (GCC 13+, Clang 17+, MSVC 2022 17.10+)
  • Qt 6.4+ (Core, Network)
  • CMake 3.28+

Quick Start

#include <QCoreApplication>
#include <QNetbox/Client>

using namespace Qt::Literals::StringLiterals;

QtCoroutine::QTask<void> run(QNetworkAccessManager *nam) {
    QNetbox::Client client(nam, {
        .url   = "https://netbox.example.com",
        .token = "your-api-token-here"
    });

    // Fetch a device
    auto device = co_await client.dcim.devices.get(42);
    if (!device) {
        qWarning() << device.error().userMessage();
        co_return;
    }
    qDebug() << "Device:" << device->name;

    // List all active devices at a site
    QNetbox::dcim::Device::Filter filter;
    filter.status = QStringList{u"active"_s};
    filter.siteId = 1;

    auto devices = co_await client.dcim.devices.listAll(filter);
    if (devices)
        qDebug() << "Found" << devices->size() << "devices";

    // Create a device with fluent builder
    auto created = co_await client.dcim.devices.create(
        QNetbox::dcim::Device::CreateData(u"sw-core-01"_s, 1, 12, 3)
            .withRack(7)
            .withSerial(u"FCW2345L0PB"_s)
            .withStatus(u"active"_s)
    );

    // Update with designated initializers
    if (created) {
        co_await client.dcim.devices.update(created->id, {
            .description = u"Core switch - Floor 3"_s,
        });
    }
}

Building

cmake -S . -B build
cmake --build build

To build and run tests:

cmake -S . -B build -DBUILD_TESTING=ON
cmake --build build
ctest --test-dir build --output-on-failure

Integration

As a subdirectory

add_subdirectory(path/to/qnetbox)
target_link_libraries(your_target PRIVATE qnetbox)

After install

find_package(qnetbox REQUIRED)
target_link_libraries(your_target PRIVATE QNetbox::qnetbox)

API Overview

Client

QNetbox::Client client(networkAccessManager, {
    .url   = "https://netbox.example.com",
    .token = "your-api-token"
});

Namespaces

Access any NetBox app through the client:

client.dcim              // Data Center Infrastructure Management
client.ipam              // IP Address Management
client.tenancy           // Tenants & Contacts
client.users             // Users & Permissions
client.circuits          // Circuits & Providers
client.vpn               // VPN Tunnels & Policies
client.virtualization    // Virtual Machines & Clusters
client.wireless          // Wireless LANs & Links
client.extras            // Tags, Custom Fields, Webhooks
client.core              // Data Sources & Jobs

CRUD Operations

Every endpoint supports the standard operations:

// Read
auto device = co_await client.dcim.devices.get(42);
auto page   = co_await client.dcim.devices.list();
auto page   = co_await client.dcim.devices.list(filter);
auto all    = co_await client.dcim.devices.listAll();
auto all    = co_await client.dcim.devices.listAll(filter, /*maxPages=*/10);

// Write
auto created = co_await client.dcim.devices.create(createData);
auto updated = co_await client.dcim.devices.update(id, updateData);
auto removed = co_await client.dcim.devices.remove(id);

// Bulk
auto created = co_await client.dcim.devices.bulkCreate(listOfCreateData);
auto updated = co_await client.dcim.devices.bulkUpdate(listOfIdUpdatePairs);
auto removed = co_await client.dcim.devices.bulkDelete(listOfIds);

Fluent Builders

Create operations use builders with required constructor arguments and optional chained setters:

auto data = QNetbox::dcim::Device::CreateData(name, siteId, deviceTypeId, roleId)
    .withRack(7)
    .withSerial(u"FCW2345L0PB"_s)
    .withStatus(u"active"_s)
    .withTenant(2);

Typed Filters

QNetbox::dcim::Device::Filter filter;
filter.status    = QStringList{u"active"_s, u"staged"_s};
filter.siteId    = 1;
filter.tag       = u"production"_s;
filter.limit     = 100;

auto devices = co_await client.dcim.devices.list(filter);

Error Handling

All operations return std::expected<T, ApiError>. Errors include structured field-level validation:

auto result = co_await client.dcim.devices.create(data);
if (!result) {
    auto &err = result.error();

    // Summary
    qWarning() << err.userMessage();

    // Field-level errors
    if (err.hasFieldErrors()) {
        for (auto it = err.fieldErrors.begin(); it != err.fieldErrors.end(); ++it)
            qWarning() << it.key() << ":" << it.value();
    }

    // Non-field errors
    for (const auto &msg : err.nonFieldErrors)
        qWarning() << msg;
}

Custom Endpoints

Some endpoints have additional actions beyond standard CRUD:

// Render a device's configuration template
auto config = co_await client.dcim.devices.renderConfig(42);

// Get rack elevation diagram
auto elevation = co_await client.dcim.racks.elevation(7);

Project Structure

include/
  QNetbox/Client           # Qt-style convenience header
  qnetbox/
    QNetbox                 # Module-level include-all
    client.hpp              # Client class
    endpoint.hpp            # ReadEndpoint, CrudEndpoint, SingletonEndpoint
    pagination.hpp          # PaginatedResponse<T>
    error.hpp               # ApiError with structured field errors
    result.hpp              # ApiResult<T> = std::expected<T, ApiError>
    detail/
      error_mapping.hpp     # QHttpError -> ApiError conversion
      query_string.hpp      # Filter -> URL query string helpers
    dcim/                   # 44 models + 2 custom endpoints
    ipam/                   # 14 models
    tenancy/                # 6 models
    users/                  # 4 models
    circuits/               # 6 models
    vpn/                    # 10 models
    virtualization/         # 6 models
    wireless/               # 3 models
    extras/                 # 13 models
    core/                   # 3 models
external/
  qhttpclient/              # HTTP client with retry, codec, coroutine support
  qtcoroutine/              # QTask<T>, signal awaiting, whenAll/whenAny
tests/                      # 11 test suites, ~139 test cases

License

MIT © 2026 Jeandré Gouws

About

Modern C++ async NetBox client for Qt 6

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors