diff --git a/backend/src/controllers/carts.c b/backend/src/controllers/carts.c
index 648a59d..fff5f69 100644
--- a/backend/src/controllers/carts.c
+++ b/backend/src/controllers/carts.c
@@ -12,13 +12,6 @@ void route_get_cart_items_from_session(HttpCtx* ctx)
     CartItemVec cart_items;
     cart_item_vec_construct(&cart_items);
 
-    DbRes db_res
-        = db_cart_items_with_user_id(cx->db, &cart_items, session->user_id);
-    if (db_res != DbRes_Ok) {
-        RESPOND_BAD_REQUEST(ctx, "Could not find cart for user");
-        return;
-    }
-
     String res;
     string_construct(&res);
 
diff --git a/backend/src/controllers/controllers.h b/backend/src/controllers/controllers.h
index 0716e55..3df36cd 100644
--- a/backend/src/controllers/controllers.h
+++ b/backend/src/controllers/controllers.h
@@ -2,6 +2,7 @@
 
 #include "../db/db.h"
 #include "../http/http.h"
+#include <pthread.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -16,16 +17,20 @@ void session_destroy(Session* session);
 
 DEFINE_VEC(Session, SessionVec, session_vec, 16)
 
-void sessions_remove(SessionVec* vec, int64_t user_id);
-Session* sessions_add(SessionVec* vec, int64_t user_id);
-const Session* sessions_find(SessionVec* vec, const char* token);
-
 typedef struct {
+    pthread_mutex_t mutex;
     int number;
     SessionVec sessions;
     Db* db;
 } Cx;
 
+void cx_construct(Cx* cx, Db* db);
+void cx_destroy(Cx* cx);
+
+void cx_sessions_remove(Cx* cx, int64_t user_id);
+Session* cx_sessions_add(Cx* cx, int64_t user_id);
+const Session* cx_sessions_find(Cx* cx, const char* token);
+
 void route_get_index(HttpCtx* ctx);
 void route_post_set_number(HttpCtx* ctx);
 void route_get_not_found(HttpCtx* ctx);
diff --git a/backend/src/controllers/cx.c b/backend/src/controllers/cx.c
new file mode 100644
index 0000000..fee0e78
--- /dev/null
+++ b/backend/src/controllers/cx.c
@@ -0,0 +1,95 @@
+#include "../util/str.h"
+#include "controllers.h"
+#include <pthread.h>
+
+void session_construct(Session* session, int64_t user_id)
+{
+    char* token = str_random(64);
+    size_t token_hash = str_fast_hash(token);
+    *session = (Session) { user_id, token, token_hash };
+}
+
+void session_destroy(Session* session)
+{
+    free(session->token);
+    *session = (Session) {
+        .user_id = 0,
+        .token = NULL,
+        .token_hash = 0,
+    };
+}
+
+void cx_construct(Cx* cx, Db* db)
+{
+    *cx = (Cx) {
+        .mutex = PTHREAD_MUTEX_INITIALIZER,
+        .number = 1,
+        .sessions = (SessionVec) { 0 },
+        .db = db,
+    };
+    session_vec_construct(&cx->sessions);
+}
+
+void cx_destroy(Cx* cx)
+{
+    pthread_mutex_destroy(&cx->mutex);
+    session_vec_destroy(&cx->sessions);
+}
+
+void cx_sessions_remove(Cx* cx, int64_t user_id)
+{
+    pthread_mutex_lock(&cx->mutex);
+
+    SessionVec* vec = &cx->sessions;
+    for (size_t i = 0; i < vec->size; ++i) {
+        if (vec->data[i].user_id == user_id) {
+            session_destroy(&vec->data[i]);
+        }
+    }
+
+    pthread_mutex_unlock(&cx->mutex);
+}
+
+Session* cx_sessions_add(Cx* cx, int64_t user_id)
+{
+    Session* res;
+    pthread_mutex_lock(&cx->mutex);
+
+    SessionVec* vec = &cx->sessions;
+    for (size_t i = 0; i < vec->size; ++i) {
+        if (vec->data[i].user_id == 0) {
+            session_construct(&vec->data[i], user_id);
+            res = &vec->data[i];
+            goto l0_return;
+        }
+    }
+    Session session;
+    session_construct(&session, user_id);
+    session_vec_push(vec, session);
+
+    res = &vec->data[vec->size - 1];
+
+l0_return:
+    pthread_mutex_unlock(&cx->mutex);
+    return res;
+}
+
+const Session* cx_sessions_find(Cx* cx, const char* token)
+{
+    const Session* res;
+    pthread_mutex_lock(&cx->mutex);
+
+    SessionVec* vec = &cx->sessions;
+    size_t token_hash = str_fast_hash(token);
+    for (size_t i = 0; i < vec->size; ++i) {
+        if (vec->data[i].token_hash == token_hash) {
+            res = &vec->data[i];
+            goto l0_return;
+        }
+    }
+    res = NULL;
+
+l0_return:
+    pthread_mutex_unlock(&cx->mutex);
+    return res;
+}
diff --git a/backend/src/controllers/sessions.c b/backend/src/controllers/sessions.c
index f3ddbdb..224ca9f 100644
--- a/backend/src/controllers/sessions.c
+++ b/backend/src/controllers/sessions.c
@@ -41,8 +41,8 @@ void route_post_sessions_login(HttpCtx* ctx)
         goto l2_return;
     }
 
-    sessions_remove(&cx->sessions, user.id);
-    Session* session = sessions_add(&cx->sessions, user.id);
+    cx_sessions_remove(cx, user.id);
+    Session* session = cx_sessions_add(cx, user.id);
 
     RESPOND_JSON(ctx, 200, "{\"ok\":true,\"token\":\"%s\"}", session->token);
 l2_return:
@@ -59,7 +59,7 @@ void route_post_sessions_logout(HttpCtx* ctx)
         RESPOND_JSON(ctx, 200, "{\"ok\":true}");
         return;
     }
-    sessions_remove(&cx->sessions, session->user_id);
+    cx_sessions_remove(cx, session->user_id);
     RESPOND_JSON(ctx, 200, "{\"ok\":true}");
 }
 
@@ -92,7 +92,7 @@ const Session* header_session(HttpCtx* ctx)
     }
     const char* token = http_ctx_req_headers_get(ctx, "Session-Token");
     // session expiration should be handled here
-    return sessions_find(&cx->sessions, token);
+    return cx_sessions_find(cx, token);
 }
 
 // Returns NULL AND responds if no valid session is found.
@@ -105,54 +105,3 @@ const Session* middleware_session(HttpCtx* ctx)
     }
     return session;
 }
-
-void session_construct(Session* session, int64_t user_id)
-{
-    char* token = str_random(64);
-    size_t token_hash = str_fast_hash(token);
-    *session = (Session) { user_id, token, token_hash };
-}
-
-void session_destroy(Session* session)
-{
-    free(session->token);
-    *session = (Session) {
-        .user_id = 0,
-        .token = NULL,
-        .token_hash = 0,
-    };
-}
-
-void sessions_remove(SessionVec* vec, int64_t user_id)
-{
-    for (size_t i = 0; i < vec->size; ++i) {
-        if (vec->data[i].user_id == user_id) {
-            session_destroy(&vec->data[i]);
-        }
-    }
-}
-
-Session* sessions_add(SessionVec* vec, int64_t user_id)
-{
-    for (size_t i = 0; i < vec->size; ++i) {
-        if (vec->data[i].user_id == 0) {
-            session_construct(&vec->data[i], user_id);
-            return &vec->data[i];
-        }
-    }
-    Session session;
-    session_construct(&session, user_id);
-    session_vec_push(vec, session);
-    return &vec->data[vec->size - 1];
-}
-
-const Session* sessions_find(SessionVec* vec, const char* token)
-{
-    size_t token_hash = str_fast_hash(token);
-    for (size_t i = 0; i < vec->size; ++i) {
-        if (vec->data[i].token_hash == token_hash) {
-            return &vec->data[i];
-        }
-    }
-    return NULL;
-}
diff --git a/backend/src/db/db.h b/backend/src/db/db.h
index cadd373..1646fea 100644
--- a/backend/src/db/db.h
+++ b/backend/src/db/db.h
@@ -29,6 +29,3 @@ DbRes db_user_with_email(Db* db, User* user, const char* email);
 
 /// Expects `vec` to be constructed.
 DbRes db_product_all(Db* db, ProductVec* vec);
-
-__attribute__((deprecated("store in memory instead"))) DbRes
-db_cart_items_with_user_id(Db* db, CartItemVec* vec, int64_t user_id);
diff --git a/backend/src/db/db_sqlite.c b/backend/src/db/db_sqlite.c
index 41383c0..f1affc3 100644
--- a/backend/src/db/db_sqlite.c
+++ b/backend/src/db/db_sqlite.c
@@ -279,44 +279,3 @@ l0_return:
     DISCONNECT;
     return res;
 }
-
-DbRes db_cart_items_with_user_id(Db* db, CartItemVec* vec, int64_t user_id)
-{
-    static_assert(sizeof(CartItem) == 32, "model has changed");
-
-    sqlite3* connection;
-    CONNECT;
-    DbRes res;
-
-    sqlite3_stmt* stmt;
-    int sqlite_res;
-
-    sqlite_res = sqlite3_prepare_v2(connection,
-        "SELECT id, user, product, amount FROM cart_items WHERE user = ?",
-        -1,
-        &stmt,
-        NULL);
-    sqlite3_bind_int64(stmt, 1, user_id);
-
-    while ((sqlite_res = sqlite3_step(stmt)) == SQLITE_ROW) {
-        CartItem cart_item = {
-            .id = GET_INT(0),
-            .user_id = GET_INT(1),
-            .product_id = GET_INT(2),
-            .amount = GET_INT(3),
-        };
-        cart_item_vec_push(vec, cart_item);
-    }
-    if (sqlite_res != SQLITE_DONE) {
-        fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
-        res = DbRes_Error;
-        goto l0_return;
-    }
-
-    res = DbRes_Ok;
-l0_return:
-    if (stmt)
-        sqlite3_finalize(stmt);
-    DISCONNECT;
-    return res;
-}
diff --git a/backend/src/main.c b/backend/src/main.c
index 7a7771a..cde38d9 100644
--- a/backend/src/main.c
+++ b/backend/src/main.c
@@ -24,11 +24,8 @@ int main(void)
 
     Db* db = db_sqlite_new();
 
-    Cx cx = {
-        .number = 1,
-        .db = db,
-    };
-    session_vec_construct(&cx.sessions);
+    Cx cx;
+    cx_construct(&cx, db);
 
     server = http_server_new((HttpServerOpts) {
         .port = 8080,
@@ -59,6 +56,7 @@ int main(void)
     http_server_listen(server);
 
     http_server_free(server);
+    cx_destroy(&cx);
     db_sqlite_free(db);
 }