Skip to main content

Boson Quickstart Guide

This quickstart guide will help you create a fully functional RESTful API with Boson in just a few minutes. By the end of this guide, you'll have a working API server with proper routing, JSON handling, and error management.

The fastest way to get started is with the Boson CLI. If you've already installed Boson, you can create a new project with:

# Create a new project
boson new my-boson-api --template api

# Navigate to the project
cd my-boson-api

# Run the development server
boson run

Your server will start at http://localhost:3000, and you can begin testing your API immediately.

Manual Project Setup

If you prefer setting up your project manually, follow these steps for complete control over your project structure.

Step 1: Create a Project Structure

First, create a directory for your project with the following structure:

my-boson-app/
├── CMakeLists.txt
├── src/
│ └── main.cpp

You can create this structure with these commands:

mkdir -p my-boson-app/src
touch my-boson-app/CMakeLists.txt
touch my-boson-app/src/main.cpp
cd my-boson-app

Step 2: Configure CMake

Create a CMakeLists.txt file in your project root with the following content:

cmake_minimum_required(VERSION 3.14)
project(my-boson-app VERSION 0.1.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Find Boson package
find_package(Boson REQUIRED)

# Add executable
add_executable(${PROJECT_NAME} src/main.cpp)

# Link against Boson library
target_link_libraries(${PROJECT_NAME} PRIVATE Boson::Boson)

This CMake configuration:

  • Sets the C++ standard to C++17 (required by Boson)
  • Finds the Boson package on your system
  • Creates an executable from your source code
  • Links it against the Boson library

Step 3: Create Your Application

In src/main.cpp, create a RESTful API server with the following code:

#include <boson/boson.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// A simple user model
struct User {
int id;
std::string name;
std::string email;

// Convert to JSON object using nlohmann::json library
nlohmann::json toJson() const {
return {
{"id", id},
{"name", name},
{"email", email}
};
}
};

// Our in-memory "database"
std::vector<User> users = {
{1, "John Doe", "john@example.com"},
{2, "Jane Smith", "jane@example.com"}
};

// Find the next available user ID
int getNextUserId() {
return users.empty() ? 1 : (std::max_element(users.begin(), users.end(),
[](const User& a, const User& b) { return a.id < b.id; })->id + 1);
}

int main() {
// Initialize framework
boson::initialize();

// Create server instance
boson::Server app;

// Add logging middleware
app.use([](const boson::Request& req, boson::Response& res, boson::NextFunction& next) {
std::cout << "[" << req.method() << "] " << req.path() << std::endl;
next();
});

// Define routes

// GET / - Welcome message
app.get("/", [](const boson::Request& req, boson::Response& res) {
res.send("Welcome to the Boson API!");
});

// GET /api/users - Get all users
app.get("/api/users", [](const boson::Request& req, boson::Response& res) {
nlohmann::json usersJson = nlohmann::json::array();

for (const auto& user : users) {
usersJson.push_back(user.toJson());
}

nlohmann::json response = {
{"users", usersJson},
{"count", users.size()}
};

res.jsonObject(response);
});

// GET /api/users/:id - Get user by ID
app.get("/api/users/:id", [](const boson::Request& req, boson::Response& res) {
try {
int id = std::stoi(req.param("id"));

auto it = std::find_if(users.begin(), users.end(),
[id](const User& user) { return user.id == id; });

if (it != users.end()) {
res.jsonObject(it->toJson());
return;
}

// User not found
res.status(404).jsonObject({
{"error", "User not found"},
{"id", id}
});
}
catch (const std::exception& e) {
res.status(400).jsonObject({
{"error", "Invalid ID format"},
{"message", e.what()}
});
}
});

// POST /api/users - Create a new user
app.post("/api/users", [](const boson::Request& req, boson::Response& res) {
try {
// Parse request body as JSON
nlohmann::json body = req.json();

// Validate required fields
if (!body.contains("name") || !body.contains("email")) {
res.status(400).jsonObject({
{"error", "Missing required fields"},
{"required", {"name", "email"}}
});
return;
}

// Create new user
User newUser;
newUser.id = getNextUserId();
newUser.name = body["name"].get<std::string>();
newUser.email = body["email"].get<std::string>();

// Add to our "database"
users.push_back(newUser);

// Return the created user
res.status(201).jsonObject(newUser.toJson());
}
catch (const std::exception& e) {
res.status(400).jsonObject({
{"error", "Invalid request"},
{"message", e.what()}
});
}
});

// PUT /api/users/:id - Update a user
app.put("/api/users/:id", [](const boson::Request& req, boson::Response& res) {
try {
int id = std::stoi(req.param("id"));
nlohmann::json body = req.json();

auto it = std::find_if(users.begin(), users.end(),
[id](const User& user) { return user.id == id; });

if (it != users.end()) {
// Update user fields if they exist in the request
if (body.contains("name")) {
it->name = body["name"].get<std::string>();
}
if (body.contains("email")) {
it->email = body["email"].get<std::string>();
}

res.jsonObject(it->toJson());
return;
}

res.status(404).jsonObject({
{"error", "User not found"},
{"id", id}
});
}
catch (const std::exception& e) {
res.status(400).jsonObject({
{"error", "Invalid request"},
{"message", e.what()}
});
}
});

// DELETE /api/users/:id - Delete a user
app.del("/api/users/:id", [](const boson::Request& req, boson::Response& res) {
try {
int id = std::stoi(req.param("id"));

auto it = std::find_if(users.begin(), users.end(),
[id](const User& user) { return user.id == id; });

if (it != users.end()) {
users.erase(it);
res.status(204).send("");
return;
}

res.status(404).jsonObject({
{"error", "User not found"},
{"id", id}
});
}
catch (const std::exception& e) {
res.status(400).jsonObject({
{"error", "Invalid request"},
{"message", e.what()}
});
}
});

// Configure the server
std::cout << "Starting server on http://127.0.0.1:3000" << std::endl;
app.configure(3000, "127.0.0.1");

// Start the server
return app.listen();
}

This application implements:

  • A simple User model with JSON serialization
  • An in-memory "database" using a vector
  • RESTful routes for CRUD operations
  • Request logging middleware
  • Error handling with appropriate HTTP status codes

Step 4: Build Your Application

Now that you have your application code ready, build it with CMake:

# Create build directory
mkdir build && cd build

# Configure with CMake
cmake ..

# Build
cmake --build .

Step 5: Run Your Application

Once built, you can run your application from the build directory:

# Run from the build directory
./my-boson-app

You should see output indicating that your server has started on port 3000.

Testing Your API

You can test your RESTful API using curl, Postman, or any HTTP client. Here are some curl examples:

Get All Users

curl http://localhost:3000/api/users

Expected response:

{
"count": 2,
"users": [
{
"email": "john@example.com",
"id": 1,
"name": "John Doe"
},
{
"email": "jane@example.com",
"id": 2,
"name": "Jane Smith"
}
]
}

Get a Specific User

curl http://localhost:3000/api/users/1

Expected response:

{
"email": "john@example.com",
"id": 1,
"name": "John Doe"
}

Create a New User

curl -X POST \
http://localhost:3000/api/users \
-H 'Content-Type: application/json' \
-d '{"name":"Bob Johnson","email":"bob@example.com"}'

Expected response:

{
"email": "bob@example.com",
"id": 3,
"name": "Bob Johnson"
}

Update a User

curl -X PUT \
http://localhost:3000/api/users/2 \
-H 'Content-Type: application/json' \
-d '{"name":"Jane Williams"}'

Expected response:

{
"email": "jane@example.com",
"id": 2,
"name": "Jane Williams"
}

Delete a User

curl -X DELETE http://localhost:3000/api/users/3

This should return a 204 No Content response with no body.

Code Breakdown

Let's examine some key parts of the application:

Server Initialization

// Initialize framework
boson::initialize();

// Create server instance
boson::Server app;

These lines initialize the Boson framework and create a server instance. The initialize() function sets up internal components.

Middleware

app.use([](const boson::Request& req, boson::Response& res, boson::NextFunction& next) {
std::cout << "[" << req.method() << "] " << req.path() << std::endl;
next();
});

This adds a simple logging middleware that prints the HTTP method and path for each request. The next() function continues processing to the next middleware or route handler.

Route Handling

app.get("/api/users/:id", [](const boson::Request& req, boson::Response& res) {
// Handler code
});

This defines a route that responds to GET requests at the path /api/users/:id, where :id is a path parameter that can be accessed with req.param("id").

JSON Handling

nlohmann::json response = {
{"users", usersJson},
{"count", users.size()}
};

res.jsonObject(response);

Boson integrates with the nlohmann::json library to make working with JSON straightforward. The jsonObject() method sets the appropriate Content-Type header and serializes the JSON object.

Error Handling

try {
// Code that might throw
}
catch (const std::exception& e) {
res.status(400).jsonObject({
{"error", "Invalid request"},
{"message", e.what()}
});
}

Proper error handling ensures that the client receives meaningful error messages with appropriate HTTP status codes.

Next Steps

Congratulations on creating your first Boson application! You now have a fully functional RESTful API with proper error handling and JSON support. To continue your Boson journey:

  • Explore the Project Structure guide to learn how to organize larger applications
  • Learn about Middleware to add functionality like authentication, CORS, etc.
  • Read the Server documentation to understand configuration options
  • Check out Request and Response for more advanced handling techniques
  • See how to use Controllers to organize your route handlers
  • Browse the Examples for more inspiration

Ready to build something more complex? Continue to the Hello World Tutorial for a more detailed walkthrough of building a multi-feature application.