Skip to content

Latest commit

 

History

History
438 lines (342 loc) · 9.31 KB

File metadata and controls

438 lines (342 loc) · 9.31 KB

HTTP API

The HTTP API is the core application API of Vix. It gives you the main building blocks for web applications and APIs.

App — Request — Response — Routes — Static files — Server lifecycle

Public header

#include <vix.hpp>

Basic example

#include <vix.hpp>
using namespace vix;

int main()
{
  App app;

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

  app.run(8080);

  return 0;
}

vix::App

App is the main application object. It owns the router, middleware chain, server lifecycle, static files, and runtime configuration.

App app;

Server lifecycle

app.run(8080);              // start and block

app.listen(8080, []() {     // start async with callback
  std::cout << "listening\n";
});

app.wait();                 // block until server stops
app.close();                // stop the server

Routes

A route connects an HTTP method and path to a C++ handler.

app.get("/users/{id}", [](Request &req, Response &res){
  const std::string id = req.param("id");
  res.json({"id", id});
});

HTTP methods

app.get("/users", list_handler);
app.post("/users", create_handler);
app.put("/users/{id}", replace_handler);
app.patch("/users/{id}", update_handler);
app.del("/users/{id}", delete_handler);
app.head("/health", head_handler);
app.options("/users", options_handler);

Path parameters

// Matches /users/1, /users/42, /users/abc
app.get("/users/{id}", [](Request &req, Response &res){
  res.json({"id", req.param("id")});
});

// Multiple parameters
app.get("/posts/{year}/{slug}", [](Request &req, Response &res){
  res.json({
    "year", req.param("year"),
    "slug", req.param("slug")
  });
});

Query parameters

// /users?page=2&limit=10
app.get("/users", [](Request &req, Response &res){
  const std::string page = req.query_value("page", "1");
  const std::string limit = req.query_value("limit", "20");

  res.json({
    "page", page,
    "limit", limit
  });
});

Use path parameters to identify a resource (/users/42), query parameters to modify how resources are read (/users?page=2).

Request

Method Purpose
req.param("id") Read a path parameter
req.param("id", "0") Read with fallback
req.query_value("page", "1") Read a query parameter with fallback
req.query() Read all query parameters
req.header("Authorization") Read a request header
req.body() Read the raw request body
req.json() Read the parsed JSON body
req.path() Read the request path
req.method() Read the HTTP method

Read JSON body

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

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

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

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

});

Response

Method Purpose
res.text("Hello") Send plain text
res.send("Hello") Send a basic response body
res.json({...}) Send JSON
res.status(201) Set HTTP status (chainable)
res.header("X-Foo", "bar") Set a response header
res.file("public/index.html") Send a file
res.status(201).json({
  "ok", true,
  "message", "created"
});
res.header("X-Powered-By", "Vix.cpp");
res.file("public/index.html");

Status codes

Status Meaning
200 OK
201 Created
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
429 Too Many Requests
500 Internal Server Error

Error helper

static void respond_error(Response &res, int status,
                           const std::string &code,
                           const std::string &message)
{
  res.status(status).json({
    "ok", false,
    "error", code,
    "message", message
  });
}

// Always return after sending an error
if (id == "0") {
  respond_error(res, 404, "user_not_found", "User not found");
  return;
}

Good response shapes

{ "ok": true, "data": {} }
{ "ok": true, "count": 2, "data": [] }
{ "ok": false, "error": "validation_failed", "message": "name is required" }

Route order

Register specific routes before generic ones:

// Correct
app.get("/users/search", search_handler);
app.get("/users/{id}", user_handler);
app.get("/*", fallback_handler);

// Wrong — wildcard catches everything first
app.get("/*", fallback_handler);
app.get("/users/{id}", user_handler);

Wildcard and static files

// Manual static file fallback
app.get("/*", [](Request &req, Response &res){
  res.header("Cache-Control", "public, max-age=86400");
  res.file("public" + req.path());
});

// Simple static directory
app.static_dir("public");

Route groups

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

  api.get("/users", [](Request &, Response &res) {
    res.json({"ok", true});
  });

  api.group("/admin", [](auto &admin){
    admin.get("/stats", [](Request &, Response &res){
      res.json({"ok", true});
    });
  });
});
// Routes: GET /api/health, GET /api/users, GET /api/admin/stats

Organizing routes

static void register_public_routes(App &app)
{
  app.get("/", [](Request &, Response &res) {
    res.json({"message", "Hello"});
  });

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

int main()
{
  App app;

  register_public_routes(app);

  app.run(8080);

  return 0;
}

Complete example

#include <vix.hpp>
using namespace vix;

static void respond_error(Response &res, int status,
                           const std::string &code, const std::string &message)
{
  res.status(status).json({
    "ok", false,
    "error", code,
    "message", message
  });
}

static void register_user_routes(App &app)
{
  app.get("/users", [](Request &req, Response &res){
    res.json({
        "ok", true,
        "page", req.query_value("page", "1"),
        "data", vix::json::array({"Alice", "Bob"})
    });
  });

  app.get("/users/{id}", [](Request &req, Response &res){
    const std::string id = req.param("id");
    if (id == "0") {
      respond_error(res, 404, "user_not_found", "User not found");
      return;
    }

    res.json({
      "ok", true,
      "data", vix::json::o("id", id),
      "name", "Ada"
    });
  });

  app.post("/users", [](Request &req, Response &res){
    const auto &body = req.json();
    if (!body.is_object()) {
      respond_error(res, 400, "invalid_body", "Expected JSON object");
      return;
    }

    const std::string name = body.value("name", "");
    if (name.empty()) {
      respond_error(res, 400, "validation_failed", "name is required");
      return;
    }

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

  app.del("/users/{id}", [](Request &req, Response &res){
    const std::string id = req.param("id");
    if (id == "0") {
      respond_error(res, 404, "user_not_found", "User not found");
      return;
    }

    res.json({
      "ok", true,
      "message", "user deleted",
      "id", id
    });
  });
}

int main()
{
  App app;

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

  register_user_routes(app);

  app.run(8080);

  return 0;
}

Test

curl -i http://127.0.0.1:8080/health
curl -i "http://127.0.0.1:8080/users?page=2"
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"}'
curl -i -X DELETE http://127.0.0.1:8080/users/1

Common mistakes

Missing leading slash

app.get("health", handler);   // Wrong
app.get("/health", handler);  // Correct

Forgetting to return after an error

// Wrong — continues after error
respond_error(res, 400, "...", "...");
res.status(201).json({"ok", true});

// Correct
respond_error(res, 400, "...", "...");
return;

Using -- for runtime arguments

vix run main.cpp -- --port 8080   # Wrong — compiler flags
vix run main.cpp --run --port 8080  # Correct — runtime args

What you should remember

The HTTP API is built around three types: App, Request, Response.

client → Request → route handler → Response

The most common starting point: #include <vix.hpp>

Next: JSON API