Connecting to a MongoDB Server#

This page will show how to asynchronously connect to a MongoDB server from a C program.

Including the amongoc APIs#

To make all amongoc APIs visible, include the everything-header:

Headers#
1#include <amongoc/amongoc.h>  // Make all APIs visible
2
3#include <stdio.h>
4#include <stdlib.h>

Creating an Event Loop#

The first thing to do before connecting to a program is to create an event loop. amongoc includes a basic default single-threaded event loop suitable for basic programs:

Create a default event loop#
 9int main(void) {
10    amongoc_loop   loop;
11    amongoc_status status = amongoc_default_loop_init(&loop);
12    amongoc_if_error (status, msg) {
13        fprintf(stderr, "Failed to prepare the event loop: %s\n", msg);
14        return 2;
15    }

Creating a Client#

A client is created and initialized asynchronously and is associated with an event loop, done using amongoc_client_new():

Create a client emitter#
18amongoc_emitter em = amongoc_client_new(&loop, "mongodb://localhost:27017");

Hint

The URI string above is a simple default for a locally-running server without TLS enabled. You should replace it with your own if your URI is running in a different location.

Create the Continuation#

An amongoc_emitter object is not a client. Rather, it is an object that represents an asynchronous operation. To get the client, we need to attach a continuation:

Continuation prototype#
7amongoc_box on_connect(amongoc_box userdata, amongoc_status* status, amongoc_box result);
Attach the continuation with amongoc_then()#
20em = amongoc_then(em, &on_connect);

The continuation function on_connect looks like this:

Continuation#
30amongoc_box on_connect(amongoc_box userdata, amongoc_status* status, amongoc_box result) {
31    // We don't use the userdata
32    (void)userdata;
33    // Check for an error
34    amongoc_if_error (*status, msg) {
35        fprintf(stderr, "Error while connecting to server: %s\n", msg);
36    } else {
37        printf("Successfully connected!\n");
38        amongoc_client* client;
39        amongoc_box_take(client, result);
40        // `client` now stores a valid client. We don't do anything else, so just delete it:
41        amongoc_client_delete(client);
42    }
43    amongoc_box_destroy(result);
44    return amongoc_nil;
45}

Create the Operation State#

When we are done defining the entire asynchronous control flow, we need to convert the amongoc_emitter to an operation and enqueue it with the event loop. There are several ways to do this, but the simplest is amongoc_detach_start():

Launch the operation#
22amongoc_detach_start(em);

This will enqueue the associated program with the event loop, and will automatically release resources associated with the operation when the operation completes.

Note

amongoc_detach_start() will “consume” the emitter object. The em emitter object is “poisoned” and cannot be manipulated further.

Run the Program#

The amongoc_detach_start() call only enqueues the operation, but does not execute it. We need to actually give control of the main thread to the event loop. This is done using amongoc_default_loop_run():

Run the program#
23amongoc_default_loop_run(&loop);

Clean up the Event Loop#

After amongoc_default_loop_run() returns, there is no more pending work in the event loop, so we are done. Before returning, we need to destroy the event loop object:

Destroy the event loop#
25amongoc_default_loop_destroy(&loop);

The Whole Program#

Here is the complete program:

connect.example.c#
 1#include <amongoc/amongoc.h>  // Make all APIs visible
 2
 3#include <stdio.h>
 4#include <stdlib.h>
 5// end:headers
 6
 7amongoc_box on_connect(amongoc_box userdata, amongoc_status* status, amongoc_box result);
 8
 9int main(void) {
10    amongoc_loop   loop;
11    amongoc_status status = amongoc_default_loop_init(&loop);
12    amongoc_if_error (status, msg) {
13        fprintf(stderr, "Failed to prepare the event loop: %s\n", msg);
14        return 2;
15    }
16
17    // Initiate a connection
18    amongoc_emitter em = amongoc_client_new(&loop, "mongodb://localhost:27017");
19    // Set the continuation
20    em = amongoc_then(em, &on_connect);
21    // Run the program
22    amongoc_detach_start(em);
23    amongoc_default_loop_run(&loop);
24    // Clean up
25    amongoc_default_loop_destroy(&loop);
26    return 0;
27}
28
29// on_connect def
30amongoc_box on_connect(amongoc_box userdata, amongoc_status* status, amongoc_box result) {
31    // We don't use the userdata
32    (void)userdata;
33    // Check for an error
34    amongoc_if_error (*status, msg) {
35        fprintf(stderr, "Error while connecting to server: %s\n", msg);
36    } else {
37        printf("Successfully connected!\n");
38        amongoc_client* client;
39        amongoc_box_take(client, result);
40        // `client` now stores a valid client. We don't do anything else, so just delete it:
41        amongoc_client_delete(client);
42    }
43    amongoc_box_destroy(result);
44    return amongoc_nil;
45}
46// on_connect end