In this article, you will learn how to connect to a Couchbase cluster to create, read, update, and delete documents and how to write simple parametrized N1QL queries.
To run this prebuilt project, you will need:
git clone https://github.com/couchbase-examples/aspnet-quickstart
cd src/Org.Quickstart.API
dotnet restore
Note: Nuget packages auto restore when building the project in Visual Studio Professional 2019 and Visual Studio for Mac
The Couchbase SDK for .NET includes a nuget package called Couchbase.Extensions.DependencyInjection
which is designed for environments like ASP.NET that takes in a configuration to connect to Couchbase and automatically registers interfaces that you can use in your code to perform full CRUD (create, read, update, delete)
operations and queries against the database.
All configuration for communication with the database is stored in the appsettings.Development.json file. This includes the connection string, username, password, bucket name, colleciton name, and scope name. The default username is assumed to be admin
and the default password is assumed to be P@$$w0rd12
. If these are different in your environment you will need to change them before running the application.
With this tutorial, it's required that a database user and bucket be created prior to running the application.
For Capella users, follow the directions found on the documentation website for creating a bucket called user_profile
. Next, follow the directions for Configure Database Credentials; name it admin
with a password of P@$$w0rd12
.
Next, open the appsettings.Development.json file. Locate the ConnectionString property and update it to match your Wide Area Network name found in the Capella Portal UI Connect tab. Note that Capella uses TLS so the connection string must start with couchbases://. Note that this configuration is designed for development environments only.
"Couchbase": {
"BucketName": "user_profile",
"ScopeName": "_default",
"CollectionName": "profile",
"ConnectionString": "couchbases://yourassignedhostname.cloud.couchbase.com",
"Username": "admin",
"Password": "P@$$w0rd12",
"IgnoreRemoteCertificateNameMismatch": true,
"HttpIgnoreRemoteCertificateMismatch": true,
"KvIgnoreRemoteCertificateNameMismatch": true
}
Couchbase Capella users that do not follow these directions will get exception errors and the Swagger portal will return errors when running the APIs.
For local installation and docker users, follow the directions found on the documentation website for creating a bucket called user_profile
. Next, follow the directions for Creating a user; name it admin
with a password of P@$$w0rd12
. For this tutorial, make sure it has Full Admin
rights so that the application can create collections and indexes.
Next, open the appsettings.Development.json file and validate the configuration information matches your setup.
NOTE: For docker and local Couchbase installations, Couchbase must be installed and running on localhost (http://127.0.0.1:8091) prior to running the the ASP.NET app.
At this point the application is ready and you can run it:
dotnet run
Once the site is up and running you can launch your browser and go to the Swagger start page to test the APIs.
A simple REST API using ASP.NET and the Couchbase SDK version 3.x with the following endpoints:
We will be setting up a REST API to manage some profile documents. Our profile document will have an auto-generated GUID for its key, first and last name of the user, an email, and hashed password. For this demo we will store all profile information in just one document in a collection named profile
:
{
"pid": "b181551f-071a-4539-96a5-8a3fe8717faf",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@couchbase.com",
"password": "$2a$10$tZ23pbQ1sCX4BknkDIN6NekNo1p/Xo.Vfsttm.USwWYbLAAspeWsC"
}
As we can see, we want our user's password to be encrypted in the database too, we can achieve this simply with bcrypt, a dependency we have installed.
To begin clone the repo and open it up in the IDE of your choice to learn about how to create, read, update and delete documents in your Couchbase Server. Before we can get into the controller code, let's review the standard way you setup the Couchbase SDK in ASP.NET.
Org.Quickstart.API.Startup
:
ASP.NET has an interface called IHostApplicationLifetime
that you can add to your Configure method to help with registration of lifetime events. The Couchbase SDK provides the ICouchbaseLifetimeService
interface for handling closing the database connections when the application closes.
It's best practice to register for the ASP.NET ApplicationStop
lifetime event and call the ICouchbaseLifetimeService
Close method so that the database connection and resources are closed and removed gracefully.
The Couchbase .NET SDK will handle all communications to the database cluster, so you shouldn't need to worry about creating a pool of connections.
if (_env.EnvironmentName == "Testing")
{
//add cors policy
app.UseCors(_devSpecificOriginsName);
//setup the database once everything is setup and running integration tests need to make sure database is fully working before running,hence running Synchronously
appLifetime.ApplicationStarted.Register(() => {
var db = app.ApplicationServices.GetService<DatabaseService>();
db.CreateBucket().RunSynchronously();
db.CreateIndex().RunSynchronously();
});
} else {
//setup the database once everything is setup and running
appLifetime.ApplicationStarted.Register(async () => {
var db = app.ApplicationServices.GetService<DatabaseService>();
//warning - we assume the bucket has already been created
//if you don't create it you will get errors
//create collection to store documents in
await db.CreateCollection();
//creates the indexes for our SQL++ query
await db.CreateIndex();
});
}
The DatabaseService class is a convience class that has a method SetupDatabase which is called in the ASP.NET ApplicationStarted
lifetime event. This is used to to automatically create the bucket, collection, scope, and indexes used in this Quickstart and reads this configuration information in from the appsettings.Development.json file.
For CRUD operations we will use the Key Value operations that are built into the Couchbase SDK to create, read, update, and delete a document. Every document will need a ID (simlar to a primary key in other databases) in order to save it to the database.
Open the ProfileController.cs file found in the Controllers folder and navigate to the Post method. Let’s break this code down. First, we check that both an email and password exist in the request. Next, we get a variable that we can use to get a connection to the bucket which allows us to get another variable to connect to the collection that we want to store our document in.
if (!string.IsNullOrEmpty(request.Email) && !string.IsNullOrEmpty(request.Password))
{
var bucket = await _bucketProvider.GetBucketAsync(_couchbaseConfig.BucketName);
var collection = bucket.Collection(_couchbaseConfig.CollectionName);
var profile = request.GetProfile();
profile.Pid = Guid.NewGuid();
await collection.InsertAsync(profile.Pid.ToString(), profile);
return Created($"/api/v1/profile1/{profile.Pid}", profile);
}
else
{
return UnprocessableEntity();
}
from Post method of Controllers/ProfileController.cs
After this we use a helper method built into the ProfileCreateRequestCommand class that returns a new Profile object:
public Profile GetProfile()
{
return new Profile
{
Pid = new Guid(),
FirstName = this.FirstName,
LastName = this.LastName,
Email = this.Email,
Password = this.Password
};
}
from Models/ProfileCreateRequestCommand.cs
The Pid
that we’re saving into the account object is a unique key. This is what we will use for our Key
to the document.
Rather than saving the password in the account object as plain text, we hash it with Bcrypt in the setter of the Profile object:
private string _password;
public string Password
{
get
{
return _password;
}
set
{
_password = BCrypt.Net.BCrypt.HashPassword(value);
}
}
from Models/Profile.cs
Our profile
document is ready to be persisted to the database. We create an async call to the collection
using the InsertAsync
method and then return the document saved and the result all as part of the same object back to the user.
Navigate to the GetById method in the ProfileController.cs file in the Controllers folder. We only need the profile ID or our Key
from the user to retrieve a particular profile document using a basic key-value operation which is passed in the method signature as a Guid. Since we created the document with a unique key we can use that key to find the document in the scope and collection it is stored in.
var bucket = await _bucketProvider.GetBucketAsync(_couchbaseConfig.BucketName);
var scope = bucket.Scope(_couchbaseConfig.ScopeName);
var collection = scope.Collection(_couchbaseConfig.CollectionName);
var result = await collection.GetAsync(id.ToString());
from GetById method of Controllers/ProfileController.cs
If the document wasn't found in the database we return the NotFound method which results in a 404 status code.
Update a Profile by Profile ID
Now let's navigate to the Update method of the ProfileController class. We first look up the existing document and make sure it exists, if it does not, return a 500 level error code and message: "Cannot update: document not found".
Then, the entire document gets replaced except for the document key and the pid
field. The ProfileUpdateRequestCommand has a helper method that returns a Profile from the request object.
Finally, we create an async call to the collection
using the ReplaceAsync
method and then return the document saved and the result just as we did in the previous endpoint.
var bucket = await _bucketProvider.GetBucketAsync(_couchbaseConfig.BucketName);
var collection = bucket.Collection(_couchbaseConfig.CollectionName);
var result = await collection.GetAsync(request.Pid.ToString());
var profile = result.ContentAs<Profile>();
var updateResult = await collection.ReplaceAsync<Profile>(request.Pid.ToString(), request.GetProfile());
from Update method of Controllers/ProfileController.cs
Navigate to the Delete method in the ProfileController class. We only need the Key
or id from the user to delete a document using a basic key-value operation.
var bucket = await _bucketProvider.GetBucketAsync(_couchbaseConfig.BucketName);
var collection = bucket.Collection(_couchbaseConfig.CollectionName);
await collection.RemoveAsync(id.ToString());
from Delete method of Controllers/ProfileController.cs
N1QL is a powerful query language based on SQL, but designed for structed and flexible JSON documents. We will use a N1QL query to search for profiles with Skip, Limit, and Search options.
Navigate to the List method in the ProfileController class. This endpoint is different from all of the others because it makes the N1QL query rather than a key-value operation. This means more overhead because the query engine is involved. We did create an index specific for this query, so it should be performant.
First, the method signature uses the [FromQuery] annotation to destructure the query to get the individual skip
, limit
, and firstNameSearch
values and store them in the ProfileRequestQuery object.
Then, we build our N1QL query using the parameters we just built.
Finally, we pass that query
to the cluster.QueryAsync
method and return the result.
Take notice of the N1QL syntax and how it targets the bucket
.scope
.collection
.
var cluster = await _clusterProvider.GetClusterAsync();
var query = $"SELECT p.* FROM {_couchbaseConfig.BucketName}.{_couchbaseConfig.ScopeName}.{_couchbaseConfig.CollectionName}
p WHERE lower(p.firstName) LIKE '%{request.Search.ToLower()}%' OR lower(p.lastName) LIKE '%{request.Search.ToLower()}%'
LIMIT {request.Limit} OFFSET {request.Skip}";
var results = await cluster.QueryAsync<Profile>(query);
var items = await results.Rows.ToListAsync<Profile>();
if (items.Count == 0)
return NotFound();
return Ok(items)
from List method of Controllers/ProfileController.cs
To run the standard integration tests, use the following commands:
cd ../Org.Quickstart.IntegrationTests/
dotnet restore
dotnet test
This project was based on the standard ASP.NET Template project and the default weather controller was removed. The HealthCheckController is provided as a santity check and is used in our unit tests.
A fully list of nuget packages are referenced below:
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.3.0" />
<PackageReference Include="CouchbaseNetClient" Version="3.2.8" />
<PackageReference Include="Couchbase.Extensions.DependencyInjection" Version="3.2.8" />
Setting up a basic REST API in ASP.NET with Couchbase is fairly simple, this project when run with Couchbase Server 7 installed creates a bucket in Couchbase, an index for our parameterized N1QL query, and showcases basic CRUD operations needed in most applications.