Using MongoDB with ASP.NET Web API

MongoDB is a popular NoSQL database that makes it a great backend for Web APIs which lend themselves towards a document store rather than a relational store. In this blog we show how you can use MongoDB with ASP.NET Web API to build an ApiController with support for HTTP GET, PUT, POST, and DELETE. Our sample controller is a simple contact manager Web API which builds on many of the same concepts as in the tutorial “Creating a Web API that Supports CRUD Operations” so I would recommend that you skim that for background.

In particular, the URI patterns are very similar to that of the tutorial:

Action HTTP Method Relative URI
Get a list of all contacts GET /api/contacts
Get a filtered list of all contacts GET /api/contacts?$top=2
Get a contacts by ID GET /api/contacts/id
Create a new contact POST /api/contacts
Update a contact PUT /api/contacts/id
Delete a contact DELETE /api/contacts/id


However, there are two differences worth noting:

  1. We host the sample ApiController in selfhost with a base URI of http://localhost:8080
  2. We have added support for OData-style queries on GET requests so that you can filter the set of contacts returned using $top, $skip, etc.

But I am getting ahead of myself – let’s get back to building the controller…

Note: Please see List of ASP.NET Web API and HttpClient Samples for the complete sample solution. Note that you still have to set up the MongoDB as described in the next section.


Before you start you obviously need to download and install MongoDB. I followed the quick start instructions which has all the information you need and got me going in 10 minutes or so.

Second you need the MongoDB driver for C#. Here we use the MongoDB C# driver provided by 10gen and which is available as a NuGet package.

As an optional third step once your MongoDB installation has been set up you can use a tool like MongoVUE which allows you to browse and interact directly with the data in a MongoDB database.

Defining the Contact Type

We use the same basic Contact type used in “Creating a Web API that Supports CRUD Operations” but decorate it with an BsonIdAttribute (see the Mongo C# driver reference documentation for details) indicating which field is the id:

   1: public class Contact
   2: {
   3:     [BsonId]
   4:     public string Id { get; set; }
   6:     public string Name { get; set; }
   8:     public string Phone { get; set; }
  10:     public string Email { get; set; }
  12:     public DateTime LastModified { get; set; }
  13: }

Using MongoVUE we can see how Contact instances show up in the MongoDB database. The sample Contact data we use in this sample looks something like this


Defining the Contact Repository

We use the common repository pattern to interact with the backend data source so the next step is to define the shape of the repository. We do this by defining an IContactRepository interface which we then implement so that it targets MongoDB. First, the interface is defined like this:

   1: public interface IContactRepository
   2: {
   3:     IEnumerable<Contact> GetAllContacts();
   5:     Contact GetContact(string id);
   7:     Contact AddContact(Contact item);
   9:     bool RemoveContact(string id);
  11:     bool UpdateContact(string id, Contact item);
  12: }

Implementing the Repository

It’s when implementing IContactRepository that we use the MongoDB C# driver API to connect to the data and to do the various CRUD operations defined by IContactRepository. First we set up the connection to database and get a Contact collection. By default we use the connection string “mongodb://localhost:27017” pointing at the localhost database we set up above.

For the purpose of this sample we also reset the database and add some default entries so we have something to start with. Note that the classes MongoServer, MongoDatabase, and MongoCollection<T> are all thread-safe so they can be used simultaneously by separate threads.

   1: public ContactRepository(string connection)
   2: {
   3:     if (string.IsNullOrWhiteSpace(connection))
   4:     {
   5:         connection = "mongodb://localhost:27017";
   6:     }
   8:     _server = MongoServer.Create(connection);
   9:     _database = _server.GetDatabase("Contacts", SafeMode.True);
  10:     _contacts = _database.GetCollection<Contact>("contacts");
  12:     // Reset database and add some default entries
  13:     _contacts.RemoveAll();
  14:     for (int index = 1; index < 5; index++)
  15:     {
  16:         Contact contact1 = new Contact
  17:         {
  18:             Email = string.Format("test{0}", index),
  19:             Name = string.Format("test{0}", index),
  20:             Phone = string.Format("{0}{0}{0} {0}{0}{0} {0}{0}{0}{0}", index)
  21:         };
  22:         AddContact(contact1);
  23:     }
  24: }

We now add the actual CRUD implementations as follows:

   1: public IEnumerable<Contact> GetAllContacts()
   2: {
   3:     return _contacts.FindAll();
   4: }
   6: public Contact GetContact(string id)
   7: {
   8:     IMongoQuery query = Query.EQ("_id", id);
   9:     return _contacts.Find(query).FirstOrDefault();
  10: }
  12: public Contact AddContact(Contact item)
  13: {
  14:     item.Id = ObjectId.GenerateNewId().ToString();
  15:     item.LastModified = DateTime.UtcNow;
  16:     _contacts.Insert(item);
  17:     return item;
  18: }
  20: public bool RemoveContact(string id)
  21: {
  22:     IMongoQuery query = Query.EQ("_id", id);
  23:     SafeModeResult result = _contacts.Remove(query);
  24:     return result.DocumentsAffected == 1;
  25: }
  27: public bool UpdateContact(string id, Contact item)
  28: {
  29:     IMongoQuery query = Query.EQ("_id", id);
  30:     item.LastModified = DateTime.UtcNow;
  31:     IMongoUpdate update = Update
  32:         .Set("Email", item.Email)
  33:         .Set("LastModified", DateTime.UtcNow)
  34:         .Set("Name", item.Name)
  35:         .Set("Phone", item.Phone);
  36:     SafeModeResult result = _contacts.Update(query, update);
  37:     return result.UpdatedExisting;
  38: }

Creating the Contact Controller

That’s it for the repository so we can now implement the actual ApiController. We base the implementation on the common pattern for supporting GET, PUT, POST, and DELETE as follows:

   1: public class ContactsController : ApiController
   2: {
   3:     private static readonly IContactRepository _contacts = new ContactRepository();
   5:     public IQueryable<Contact> Get()
   6:     {
   7:         return _contacts.GetAllContacts().AsQueryable();
   8:     }
  10:     public Contact Get(string id)
  11:     {
  12:         Contact contact = _contacts.GetContact(id);
  13:         if (contact == null)
  14:         {
  15:             throw new HttpResponseException(HttpStatusCode.NotFound);
  16:         }
  18:         return contact;
  19:     }
  21:     public Contact Post(Contact value)
  22:     {
  23:         Contact contact = _contacts.AddContact(value);
  24:         return contact;
  25:     }
  27:     public void Put(string id, Contact value)
  28:     {
  29:         if (!_contacts.UpdateContact(id, value))
  30:         {
  31:             throw new HttpResponseException(HttpStatusCode.NotFound);
  32:         }
  33:     }
  35:     public void Delete(string id)
  36:     {
  37:         if (!_contacts.RemoveContact(id))
  38:         {
  39:             throw new HttpResponseException(HttpStatusCode.NotFound);
  40:         }
  41:     }
  42: }

Note: We use IQueryable<Contact> as the return type of the Get() method. This enables automatic query-support using OData query syntax including $top and $skip. That is, if you use a URI like “/api/contacts?$top=2” with a “?$top=2” query component then you will only get the first two entries back.

Hosting the Controller

Now that we have the controller we can either host it in ASP or as selfhost. Here we use selfhost to host the controller in a simple console application but it would work exactly the same if hosted in ASP. As usual we create a HttpSelfHostConfiguration, add a route, then create a server, and start it:

   1: static void Main(string[] args)
   2: {
   3:     HttpSelfHostServer server = null;
   4:     try
   5:     {
   6:         // Set up server configuration
   7:         HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://localhost:8080");
   9:         config.Routes.MapHttpRoute(
  10:             name: "DefaultApi",
  11:             routeTemplate: "api/{controller}/{id}",
  12:             defaults: new { id = RouteParameter.Optional }
  13:         );
  15:         // Create server
  16:         server = new HttpSelfHostServer(config);
  18:         // Start listening
  19:         server.OpenAsync().Wait();
  21:         Console.WriteLine("Hit ENTER to exit...");
  22:         Console.ReadLine();
  24:     }
  25:     finally
  26:     {
  27:         if (server != null)
  28:         {
  29:             // Stop listening
  30:             server.CloseAsync().Wait();
  31:         }
  32:     }
  33: }

Note: In order to successfully start the selfhost server you have to run as admin (or configure http.sys with the appropriate URI prefix using netsh).

Trying out the Controller

When running the controller can be accessed using any HTTP client. In the full sample I show how to use HttpClient to do GET and POST but Fiddler is often very useful for trying out various combinations of GET, PUT, POST, and DELETE by using the Composer tab manually to create requests. For example, you can create a POST request like this to insert a new Contact and then hit Execute:


Similarly you can create a GET request to ask for the two first entries which will yield a result like this where the response body contains the first two Contacts:


As mentioned above, when you modify the data you can track the contents of the MongoDB database using MongoVUE.

Have fun!


Comments (14)

  1. Johan says:

    Some questions:

    1. Can you have a default $top value? Returning ALL the documents in the database seems like a very bad idea.

    2. Can you return a more web friendly viewmodel instead of the actual database object and still use IQueryable<>? In most cases you have a lot of data in the documents that you don't wan't to expose to the api.

  2. 1. There's no out-of-the-box way to have a default $top value, but it's fairly easy to implement with an action filter (see code below). And on the operation you want the default $top value, you'd apply something like [DefaultQueryCompValues(DefaultTop = 10)]

    2. You can always create some data transfer objects (DTOs) and map the values from the DB to the DTO prior to returning them (_contacts.GetAllContacts().Select(c => ToDTO(c)).AsQueryable())

       public class DefaultQueryCompValuesAttribute : ActionFilterAttribute


           private int defaultTop = -1;

           public int DefaultTop


               get { return this.defaultTop; }

               set { this.defaultTop = value; }


           public override void OnActionExecuting(HttpActionContext actionContext)


               if (this.DefaultTop >= 0)


                   HttpRequestMessage request = actionContext.Request;

                   NameValueCollection queryParams = HttpUtility.ParseQueryString(request.RequestUri.Query);

                   if (string.IsNullOrEmpty(queryParams.Get("$top")))


                       UriBuilder uriBuilder = new UriBuilder(request.RequestUri);

                       if (string.IsNullOrEmpty(uriBuilder.Query))


                           uriBuilder.Query = "$top=" + this.DefaultTop;




                           uriBuilder.Query = uriBuilder.Query + "&$top=" + this.DefaultTop;


                       request.RequestUri = uriBuilder.Uri;





  3. Authentication says:

    Hi Henrik,

    I'm currently trying out your tutorial, looks great so far. I'm wondering how to go about authenticating calls to the API. I understand how this is done using forms, but here I'm not sure what the best approach is. Would you need to set up a secure connection using https and then pass the username/password on each call?


  4. Mike Stokes says:

    Nice job Henrik… You've provided a nice simple example that follows real-world needs as opposed to so many of Microsoft's "samples" which don't provide any level of separation of concerns.

  5. Chad Moran says:

    return _contacts.GetAllContacts().AsQueryable();

    Doesn't that pull down ALL contacts?  I don't think AsQueryable does what you think it does.

  6. Lenny says:

    There is cool alternative REST-framework at

  7. Daniel Harman says:

    Pulling all the docs to support AsQueryable is not going to scale! Mongo isn't good at paging requests, but at the very least I'd recommend using fluent mongo here as it exposes a Queryable<> that maps to native mongo commands for skip and take.

  8. Alex says:

    Why is Id string and not an ObjectId in the controller methods?  I have been trying this with Custom Model Binders and I can't get it to work so I am wondering whether there is some functionality I am missing.  Works great with MVC.

  9. DJP says:

    Have you tried Couchbase Server?

  10. mare says:

    Wow, what a bad implementation of GetAllContacts(). He actually goes and returns all the contacts in DB and then does IQueryable on top of that. Try doing that with hundreds of thousands of records. Really lame. How does one get to post this kind of stuff on MSDN blogs? Also, no notion of view models or DTOs, he's just sending back business object over the wire.

    Hopefully ppl won't try to follow anything from this post..

  11. Greg says:


    Thanks for the article, very good for starters like me.

    however, in order to remove an entry, I had to use  Query.EQ("_id", new ObjectId(id)) instead of Query.EQ("_id", id). Otherwise it returned DocumentsAffected == 0

    Any idea ??


  12. John Fly says:

    This seems to actual let MongoDb handle the query and not return the full set to the app


           public IQueryable<Form> Get()


               return _collection.AsQueryable();


  13. Sadiq says:

    Any further thought to write WebAPi and MongoDB articles? especially best practices

  14. Smith Cole says:

    Wow!!! Very good post. I am really so happy to read your post. I have no idea about MongoDB with ASP.NET Web API. I only knew about to read . But now getting little knowledge. your post is so quality, so always read your every post. thanks to share.

Skip to main content