Post

Backend server with Crow C++

Backend server with Crow C++

Hello World !

This is my favorite backend server framework with C++ so i will share simple CRUD with linux debian setup

✅ Requirements

  • C/C++ Compiler with pthread support
  • C++17 or newer
  • Crow installed framework

install dependencies

1
$ apt install -y build-essential cmake curl git libasio-dev libboost-all-dev

installed crow to path external/crow

1
2
$ mkdir -p external
$ git clone https://github.com/CrowCpp/Crow.git external/crow

🗃️ Project structure file

here is our project structure file

1
2
3
4
5
6
7
8
9
10
.
├── external/
│   └── crow/
├── include/
│   └── item.hpp
├── src/
│   ├── controllers.cpp
│   ├── main.cpp
│   └── routes.cpp
└── CMakeLists.txt

🛠️ Build system

first we need to init our build system here in CMakeLists.txt

I use CMake build system for this project

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cmake_minimum_required(VERSION 3.10)
project(backend_crow)

set(CMAKE_CXX_STANDARD 17)

include_directories(include)
include_directories(external/crow/include)

find_package(Boost REQUIRED system)

add_executable(app
    src/controllers.cpp
    src/main.cpp
    src/routes.cpp
)

target_link_libraries(app Boost::system pthread)

📄 Header

create our data model header in include/item.hpp

1
2
3
4
5
6
7
#pragma once
#include <string>

struct Item {
    int id;
    std::string name;
};

💻 Setup code

first create our logic business logic in src/controllers.cpp

so because this was a simple project we will keep our data into memory

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
#include "../include/item.hpp"
#include <vector>
#include <optional>

static std::vector<Item> items;
static int nextId = 1;

std::vector<Item> &getItems() {
    return items;
}

int createItem(const std::string &name) {
    items.push_back({nextId, name});
    return nextId++;
}

bool updateItem(int id, const std::string &name) {
    for (auto &i : items) {
        if (i.id == id) {
            i.name = name;
            return true;
        }
    }
    return false;
}

bool deleteItem(int id) {
    for (auto it = items.begin(); it != items.end(); ++it) {
        if (it->id == id) {
            items.erase(it);
            return true;
        }
    }
    return false;
}

next create the routes in src/routes.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 "crow.h"
#include "../include/item.hpp"
#include <vector>

std::vector<Item> &getItems();
int createItem(const std::string &name);
bool updateItem(int id, const std::string &name);
bool deleteItem(int id);

void setupRoutes(crow::SimpleApp &app) {
    // read
    CROW_ROUTE(app, "/items").methods("GET"_method)
    ([](){
        auto &items = getItems();

        crow::json::wvalue res;
        crow::json::wvalue::list list;

        for (auto &i : items) {
            crow::json::wvalue item;
            item["id"] = i.id;
            item["name"] = i.name;
            list.push_back(item);
        }

        res["items"] = std::move(list);
        return res;
    });

    // create
    CROW_ROUTE(app, "/items").methods("POST"_method)
    ([](const crow::request &req){
        auto body = crow::json::load(req.body);
        if (!body || !body.has("name"))
            return crow::response(400, "Invalid JSON");

        int id = createItem(body["name"].s());

        crow::json::wvalue res;
        res["message"] = "created";
        res["id"] = id;

        return crow::response(res);
    });

    // get per items
    CROW_ROUTE(app, "/items/<int>").methods("GET"_method)
    ([](int id){
        auto &items = getItems();

        for (auto &i : items) {
            if (i.id == id) {
                crow::json::wvalue res;
                res["id"] = i.id;
                res["name"] = i.name;

                return crow::response(res);
            }
        }

        return crow::response(404, "Not found");
    });

    // update
    CROW_ROUTE(app, "/items/<int>").methods("PUT"_method)
    ([](const crow::request &req, int id){
        auto body = crow::json::load(req.body);
        if (!body || !body.has("name"))
            return crow::response(400, "Invalid JSON");

        bool ok = updateItem(id, body["name"].s());

        if (!ok)
            return crow::response(404, "Not found");

        crow::json::wvalue res;
        res["message"] = "updated";
        return crow::response(res);
    });

    // delete
    CROW_ROUTE(app, "/items/<int>").methods("DELETE"_method)
    ([](int id){
        bool ok = deleteItem(id);

        if (!ok)
            return crow::response(404, "Not found");

        crow::json::wvalue res;
        res["message"] = "deleted";
        return crow::response(res);
    });
}

finally create our main config file in src/main.cpp

1
2
3
4
5
6
7
8
9
10
#include "crow.h"

void setupRoutes(crow::SimpleApp &app);

int main() {
    crow::SimpleApp app;
    setupRoutes(app);

    app.port(8000).multithreaded().run();
}

🚀 Build and run

after setup our code lets build and run with this command

1
2
3
4
$ mkdir build && cd build
$ cmake ..
$ make
$ ./app

server will be run on: http://localhost:8000

test our project with curl

create

1
2
3
$ curl -X POST http://localhost:8000/items \
  -H "Content-Type: application/json" \
  -d '{"name":"Apple"}'

Read all

1
$ curl http://localhost:8000/items

Read one per items

1
$ curl http://localhost:8000/items/1

update

1
2
3
$ curl -X PUT http://localhost:8000/items/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Banana"}'

delete

1
$ curl -X DELETE http://localhost:8000/items/1

🌐 Other frameworks

Crow is popular c++ framework for backend server especially best for small and fast microservices but you can try popular c++ framework among this:

This post is licensed under CC BY 4.0 by the author.