Skip to content

Devices example

examples/Devices returns a list of Device records and a single device by id, with the typical 200 / 400 / 404 trio of typed results.

Source

examples/Devices/src/main.cpp:

examples/Devices/src/main.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <Baldr/Baldr.hpp>

#include <optional>
#include <variant>

#include "Device.hpp"

namespace
{
    std::vector<Device> makeDevices()
    {
        return std::vector<Device> {
            Device { .id         = 1,
                     .uuid       = "9add349c-c35c-4d32-ab0f-53da1ba40a2a",
                     .mac        = "EF-2B-C4-F5-D6-34",
                     .firmware   = "2.1.5",
                     .created_at = "2024-05-28T15:21:51.137Z",
                     .updated_at = "2024-05-28T15:21:51.137Z" },
            Device { .id         = 2,
                     .uuid       = "d2293412-36eb-46e7-9231-af7e9249fffe",
                     .mac        = "E7-34-96-33-0C-4C",
                     .firmware   = "1.0.3",
                     .created_at = "2024-01-28T15:20:51.137Z",
                     .updated_at = "2024-01-28T15:20:51.137Z" },
            Device { .id         = 3,
                     .uuid       = "eee58ca8-ca51-47a5-ab48-163fd0e44b77",
                     .mac        = "68-93-9B-B5-33-B9",
                     .firmware   = "4.3.1",
                     .created_at = "2024-08-28T15:18:21.137Z",
                     .updated_at = "2024-08-28T15:18:21.137Z" },
            Device { .id         = 4,
                     .uuid       = "ab4efcd0-f542-4944-9dd9-0ad844dfcbd3",
                     .mac        = "E7-6F-69-99-F1-ED",
                     .firmware   = "6.2.0",
                     .created_at = "2024-08-29T15:18:21.137Z",
                     .updated_at = "2024-08-29T15:18:21.137Z" },
            Device { .id         = 5,
                     .uuid       = "9e725cbc-2c4e-446c-a274-962531f90927",
                     .mac        = "9F-57-E5-1F-F5-6B",
                     .firmware   = "0.6.4",
                     .created_at = "2024-18-28T15:18:21.137Z",
                     .updated_at = "2024-18-28T15:18:21.137Z" },
        };
    }

    std::optional<Device> findById(int id)
    {
        for (const auto& d : makeDevices())
        {
            if (d.id == id)
                return d;
        }
        return std::nullopt;
    }
} // namespace

int main()
{
    auto builder =
        skr::ApplicationBuilder().WithExtension<baldr::BaldrExtension>();

    auto app = builder.Build<baldr::WebApplication>();

    app->MapGet("/api/devices", []() { return makeDevices(); });

    app->MapGet("/api/devices/:id")
        .WithSummary("Get a device by id")
        .Handle([](baldr::HttpRequest& request)
                    -> std::variant<
                        baldr::JsonResult<Device, baldr::StatusCode::OK>,
                        baldr::BadRequestResult,
                        baldr::NotFoundResult> {
            int id = 0;
            try
            {
                id = std::stoi(request.params.at("id"));
            }
            catch (...)
            {
                return baldr::Results::BadRequest();
            }

            auto found = findById(id);
            if (!found)
                return baldr::Results::NotFound();

            return baldr::Results::Json<Device, baldr::StatusCode::OK>(*found);
        });

    app->Run();

    return 0;
}

examples/Devices/src/Device.hpp:

Device.hpp
#pragma once

#include <string>

struct Device
{
    int         id;
    std::string uuid;
    std::string mac;
    std::string firmware;
    std::string created_at;
    std::string updated_at;
};

What it shows

  • Returning a std::vector<T> from a handler. The framework serialises the entire vector as a JSON array — no special-casing required.
  • Organising the response DTO in a separate header (Device.hpp) so the type can be reused.
  • The fluent route API on the :id route: .WithSummary(...) + .Handle(lambda).
  • Returning a std::variant<JsonResult, BadRequestResult, NotFoundResult> to express the 200 / 400 / 404 outcomes. The framework picks the right status and serialises the body automatically.
  • Parsing path parameters from HttpRequest::params and translating parse failures into a typed Results::BadRequest().

Try it

cmake -S . -B build
cmake --build build
./build/Devices

In another terminal:

curl http://localhost:8080/api/devices
curl http://localhost:8080/api/devices/1
curl -i http://localhost:8080/api/devices/abc     # 400 Bad Request
curl -i http://localhost:8080/api/devices/999     # 404 Not Found

Next steps

  • See Results for the typed result family.
  • See Route options for adding summary, tags, and an OpenAPI operation id to this route.
  • See the OpenAPI example for the end-to-end extension that generates a spec from this pattern.
  • Browse all examples.