CRUD Operation To CouchDB Via Rest API In ASP.NET Core Application

Introduction

 
This article demonstrates CRUD operation to CouchDB via HTTP-based REST API in an ASP.NET Core application.
 
CouchDB is an open source NoSQL document database where data are stored in JSON based document format in simple key/value pair manner.
 
CouchDB falls into the AP category of CAP theorem (Availability and Partition Tolerance), whereas MongoDB falls into the CP category of CAP theorem (Consistency and Partition Tolerance).
 
 
Each document in CouchDB has a document-level unique identifier (_id) as well as a revision (_rev) number for each change that is made into the database.
 
Prerequisites
  • The CouchDB Server is properly configured and running at http://<<dbserveripaddress>>:5789/.
  • Database is present in server where we will perform CRUD operation, in my case it is “my-couch-db”.

Performing CRUD operation to Couch DB from a ASP.NET Core application

 
I created ASP.NET Core Web API project and created an API controller called “CourseContoller”. From controller I am calling repository class to perform DB operation. Here, I will perform operation on course enrollment as an example.
 
 
The Approach
  1. Add couch db configuration into appsettings.json and install NuGet package.
  2. Create Models for handling input from User.
  3. Create ICouchRepository.cs and implement it to CouchRepository.cs
  4. Do Rest Call to CouchDB from CouchRepository.cs.
  5. Register Repository into stratup.cs.
  6. Modify/Create api controller to take the input from user and call repository method to perform CRUD activity.
  7. Test from Postman and validate against CouchDB.
Step 1 - Modify appsettings.json
 
Before performing any operation, let's added my couch db configuration into appsettings.json.
  1. "CouchDB": {  
  2.     "URL": "http://127.0.0.1:5984",  
  3.     "DbName": "my-couch-db",  
  4.     "User": "Username:Password"  
  5.   }  
Install below NuGet package.
  1. <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />   
Step 2 - Create Models
 
EnrollCourse.cs
  1. public class EnrollCourse  
  2. {  
  3.     public string Name { getset; }  
  4.     public string EmailAddress { getset; }  
  5.     public string CourseName { getset; }  
  6.     public DateTime EnrolledOn { getset; }  
  7.     public DateTime UpdatedOn { getset; }  
  8. }  
EnrollInfo.cs
  1. public class EnrollInfo  
  2. {  
  3.     [JsonProperty("_id")]  
  4.     public string Id { getset; }  
  5.     [JsonProperty("_rev")]  
  6.     public string Rev { getset; }  
  7.     public string Name { getset; }  
  8.     public string EmailAddress { getset; }  
  9.     public string CourseName { getset; }  
  10.     public DateTime EnrolledOn { getset; }  
  11.     public DateTime UpdatedOn { getset; }  
  12. }  
UpdateEnroll.cs
  1. public class UpdateEnroll  
  2. {  
  3.     public string Id { getset; }  
  4.     [JsonIgnore]  
  5.     public string Rev { getset; }  
  6.     public string Name { getset; }  
  7.     public string EmailAddress { getset; }  
  8.     public string CourseName { getset; }  
  9.     public DateTime EnrolledOn { getset; }  
  10.     public DateTime UpdatedOn { getset; }  
  11. }  
HttpClientResponse.cs
  1. public class HttpClientResponse  
  2. {  
  3.     public bool IsSuccess { getset; }  
  4.     public dynamic SuccessContentObject { getset; }  
  5.     public string FailedReason { getset; }  
  6. }  
  7. public class SavedResult  
  8. {  
  9.     public string Id { getset; }  
  10.     public string Rev { getset; }  
  11. }  
Step 3
 
Create ICouchRepository.cs and implement it to CouchRepository.cs
 
Step 4 - Rest Call to CouchDB from CouchRepository.cs
 
Let's create DBContext folder and add ICouchRepository.cs and CouchRepository.cs into it. From CouchRepository , we will do REST call to CouchDB to perform CRUD operation.
  • GET http://{CouchDB_hostname_or_IP}:{Port}/{couchDbName}/{_id}
  • POST http://{CouchDB_hostname_or_IP}:{Port}/{couchDbName}
  • PUT http://{CouchDB_hostname_or_IP}:{Port}/{couchDbName}/{_id}/?rev={_rev} 
  • DELETE http://{CouchDB_hostname_or_IP}:{Port}/{couchDbName}/{_id}/?rev={_rev}
ICouchRepository.cs 
  1. public interface ICouchRepository  
  2. {  
  3.     Task<HttpClientResponse> PostDocumentAsync(EnrollCourse enrollCourse);  
  4.     Task<HttpClientResponse> PutDocumentAsync(UpdateEnroll update);  
  5.     Task<HttpClientResponse> GetDocumentAsync(string id);  
  6.     Task<HttpClientResponse> DeleteDocumentAsync(string id, string rev);  
  7. }  
 CouchRepository.cs
  1. public class CouchRepository : ICouchRepository  
  2.  {  
  3.      private readonly string _couchDbUrl;  
  4.      private readonly string _couchDbName;  
  5.      private readonly string _couchDbUser;  
  6.      private readonly IConfiguration _configuration;  
  7.      private readonly IHttpClientFactory _clientFactory;  
  8.      public CouchRepository(IConfiguration configuration, IHttpClientFactory clientFactory)  
  9.      {  
  10.   
  11.          _configuration = configuration;  
  12.          _clientFactory = clientFactory;  
  13.          _couchDbUrl = this._configuration["CouchDB:URL"];  
  14.          _couchDbName = this._configuration["CouchDB:DbName"];  
  15.          _couchDbUser = this._configuration["CouchDB:User"];  
  16.      }
  17.   
  18.      public async Task<HttpClientResponse> DeleteDocumentAsync(string id, string rev)  
  19.      {  
  20.          HttpClientResponse response = new HttpClientResponse();  
  21.          var dbClient = DbHttpClient();  
  22.   
  23.          //CouchDB URL : DELETE http://{hostname_or_IP}:{Port}/{couchDbName}/{_id}/?rev={_rev}  
  24.          var dbResult = await dbClient.DeleteAsync(_couchDbName + "/" + id + "?rev=" + rev);  
  25.   
  26.          if (dbResult.IsSuccessStatusCode)  
  27.          {  
  28.              response.IsSuccess = true;  
  29.              response.SuccessContentObject = await dbResult.Content.ReadAsStringAsync();  
  30.          }  
  31.          else  
  32.          {  
  33.              response.IsSuccess = false;  
  34.              response.FailedReason = dbResult.ReasonPhrase;  
  35.          }  
  36.          return response;  
  37.      }  
  38.   
  39.      public async Task<HttpClientResponse> GetDocumentAsync(string id)  
  40.      {  
  41.          HttpClientResponse response = new HttpClientResponse();  
  42.          var dbClient = DbHttpClient();  
  43.   
  44.          //CouchDB URL : GET http://{hostname_or_IP}:{Port}/{couchDbName}/{_id}  
  45.          var dbResult = await dbClient.GetAsync(_couchDbName + "/" + id);  
  46.   
  47.          if (dbResult.IsSuccessStatusCode)  
  48.          {  
  49.              response.IsSuccess = true;  
  50.              response.SuccessContentObject = await dbResult.Content.ReadAsStringAsync();  
  51.          }  
  52.          else  
  53.          {  
  54.              response.IsSuccess = false;  
  55.              response.FailedReason = dbResult.ReasonPhrase;  
  56.          }  
  57.          return response;  
  58.      }  
  59.   
  60.      public async Task<HttpClientResponse> PostDocumentAsync(EnrollCourse enrollCourse)  
  61.      {  
  62.          HttpClientResponse response = new HttpClientResponse();  
  63.          var dbClient = DbHttpClient();  
  64.          var jsonData = JsonConvert.SerializeObject(enrollCourse);  
  65.          var httpContent = new StringContent(jsonData, Encoding.UTF8, "application/json");  
  66.   
  67.          //CouchDB URL : POST http://{hostname_or_IP}:{Port}/{couchDbName}  
  68.          var postResult = await dbClient.PostAsync(_couchDbName, httpContent).ConfigureAwait(true);  
  69.   
  70.          if (postResult.IsSuccessStatusCode)  
  71.          {  
  72.              response.IsSuccess = true;  
  73.              response.SuccessContentObject = await postResult.Content.ReadAsStringAsync();  
  74.          }  
  75.          else  
  76.          {  
  77.              response.IsSuccess = false;  
  78.              response.FailedReason = postResult.ReasonPhrase;  
  79.          }  
  80.          return response;  
  81.      }  
  82.   
  83.      public async Task<HttpClientResponse> PutDocumentAsync(UpdateEnroll update)  
  84.      {  
  85.          HttpClientResponse response = new HttpClientResponse();  
  86.          var dbClient = DbHttpClient();  
  87.          var updateToDb = new  
  88.          {  
  89.              update.Name,  
  90.              update.EmailAddress,  
  91.              update.CourseName,  
  92.              update.EnrolledOn,  
  93.              update.UpdatedOn  
  94.          };  
  95.          var jsonData = JsonConvert.SerializeObject(updateToDb);  
  96.          var httpContent = new StringContent(jsonData, Encoding.UTF8, "application/json");  
  97.   
  98.          //CouchDB URL : PUT http://{hostname_or_IP}:{Port}/{couchDbName}/{_id}/?rev={_rev}  
  99.          var putResult = await dbClient.PutAsync(_couchDbName + "/" +   
  100.                                                    update.Id +   
  101.                                                    "?rev=" + update.Rev, httpContent).ConfigureAwait(true);  
  102.   
  103.          if (putResult.IsSuccessStatusCode)  
  104.          {  
  105.              response.IsSuccess = true;  
  106.              response.SuccessContentObject = await putResult.Content.ReadAsStringAsync();  
  107.          }  
  108.          else  
  109.          {  
  110.              response.IsSuccess = false;  
  111.              response.FailedReason = putResult.ReasonPhrase;  
  112.          }  
  113.          return response;  
  114.      }  
  115.   
  116.      private HttpClient DbHttpClient()  
  117.      {  
  118.          var httpClient = this._clientFactory.CreateClient();  
  119.          httpClient.DefaultRequestHeaders.Accept.Clear();  
  120.          httpClient.DefaultRequestHeaders.Clear();  
  121.   
  122.          httpClient.BaseAddress = new Uri(_couchDbUrl);  
  123.          var dbUserByteArray = Encoding.ASCII.GetBytes(_couchDbUser);  
  124.          httpClient.DefaultRequestHeaders.Add("Authorization""Basic " + Convert.ToBase64String(dbUserByteArray));  
  125.          return httpClient;  
  126.      }  
  127.  }  
Step 5 - Register Repository into stratup.cs
 
Add below code into ConfigureServices method.
  1. services.AddHttpClient();  
  2. services.AddTransient<ICouchRepository, CouchRepository>();  
Step 6 - Modify API Controller to take the input from user and call respective repository methods
 
Let's modify the controller code as like below. 
  1. [Route("api/[controller]")]  
  2. [ApiController]  
  3. public class CourseController : ControllerBase  
  4. {  
  5.     private readonly ILogger<CourseController> _logger;  
  6.     private readonly ICouchRepository _couchRepository;  
  7.     public CourseController(ILogger<CourseController> logger, ICouchRepository couchRepository)  
  8.     {  
  9.         _logger = logger;  
  10.         _couchRepository = couchRepository;  
  11.     }  
  12.   
  13.     [HttpGet("{id}")]  
  14.     public async Task<IActionResult> Get(string id)  
  15.     {  
  16.         var result = await _couchRepository.GetDocumentAsync(id);  
  17.         if (result.IsSuccess)  
  18.         {  
  19.             var sResult = JsonConvert.DeserializeObject<EnrollInfo>(result.SuccessContentObject);  
  20.             return new OkObjectResult(sResult);  
  21.         }  
  22.         return new NotFoundObjectResult("NotFound");  
  23.     }  
  24.   
  25.     [HttpPost]  
  26.     public async Task<IActionResult> PostAsync([FromBody] EnrollCourse enrollCourse)  
  27.     {  
  28.         enrollCourse.EnrolledOn = DateTime.Now;  
  29.         var result = await _couchRepository.PostDocumentAsync(enrollCourse);  
  30.         if (result.IsSuccess)  
  31.         {  
  32.             var sResult = JsonConvert.DeserializeObject<SavedResult>(result.SuccessContentObject);  
  33.             return new CreatedResult("Success", sResult);  
  34.         }  
  35.   
  36.         return new UnprocessableEntityObjectResult(result.FailedReason);  
  37.     }  
  38.   
  39.     [HttpPut]  
  40.     public async Task<IActionResult> PutAsync([FromBody] UpdateEnroll enrollCourse)  
  41.     {  
  42.         var httpClientResponse = await _couchRepository.GetDocumentAsync(enrollCourse.Id);  
  43.   
  44.         if (httpClientResponse.IsSuccess)  
  45.         {  
  46.             EnrollInfo existingInfo = JsonConvert.DeserializeObject<EnrollInfo>(httpClientResponse.SuccessContentObject);  
  47.             enrollCourse.Rev = existingInfo.Rev;  
  48.             enrollCourse.Name = String.IsNullOrEmpty(enrollCourse.Name) ? existingInfo.Name : enrollCourse.Name;  
  49.             enrollCourse.CourseName = String.IsNullOrEmpty(enrollCourse.CourseName) ? existingInfo.CourseName : enrollCourse.CourseName;  
  50.             enrollCourse.EmailAddress = String.IsNullOrEmpty(enrollCourse.EmailAddress) ? existingInfo.EmailAddress : enrollCourse.EmailAddress;  
  51.             enrollCourse.EnrolledOn = enrollCourse.EnrolledOn == DateTime.MinValue ? existingInfo.EnrolledOn : enrollCourse.EnrolledOn;  
  52.             enrollCourse.UpdatedOn = enrollCourse.UpdatedOn == DateTime.MinValue ? DateTime.Now : enrollCourse.UpdatedOn;  
  53.   
  54.             var result = await _couchRepository.PutDocumentAsync(enrollCourse);  
  55.             if (httpClientResponse.IsSuccess)  
  56.             {  
  57.                 var sResult = JsonConvert.DeserializeObject<SavedResult>(result.SuccessContentObject);  
  58.                 return new CreatedResult("Success", sResult);  
  59.             }  
  60.             return new UnprocessableEntityObjectResult(result.FailedReason);  
  61.         }  
  62.   
  63.         return new UnprocessableEntityObjectResult(httpClientResponse.FailedReason);  
  64.     }  
  65.   
  66.     [HttpDelete("{id}")]  
  67.     public async Task<IActionResult> DeleteAsync(string id)  
  68.     {  
  69.         var httpClientResponse = await _couchRepository.GetDocumentAsync(id);  
  70.           
  71.         if (httpClientResponse.IsSuccess)  
  72.         {  
  73.             EnrollInfo sResult = JsonConvert.DeserializeObject<EnrollInfo>(httpClientResponse.SuccessContentObject);  
  74.             httpClientResponse = await _couchRepository.DeleteDocumentAsync(id, sResult.Rev);  
  75.             if (httpClientResponse.IsSuccess)  
  76.             {  
  77.                 return new OkObjectResult("Success");  
  78.             }  
  79.             return new NotFoundObjectResult("Failed to delete the document");  
  80.         }  
  81.   
  82.         return new NotFoundObjectResult("Document not exists.");  
  83.     }  
  84. }  
Step 7 - Test from Postman and validate against CouchDB
 
Excellent! Now we are ready to test from Postman and validate the same against CouchDB to see the changes are reflecting or not.
 
I logged in into my couch db server http://127.0.0.1:5984/_utils/#login and check the document. 
 
Create Document
 
Postman vs CouchDB Snapshot
 
 
Get Document
 
Postman Snapshot
 
Update Document
 
Postman vs CouchDB Snapshot
 
 
Delete Document
 
Postman vs CouchDB Snapshot
 
 

Conclusion

 
In this article, we have seen how to do CRUD operations for a document into CouchDB via Rest Call from a .NET core application. Hope you find this information useful!
 
Happy Learning!