My 14-step Web API design cheatsheet (with examples)


"Magic".

Despised by developers (magic strings, numbers).

But adored by football fans:

  • A pass that no one saw coming.
  • An assist that no defender was able to prevent.
  • A goal that makes you go from devastated to delirious in 1 second.

There were many magic moments during the recent Euro 2024.

However, one moment that stuck in my head was this:

During the penalty shoutouts in a match against Switzerland, England's goalkeeper had a cheat sheet on a bottle. With instructions on defending a penalty kick from every player.

As a result, England went through.

So, if the biggest players in the most crucial moments can have cheatsheets, so can you when building Web APIs.

Here it is, written on a bottle.

I won’t be selling these bottles. Yet.

But I’ll give you a guide on how to build well-thought-out Web APIs.

That delights other developers, puts less pressure on servers, and it’s extremely easy to extend and work with.

Here is the list of 14 Web API design good practices we’ll cover.

  1. Consistent naming
  2. Request object for POST data
  3. Error handling
  4. Input validation
  5. Pagination
  6. Filtering
  7. Avoid long-running HTTP API request
  8. Meaningful response codes
  9. Implement security measures
  10. Caching
  11. Versioning
  12. Rate limiting
  13. API testing
  14. Documentation

This will be a 2-part guide.

This email covers the first 7 principles.

And next week’s newsletter will cover the remaining 7.

So grab yourself a bottle of water. And let’s dive into the guide.

1. Consistent naming

The best-designed Web APIs I have worked with use clear and consistent naming.

Consistent naming allows you to understand and use the API more efficiently.

It increases:

  • Predictability
  • Readability
  • Maintainability

Some good practices to consider are:

Endpoints - Use consistent and descriptive names for endpoints. If you use plural nouns, use them everywhere:

  • /api/products
  • /api/users

HTTP Methods - Match resource actions with the appropriate HTTP methods:

  • GET: Retrieve resources.
    • Example: GET /api/products
  • POST: Create a new resource.
    • Example: POST /api/products
  • PUT: Update an existing resource.
    • Example: PUT /api/products/{id}
  • DELETE: Delete a resource.
    • Example: DELETE /api/products/{id}

Parameters - Name query parameters and route variables consistently.

2. Request object for POST data

Instead of passing multiple parameters directly to an API endpoint, encapsulate them in a single object.

This approach is beneficial when you pass multiple parameters. When you use a request object, you can easily add and remove parameters. You only need to update the request object.

Example without the request object:

Example with the request object:

3. Error handling

What should you do when an endpoint produces an exception?

Simple:

  1. Log the exception.
  2. Use a generic error message as a response.

Why?

When you display the whole exception message as a response, you invite hackers to come and read your code like an open book.

In older versions of ASP.NET, this was known as the "Yellow Screen of Death".

But when you log and return a friendly error message,

You may even produce a laugh.

Instead of anger and cry.

Here are messages that IMDB returns when there is a server error.

4. Input validation

Expecting users to send all valid data to your API is like expecting your computer to run fast when you are in a hurry.

You can't rely on that.

There are many ways to implement input validation in ASP.NET Core.

But one of the most popular and easiest is to use FluentValidation.

It’s a popular .NET validation library for building strongly-typed validation rules.

With its validators, you can set rules that a field:

  • is not null → .NotNull()
  • is not empty → .NotEmpty()
  • is an email address → .EmailAddress()
  • has a certain length → .Length(1, 250)

and many, many more.

To use it:

  1. Create a new class and let it inherit from AbstractValidator
  2. Create rules using RuleFor() method.
  3. Use chaining methods such as .NoEmpty().Length(5, 250) to validate a field.

The more validation you do at the start of the flow, the less bad data and bugs you will have at the end of the flow.

5. Pagination

Your Web API will be blazing fast when you minimize the data you need to return.

The simplest way is with paging.

In one of the apps I have worked on, one API endpoint had to return up to 2000 results.

But let me tell you something.

No matter how well you:

  • optimize your query,
  • how many indexes you add,

It’s significantly harder to optimize the endpoint that calculates and returns thousands of objects.

Paging to the rescue.

It’s a technique for returning a small data set, say 20-50 records at a time.

Here’s a quick example of how to return 20 rows when you use Entity Framework Core:

You use Skip and Take methods to return only a small subset of data.

Users can’t and won’t process 1000s of records in one go, anyway.

6. Filtering

With filtering, the user can specify additional conditions that will apply to the result data set.

This has a few benefits:

  • Performance - it decreases the number of data returned in a collection and minimizes to load on the server.
  • User experience - it delivers precise results.
  • Flexibility - allows clients to dynamically query data without changing the server-side code.

For example, you can use a query string filtering like:

  • http://localhost:5000/api/Products?minprice=500&maxprice=1500&category=Electronics

To get only the electronic products within a specific price range.

7. Avoid long-running HTTP API requests

Long-running requests can cause various issues, including server resource exhaustion, increased latency, and potential timeouts.

If you need to execute a long-running task, you can use asynchronous processing. It means that you:

  1. Receive the request.
  2. Return the response and acknowledge you have received the request.
  3. Create a background task to process the request.

This helps in performance since the API request isn’t waiting for the response while you process it.

Instead, it gets the confirmation that processing is started and that the API request is fulfilled.

But, how do you actually process the request?

Some options are:

  • Create a task on a background thread. Hangfire are Quartz.NET are excellent and popular choices.
  • Offload the task to another service. Use a messaging queue (e.g. RabbitMQ, Apache Kafka, or Azure Service Bus) to send a message to another web service that will do the work. This web service doesn’t have to be anything fancy or complicated. For example, you can use Azure Functions.

That wraps up part 1 of Web API design good practices.

If you have any questions, hit reply and let me know.

Enjoy your weekend (and stay hydrated).

Kristijan

P.S. I would love to hear from you. If you enjoy the emails, please let me know here. Thanks!

Kristijan Kralj

Weekly newsletter packed with code-improving tips, tools, and strategies to become a better .NET developer.

Read more from Kristijan Kralj

Lately, I’ve been paying more attention to what’s happening in the AI space. Maybe because of all the hype that surrounds it. Maybe because of the anxiety of whether AI will take my software development job. That's why I’ve decided to spend some spare time during the Xmas holiday to explore the state of AI software development tools. This email combines: My 1+ years of experience using a paid version of GitHub Copilot. 5+ hours of YouTube videos I’ve consumed in the last 2 weeks. So, let’s...

Today's issue is brought to you by the C# 13 and .NET 9 – Modern Cross-Platform Development Fundamentals. Build confidence in creating professional and high-performance web applications using the latest technologies in C# 13 and .NET 9 by Mark Price. Find out more here: C# 13 and .NET 9 Yesterday, We had a company Xmas party. Before dinner at a restaurant, we went to the escape room event. If you are unfamiliar with escape rooms, they're interactive puzzle experiences where you and your...

2 weeks ago, .NET 9 was released. If you haven’t had time to read the official release docs, don’t worry. I spent 1 hour investigating what's new in .NET 9. So you don't have to. Here are the top 10 improvements for C#, ASP.NET Core, and EF Core. 1. LINQ Index LINQ has always been an extremely useful tool for .NET developers. However, with .NET 9, LINQ comes with 3 new methods. Let's begin with the LINQ Index. The Index method places every collection element against its position within that...