How to make Dapr client works well with Refit and RestEase in 5 minutes
Tags: dotnet, dapr, webdev, beginners
Published: 2021-03-08 07:53:43

---
title: How to make Dapr client works well with Refit and RestEase in 5 minutes
published: true
description: This short article shows you how can we adapt Dapr client with Refit and RestEase to make us call Rest Api comfortable and easier.
tags: dotnet,dapr,webdev,beginners
//cover_image: https://direct_url_to_image.jpg
---

# Prerequisition
- [Dapr](https://github.com/dapr/dapr): cli 1.0, Runtime 1.0
- [.NET 5 SDK](https://dotnet.microsoft.com/download): 5.0.103
- Dapr client: [Dapr.AspNetCore 1.0.0] (https://github.com/dapr/dotnet-sdk)
- [Tye](https://github.com/dotnet/tye): 0.7.0-alpha.21070.7+eb3b50699b7a5f2d0997a5cc8c5185d056dde8ec

If you want to go directly to the source code, then it is at [https://github.com/thangchung/practical-clean-ddd](https://github.com/thangchung/practical-clean-ddd) ⭐⭐⭐⭐⭐

# Begin story

With the Dapr client in code, we need to follow its patterns and the way to call another Daprized App to its Rest API. Normally, we use `service invocation` to the Dapr server app. The code as below

```csharp
var client = DaprClient.CreateInvokeHttpClient(appId: "routing");
var deposit = new Transaction  { Id = "17", Amount = 99m };
var response = await client.PostAsJsonAsync("/deposit", deposit, cancellationToken);
var account = await response.Content.ReadFromJsonAsync<Account>(cancellationToken: cancellationToken);
```

And this way of calling is really easy to make [Leaky Abstraction](https://en.wikipedia.org/wiki/Leaky_abstraction)
But what we think in my mind is something like this

```csharp
public interface IRoutingApi
{
    [Post("/deposits")]
    Task<Account> DepositTransaction([Body] Transaction body);
}

var account = RestService.For<IRoutingApi>("http://routing");
```

We can use [Refit](https://github.com/reactiveui/refit) or [RestEase](https://github.com/canton7/RestEase) to make it better to read and use just like above.

It must be easy and in a Rest-way to call the Rest API, right? Make Rest back to its natural power.

With `Dapr.AspnetCore 1.0` we can luckily make Dapr client work naturally with `Refit` or `RestEase`. 

[Ryan Nowak](https://github.com/rynowak) had worked on the task to make it work together really well 👍. You can find the version of `Dapr client` with `Refit` at https://github.com/rynowak/dapr-httpclient-extravaganza/blob/main/samples/BankClient/RefitExample.cs, and the official implementation of the adaptor for `Dapr client` can be found at https://github.com/dapr/dotnet-sdk/blob/master/src/Dapr.Client/InvocationHandler.cs

# Dapr loves RestEase as well ❤️❤️❤️

In this article, we focus on how to make `Dapr client` work with `RestEase`. Let imagine that we have a service with the name `customer service` which lets end-user register their information. And we should have the Rest API like `POST http://localhost:5000/customer-api/customers`, then in this API we call to another service, let says `setting service` to check whether the country of customer is valid or not? We have the Rest API for `setting service` just like `GET http://settingapp:5005/api/countries/{id}`, the code for this API as below

```csharp
[HttpGet("/api/countries/{id:guid}")]
public override async Task<ActionResult<CountryDto>> HandleAsync(Guid id,
    CancellationToken cancellationToken = new())
{
    var request = new Query {Id = id};

    return Ok(await Mediator.Send(request, cancellationToken));
}
```

Now we set up the code to call this API in `customer service` as below

Define the API contract:

```csharp
// ICountryApi.cs
public interface ICountryApi
{
    [Get("api/countries/{countryId}")]
    Task<ResultModel<CountryDto>> GetCountryByIdAsync([Path] Guid countryId);
}
```

Wire up some code in the `Startup.cs`

```csharp
// Startup.cs
var settingAppUri = IsRunOnTye
    ? $"http://{AppConsts.SettingAppName}:5005"
    : "http://localhost:5005"; 

services.AddScoped<InvocationHandler>();
services.AddRestEaseClient<ICountryApi>(settingAppUri, client =>
{
    client.RequestPathParamSerializer = new StringEnumRequestPathParamSerializer();
}).AddHttpMessageHandler<InvocationHandler>();
```

And the code in the handler:

```csharp
// CreateCustomer.cs
internal class Handler : IRequestHandler<Command, ResultModel<CustomerDto>>
{
    private readonly IRepository<Customer> _customerRepository;
    private readonly ICountryApi _countryApi;

    public Handler(IRepository<Customer> customerRepository,
        ICountryApi countryApi)
    {
        _customerRepository = customerRepository ?? throw new ArgumentNullException(nameof(customerRepository));
        _countryApi = countryApi ?? throw new ArgumentNullException(nameof(countryApi));
    }

    public async Task<ResultModel<CustomerDto>> Handle(Command request,
        CancellationToken cancellationToken)
    {
        var alreadyRegisteredSpec = new CustomerAlreadyRegisteredSpec(request.Model.Email);

        var existingCustomer = await _customerRepository.FindOneAsync(alreadyRegisteredSpec);

        if (existingCustomer != null)
            throw new Exception("Customer with this email already exists");

        // check country is exists and valid
        var (countryDto, isError, _) = await _countryApi.GetCountryByIdAsync(request.Model.CountryId);
        if (isError || countryDto.Id.Equals(Guid.Empty))
        {
            throw new Exception("Country Id is not valid.");
        }

        var customer = Customer.Create(request.Model.FirstName, request.Model.LastName, request.Model.Email, request.Model.CountryId);

        var created = await _customerRepository.AddAsync(customer);

        return new ResultModel<CustomerDto>(new CustomerDto
        {
            Id = created.Id,
            FirstName = created.FirstName,
            LastName = created.LastName,
            Email = created.Email,
            CountryId = created.CountryId
        });
    }
}
```

The main codes to __notice__ as 

```csharp
// check country is exists and valid
var (countryDto, isError, _) = await _countryApi.GetCountryByIdAsync(request.Model.CountryId);
```

Now let use `tye` to init the core components:

```bash
$ tye run
```

Go to `http://localhost:8000`, and make sure all the apps running well.

Finally, you make a call to `POST http://localhost:5000/customer-api/customers` with the body as

```json
{
  "model": {
    "firstName": "firstName 1",
    "lastName": "lastName 1",
    "email": "email1@nomail.com",
    "countryId": "18a4a8ae-3338-484a-a4ed-6e64d13d84dc"
  }
}
```

Now you can see the logs as below:

```
[customerapp_5ed4a92a-f]: info: System.Net.Http.HttpClient.CoolStore.AppContracts.RestApi.ICountryApi.LogicalHandler[100]
[customerapp_5ed4a92a-f]: Start processing HTTP request GET http://settingapp:5005/api/countries/18a4a8ae-3338-484a-a4ed-6e64d13d84dc
[customerapp_5ed4a92a-f]: info: System.Net.Http.HttpClient.CoolStore.AppContracts.RestApi.ICountryApi.ClientHandler[100]
[customerapp_5ed4a92a-f]: Sending HTTP request GET http://127.0.0.1:50910/v1.0/invoke/settingapp/method/api/countries/18a4a8ae-3338-484a-a4ed-6e64d13d84dc
[customerapp_5ed4a92a-f]: info: System.Net.Http.HttpClient.CoolStore.AppContracts.RestApi.ICountryApi.ClientHandler[101]
[customerapp_5ed4a92a-f]: Received HTTP response headers after 695.0478ms - 200
[customerapp_5ed4a92a-f]: info: System.Net.Http.HttpClient.CoolStore.AppContracts.RestApi.ICountryApi.LogicalHandler[101]
[customerapp_5ed4a92a-f]: End processing HTTP request after 710.2258ms - 200
```

Have you noticed some lines of logs above at 

```
...
Start processing HTTP request GET http://settingapp:5005/api/countries/18a4a8ae-3338-484a-a4ed-6e64d13d84dc
...
Sending HTTP request GET http://127.0.0.1:50910/v1.0/invoke/settingapp/method/api/countries/18a4a8ae-3338-484a-a4ed-6e64d13d84dc
...
``` 

Here ya go! `Dapr client` helps us to handle the work which is mainly on `RestEase` way. 

Easy peasy, right? Give the star ⭐ for the repository below if you feel like this article helps you out. Thank you! ❤️❤️❤️

Source code of this article can be found at [https://github.com/thangchung/practical-clean-ddd](https://github.com/thangchung/practical-clean-ddd) ⭐⭐⭐⭐⭐