A type-safe, async C++23 client library for the NetBox REST API (v4.5+), built on Qt 6 and C++20 coroutines.
- 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 CRUD —
get,list,create,update,removeon every endpoint - Bulk operations —
bulkCreate,bulkUpdate,bulkDelete - Auto-pagination —
listAll()follows NetBoxnextlinks with optional page limit - Structured error handling — field-level validation errors via
std::expected<T, ApiError> - Custom endpoints —
DevicesEndpoint::renderConfig(),RacksEndpoint::elevation() - Header-only — no compilation step, just add to your include path
- Qt-style includes —
#include <QNetbox>or#include <QNetbox/Client>
- C++23 compiler (GCC 13+, Clang 17+, MSVC 2022 17.10+)
- Qt 6.4+ (Core, Network)
- CMake 3.28+
#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,
});
}
}cmake -S . -B build
cmake --build buildTo build and run tests:
cmake -S . -B build -DBUILD_TESTING=ON
cmake --build build
ctest --test-dir build --output-on-failureadd_subdirectory(path/to/qnetbox)
target_link_libraries(your_target PRIVATE qnetbox)find_package(qnetbox REQUIRED)
target_link_libraries(your_target PRIVATE QNetbox::qnetbox)QNetbox::Client client(networkAccessManager, {
.url = "https://netbox.example.com",
.token = "your-api-token"
});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 & JobsEvery 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);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);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);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;
}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);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
MIT © 2026 Jeandré Gouws