# Getting Started with MongoDB and C

# Getting Started with MongoDB and C

In this article we'll install the MongoDB C driver on macOS, and use this driver to write some sample console applications that can interact with your MongoDB data by performing basic CRUD operations. We'll use Visual Studio Code to type in the code and the command line to compile and run our programs. If you want to try it out now, all source code is in the GitHub repository.

## Table of contents

- Prerequisites
- Installation: VS Code, C Extensions, Xcode
- Installing the C Driver
- Hello World MongoDB!
- Setting up the client and pinging MongoDB Atlas
- Compiling and running our code
- Connecting to the database and listing all collections
- Creating a JSON object in C
- CRUD in MongoDB using the C driver
- Querying data
- Inserting a new document
- Deleting a document
- Updating a document
- Wrapping up

## Prerequisites

1. A MongoDB Atlas account with a cluster created.
2. The sample dataset loaded into the Atlas cluster (or you can modify the sample code to use your own database and collection).
3. Your machine’s IP address whitelisted. Note: You can add 0.0.0.0/0 as the IP address, which should allow access from any machine. This setting is not recommended for production use.

## VS Code, C extensions, Xcode

1. We will use Visual Studio Code, available in macOS, Windows, and Linux, because it has official support for C code. Just download and install the appropriate version. 
2. We need the C extensions, which will be suggested when you open a C file for the first time. You can also open extensions and search for "C/C++" and install them. This will install several extensions: C/C++, C/C++ Themes, and CMake.
3. The last step is to make sure we have a C compiler. For that, either install Xcode from the Mac App Store or run in a terminal:

```bash
$ xcode-select --install
```

Although we can use CMake to build our C applications (and you have detailed instructions on how to do it), we'll use VS Code to type our code in and the terminal to build and run our programs.

## Installing the C driver

In macOS, if we have the package manager homebrew installed (which you should), then we just open a terminal and type in:

```bash
$ brew install mongo-c-driver
```

You can also download the source code and build the driver, but using brew is just way more convenient. 

## Configuring VS Code extensions

To make autocomplete work in VS Code, we need to change the extension's config to make sure it "sees" these new libraries installed. We want to change our INCLUDE_PATH to allow both IntelliSense to check our code while typing it and be able to build our app from VS Code.

To do that, from VS Code, open the `.vscode` hidden folder, and then click on c_cpp_properties.json and add these lines:

```javascript
{
    "configurations": 
        {
            "name": "Mac",
            "includePath": [
                "/usr/local/include/libbson-1.0/**",
                "/usr/local/include/libmongoc-1.0/**",
                "${workspaceFolder}/**"
            ],
...
}
   ]
}
```

Now, open tasks.json and add these lines to the args array:

```
"-I/usr/local/include/libmongoc-1.0",
"-I/usr/local/include/libbson-1.0",
"-lmongoc-1.0",
"-lbson-1.0",`
```

With these, we're telling VS Code where to find the MongoDB C libraries so it can compile and check our code as we type. 

# Hello World MongoDB! 

The source code is available on [GitHub. 

## Setting up the client and pinging MongoDB Atlas

Let’s start with a simple program that connects to the MongoDB Atlas cluster and pings the server. For that, we need to get the connection string (URI) to the cluster and add it in our code. The best way is to create a new environment variable with the key “MONGODB_URI” and value the connection string (URI). It’s a good practice to keep the connection string decoupled from the code, but in this example, for simplicity, we'll have our connection string hardcoded.

We include the MongoDB driver and send an "echo" command from our `main` function. This example shows us how to initialize the MongoDB C client, how to create a command, manipulate JSON (in this case, BCON, BSON C Object Notation), send a command, process the response and error, and release any memory used.

 ```c
 // hello_mongo.c
#include 

int main(int argc, char const *argv]) {
  // your MongoDB URI connection string
  const char *uri_string = "mongodb+srv://";
  // MongoDB URI created from above string
  mongoc_uri_t *uri;
  // MongoDB Client, used to connect to the DB
  mongoc_client_t *client;
  // Command to be sent, and reply
  bson_t *command, reply;
  // Error management
  bson_error_t error;
  // Misc
  char *str;
  bool retval;

  /*
   * Required to initialize libmongoc's internals
   */
  mongoc_init();

  /*
   * Optionally get MongoDB URI from command line
   */
  if (argc > 1) {
    uri_string = argv[1];
  }

  /*
   * Safely create a MongoDB URI object from the given string
   */
  uri = mongoc_uri_new_with_error(uri_string, &error);
  if (!uri) {
    fprintf(stderr,
            "failed to parse URI: %s\n"
            "error message:       %s\n",
            uri_string, error.message);
    return EXIT_FAILURE;
  }

  /*
   * Create a new client instance, here we use the uri we just built
   */
  client = mongoc_client_new_from_uri(uri);
  if (!client) {
    return EXIT_FAILURE;
  }

  /*
   * Register the application name so we can track it in the profile logs
   * on the server. This can also be done from the URI (see other examples).
   */
  mongoc_client_set_appname(client, "connect-example");

  /*
   * Do work. This example pings the database and prints the result as JSON
   * BCON == BSON C Object Notation
   */
  command = BCON_NEW("ping", BCON_INT32(1));

  // we run above command on our DB, using the client. We get reply and error
  // (if any)
  retval = mongoc_client_command_simple(client, "admin", command, NULL, &reply,
                                        &error);

  // mongoc_client_command_simple returns false and sets error if there are
  // invalid arguments or a server or network error.
  if (!retval) {
    fprintf(stderr, "%s\n", error.message);
    return EXIT_FAILURE;
  }

  // if we're here, there's a JSON response
  str = bson_as_json(&reply, NULL);
  printf("%s\n", str);

  /*
   * Clean up memory
   */
  bson_destroy(&reply);
  bson_destroy(command);
  bson_free(str);

  /*
   * Release our handles and clean up libmongoc
   */

  mongoc_uri_destroy(uri);
  mongoc_client_destroy(client);
  mongoc_cleanup();

  return EXIT_SUCCESS;
}
 ```

## Compiling and running our code

Although we can use way more sophisticated methods to compile and run our code, as this is just a C source code file and we're using just a few dependencies, I'll just compile from command line using good ol' gcc:

 ```bash
 gcc -o hello_mongoc hello_mongoc.c \                         
  -I/usr/local/include/libbson-1.0
  -I/usr/local/include/libmongoc-1.0 \
  -lmongoc-1.0 -lbson-1.0
 ```

To run the code, just call the built binary:

 ```bash
 ./hello_mongo
 ```

In the [repo that accompanies this post, you'll find a shell script that builds and runs all examples in one go.

## Connecting to the database and listing all collections

Now that we have the skeleton of a C app, we can start using our database. In this case, we'll connect to the database` sample_mflix`, and we'll list all collections there.

After connecting to the database, we list all connections with a simple` for` loop after getting all collection names with `mongoc_database_get_collection_names`.

```c
if ((collection_names =
           mongoc_database_get_collection_names(database, &error))) {
    for (i = 0; collection_namesi]; i++) {
      printf("%s\n", collection_names[i]);
    }

  }
```

The complete sample follows.

```c
// list_collections.c
#include 

int main(int argc, char const *argv[]) {
  // your MongoDB URI connection string
  const char *uri_string = "mongodb+srv://";

  // MongoDB URI created from above string
  mongoc_uri_t *uri;
  // MongoDB Client, used to connect to the DB
  mongoc_client_t *client;

  // Error management
  bson_error_t error;

  mongoc_database_t *database;
  mongoc_collection_t *collection;
  char **collection_names;
  unsigned i;

  /*
   * Required to initialize libmongoc's internals
   */
  mongoc_init();

  /*
   * Safely create a MongoDB URI object from the given string
   */
  uri = mongoc_uri_new_with_error(uri_string, &error);
  if (!uri) {
    fprintf(stderr,
            "failed to parse URI: %s\n"
            "error message:       %s\n",
            uri_string, error.message);
    return EXIT_FAILURE;
  }

  /*
   * Create a new client instance, here we use the uri we just built
   */
  client = mongoc_client_new_from_uri(uri);
  if (!client) {
    return EXIT_FAILURE;
  }

  /*
   * Register the application name so we can track it in the profile logs
   * on the server. This can also be done from the URI (see other examples).
   */
  mongoc_client_set_appname(client, "connect-example");

  /*
   * Get a handle on the database "db_name" and collection "coll_name"
   */
  database = mongoc_client_get_database(client, "sample_mflix");

// getting all collection names, here we're not passing in any options
  if ((collection_names = mongoc_database_get_collection_names_with_opts(
           database, NULL, &error))) {
  
    for (i = 0; collection_names[i]; i++) {
      printf("%s\n", collection_names[i]);
    }

  } else {
    fprintf(stderr, "Error: %s\n", error.message);
    return EXIT_FAILURE;
  }

  /*
   * Release our handles and clean up libmongoc
   */

  mongoc_uri_destroy(uri);
  mongoc_client_destroy(client);
  mongoc_cleanup();

  return EXIT_SUCCESS;
}
```

If we compile and run it, we'l get this output:

```
$ ./list_collections                          
sessions
users
theaters
movies
comments
```

## Creating a JSON object in C

Being a document-based database, creating JSON documents is crucial for any application that interacts with MongoDB. Being this is C code, we don't use JSON. Instead, we use BCON ([BSON C Object Notation, as mentioned above). To create a new document, we call `BCON_NEW`, and to convert it into a C string, we call `bson_as_canonical_extended_json.`

```c
// bcon.c
// https://mongoc.org/libmongoc/current/tutorial.html#using-bcon
#include 

// Creating the JSON doc:
/*
{
   born : ISODate("1906-12-09"),
   died : ISODate("1992-01-01"),
   name : {
      first : "Grace",
      last : "Hopper"
   },
   languages :  "MATH-MATIC", "FLOW-MATIC", "COBOL" ],
   degrees: [ { degree: "BA", school: "Vassar" },
              { degree: "PhD", school: "Yale" } ]
}
*/

int main(int argc, char *argv[]) {
  struct tm born = {0};
  struct tm died = {0};
  bson_t *document;
  char *str;

  born.tm_year = 6;
  born.tm_mon = 11;
  born.tm_mday = 9;

  died.tm_year = 92;
  died.tm_mon = 0;
  died.tm_mday = 1;

  //   document = BCON_NEW("born", BCON_DATE_TIME(mktime(&born) * 1000),
  //                       "died", BCON_DATE_TIME(mktime(&died) * 1000),
  //                       "name", "{",
  //                         "first", BCON_UTF8("Grace"),
  //                         "last", BCON_UTF8("Hopper"),
  //                       "}",
  //                       "languages", "[",
  //                         BCON_UTF8("MATH-MATIC"),
  //                         BCON_UTF8("FLOW-MATIC"),
  //                         BCON_UTF8("COBOL"),
  //                       "]",
  //                       "degrees", "[",
  //                         "{", "degree", BCON_UTF8("BA"), "school",
  //                         BCON_UTF8("Vassar"), "}",
  //                         "{", "degree", BCON_UTF8("PhD"),"school",
  //                         BCON_UTF8("Yale"), "}",
  //                       "]");

  document = BCON_NEW("born", BCON_DATE_TIME(mktime(&born) * 1000), "died",
                      BCON_DATE_TIME(mktime(&died) * 1000), "name", "{",
                      "first", BCON_UTF8("Grace"), "last", BCON_UTF8("Hopper"),
                      "}", "languages", "[", BCON_UTF8("MATH-MATIC"),
                      BCON_UTF8("FLOW-MATIC"), BCON_UTF8("COBOL"), "]",
                      "degrees", "[", "{", "degree", BCON_UTF8("BA"), "school",
                      BCON_UTF8("Vassar"), "}", "{", "degree", BCON_UTF8("PhD"),
                      "school", BCON_UTF8("Yale"), "}", "]");

  /*
   * Print the document as a JSON string.
   */
  str = bson_as_canonical_extended_json(document, NULL);
  printf("%s\n", str);
  bson_free(str);

  /*
   * Clean up allocated bson documents.
   */
  bson_destroy(document);
  return 0;
}
```

## CRUD in MongoDB using the C driver

Now that we've covered the basics of connecting to MongoDB, let's have a look at how to manipulate data.

## Querying data

Probably the most used function of any database is to retrieve data fast. In most use cases, we spend way more time accessing data than inserting or updating that same data. In this case, after creating our MongoDB client connection, we call `mongoc_collection_find_with_opts`, which will find data based on a query we can pass in. Once we have results, we can iterate through the returned cursor and do something with that data:

```c
// All movies from 1984!
  BSON_APPEND_INT32(query, "year", 1984);
  cursor = mongoc_collection_find_with_opts(collection, query, NULL, NULL);

  while (mongoc_cursor_next(cursor, &query)) {
    str = bson_as_canonical_extended_json(query, NULL);
    printf("%s\n", str);
    bson_free(str);
  }
```

The complete sample follows.

```c
// find.c
#include "URI.h"
#include 

int main(int argc, char const *argv[]) {
  // your MongoDB URI connection string
  const char *uri_string = MY_MONGODB_URI;
  // MongoDB URI created from above string
  mongoc_uri_t *uri;
  // MongoDB Client, used to connect to the DB
  mongoc_client_t *client;

  // Error management
  bson_error_t error;

  mongoc_collection_t *collection;
  char **collection_names;
  unsigned i;

  // Query object
  bson_t *query;
  mongoc_cursor_t *cursor;

  char *str;

  /*
   * Required to initialize libmongoc's internals
   */
  mongoc_init();

  /*
   * Safely create a MongoDB URI object from the given string
   */
  uri = mongoc_uri_new_with_error(uri_string, &error);
  if (!uri) {
    fprintf(stderr,
            "failed to parse URI: %s\n"
            "error message:       %s\n",
            uri_string, error.message);
    return EXIT_FAILURE;
  }

  /*
   * Create a new client instance, here we use the uri we just built
   */
  client = mongoc_client_new_from_uri(uri);
  if (!client) {
    puts("Error connecting!");
    return EXIT_FAILURE;
  }

  /*
   * Register the application name so we can track it in the profile logs
   * on the server. This can also be done from the URI (see other examples).
   */
  mongoc_client_set_appname(client, "connect-example");

  /*
   * Get a handle on the database "db_name" and collection "coll_name"
   */
  collection = mongoc_client_get_collection(client, "sample_mflix", "movies");

  query = bson_new();

  // All movies from 1984!
  BSON_APPEND_INT32(query, "year", 1984);
  cursor = mongoc_collection_find_with_opts(collection, query, NULL, NULL);

  while (mongoc_cursor_next(cursor, &query)) {
    str = bson_as_canonical_extended_json(query, NULL);
    printf("%s\n", str);
    bson_free(str);
  }

  /*
   * Release our handles and clean up libmongoc
   */

  bson_destroy(query);

  mongoc_collection_destroy(collection);
  mongoc_uri_destroy(uri);
  mongoc_client_destroy(client);
  mongoc_cleanup();

  return EXIT_SUCCESS;
}
````

## Inserting a new document

OK, we know how to read data, but how about inserting fresh data in our MongoDB database? It's easy! We just create a BSON document to be inserted and call  `mongoc_collection_insert_one.`

```c
doc = bson_new();
  bson_oid_init(&oid, NULL);
  BSON_APPEND_OID(doc, "_id", &oid);
  BSON_APPEND_UTF8(doc, "name", "My super new picture");

  if (!mongoc_collection_insert_one(collection, doc, NULL, NULL, &error)) {
    fprintf(stderr, "%s\n", error.message);
  }
```

The complete sample follows.

```c
// insert.c
#include "URI.h"
#include 

int main(int argc, char const *argv[]) {
  // your MongoDB URI connection string
  const char *uri_string = MY_MONGODB_URI;
  // MongoDB URI created from above string
  mongoc_uri_t *uri;
  // MongoDB Client, used to connect to the DB
  mongoc_client_t *client;

  // Error management
  bson_error_t error;

  mongoc_collection_t *collection;
  char **collection_names;
  unsigned i;

  // Object id and BSON doc
  bson_oid_t oid;
  bson_t *doc;

  char *str;

  /*
   * Required to initialize libmongoc's internals
   */
  mongoc_init();

  /*
   * Safely create a MongoDB URI object from the given string
   */
  uri = mongoc_uri_new_with_error(uri_string, &error);
  if (!uri) {
    fprintf(stderr,
            "failed to parse URI: %s\n"
            "error message:       %s\n",
            uri_string, error.message);
    return EXIT_FAILURE;
  }

  /*
   * Create a new client instance, here we use the uri we just built
   */
  client = mongoc_client_new_from_uri(uri);
  if (!client) {
    return EXIT_FAILURE;
  }

  /*
   * Register the application name so we can track it in the profile logs
   * on the server. This can also be done from the URI (see other examples).
   */
  mongoc_client_set_appname(client, "connect-example");

  /*
   * Get a handle on the database "db_name" and collection "coll_name"
   */
  collection = mongoc_client_get_collection(client, "sample_mflix", "movies");

  doc = bson_new();
  bson_oid_init(&oid, NULL);
  BSON_APPEND_OID(doc, "_id", &oid);
  BSON_APPEND_UTF8(doc, "name", "My super new picture");

  if (!mongoc_collection_insert_one(collection, doc, NULL, NULL, &error)) {
    fprintf(stderr, "%s\n", error.message);
  } else {
    printf("Document inserted!");
    /*
     * Print the document as a JSON string.
     */
    str = bson_as_canonical_extended_json(doc, NULL);
    printf("%s\n", str);
    bson_free(str);
  }

  /*
   * Release our handles and clean up libmongoc
   */

  mongoc_collection_destroy(collection);
  mongoc_uri_destroy(uri);
  mongoc_client_destroy(client);
  mongoc_cleanup();

  return EXIT_SUCCESS;
}
````

## Deleting a document

To delete a document, we call `mongoc_collection_delete_one.` We need to pass in a document containing the query to restrict the documents we want to find and delete.

```c
doc = bson_new();
  BSON_APPEND_OID(doc, "_id", &oid);

  if (!mongoc_collection_delete_one(collection, doc, NULL, NULL, &error)) {
    fprintf(stderr, "Delete failed: %s\n", error.message);
  }
```

The complete sample follows.

```c
// delete.c
#include "URI.h"
#include 

int main(int argc, char const *argv[]) {
  // your MongoDB URI connection string
  const char *uri_string = MY_MONGODB_URI;
  // MongoDB URI created from above string
  mongoc_uri_t *uri;
  // MongoDB Client, used to connect to the DB
  mongoc_client_t *client;

  // Error management
  bson_error_t error;

  mongoc_collection_t *collection;
  char **collection_names;
  unsigned i;

  // Object id and BSON doc
  bson_oid_t oid;
  bson_t *doc;

  char *str;

  /*
   * Required to initialize libmongoc's internals
   */
  mongoc_init();

  /*
   * Safely create a MongoDB URI object from the given string
   */
  uri = mongoc_uri_new_with_error(uri_string, &error);
  if (!uri) {
    fprintf(stderr,
            "failed to parse URI: %s\n"
            "error message:       %s\n",
            uri_string, error.message);
    return EXIT_FAILURE;
  }

  /*
   * Create a new client instance, here we use the uri we just built
   */
  client = mongoc_client_new_from_uri(uri);
  if (!client) {
    return EXIT_FAILURE;
  }

  /*
   * Register the application name so we can track it in the profile logs
   * on the server. This can also be done from the URI (see other examples).
   */
  mongoc_client_set_appname(client, "connect-example");

  /*
   * Get a handle on the database "db_name" and collection "coll_name"
   */
  collection = mongoc_client_get_collection(client, "sample_mflix", "movies");

  // Let's insert one document in this collection!
  doc = bson_new();
  bson_oid_init(&oid, NULL);
  BSON_APPEND_OID(doc, "_id", &oid);
  BSON_APPEND_UTF8(doc, "name", "My super new picture");

  if (!mongoc_collection_insert_one(collection, doc, NULL, NULL, &error)) {
    fprintf(stderr, "%s\n", error.message);
  } else {
    printf("Document inserted!");
    /*
     * Print the document as a JSON string.
     */
    str = bson_as_canonical_extended_json(doc, NULL);
    printf("%s\n", str);
    bson_free(str);
  }

  bson_destroy(doc);

  // Delete the inserted document!

  doc = bson_new();
  BSON_APPEND_OID(doc, "_id", &oid);

  if (!mongoc_collection_delete_one(collection, doc, NULL, NULL, &error)) {
    fprintf(stderr, "Delete failed: %s\n", error.message);
  } else {
    puts("Document deleted!");
  }

  /*
   * Release our handles and clean up libmongoc
   */

  mongoc_collection_destroy(collection);
  mongoc_uri_destroy(uri);
  mongoc_client_destroy(client);
  mongoc_cleanup();

  return EXIT_SUCCESS;
}
````

## Updating a document

Finally, to update a document, we need to provide the query to find the document to update and a document with the fields we want to change.

```c
query = BCON_NEW("_id", BCON_OID(&oid));
update =
        BCON_NEW("$set", "{", "name", BCON_UTF8("Super new movie was boring"),
                 "updated", BCON_BOOL(true), "}");

if (!mongoc_collection_update_one(collection, query, update, NULL, NULL,
                                      &error)) {
   fprintf(stderr, "%s\n", error.message);
}
```

The complete sample follows.

```c
// update.c
#include "URI.h"
#include 

int main(int argc, char const *argv[]) {
  // your MongoDB URI connection string
  const char *uri_string = MY_MONGODB_URI;
  // MongoDB URI created from above string
  mongoc_uri_t *uri;
  // MongoDB Client, used to connect to the DB
  mongoc_client_t *client;

  // Error management
  bson_error_t error;

  mongoc_collection_t *collection;
  char **collection_names;
  unsigned i;

  // Object id and BSON doc
  bson_oid_t oid;
  bson_t *doc;

  // document to update and query to find it
  bson_t *update = NULL;
  bson_t *query = NULL;
  char *str;

  /*
   * Required to initialize libmongoc's internals
   */
  mongoc_init();

  /*
   * Safely create a MongoDB URI object from the given string
   */
  uri = mongoc_uri_new_with_error(uri_string, &error);
  if (!uri) {
    fprintf(stderr,
            "failed to parse URI: %s\n"
            "error message:       %s\n",
            uri_string, error.message);
    return EXIT_FAILURE;
  }

  /*
   * Create a new client instance, here we use the uri we just built
   */
  client = mongoc_client_new_from_uri(uri);
  if (!client) {
    return EXIT_FAILURE;
  }

  /*
   * Register the application name so we can track it in the profile logs
   * on the server. This can also be done from the URI (see other examples).
   */
  mongoc_client_set_appname(client, "connect-example");

  /*
   * Get a handle on the database "db_name" and collection "coll_name"
   */
  collection = mongoc_client_get_collection(client, "sample_mflix", "movies");

  // we create a new BSON Document
  doc = bson_new();
  bson_oid_init(&oid, NULL);
  BSON_APPEND_OID(doc, "_id", &oid);
  BSON_APPEND_UTF8(doc, "name", "My super new movie");

  // Then we insert it in the movies collection
  if (!mongoc_collection_insert_one(collection, doc, NULL, NULL, &error)) {
    fprintf(stderr, "%s\n", error.message);
  } else {
    printf("Document inserted!\n");
    /*
     * Print the document as a JSON string.
     */
    str = bson_as_canonical_extended_json(doc, NULL);
    printf("%s\n", str);
    bson_free(str);

    // now we search for that document to update it
    query = BCON_NEW("_id", BCON_OID(&oid));
    update =
        BCON_NEW("$set", "{", "name", BCON_UTF8("Super new movie was boring"),
                 "updated", BCON_BOOL(true), "}");

    if (!mongoc_collection_update_one(collection, query, update, NULL, NULL,
                                      &error)) {
      fprintf(stderr, "%s\n", error.message);
    } else {
      printf("Document edited!\n");
      str = bson_as_canonical_extended_json(update, NULL);
      printf("%s\n", str);
    }
  }

  /*
   * Release our handles and clean up libmongoc
   */

  if (doc) {
    bson_destroy(doc);
  }
  if (query) {
    bson_destroy(query);
  }
  if (update) {
    bson_destroy(update);
  }

  mongoc_collection_destroy(collection);
  mongoc_uri_destroy(uri);
  mongoc_client_destroy(client);
  mongoc_cleanup();

  return EXIT_SUCCESS;
}
````

## Wrapping up

With this article, we covered the installation of the MongoDB C driver, configuring VS Code as our editor and setting up other tools. Then, we created a few console applications that connect to MongoDB Atlas and perform basic CRUD operations.

[Get more information about the C driver, and to try this code, the easiest way would be to register for a free MongoDB account. We can't wait to see what you build next!