0

I’m building a local web chat feature using CivetWeb (C++), SQLite, and a frontend in plain HTML/JS. When a message is sent, the server log shows:

Attempting to send message: product_id=3, sender=1, receiver=Admin, message=1

So the /send_message endpoint is triggered correctly. However, the new message does not appear on the chat page (message_feature.html), even though I can see it was inserted in the log.

Here’s what I have:

Frontend (message_feature.html)

async function loadConversation() {
  const res = await fetch(`/api/conversation?product_id=${productId}`);
  if (!res.ok) return;
  const msgs = await res.json();
  const box = document.getElementById('messages');
  box.innerHTML = '';
  msgs.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
  msgs.forEach(m => {
    const div = document.createElement('div');
    div.className = 'msg ' + (m.sender === 'Admin' ? 'received' : 'sent');
    div.innerHTML = `<div>${m.message}</div>`;
    box.appendChild(div);
  });
}

Backend (C++ with CivetWeb)

// POST /send_message
mg_set_request_handler(ctx, "/send_message", [](mg_connection *conn, void *) -> int {
    if (strcmp(mg_get_request_info(conn)->request_method, "POST") != 0)
        return 405;

    const struct mg_request_info *ri = mg_get_request_info(conn);
    int len = (int)ri->content_length;
    if (len <= 0 || len > 4096) len = 4096;
    std::vector<char> post_data(len + 1);
    int n = mg_read(conn, post_data.data(), len);
    post_data[n] = '\0';

    char product_id_str[32] = {0}, sender[128] = {0}, receiver[128] = {0}, message[1024] = {0};
    mg_get_var(post_data.data(), n, "product_id", product_id_str, sizeof(product_id_str));
    mg_get_var(post_data.data(), n, "sender", sender, sizeof(sender));
    mg_get_var(post_data.data(), n, "receiver", receiver, sizeof(receiver));
    mg_get_var(post_data.data(), n, "message", message, sizeof(message));

    fprintf(stderr, "Attempting to send message: product_id=%s, sender=%s, receiver=%s, message=%s\n",
            product_id_str, sender, receiver, message);

    sqlite3 *db = safe_open("C:/Users/priva/OneDrive/Desktop/supplierbuyer/products.db");
    const char *sql = "INSERT INTO messages (product_id, sender, receiver, message) VALUES (?, ?, ?, ?);";
    sqlite3_stmt *stmt = nullptr;
    sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
    sqlite3_bind_int(stmt, 1, atoi(product_id_str));
    sqlite3_bind_text(stmt, 2, sender, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, receiver, -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 4, message, -1, SQLITE_TRANSIENT);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    sqlite3_close(db);

    mg_printf(conn, "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n");
    return 201;
}, nullptr);

// GET /api/conversation
mg_set_request_handler(ctx, "/api/conversation", [](mg_connection *conn, void *) -> int {
    const mg_request_info *ri = mg_get_request_info(conn);
    char product_id_str[32] = {0};
    if (ri->query_string)
        mg_get_var(ri->query_string, strlen(ri->query_string), "product_id", product_id_str, sizeof(product_id_str));
    int product_id = atoi(product_id_str);

    sqlite3 *db = safe_open("C:/Users/priva/OneDrive/Desktop/supplierbuyer/products.db");
    const char *sql = R"(
        SELECT sender, receiver, message, strftime('%Y-%m-%d %H:%M:%S', created_at)
        FROM messages WHERE product_id = ? ORDER BY id ASC;
    )";
    sqlite3_stmt *stmt = nullptr;
    sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
    sqlite3_bind_int(stmt, 1, product_id);

    std::string json = "[";
    bool first = true;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        if (!first) json += ",";
        first = false;
        const char *sender = (const char *)sqlite3_column_text(stmt, 0);
        const char *receiver = (const char *)sqlite3_column_text(stmt, 1);
        const char *message = (const char *)sqlite3_column_text(stmt, 2);
        const char *created_at = (const char *)sqlite3_column_text(stmt, 3);
        char buf[512];
        snprintf(buf, sizeof(buf),
            R"({"sender":"%s","receiver":"%s","message":"%s","created_at":"%s"})",
            sender, receiver, message, created_at);
        json += buf;
    }
    json += "]";
    sqlite3_finalize(stmt);
    sqlite3_close(db);

    mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n%s", json.c_str());
    return 200;
}, nullptr);

Messages are saved in the same products.db file under a messages table. I also checked that loadConversation() runs after sending a message, but nothing shows in the chat box.

Here’s the relevant part of my message_feature.html code:

document.getElementById('chatForm').addEventListener('submit', async e => {
  e.preventDefault();
  
  const fd = new FormData(e.target);
  const userRes = await fetch('/api/get_current_user');
  const { username } = await userRes.json();
  fd.append('sender', username);

  const body = new URLSearchParams(fd);

  // Send the message to the backend
  const res = await fetch('/send_message', { 
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: body
  });

  // I wait for the response before refreshing messages
  if (res.ok) {
    document.getElementById('message').value = '';
    loadConversation();  // ✅ Called right after successful response
  } else {
    alert('Failed to send message.');
  }
});

So loadConversation() is only called after the POST request returns successfully (status 201). However, even then, the chat box doesn’t update — it still shows no messages, even though /send_message logs the insert on the server.

I also tried adding a small delay before calling loadConversation() (using setTimeout(loadConversation, 1000)), but it made no difference — the messages still don’t show up.


Question:

Even though messages are inserted (the log shows they’re sent), they don’t appear when fetched from /api/conversation. What could cause the frontend to not display them — is it a problem with the query, JSON response, or DOM rendering?


What I Tried

  • I verified that the /send_message endpoint works — it logs

    Attempting to send message: product_id=3, sender=1, receiver=Admin, message=1
    

    every time I click Send.

  • I checked that the message is inserted into the SQLite database (messages table) manually using DB Browser.

  • I confirmed that /api/conversation?product_id=3 returns a JSON array, but sometimes it’s empty or missing the new message.

  • I also added console.log(msgs) inside loadConversation() in message_feature.html — the console shows an empty array [] even though the message exists in the DB.

  • I tried calling loadConversation() immediately after sending (and also with a 3-second interval), but it still doesn’t update the chat box.

What I Expected

I expected that after sending a message, the new message would:

  1. Be inserted into the messages table.
  2. Appear immediately in the chat box under the correct side (sent or received).
  3. Also appear in the admin view (messageadmin.html) after a few seconds (since it refreshes automatically).

Full project source code:
👉 https://github.com/misbagas/supplierbuyer

3
  • 1
    "loadConversation() runs after sending a message"... how soon after? Are you waiting for the response from /send_message first? Please include enough code to represent the application logic flow Commented Oct 17 at 5:26
  • Yes — I’m calling loadConversation() after waiting for the /send_message request to finish. Commented Oct 21 at 11:44
  • I think you might need to include more of your C++ code, particularly the parts obfuscated by ... Commented Oct 21 at 22:44

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.