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-serverThe 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 runWendy 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 matchingPOST /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 runThe 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:3000Replace 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:
-
Include Headers: We include
httplib.hfor HTTP server functionality andjson.hppfor JSON parsing/serialization. -
Create Server: We create an
httplib::Serverinstance that will handle incoming requests. -
Define Routes: We define three routes using lambda functions:
GET /returns a simple text responseGET /hello/:nameuses regex to capture path parametersPOST /usersparses JSON from the request body and returns a JSON response
-
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