Skip to content

Goeries/qhttpclient

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QHttpClient

A typed, async HTTP client library for Qt 6.2+ and C++23. Wraps QNetworkAccessManager with a codec-generic API, automatic retries, and first-class support for signals/slots, callbacks, futures, and coroutines.

Features

  • Typed responses (client->get<User>(...) returns QHttpReply<User>*)
  • Codec-generic design (ships with QJsonCodec, bring your own via the Codec concept)
  • Per-request option overrides with QHttpOptions
  • Configurable retry policies (linear, exponential, Fibonacci backoff)
  • Five async patterns: reply objects, signals/slots, callbacks, futures, coroutines

Requirements

  • Qt 6.2+
  • C++23 compiler (GCC 13+, Clang 17+, MSVC 17.10+)
  • CMake 3.19+

Tested On

Platform Qt Compiler
Ubuntu 24.04 (WSL2) 6.4.2 GCC 13.3
Windows 11 6.x MSVC

Integration

All methods below expose the same CMake target. In your code:

#include <QHttpClient>

Option 1: CMake FetchContent (recommended)

include(FetchContent)
FetchContent_Declare(
    QHttpClient
    GIT_REPOSITORY https://github.com/goeries/qhttpclient.git
    GIT_TAG v0.1.0-alpha
)
FetchContent_MakeAvailable(QHttpClient)

target_link_libraries(your_target PRIVATE QHttpClient::QHttpClient)

Option 2: Git submodule

git submodule add git@github.com:goeries/qhttpclient.git external/qhttpclient
add_subdirectory(external/qhttpclient)
target_link_libraries(your_target PRIVATE QHttpClient::QHttpClient)

Clone with --recurse-submodules to pull it automatically.

Option 3: Copy into your project

Copy the include/, src/, and CMakeLists.txt into your project (e.g., external/qhttpclient/):

add_subdirectory(external/qhttpclient)
target_link_libraries(your_target PRIVATE QHttpClient::QHttpClient)

Usage

Configure the Client

QNetworkAccessManager nam;

QHttpClientConfig config {
    .baseUrl = "https://api.example.com",
    .bearerToken = "my-token",
    .timeout = std::chrono::seconds(30),
    .retry = QHttpRetryPolicy {
        .maxAttempts = 3,
        .backoff = Backoff::Exponential,
    },
};

QHttpClient client(&nam, config);

Per-Request Overrides with QHttpOptions

// Use client defaults
auto *reply = client.get<User>("/api/users/1");

// Override timeout and add a header for this request only
auto *reply2 = client.get<User>("/api/users/2", {
    .headers = {{"X-Request-Id", "abc123"}},
    .timeout = std::chrono::seconds(5),
});

Async Patterns

Every request returns a QHttpReply<T>* that can be consumed in any of the following ways.

Reply Objects

auto *reply = client.get<User>("/api/users/1");
// reply is live immediately; attach any handler below

Signals and Slots

Client-level signals for centralized logging:

QObject::connect(&client, &QHttpClientBase::replyFinished,
                 [](QHttpReplyBase *reply) {
                     qDebug() << reply->httpStatusCode();
                 });

QObject::connect(&client, &QHttpClientBase::errorOccurred,
                 [](const QHttpError &error) {
                     qWarning() << error.message();
                 });

Reply-level signals for per-request handling:

auto *reply = client.get<User>("/api/users/1");

QObject::connect(reply, &QHttpReplyBase::finished,
                 reply, [reply]() {
                     if (!reply->hasError())
                         useValue(reply->getValue());
                 });

Callbacks

Inline with the request:

auto *reply = client.get<User>("/api/users/1",
    this, [](QHttpReply<User> *reply) {
        if (reply->hasError())
            return handleError(*reply->error());
        useValue(reply->getValue());
    });

Or attached to the reply:

auto *reply = client.get<User>("/api/users/1");

reply->onFinished(this, [](QHttpReply<User> *reply) {
    if (reply->hasError())
        return handleError(*reply->error());
    useValue(reply->getValue());
});

Futures

auto *reply = client.get<User>("/api/users/1");

reply->makeFuture()
    .then([](std::expected<User, QHttpError> result) {
        if (!result)
            return handleError(result.error());
        useValue(*result);
    });

Coroutines

auto *reply = client.get<User>("/api/users/1");
co_await *reply;

if (auto err = reply->error()) {
    handleError(*err);
    co_return;
}

useValue(reply->getValue());

HTTP Methods

client.get<T>(path, opts);
client.get<T>(path, body, opts);
client.post<T>(path, body, opts);
client.put<T>(path, body, opts);
client.patch<T>(path, body, opts);
client.deleteResource<T>(path, body, opts);
client.head<T>(path, body, opts);
client.sendCustomRequest<T>(path, body, opts);

Use <void> for requests where you don't need a response body:

auto *reply = client.post<void>("/api/users", userJson);

Building from Source

git clone https://github.com/goeries/qhttpclient.git
cd qhttpclient
cmake -B build -G Ninja -DQHTTPCLIENT_BUILD_TESTING=ON
cmake --build build
ctest --test-dir build --output-on-failure

License

MIT © 2026 Jeandré Gouws

About

Modern async HTTP client for Qt 6 with coroutine, future, callback, and signal/slot support

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors