The Power of Asynchronous Code in your web API

Hello my fellow developers, on the last article I showed you how to add data validation using the fluent validation Nuget package. We discussed the relevance of implementing data validation for our API and for many other kinds of projects and also we saw how easy it was to implement yet the benefits are huge.

Today I will teach you how you can implement asynchronous code for your web API, you will see how easy it is and how you will leverage the power of asynchronous code for your app. But before we can start coding we need to understand what is asynchronous programming and what uses it have. So without further ado letโ€™s get into todayโ€™s article.

What is asynchronous programming?

Software development has two primary paradigms when it comes to handling tasks: synchronous and asynchronous programming. Synchronous programming executes tasks one after the other, in a predetermined order. On the other hand, asynchronous programming enables the execution of multiple tasks simultaneously, without waiting for the previous task to finish. This article will dive deeper into asynchronous programming, its importance, and its various uses.

Why is Asynchronous Programming Important?

Asynchronous programming is essential for improving the performance and responsiveness of software applications. It is particularly useful for applications that require heavy data processing or network communication. By allowing multiple tasks to execute simultaneously, applications can run more efficiently and effectively. It is beneficial because it eliminates the need to wait for each task to complete before moving on to the next one.

Another advantage of asynchronous programming is its ability to handle long-running tasks without blocking the application's main thread. When a task takes a long time to complete, synchronous programming can cause the application to become unresponsive or even crash. Asynchronous programming enables applications to keep functioning normally while long-running tasks are processed in the background, thereby improving the overall user experience.

What Uses Does Asynchronous Programming Have?

Asynchronous programming has a wide range of uses in software development. One of its primary uses is in web development, where it handles requests and responses from servers. By using asynchronous programming, web applications can process multiple requests simultaneously, improving the overall performance and user experience.

Mobile app development is another area where asynchronous programming is commonly used. It handles tasks such as data synchronization, database access, and network communication. As a result, mobile apps can continue to function smoothly and responsively even when performing complex tasks in the background.

Asynchronous programming is also useful in gaming, artificial intelligence, and machine learning. These applications require the processing of multiple tasks simultaneously, which can be achieved through asynchronous programming. It helps to improve the performance and efficiency of these applications, leading to better overall results.

Clone the repo on GitHub

Before starting if you want to follow along with this tutorial you can clone the GitHub repository and make sure you get the code from the FluentValidation branch which contains the latest code changes up to this point.

Update the IPostRepository interface

We need to update the post repository class to access the database asynchronously but before we can do that we actually need to update the IPostRepository interface as it defines the contract the repository class needs to comply.

public interface IPostRepository 
{
    Task<IEnumerable<Post>> GetPostAsync();
    Task<Post> GetPostAsync(int id);
    Task AddAsync(Post post);
    Task EditAsync(Post post);
    Task DeleteAsync(int id);
}

We need to return a Task<T> so for the Get methods we will wrap the return type inside a task and for the Add, Edit, and Delete we will simply return a Task as we cannot return void on an async method and also we will update the name of every function as they will now work asynchronously we will append the Async word.

Update PostRepository class ๐Ÿฆ

Now we should be getting an error because we changes the return type for every method defined in the IPostRepository interface so we need to update our PostRepository as well to make sure we comply with what our interface states.

public class PostRepository : IPostRepository
{
    private readonly BlogDbContext context;
    public PostRepository(BlogDbContext context)
    {
        this.context = context;
    }

    public async Task AddAsync(Post post)
    {
        context.Add(post);
        await context.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var post = context.Posts.Find(id);
        context.Remove(post);
        await context.SaveChangesAsync();
    }

    public async Task EditAsync(Post post)
    {
        context.Entry(post).State = EntityState.Modified;
        await context.SaveChangesAsync();
    }

    public async Task<IEnumerable<Post>> GetPostAsync()
    {
        var allPosts = await context.Posts.ToListAsync();
        return allPosts;
    }

    public async Task<Post> GetPostAsync(int id)
    {
        var post = await context.Posts.FindAsync(id);
        return post;
    }
}

Letโ€™s begin by changing the names of all the methods and appending the Async word as they will now be working asynchronously, then add the async keyword and change the return type for every method to fully comply with the method signature defined by our interface.

Now the last step is to call SaveChangesAsync method in our Add, Edit, and Delete methods. For the Get method that retrieves all the posts we need to change the ToList() to ToListAsync and that would be enough, we need to do the same for the Get method that retrieves a single post but instead of using ToListAsync we will replace the Find method for its async version FindAsync and there you go now the repository class is calling the database asynchronously.

Making the Post Controller Async ๐ŸŽฎ

Now all that is left is to update our controller endpoints to make them work asynchronously.

**[HttpGet]
public async Task<IActionResult> GetPost()
{
    var posts = await repository.GetPostAsync();
    var postsDto = mapper.Map<IEnumerable<PostResponseDTO>>(posts);
    logger.LogDebug($"Get method called, got {postsDto.Count()} results");
    return Ok(postsDto);
}

[HttpGet("{id:int}")]
public async Task<IActionResult> GetPost(int id)
{
    try
    {
        var post = await repository.GetPostAsync(id);
        var postDto = mapper.Map<PostResponseDTO>(post);

        return Ok(postDto);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, $"Error getting post with id {id}");
        throw;
    }
}

[HttpPost]
public async Task<IActionResult> CreatePost(AddPostDTO addPostDTO)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();
        }

        var newPost = mapper.Map<AddPostDTO, Post>(addPostDTO);
        newPost.CreatedDate = DateTime.Now;
        await repository.AddAsync(newPost);
        return CreatedAtAction(nameof(GetPost), new { id = newPost.Id }, null);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Unexpected error on Post method");
        throw;
    }
}

[HttpPut]
public async Task<IActionResult> EditPost([FromBody] EditPostDTO editPostDto)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest();
        }
        var post = mapper.Map<EditPostDTO, Post>(editPostDto);

        post.LastUpdated = DateTime.Now;
        await repository.EditAsync(post);
        return NoContent();
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Unexpected error on Put(Edit) Method");
        throw;
    }
}

[HttpDelete("{id:int}")]
public async Task<IActionResult> DeletePost(int id)
{
    try
    {
        await repository.DeleteAsync(id);

        return NoContent();
    }
    catch (Exception ex)
    {
        logger.LogError(ex, $"Unexpected error on Delete method trying to delete post with Id {id}");
        throw;
    }
}**

Here what we did is that in every endpoint we added the async keyword and set the return type to Task<IActionResult>. Also, we now call the async version of the repository methods AddAsync, EditAsync, DeleteAsync, and GetAsync.

Test the API ๐Ÿงช

Now you can proceed to test the API, you will notice no change as it will run as before but now behind the scenes, it can take requests asynchronously and also the interaction with the database is asynchronous.

Conclusion ๐ŸŒ‡

Asynchronous programming is an essential paradigm in software development, providing a wide range of uses and benefits. It enables the execution of multiple tasks simultaneously, leading to more efficient and effective software applications. It also enables long-running tasks to be processed without blocking the main thread, improving the overall user experience. As such, it is a critical tool for developers looking to create high-performance software applications. By embracing asynchronous programming, developers can create faster, more efficient, and more responsive applications, leading to better overall user experiences.

As always thanks for reading and considering supporting me on my blog Unit Coding and on my youtube channel under the same name Unit Coding. Keep posted for my future articles on web API development and also for cool projects using ASP NET Core.

Did you find this article valuable?

Support Unit Coding by becoming a sponsor. Any amount is appreciated!

ย