Skip to content

Database Integration

Crest provides database helpers for easy integration with SQL databases.

Overview

Features: - Connection pooling - Query builder - ORM-like models - Transaction support - Prepared statements

Connection

#include "crest/database.hpp"

// Create connection (implement for your database)
class MySQLConnection : public crest::db::Connection {
    // Implement virtual methods
};

auto conn = std::make_shared<MySQLConnection>();
conn->connect("host=localhost;db=mydb;user=root;password=secret");

Connection Pool

crest::db::ConnectionPool::Config pool_config;
pool_config.connection_string = "host=localhost;db=mydb";
pool_config.min_connections = 2;
pool_config.max_connections = 10;
pool_config.timeout_seconds = 30;

crest::db::ConnectionPool pool(pool_config);

// Acquire connection
auto conn = pool.acquire();

// Use connection
auto results = conn->execute("SELECT * FROM users");

// Release back to pool
pool.release(conn);

Query Builder

SELECT Queries

crest::db::QueryBuilder qb;

// Simple select
qb.select({"id", "name", "email"})
  .from("users")
  .where("age", ">", 18)
  .order_by("name", true)
  .limit(10);

std::string query = qb.build();
// SELECT id, name, email FROM users WHERE age > ? ORDER BY name ASC LIMIT 10

auto results = conn->execute(query, qb.get_params());

INSERT Queries

crest::db::QueryBuilder qb;

qb.insert_into("users")
  .values({
      {"name", "John Doe"},
      {"email", "john@example.com"},
      {"age", 30}
  });

conn->execute_update(qb.build(), qb.get_params());

UPDATE Queries

crest::db::QueryBuilder qb;

qb.update("users")
  .set({
      {"name", "Jane Doe"},
      {"email", "jane@example.com"}
  })
  .where("id", "=", 1);

conn->execute_update(qb.build(), qb.get_params());

DELETE Queries

crest::db::QueryBuilder qb;

qb.delete_from("users")
  .where("id", "=", 1);

conn->execute_update(qb.build(), qb.get_params());

Complex Queries

crest::db::QueryBuilder qb;

qb.select({"users.id", "users.name", "orders.total"})
  .from("users")
  .join("orders", "users.id = orders.user_id")
  .where("orders.status", "=", "completed")
  .and_where("orders.total", ">", 100)
  .order_by("orders.total", false)
  .limit(20)
  .offset(0);

auto results = conn->execute(qb.build(), qb.get_params());

Models

class User : public crest::db::Model {
public:
    int id;
    std::string name;
    std::string email;
    int age;

    std::string table_name() const override {
        return "users";
    }

    crest::db::Row to_row() const override {
        return {
            {"id", id},
            {"name", name},
            {"email", email},
            {"age", age}
        };
    }

    void from_row(const crest::db::Row& row) override {
        id = std::get<int>(row.at("id"));
        name = std::get<std::string>(row.at("name"));
        email = std::get<std::string>(row.at("email"));
        age = std::get<int>(row.at("age"));
    }
};

// Usage
User user;
user.name = "Alice";
user.email = "alice@example.com";
user.age = 25;

// Save (insert or update)
user.save(*conn);

// Delete
user.remove(*conn);

// Find all
auto results = User::find_all(*conn, "users");

// Find by ID
auto row = User::find_by_id(*conn, "users", 1);

Transactions

auto conn = pool.acquire();

try {
    conn->begin_transaction();

    // Multiple operations
    conn->execute_update("INSERT INTO users (name) VALUES (?)", {"Alice"});
    conn->execute_update("INSERT INTO orders (user_id) VALUES (?)", {1});

    conn->commit();
} catch (const std::exception& e) {
    conn->rollback();
    std::cerr << "Transaction failed: " << e.what() << std::endl;
}

pool.release(conn);

API Integration Example

#include "crest/crest.hpp"
#include "crest/database.hpp"

int main() {
    crest::App app;

    // Setup connection pool
    crest::db::ConnectionPool::Config pool_config;
    pool_config.connection_string = "host=localhost;db=api";
    crest::db::ConnectionPool pool(pool_config);

    // Get all users
    app.get("/users", [&pool](crest::Request& req, crest::Response& res) {
        auto conn = pool.acquire();

        crest::db::QueryBuilder qb;
        qb.select({}).from("users");

        auto results = conn->execute(qb.build());
        pool.release(conn);

        // Convert to JSON (simplified)
        res.json(200, R"({"users":[]})");
    });

    // Create user
    app.post("/users", [&pool](crest::Request& req, crest::Response& res) {
        auto conn = pool.acquire();

        // Parse request body (simplified)
        crest::db::QueryBuilder qb;
        qb.insert_into("users").values({
            {"name", "John"},
            {"email", "john@example.com"}
        });

        conn->execute_update(qb.build(), qb.get_params());
        pool.release(conn);

        res.json(201, R"({"message":"User created"})");
    });

    app.run("0.0.0.0", 8000);
    return 0;
}

Best Practices

  • Always use connection pooling in production
  • Use prepared statements to prevent SQL injection
  • Handle transactions properly with try-catch
  • Release connections back to pool
  • Set appropriate pool size based on load
  • Use query builder for complex queries
  • Implement proper error handling
  • Close connections on application shutdown