{"id":20347988,"url":"https://github.com/shlomnissan/express-client","last_synced_at":"2025-04-12T01:04:03.284Z","repository":{"id":69166455,"uuid":"589097577","full_name":"shlomnissan/express-client","owner":"shlomnissan","description":"⚡ Promised-based HTTP client for modern C++ development","archived":false,"fork":false,"pushed_at":"2023-09-03T20:18:15.000Z","size":316,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-12T01:03:54.556Z","etag":null,"topics":["cpp","http","http-client"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shlomnissan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-15T03:17:51.000Z","updated_at":"2024-06-07T17:40:57.000Z","dependencies_parsed_at":"2024-11-17T22:38:13.901Z","dependency_job_id":null,"html_url":"https://github.com/shlomnissan/express-client","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shlomnissan%2Fexpress-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shlomnissan%2Fexpress-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shlomnissan%2Fexpress-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shlomnissan%2Fexpress-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shlomnissan","download_url":"https://codeload.github.com/shlomnissan/express-client/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501862,"owners_count":21114683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","http","http-client"],"created_at":"2024-11-14T22:18:48.533Z","updated_at":"2025-04-12T01:04:03.278Z","avatar_url":"https://github.com/shlomnissan.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n   \u003cb\u003e\n        \u003cimg style=\"margin: 1rem 0\" src=\"https://github-production-user-asset-6210df.s3.amazonaws.com/3165988/253027928-4a160e7d-7a4b-46e4-a1e1-db10e96d83c1.svg\" /\u003e \n    \u003c/b\u003e\n\u003c/h1\u003e\n\n\n\u003cp align=\"center\"\u003ePromised-based HTTP client for modern C++ development.\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Ubuntu](https://github.com/shlomnissan/express-client/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/shlomnissan/express-client/actions/workflows/ubuntu.yml)\n[![MacOS](https://github.com/shlomnissan/express-client/actions/workflows/macos.yml/badge.svg)](https://github.com/shlomnissan/express-client/actions/workflows/macos.yml)\n[![Windows](https://github.com/shlomnissan/express-client/actions/workflows/windows.yml/badge.svg)](https://github.com/shlomnissan/express-client/actions/workflows/windows.yml)\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![codecov](https://codecov.io/gh/shlomnissan/express-client/graph/badge.svg?token=0RH2V39J2S)](https://codecov.io/gh/shlomnissan/express-client)\n\n\u003c/div\u003e\n\n## Table of Contents\n- [Overview](#overview)\n- [Features](#features)\n- [Getting Started](#getting-started)\n   - [Platform Support](#platform-support)\n   - [Installation](#installation)\n   - [Example](#example)\n- [Express Client API](#express-client-api)\n   - [Request](#request)\n   - [Types](#types)\n   - [Response](#response)\n   - [Error Handling](#error-handling)\n- [Licence](#licence)\n\n## Overview\nExpress Client is a promise-based HTTP client for modern C++ development. It currently targets C++20 with no dependencies other than the standard library. The project aims to provide a modern interface for making HTTP requests in C++ applications.\n\n## Features\n- A simple interface.\n- Asynchronous requests.\n- Cross-platform support.\n- Basic HTTP authentication.\n- Zero dependencies.\n- Comprehensive tests.\n\n#### Upcoming Features\n- HTTPS support.\n- File upload and download with progress.\n- Connection pooling.\n\n## Getting Started\n\n### Platform Support\n\nExpress Client supports both Windows and Unix-based operating systems. It is currently being tested on the following operating systems and compilers:\n\n![Ubuntu](https://raw.githubusercontent.com/EgoistDeveloper/operating-system-logos/master/src/16x16/UBT.png) Ubuntu 22.04 and GCC 11.3.0\n\n![macOS](https://raw.githubusercontent.com/EgoistDeveloper/operating-system-logos/master/src/16x16/MAC.png) macOS 12 and Clang 14.0.0\n\n![Windows](https://raw.githubusercontent.com/EgoistDeveloper/operating-system-logos/master/src/16x16/WIN.png) Windows 10 and MSVC 19.36\n\n### Installation\nExpress Client uses [CMake](https://cmake.org/) as its default build system. There are multiple ways to import a CMake project into an existing codebase, but it is highly recommended to import the library using CMake Install following the steps below. This ensures that symbol visibility is maintained, which makes versioning and future updates more reliable.\n\n#### 1. Installing CMake\nThe first step is to ensure that CMake is installed on your system. Follow the [CMake installation steps](https://cmake.org/install/) for your operating system and run the following command from the terminal to verify the installation. If CMake was installed successfully, you should see similar output:\n\u003cpre\u003e\n/$ \u003cstrong\u003ecmake --version\u003c/strong\u003e\n-- cmake version 3.26.4\n-- CMake suite maintained and supported by Kitware (kitware.com/cmake).\n\u003c/pre\u003e\n_Express Client requires a minimum version of CMake 3.22._\n#### 2. Download\nThe next step is downloading the Express Client source code. The easiest way to download Express Client is cloning the repository from GitHub:\n\u003cpre\u003e\n  /$ \u003cstrong\u003egit clone https://github.com/shlomnissan/express-client.git\u003c/strong\u003e\n\u003c/pre\u003e\nYou can also download an archive file directly from the GitHub UI.\n#### 3. Build\nTo build Express Client using CMake, navigate to the project's root directory and execute the following commands:\n\u003cpre\u003e\n# Create the build directory\n/$ \u003cstrong\u003emkdir build\u003c/strong\u003e\n/$ \u003cstrong\u003ecd build\u003c/strong\u003e\n\n# Generate the build\n/$ \u003cstrong\u003ecmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF\u003c/strong\u003e\n\u003c/pre\u003e\nThe CMake CLI used to generate the build specifies several modifiable options:\n\n- `CMAKE_BUILD_TYPE` is set to `Release`, which is desirable for installation. However, if you are actively testing and modifying the project, you can change this value to `Debug`.\n- `BUILD_SHARED_LIBS` is set to `ON`, which makes the build output a shared library. Omitting this option altogether results in building a static library.\n- `BUILD_TESTS` and `BUILD_EXAMPLES` are self-explanatory.\n\nThe next step is building the project:\n\u003cpre\u003e\n# Build the project\n/$ \u003cstrong\u003ecmake --build . --config Release\u003c/strong\u003e\n\u003c/pre\u003e\nWe are specifying the build configuration again which is required for multi-config build systems. If you changed the value to `Debug` during the build phase, make sure to change it here as well.\n\n#### 4. Install\nFinally, you can use CMake to install the library on your host.\n\u003cpre\u003e\n# Install the project\n/$ \u003cstrong\u003ecmake --install .\u003c/strong\u003e\n\u003c/pre\u003e\nAssuming everything went well, you should now have Express Client installed locally and can include it directly in your projects. If you're on a Unix-based system, running the following command will list the installed files and their respective paths:\n\u003cpre\u003e\n/$ \u003cstrong\u003ecat install_manifest.txt\u003c/strong\u003e\n\u003c/pre\u003e\nThe next section shows a simple example of using Express Client.\n\n### Example\nThe following example shows how to perform a simple HTTP GET request in your C++ projects and print the output to `stdout`.\n```cpp\n#include \u003ciostream\u003e\n#include \u003cexpress/client.h\u003e\n\nauto main() -\u003e int {\n  Express::Client client;\n\n  auto result = client.Request({\n    .url = \"http://example.com/\",\n    .method = Express::Method::Get,\n  });\n\n  auto response = result.get();\n\n  if (response.status_code == 200) {\n    std::cout \u003c\u003c response.data \u003c\u003c '\\n';\n  }\n\n  return 0;\n}\n```\nNow you can compile and run the executable.\n\u003cpre\u003e\n# Compile main.cc and link the express client library\n/$ \u003cstrong\u003eg++ main.cc -lexpress_client -std=c++20\u003c/strong\u003e\n\n# Run the executable\n/$ \u003cstrong\u003e./a.out\u003c/strong\u003e\n\u003c/pre\u003e\n\n- Note that all of the examples in this article are using [Designated Initializers](https://en.cppreference.com/w/cpp/language/aggregate_initialization), which is a feature introduced in C++20. This feature enables us to directly initialize members of a class type using their name.\n- We include the `\u003cexpress/client.h\u003e` header file and link the executable with `libexpress_client`. If you have followed the installation steps, the header files and library should be available in your system's default search paths.\n\n#### Troubleshooting\n- If you have followed the steps to install the library locally and are seeing the error message \"error while loading shared libraries\" when running the executable, you may need to update your local shared library cache by running the following command:\n\u003cpre\u003e\n/$ \u003cstrong\u003eldconfig\u003c/strong\u003e\n\u003c/pre\u003e\n\n## Express Client API\n\n### Request\nThe Express Client interface has a single operation, which is making an HTTP request. Therefore, every interaction with the library begins by calling the `Request()` method on the client's object and passing it a configuration object.\n\n```cpp\n#include \u003cexpress/client.h\u003e\n\nnamespace Express {\n  class Client {\n    auto Request(const Config\u0026 config) const -\u003e std::future\u003cResponse\u003e;\n  };\n}\n```\n- This method processes the request in the background without blocking execution. It returns a `std::future`, which lets you access the result of the asynchronous operation.\n- `std::future` provides several methods for querying, waiting for, or extracting a value. The most commonly used method is `std::future\u003cT\u003e::get()`, which waits until the future has a valid result and retrieves it. If the future does not have a result, this method will block the execution, waiting until the result becomes available.\n- Here is a code snippet from the example we showed earlier with clarifying comments:\n```cpp\n// Instantiate our HTTP client\nExpress::Client client;\n\n// Send an asynchronous request to http://example.com/\nauto result = client.Request({\n  .url = \"http://example.com/\",\n  .method = Express::Method::Get,\n});\n\n// Get the response if it's available, otherwise wait until it's available\nauto response = result.get();\n```\nThe following section will describe the different types provided by the Express Client. We will start with the configuration object that is used to make requests, which includes all the options that can be set when making an HTTP request.\n\n### Types\n\n#### Express::Config\nEvery request requires a configuration object of type `Express::Config`. This is a simple data type with fields that lets you configure your HTTP request. We’ve shown two fields in the example above, `url` which is required, and `method`. The following table lists all available fields, their types, and a description.\n\n\n| Name | Type | Description |\n| ------------- | ------------- | ------------- |\n| **url**  | `std::string_view`  | A valid URL that includes the URL scheme. |\n| **method**  | `Express::Method`  | An HTTP method supported by Express. |\n| **headers**  | `Express::Headers`  | A collection of headers for the HTTP request. |\n| **data**  | `std::string_view`  | Data to include with the request. |\n| **auth**  | `Express::UserAuth`  | A username and password pair for authentication. |\n| **timeout**  | `std::chrono::milliseconds`  | A request timeout in milliseconds. |\n\nBefore we delve into the nested types, let's take a look at an example of an HTTP request that uses all the fields in the configuration object:\n\n```cpp\nauto result = client.Request({\n  .url = \"http://example.com/user\",\n  .method = Express::Method::Post,\n  .headers = {{\n    {\"Content-Type\", \"application/x-www-form-urlencoded\"}\n  }},\n  .data = \"age=22\u0026email=myemail@hello.com\",\n  .auth = {\n    .username = \"aladdin\",\n    .password = \"opensesame\",\n  },\n  .timeout = 15s\n});\n```\nNote that when using designated initializers, the initialization expression must have the same order of data members as in the class declaration. However, we are allowed to omit members.\n\n#### Express::Method\n\n`Express::Method` is an enum class used to specify the HTTP method within the request object. Express Client currently supports the following methods:\n\n```\nExpress::Method::Delete\nExpress::Method::Get\nExpress::Method::Head\nExpress::Method::Options\nExpress::Method::Patch\nExpress::Method::Post\nExpress::Method::Put\n```\n\n#### Express::Headers\n\n`Express::Headers` is a collection type used to store headers for a request or retrieve headers from a response. It can be initialized by passing a vector of string pairs, or using an initializer list:\n\n```cpp\nExpress::Headers headers {{\n  {\"Header-Name\", \"Value\"},\n  {\"Header-Name\", \"Value\"}\n}};\n```\n\nThis collection object includes a couple of mutators (`Express::Headers::Add`, `Express::Headers::Remove`) and accessors (`Express::Headers::Contains`, `Express::Headers::Get`) to configure the object as part of an HTTP request and query the object as part of an HTTP response.\n\n```cpp\nauto Add(const std::string\u0026 name, std::string value) -\u003e void;\nauto Remove(const std::string\u0026 name) -\u003e void;\n\n[[nodiscard]] auto Contains(const std::string\u0026 name) const -\u003e bool;\n[[nodiscard]] auto Get(const std::string\u0026 name) -\u003e std::string;\n```\nThe headers collection is a simple wrapper around an unordered map. However, it accounts for case-insensitive header names and provides basic validation for allowed characters in the header names and values.\n\n#### Express::UserAuth\n\n`Express::UserAuth` is a simple aggregate type used for HTTP authentication. It can be initialized with designated initializers as shown below:\n\n```cpp\nExpress::UserAuth user_auth {\n  .username = \"aladdin\",\n  .password = \"opensesame\"\n};\n```\nThis user-defined type is part of the request object. If the username or password are set, the request builder will add an Authorization header with this information encoded in base64, following the specification for Basic HTTP Authentication ([RFC 7617](https://datatracker.ietf.org/doc/html/rfc76170)).\n### Response\n\nThe type of value we receive from the request method's future is `Express::Response`. This is another simple data structure that mostly includes standard library types. The table below lists all available fields, their types, and default values.\n\n| Name | Type | Description |\n| ------------- | ------------- | ------------- |\n| **status_code**  | `int`  | An [HTTP response status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status). |\n| **status_text**  | `std::string`  | An HTTP status code text. |\n| **data**  | `std::string`  | A string that includes the body’s data. |\n| **headers**  | `Express::Headers`  | A collection of key/value headers from the HTTP response. |\n\nThe only user-defined type in this data structure is `Express::Headers`, which was covered in the previous section. Here is an example that makes a simple request and prints the entire response using all the fields in the response object.\n\n```cpp\nauto result = client.Request({\n  .url = \"http://example.com\",\n  .method = Express::Method::Get,\n});\n\nExpress::Response response = result.get();\n\nstd::cout \u003c\u003c \"Status: \"\n  \u003c\u003c response.status_code \u003c\u003c \" \"\n  \u003c\u003c response.status_text \u003c\u003c \"\\n\\n\";\n\nstd::cout \u003c\u003c \"Headers: \\n\";\nfor (const auto\u0026 [_, header] : response.headers) {\n  std::cout \u003c\u003c header.first \u003c\u003c \": \" \u003c\u003c header.second \u003c\u003c '\\n';\n}\n\nif (!response.data.empty()) {\n  std::cout \u003c\u003c '\\n' \u003c\u003c response.data;\n}\n```\n_This code snippet was taken from the `hello_client.cc` example_.\n\n### Error Handling\n\nExpress Client uses exceptions as its primary error handling mechanism. Your application will need to handle three types of exceptions:\n\n| Exception Type | Description |\n| ------------- | ------------- |\n| `std::system_error` | A standard library exception that indicates a low-level system error, typically in the networking APIs. This error can occur in cases of resource limitations, permission issues, or underlying network issues, such as when the destination is unreachable.|\n| `Express::RequestError` | A library exception that indicates a configuration error preventing the library from making a request. This exception is derived from `std::logic_error`, and can occur in cases such as a bad URL format, invalid header values, or bad data formatting.|\n| `Express::ResponseError` | A library exception that indicates an unexpected runtime error that happened after the library made a request to the server. This exception is derived from `std::runtime_error`, and can occur in cases such as failure to connect to the server, timeout,  unexpected server responses, and more. |\n\n- The library exceptions are defined in `\u003cexpress/exception.h\u003e`.\n- The three exceptions all include error messages that provide information about the source of the error and the issues that caused the exception to be thrown.\n\n## Licence\n```\n    ____       __                             __  \n   / __ )___  / /_____ _____ ___  ____ ______/ /__\n  / __  / _ \\/ __/ __ `/ __ `__ \\/ __ `/ ___/ //_/\n / /_/ /  __/ /_/ /_/ / / / / / / /_/ / /  / ,\u003c   \n/_____/\\___/\\__/\\__,_/_/ /_/ /_/\\__,_/_/  /_/|_|  \n                                                  \nCopyright (c) 2023-present Shlomi Nissan\nhttps://betamark.com\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshlomnissan%2Fexpress-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshlomnissan%2Fexpress-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshlomnissan%2Fexpress-client/lists"}