WendyOS Docs
Guides & TutorialsC++ Guides

Simple Web Server

Build a long-running HTTP server on WendyOS using C++

Building a Web Server with cpp-httplib

Often times you'll want a long-running server where you can make HTTP or WebSocket calls to your WendyOS device. This allows your device to accept incoming requests and respond to them, making it easy to build interactive applications or APIs that can be accessed from other devices on your network.

This guide shows you how to build a web server using cpp-httplib, a lightweight header-only HTTP library for C++, along with nlohmann/json for JSON handling.

Prerequisites

  • Wendy CLI installed on your development machine
  • CMake 3.16 or later installed
  • A C++ compiler (g++ or clang++)
  • A WendyOS device plugged in over USB or connectable over Wi-Fi

Setting Up Your Project

Initialize the Project

Start from the Wendy C++ web server template:

wendy init simple-server --target wendyos --language cpp --template simple-api --var APP_ID=simple-server --var PORT=3000 --assistant skip --git-init no
cd simple-server

The template creates CMakeLists.txt, main.cpp, Dockerfile, and wendy.json. The sections below explain the generated server and the pieces you can customize.

Run on WendyOS

wendy run

Wendy will build the app, ask you to select a device if one is not already configured, deploy the app, and print the URL or run output.

Code Breakdown

Generated Web Server

The generated main.cpp contains the server logic:

#include "httplib.h"
#include "json.hpp"

#include <iostream>
#include <string>

using json = nlohmann::json;

int main() {
    httplib::Server svr;

    // GET / - Returns "Hello, World!"
    svr.Get("/", [](const httplib::Request&, httplib::Response& res) {
        res.set_content("Hello, World!", "text/plain");
    });

    // GET /hello/:name - Returns personalized greeting
    svr.Get(R"(/hello/(\w+))", [](const httplib::Request& req, httplib::Response& res) {
        std::string name = req.matches[1];
        res.set_content("Hello, " + name + "!", "text/plain");
    });

    // POST /users - Creates a new user from JSON body
    svr.Post("/users", [](const httplib::Request& req, httplib::Response& res) {
        try {
            auto body = json::parse(req.body);
            std::string username = body.value("username", "");

            json user;
            user["id"] = 1;
            user["username"] = username;

            res.status = 201;
            res.set_content(user.dump(), "application/json");
        } catch (const json::exception& e) {
            res.status = 400;
            res.set_content(R"({"error": "Invalid JSON"})", "application/json");
        }
    });

    std::cout << "Server running on http://localhost:3000" << std::endl;
    svr.listen("0.0.0.0", 3000);

    return 0;
}

Multiple Routes: This example includes three routes:

  • GET / - Returns "Hello, World!"
  • GET /hello/:name - Returns a personalized greeting using regex path matching
  • POST /users - Creates a new user from JSON body and returns it

Generated CMakeLists.txt

Review the generated CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(simple-server VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Find required packages
find_package(Threads REQUIRED)

# Include directories for header-only libraries
include_directories(${CMAKE_SOURCE_DIR}/include)

add_executable(simple-server main.cpp)

# Link pthread for httplib
target_link_libraries(simple-server PRIVATE Threads::Threads)

Generated Dockerfile

The generated project includes a Dockerfile. It downloads the header-only libraries during build and is configured for the NVIDIA Jetson Orin Nano (ARM64):

# Build stage - using ARM64-compatible image for NVIDIA Jetson Orin Nano
FROM arm64v8/debian:bookworm-slim AS builder

# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    cmake \
    curl \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Download header-only libraries
RUN mkdir -p include && \
    curl -sL https://raw.githubusercontent.com/yhirose/cpp-httplib/v0.18.1/httplib.h -o include/httplib.h && \
    curl -sL https://raw.githubusercontent.com/nlohmann/json/v3.11.3/single_include/nlohmann/json.hpp -o include/json.hpp

COPY CMakeLists.txt main.cpp ./

# Build the application
RUN cmake -B build -DCMAKE_BUILD_TYPE=Release && \
    cmake --build build --config Release

# Runtime stage
FROM arm64v8/debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/build/simple-server /usr/local/bin/simple-server

EXPOSE 3000

CMD ["simple-server"]

Important: The server runs on 0.0.0.0 and not localhost or 127.0.0.1. This is because your WendyOS device needs to accept connections from other devices on the network. Binding to 0.0.0.0 makes the server accessible externally.

Run Again on WendyOS

Deploy your containerized web server to your WendyOS device:

wendy run

The Wendy CLI will build your Docker image, transfer it to your device, and run it with the appropriate port mappings.

Test Your Server on WendyOS

After deploying your server to your WendyOS device, you can test it from your development machine.

You can configure wendy.json with a readiness probe and postStart hook to automatically open your browser when the server is ready:

{
  "readiness": {
    "tcpSocket": { "port": 3000 },
    "timeoutSeconds": 30
  },
  "hooks": {
    "postStart": {
      "cli": "wendy utils open-browser http://${WENDY_HOSTNAME}:3000"
    }
  }
}

Or open your browser manually and navigate to:

http://wendyos-true-probe.local:3000

Replace the hostname: Each WendyOS device has a unique hostname. Replace wendyos-true-probe with your device's actual hostname shown in the CLI output. In addition, don't forget to add the port to the hostname.

You should see the following output:

Hello, World!

This confirms your web server is successfully running on your WendyOS device and accepting requests from your network.

Verifying Deployment

You can verify the server container is running by listing the applications on your device:

wendy device apps list
✔︎ Searching for WendyOS devices [5.3s]
✔︎ Listing applications: True Probe [USB, Ethernet, LAN]
╭───────────────┬─────────┬─────────┬──────────╮
 App Version State Failures
├───────────────┼─────────┼─────────┼──────────┤
 simple-server 0.0.0 Stopped 0
╰───────────────┴─────────┴─────────┴──────────╯

You should see your containerized web server listed among the running applications.

More Code Notes

Let's break down what the code does:

  1. Include Headers: We include httplib.h for HTTP server functionality and json.hpp for JSON parsing/serialization.

  2. Create Server: We create an httplib::Server instance that will handle incoming requests.

  3. Define Routes: We define three routes using lambda functions:

    • GET / returns a simple text response
    • GET /hello/:name uses regex to capture path parameters
    • POST /users parses JSON from the request body and returns a JSON response
  4. Start Server: The svr.listen() function starts the server and blocks, listening for connections on port 3000.

Libraries Used

This example uses two popular header-only C++ libraries:

  • cpp-httplib: A lightweight, single-file HTTP/HTTPS server and client library
  • nlohmann/json: A modern C++ JSON library with an intuitive API

Both libraries are header-only, meaning they don't require compilation or linking against external libraries.

Alternative Frameworks

While this guide uses cpp-httplib for simplicity, other excellent C++ web frameworks are available:

  • Crow: A Flask-inspired micro web framework
  • Drogon: A high-performance C++14/17 HTTP framework
  • Pistache: A modern and elegant HTTP and REST framework
  • Oat++: Light and powerful C++ web framework

All of these frameworks can be containerized and deployed to WendyOS following a similar Docker-based approach.

Next Steps

Now that you have a basic web server running:

  • Add more routes to handle different endpoints
  • Implement PUT and DELETE routes for a full REST API
  • Add request validation and error handling
  • Parse query parameters
  • Connect to WendyOS device features to control hardware via HTTP
  • Implement WebSocket support for real-time communication

On this page