Vix.cpp v2.8.5 is here Read the blog
Skip to content

Build a REST API ​

This guide shows how to build a small REST API with Vix.

You will create:

  • GET /
  • GET /health
  • GET /users
  • GET /users/{id}
  • POST /users

Requirements ​

bash
vix --version
vix new rest-api
cd rest-api

Minimal server ​

Create or replace src/main.cpp:

cpp
#include <vix.hpp>

using namespace vix;

int main()
{
  App app;

  app.get("/", [](Request &, Response &res){
    res.json({
        "message", "Hello from Vix",
        "framework", "Vix.cpp"
    });
  });

  app.run(8080);
  return 0;
}
bash
vix dev
curl -i http://127.0.0.1:8080/

Basic Vix HTTP structure ​

PartPurpose
App app;Creates the HTTP application instance.
app.get(...)Registers a GET route handler.
Request &reqProvides access to request data.
Response &resProvides methods for sending responses.
res.json(...)Sends a JSON response.
app.run(8080)Starts the server on port 8080.

Create a health route ​

cpp
app.get("/health", [](Request &, Response &res){
  res.json({
      "ok", true,
      "service", "rest-api"
  });
});

Add an in-memory users list ​

cpp
std::vector<json::Json> users = {
    { {"id", 1}, {"name", "Alice"}, {"role", "admin"} },
    { {"id", 2}, {"name", "Bob"}, {"role", "user"}}
};

Add GET /users ​

cpp
app.get("/users", [&users](Request &, Response &res){
  res.json({
      "ok", true,
      "count", users.size(),
      "data", users
  });
});

Add GET /users/ ​

cpp
app.get("/users/{id}", [&users](Request &req, Response &res){

  const std::string id = req.param("id");
  for (const auto &user : users)
  {
    if (std::to_string(user.value("id", 0)) == id)
    {
      res.json({
        "ok", true,
        "data", user
      });

      return;
    }
  }

  res.status(404).json({
      "ok", false,
      "error", "user not found",
      "id", id
  });

});

Add query params ​

cpp
app.get("/users", [&users](Request &req, Response &res){

  const std::string page = req.query_value("page", "1");
  const std::string limit = req.query_value("limit", "10");

  res.json({
      "ok", true,
      "page", page,
      "limit", limit,
      "count", users.size(),
      "data", users
  });

});

Useful request APIs:

APIPurpose
req.param("id")Reads a route path parameter.
req.param("id", "0")Reads a route parameter with fallback.
req.query_value("page", "1")Reads a query parameter with fallback.
req.query()Reads all query parameters.
req.body()Reads the raw request body.
req.json()Reads the parsed JSON body.
req.header("Authorization")Reads a request header value.

Add POST /users ​

cpp
app.post("/users", [&users](Request &req, Response &res){

  const auto &body = req.json();
  if (!body.is_object())
  {
    res.status(400).json({
      "ok", false,
      "error", "expected JSON object body"
    });

    return;
  }

  const std::string name = body.value("name", "");
  const std::string role = body.value("role", "user");
  if (name.empty())
  {
    res.status(400).json({
      "ok", false,
      "error", "field 'name' is required"
    });

    return;
  }

  const int new_id = users.empty() ? 1 : users.back().value("id", 0) + 1;

  json::Json user = {
    {"id", new_id},
    {"name", name},
    {"role", role}
  };

  users.push_back(user);

  res.status(201).json({
    "ok", true,
    "message", "user created",
    "data", user
  });

});
bash
curl -i -X POST http://127.0.0.1:8080/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","role":"user"}'

Final version ​

Replace src/main.cpp with this complete, organized version:

cpp
#include <vix.hpp>
#include <string>
#include <vector>

using namespace vix;

struct AppState
{
  std::vector<json::Json> users;
};

static AppState create_state()
{
  AppState state;

  state.users = {
    { {"id", 1}, {"name", "Alice"}, {"role", "admin"} },
    { {"id", 2}, {"name", "Bob"}, {"role", "user"} }
  };

  return state;
}

static void register_public_routes(App &app)
{
  app.get("/", [](Request &, Response &res){
    res.json({
      "message", "Vix REST API",
      "framework", "Vix.cpp"
    });
  });

  app.get("/health", [](Request &, Response &res){
    res.json({
      "ok", true,
      "service", "rest-api"
    });
  });
}

static void register_user_routes(App &app, AppState &state)
{
  app.get("/users", [&state](Request &req, Response &res){
    const std::string page = req.query_value("page", "1");
    const std::string limit = req.query_value("limit", "10");

    res.json({
      "ok", true,
      "page", page,
      "limit", limit,
      "count", state.users.size(),
      "data", state.users
    });
  });

  app.get("/users/{id}", [&state](Request &req, Response &res){

    const std::string id = req.param("id");
    for (const auto &user : state.users)
    {
      if (std::to_string(user.value("id", 0)) == id)
      {
        res.json({
          "ok", true,
          "data", user
        });

        return;
      }
    }

    res.status(404).json({
      "ok", false,
      "error", "user not found",
      "id", id
    });

  });

  app.post("/users", [&state](Request &req, Response &res){

    const auto &body = req.json();
    if (!body.is_object())
    {
      res.status(400).json({
        "ok", false,
        "error", "expected JSON object body"
      });

      return;
    }

    const std::string name = body.value("name", "");
    const std::string role = body.value("role", "user");
    if (name.empty())
    {
      res.status(400).json({
        "ok", false,
        "error", "field 'name' is required"
      });

      return;
    }

    const int new_id = state.users.empty() ? 1 : state.users.back().value("id", 0) + 1;

    json::Json user = {
      {"id", new_id},
      {"name", name},
      {"role", role}
    };

    state.users.push_back(user);

    res.status(201).json({
      "ok", true,
      "message", "user created",
      "data", user
    });

  });
}

int main()
{
  App app;

  AppState state = create_state();
  register_public_routes(app);
  register_user_routes(app, state);

  app.run(8080);

  return 0;
}

Test the API ​

bash
curl -i http://127.0.0.1:8080/
curl -i http://127.0.0.1:8080/health
curl -i http://127.0.0.1:8080/users
curl -i "http://127.0.0.1:8080/users?page=1&limit=10"
curl -i http://127.0.0.1:8080/users/1
curl -i -X POST http://127.0.0.1:8080/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","role":"user"}'

Response helpers ​

cpp
res.json({"ok", true, "message", "Hello"});   // JSON
res.text("Hello Vix");                         // Text
res.send("Created");                           // Generic
res.status(201).json({"ok", true});            // With status
res.file("public/index.html");                 // File

Status codes ​

StatusMeaningExample
200OK.Successful GET request.
201Created.Successful POST request.
400Bad Request.Invalid input was provided.
401Unauthorized.Authentication is missing.
403Forbidden.Access is not allowed.
404Not Found.Requested resource is missing.
500Internal Server Error.Server-side failure occurred.
json
{ "ok": true, "data": {} }
{ "ok": false, "error": "message" }
{ "ok": true, "count": 2, "data": [] }

Common mistakes ​

Forgetting to return after sending an error ​

cpp
// Wrong
if (name.empty()) {
  res.status(400).json({
    "error", "name is required"
  });
}

res.json({"ok", true});

// Correct
if (name.empty()) {
  res.status(400).json({
    "error", "name is required"
  });
  return;
}

res.json({"ok", true});

Passing runtime args incorrectly ​

bash
vix run main.cpp --run --port 8080   # correct
vix run main.cpp -- --port 8080      # wrong: -- is for compiler flags

Expecting in-memory data to persist ​

The users vector is in memory. If the server restarts, created users are lost. For persistence, use SQLite or MySQL.

What to use next ​

Released under the MIT License.