Build A Todo API With .NET And Appwrite
Tags: dotnet, appwrite, opensource, csharp
Published: 2023-07-17 14:32:19

Last week, [Appwrite launched its .NET  SDK in Beta](https://github.com/appwrite/appwrite/discussions/5820) to support C# and .NET in building server-side apps more efficiently and productively. The announcement had me very excited because .NET and C# are among my absolute favourite technologies!

To help showcase how Appwrite can be used be used with .NET, I built a basic [Todo API](https://dotnet-appwrite-todo.azurewebsites.net/swagger) project with all fundamental CRUD functionalities. Let's learn how you can build such a project and leverage **.NET 7** and **Appwrite** to build powerful server-side applications.

![Steve Harvey excited](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lobr3o6mrf8kl762xfu.gif)

## Setup Your Appwrite Instance ☁️

First things first, we need to setup an Appwrite instance to build this project.

> If you're trying **Appwrite** for the first time, [Appwrite](https://appwrite.io) is an **open-source Backend-as-a-Service** platform that provides you with pre-built APIs for fundamental services such as databases, storage, authentication, cloud functions, etc., that you need to build any application you'd like. You can either self-host Appwrite using Docker, or sign up for our managed service, **Appwrite Cloud**.

### Step 1: Create an Appwrite Project

The simplest and quickest way to get your Appwrite project up and running is to [sign up for Appwrite Cloud](https://cloud.appwrite.io) (you can login if you already have an account).

![Sign Up For Appwrite Cloud](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gbq1w5yiiumlpflyvvdo.png)

After that, you can go ahead and create your first project by clicking on the `Create Project` button.

![Create a new Appwrite project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/secvsrys2isqg5uur88z.png)

## Step 2: Create an API Key

Once you have created your Appwrite project, you must go ahead and create an **API Key**. You can do so by clicking on the `API Key` button visible in the **Integrate With Your Server** section.

![Create an API Key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9v4gyupmhdlxlugmi3vd.png)

While creating your API Key, make sure to select the `documents.read` and `documents.write` scopes.

## Step 3: Create a Database

Once your API key is created, you need to setup a database for the API to communicate with. Go to the `Databases` tab on the left, and create a database, followed by a collection. Within this collection, we need to create the following attributes:

| Attribute ID | Type    | Size | Default Value | Required | Array |
|--------------|---------|------|---------------|----------|-------|
| description  | string  | 255  |               | Yes      | No    |
| isCompleted  | boolean |      |               | Yes      | No    |

![Database Attributes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smjhzvn9xn8p0xq5hank.png)

These attributes, `description` and `isCompleted` will contain the details of the todos that we create and consume for ourselves.

Once all these steps are complete, grab your **Project Id**, **Database Id**, **Collection Id**, and **API Key**, as we will need these when we create the .NET API.

## Create A .NET 7 Minimal API 🧑‍💻

Now that our Appwrite project is configured properly, we are ready to build the .NET Web API. 

> Please make sure to [install the .NET 7 SDK](https://dotnet.microsoft.com/en-us/download) on your system, if you haven't already.

### Step 1: Create a Minimal API project

In order to create a .NET minimal API project, you must open your terminal and run the following command:

```sh
dotnet new web -o TodoApi
```

This will generate a minimal API project in the `./TodoApi` directory using the scaffolding offered along with the .NET CLI.

You can go ahead and enter the directory and open it in your preferred code editor.

### Step 2: Install Appwrite SDK and Setup the Project Configuration

Now that a project has been created, let's install the Appwrite SDK: 

```sh
dotnet add package Appwrite
```

This will install the [Appwrite 0.4.2 NuGet package](https://www.nuget.org/packages/Appwrite/0.4.2), which includes the current latest version of the SDK.

After that, you can open the `appsettings.json` file and add the following (after the `Logging` and `AllowedHosts` fields):

```json
  "Appwrite": {
    "Project_Id": "[PROJECT_ID]",
    "Database_Id": "[DATABASE_ID]",
    "Collection_Id": "[COLLECTION_ID]",
    "Api_Key": "[API_KEY]"
  }
```

This is where we will need the Project Id, Database Id, Collection Id, and API key you grabbed from your Appwrite project earlier.

### Step 3: Create the Todo Model

Once the project is setup and configured, first let's create a model for the Todos themselves. Create a new class `Todo` within the project as follows:

```csharp
public class Todo
{
    public string Description { get; set; }
    public bool IsCompleted { get; set; }
}
```

This model corresponds with the attributes we created in the collection in our Appwrite database, and will be especially important when we add or update any todos in the database.

### Step 4: Write the Appwrite CRUD functions

Now that we're all set, let's create the API endpoints and CRUD functions themselves. Head over to the `Program.cs` file and replace it with the following code:

```csharp
// Relevant using directives

using Appwrite;
using Appwrite.Services;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Get necessary Appwrite configuration from appsettings.json

var projectId = builder.Configuration["Appwrite:Project_Id"];
var apiKey = builder.Configuration["Appwrite:Api_Key"];
var databaseId = builder.Configuration["Appwrite:Database_Id"];
var collectionId = builder.Configuration["Appwrite:Collection_Id"];

// Initialize object for Appwrite Client

var client = new Client()
    .SetEndpoint("https://cloud.appwrite.io/v1")
    .SetProject(projectId)
    .SetKey(apiKey);

// Initialize object for Databases service APIs

var databases = new Databases(client);

// Create CRUD API endpoints

app.MapGet("/todos", async () =>
{
    try
    {
        var todos = await databases.ListDocuments(
            databaseId: databaseId,
            collectionId: collectionId);

        return Results.Ok(todos);
    }
    catch (AppwriteException e)
    {
        return Results.NotFound(new Dictionary<string, string> { { "message", e.Message } });
    }
})
.WithName("GetAllTodos");

app.MapGet("/todos/{id}", async (string id) =>
{
    try
    {
        var todo = await databases.GetDocument(
            databaseId: databaseId,
            collectionId: collectionId,
            documentId: id);

        return Results.Ok(todo);
    }
    catch(AppwriteException e)
    {
        return Results.NotFound(new Dictionary<string,string> { { "message", e.Message } });
    }
})
.WithName("GetTodo");

app.MapPost("/todos", async (Todo todo) =>
{
    try
    {
        var document = await databases.CreateDocument(
            databaseId: databaseId,
            collectionId: collectionId,
            documentId: ID.Unique(),
            data: todo);

        return Results.Created($"/todos/{document.Id}", document);
    }
    catch (AppwriteException e)
    {
        return Results.BadRequest(new Dictionary<string, string> { { "message", e.Message } });
    }
})
.WithName("CreateTodo");

app.MapPut("/todos/{id}", async (string id, Todo todo) =>
{
    try
    {
        var document = await databases.UpdateDocument(
            databaseId: databaseId,
            collectionId: collectionId,
            documentId: id,
            data: todo);

        return Results.NoContent();
    }
    catch (AppwriteException e)
    {
        return Results.BadRequest(new Dictionary<string, string> { { "message", e.Message } });
    }
})
.WithName("UpdateTodo");

app.MapDelete("/todos/{id}", async (string id) =>
{
    try
    {
        var document = await databases.DeleteDocument(
            databaseId: databaseId,
            collectionId: collectionId,
            documentId: id);

        return Results.Ok(document);
    }
    catch (AppwriteException e)
    {
        return Results.NotFound(new Dictionary<string, string> { { "message", e.Message } });
    }
})
.WithName("DeleteTodo");

app.Run();
```

## Test The Application 🌐

Now that the API is ready, we can go ahead and test it! Go ahead and run the following command:

```sh
dotnet watch
```

You can now test out all the endpoints locally using **cURL**, **Postman**, or any other API testing tool that can send HTTP Requests.

| API Endpoint | Description | Request Body |
| - | - | - |
| GET `/todos` | Gets all todos | |
| GET `/todos/{id}` | Gets a todo by Id | |
| POST `/todos` | Add a todo | Todo item:<br>`{ "description": "<Enter todo description>", "isCompleted": false }` |
| PUT `/todos/{id}` | Updates a todo | Todo item:<br>`{ "description": "<Enter todo description>", "isCompleted": false }` |
| DELETE `/todos/{id}` | Deletes a todo | |

You can also check out the following repository, where the minimal API comes with a [Swagger UI](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/openapi?view=aspnetcore-7.0&source=recommendations) to make API testing even simpler.

{% github adityaoberai/.NET-Appwrite-Todo-API %}

## Moving Forward 🛣️

Thank you so much for reading and trying out this tutorial, folks! If you liked it, please consider sharing this blog with your peers and social media.

If you'd like to learn more about Appwrite, check out the [official documentation](https://appwrite.io/docs) and join our [Discord server](https://appwrite.io/discord).

Stay safe, and happy building :)