FastEndpoints and Dapper with .NET

Poatek
Poatek
Published in
4 min readMay 24, 2023

In this article, we will see some examples of how to make use of these technologies together, understand their gains, and their ease of use.
Before practice, let’s see the concept of each one:

FastEndpoints is a developer-friendly alternative to Minimal APIs & MVC.
Performance is on par with Minimal APIs. It’s faster, uses less memory, and does around 35k more requests per second than an MVC Controller.

Dapper is an open-source object-relational mapping (ORM) library for .NET and .NET Core applications. The library allows developers quickly and easily access data from databases without the need to write tedious code. Dapper allows you to execute raw SQL queries, map the results to objects, and execute stored procedures, among other things.

Now that the concept has been introduced, let’s actually create the API.

Create the project and install the packages

We need to create the Web API and add the required packages.

dotnet new web -n ApiWithFastEndpoints
cd ApiWithFastEndpoints
dotnet add package FastEndpoints
dotnet add package Dapper

Program.cs

Make changes in the Program.cs class as described below, check that we are adding AddFastEndpoints in the ServiceCollection, in addition to the database connection. And finally, building the application with some default settings.

global using FastEndpoints;
global using FluentValidation;

using ApiWithFastEndpoints.Database;
using FastEndpoints.Swagger;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFastEndpoints();
builder.Services.AddSwaggerDoc();
builder.Services.AddSingleton(_ =>
new SqliteConnectionFactory(connectionString: builder.Configuration.GetConnectionString("Database")!));

var app = builder.Build();
app.UseAuthorization();
app.UseFastEndpoints(c =>
{
c.Endpoints.RoutePrefix = "api";
c.Serializer.Options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
app.UseSwaggerGen();

Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;

app.Run();

Sqlite connection class

This will be the database connection class; in this example, the option was for the Sqlite database. In which the data will be read from a .db file.

public class SqliteConnectionFactory
{
private readonly string _connectionString;

public SqliteConnectionFactory(string connectionString)
{
_connectionString = connectionString;
}

public async Task<IDbConnection> CreateConnectionAsync()
{
var connection = new SqliteConnection(_connectionString);
await connection.OpenAsync();
return connection;
}
}

Create an Entity class

Will be necessary a model class representing the database’s Movie entity.

public class Movie
{
public long Id { get; set; }
public string ImdbId { get; set; }
public string Title { get; set; }
public string Director { get; set; }
public int Year { get; set; }
public string Rating { get; set; }
public string Genres { get; set; }
public int Runtime { get; set; }
public string Country { get; set; }
public string Language { get; set; }
public double ImdbScore { get; set; }
public int ImdbVotes { get; set; }
public double MetacriticScore { get; set; }
}

Create a Response DTO class

This class will return data from the endpoint, which will be mapped with selected records from the database.

public class MovieResponse
{
public long Id { get; set; }
public string Title { get; set; }
public string Genres { get; set; }
public double ImdbScore { get; set; }
}

Create a Mapper class

To return a MovieResponse through a Movies entity class, it is possible to use a mapping class by inheriting the Mapper class from the library itself, notice this line of code, here Mapper<EmptyRequest, MovieResponse, Movie> the expected types are defined, and with that the possibility to override the ToEntity and FromEntity methods.

public class MovieMapper : Mapper<EmptyRequest, MovieResponse, Movie>
{
public override MovieResponse FromEntity(Movie movie) => new()
{
Id = movie.Id,
Title = movie.Title,
Genres = movie.Genres,
ImdbScore = movie.ImdbScore,
};
}

Create An Endpoint Class

Here the magic happens; the class represents the GET Movies endpoint that inherits from Endpoint and defines the expected types of request, response, and mapper. In addition, a constructor receives the connection to the database through dependency injection. And a HandleAsync method that receives the request queries the database using connection.QueryAsync() and returns the mapped entity as a list of movies.

[HttpGet("movies"), AllowAnonymous]
public class GetMoviesEndpoint : Endpoint<EmptyRequest, IEnumerable<MovieResponse>, MovieMapper>
{
private readonly SqliteConnectionFactory _connectionFactory;

public GetMoviesEndpoint(SqliteConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}

public override async Task HandleAsync(EmptyRequest request, CancellationToken ct)
{
using var connection = await _connectionFactory.CreateConnectionAsync();

var movies = await connection.QueryAsync<Movie>(
@"SELECT id, title, genres, CAST(imdb_score AS REAL) AS imdb_score FROM movies");

await SendAsync(movies.Select(Map.FromEntity), cancellation: ct);
}
}

Final results

Conclusion

Well, it was possible to visualize the nomenclature of a FastEndpoint returning results from an SQL query using Dapper, and verifying its easy readability. Next time you are creating a Minimal API project, consider using FastEndpoints. And if you want a data access library with better performance and greater control over queries, use Dapper.

If you want to analyze the complete code of the project, just access the repository on GitHub.

Feel free to explore the library, you can try adding some validators for requests, endpoints that add or remove data, and check returns with different status codes.

Tiago Almeida

--

--

Poatek
Poatek
Editor for

We’re a software engineering company filled with the best tech talent!📍Porto Alegre, São Paulo, Miami and Lisbon linktr.ee/poatek.official