Telerik blogs

Creating a good API requires some extra care. Check out in this blog post seven tips that will help you create a good API in ASP.NET Core.

Good APIs are made up of many factors, including logs, documentation, performance and others.

With the popularity of Web APIs, it is extremely important that developers have the necessary knowledge to implement good APIs—after all, this will be a very common task in your routine as a web developer, especially using Microsoft technologies such as ASP.NET Core.

In this article we will cover seven tips:

  1. Write good endpoints
  2. Use the right HTTP verbs
  3. Avoid putting business rules on endpoints
  4. Use pagination and filtering
  5. Make a Health endpoint available
  6. Use Caching API
  7. Write good documentation

Tip 1: Write Good Endpoints

Always Use Nouns

When creating endpoints, always use nouns instead of verbs. See below the wrong way and the right way.

❌ Wrong:
/api/GetOffer/{offer}

βœ… Correct:
/api/offers/{offer}

Naming Conventions

There are differences regarding the nomenclature, but it is very common to find URLs from technology giants that use their URLs always in lowercase. In case it is necessary to use a word composed of more of a noun, try to use an underscore (_) or a dash (-) to separate them. See the following example:

❌ Wrong:
/Api/Orders/AdditionalFields

βœ… Correct:
/api/orders/additional_fields
or
/api/orders/additional-fields

Nouns in the Plural or Singular?

As seen before, you should use nouns instead of verbs, but a common question is whether to write these nouns in the plural or singular.

And the answer is that there is no correct form—it’s up to you to decide. However, I prefer to use the plural form because that way they indicate a set of characteristics that can be one or more. When you use the singular, you restrict the endpoint to just one item—at least that’s the impression it gives.

Regardless of which way you use it, the goal is always to simplify things.

πŸ‘Œ Good (singular):
/api/order/additional_field

πŸš€ Better (plural):
/api/orders/additional_fields

Versions

It is very common for APIs to evolve after their first release, so it is important to create different versions of the same API. Many companies choose to make it explicit in the endpoints which is the API version through the letter “v” + the version number. So, normally in the first version we see “v1.”

Versioning APIs can help with system maintenance. Systems that use the v1 endpoint are not impacted, as all changes are made available on another endpoint—in this case, v2.

But it is important to make it clear that this is not mandatory. Many companies do not usually create versions for their APIs. In these cases, new APIs are created and replace the old ones.

πŸ‘Œ Good:
Unversioned endpoint: api/offers/{offer}

πŸš€ Better:
Versioned endpoint: api/v1/offers/{offer}

Tip 2: Use the Right HTTP Verbs

First of all, you should learn the basics of HTTP. This is not a complicated subject, so we often put aside the documentation and go to practice, which results in the misuse of the resources available in this technology. A great reference to understand HTTP concepts is this page created by Mozilla Firefox: Basics of HTTP.

Following are the main HTTP verbs, and their respective functions:

HTTP MethodDescription
GETFetch a resource
POSTCreate a resource
PUTUpdates a resource
DELETEDelete a resource
PATCHPartially updates a resource

Use the Right HTTP Status Codes

A valuable feature available in HTTP is status. These statuses are returned in the server response, depending on the result of the action sent in the request, which can be a hit or miss. Regardless of the result, there are several types of status—it is up to the developer to implement the correct status according to the situation.

For example, an API receives via query string the id of the seller. If the id exists in the database, the API returns the data of the corresponding seller. If not, the API returns an error with a detailed description. Below are the two scenarios:

  1. Seller found
HTTP/1.1 200 OK
Content-Type: text/html

{
    "status": "success",
    "data": 
    {
        {
            "idSeller": "100002132",
            "name": "SellerExample",
            "offerCode": "4",
            "smallSeller": false
        }
    }
}
  1. Seller not found
HTTP/1.1 404 Not Found
Content-Type: text/html

{
    "status": "error",
    "messages": [
        {
            "code": "018",
            "message": "Seller not found"
        }
    ]
}

Another important point is the consistent use of the return status. For example, in the case of success, for each verb there is a commonly used pattern.

  • GET: 200 OK
  • POST: 201 Created
  • PUT: 200 OK
  • DELETE: 204 No Content
  • PATCH: 200 OK

You can check the complete list of available statuses in the Mozilla Firefox documentation: HTTP status codes.

Tip 3: Avoid Putting Business Rules on Endpoints

The endpoints of an API are used for the input and output of data, so the best option is to create a class, commonly called Service, put the business rules in it, and then just invoke the main method of the service class on the endpoints. As you can see in the examples below:

❌ Wrong:

app.MapPost("v1/products", (Product product, ProductDbContext dbContext) =>
{
    string errorMessage = string.Empty;

    if (string.IsNullOrEmpty(product.Name))
        errorMessage += "Name is mandatory";

    if (string.IsNullOrEmpty(product.Category))
        errorMessage += "Category is mandatory";

    if (!string.IsNullOrEmpty(product.Name))
        Results.BadRequest("Error creating the product");

    else
    {
        dbContext.Products.Add(product);
        dbContext.SaveChanges();
        return Results.Ok();
    }

}).WithName("CreateProduct");

βœ… Correct:


app.MapPost("v1/products", (ProductService service, Product product) =>
{
    var resultMessage = service.Create(product);

    if (!string.IsNullOrEmpty(resultMessage))
        Results.BadRequest($"Error creating the product, error validation: {resultMessage}");

    return Results.Ok();

}).WithName("CreateProduct");

Tip 4: Use Pagination and Filtering

It is very common for applications to grow over time, and it is important to allow API consumers to have the option to only get a certain amount of items as needed. A suggestion for this is pagination.

To implement paging without demanding much performance from the database, the best way is to provide a parameter (identifier) that can “cut” a collection of records and a quantity limiter.

You can see a bad and a good example below. In the first example, there are no paging options or limitations. In the second, it is possible to notice these parameters present in the endpoint route.

πŸ‘Œ Good:
GET v1/products

πŸš€ Better:
GET v1/products?limit=100&category=headphone

Tip 5: Make a Health Endpoint Available

It is a good practice to make available to all consumers a Health route, which serves, as its name suggests, to check the health of an API. In this case, not only if the API is available, but you can check the API dependencies and return the result obtained in each one.

For example, in an API that needs to generate a token in an external API, you can check in the Health endpoint if that external API is available and return the result of the check in the Health route.

So it is possible for consumers to quickly know where the cause of the problem may be, in a case where their API is returning an error 500 (internal server error). Below is an example of a Health endpoint.

GET - v1/products/health

app.MapGet("v1/products/health", (ProductService service) =>
{
    var externalAPIResponse = service.ExternalAPIHealthVerify();
    return Results.Ok($"External API response: {externalAPIResponse.StatusCode} - {externalAPIResponse.Message}");

}).WithName("Health");

Tip 6: Use API Caching

The Cache is a technique used to store data that is used frequently. It’s very useful as it aims to gain performance and reduce the load on web/database services by storing data in a place of easy access, which can be In-Memory Cache (server’s memory), Persistent in-process Cache (file or database) or Distributed Cache (multiple processes).

Below you can see an example of memory caching implementation in an ASP.NET Core Web API.

app.MapGet ("v1/products", (ProductService service) =>
{
    const string ProductsKey = "_Products";

    if (!cache.TryGetValue(ProductsKey, out List<Product> products))
    {
        products = service.GetAll();
        var cacheEntryOptions = new MemoryCacheEntryOptions
        {
            AbsoluteExpiration = DateTime.Now.AddMinutes(3),
            SlidingExpiration = TimeSpan.FromMinutes(2),
            Size = 1024,
        };
        cache.Set(ProductsKey, products, cacheEntryOptions);
    }
    return Results.Ok(products);

}).WithName("GetProducts");

Tip 7: Write Good Documentation

Writing good documentation is essential in developing an API—after all, it is through documentation that developers will implement the consumers of this API.

Large companies have used a model very similar to each other to provide documentation about their APIs. It is usually a very simple webpage that contains all the information necessary for a consumer to be created from scratch.

Below are some requirements for good documentation.

  • Make a brief description of the API functions. Always add important details about any business rule. Example: “To cancel a purchase, you will need to wait 24 hours after placing the order…”
  • Always separate the Request from the Response. For each of them, provide the HTTP verb and the route to be accessed, also leave an example of the data to be sent in the Request and returned in the Response.
  • If the API contains some complexity, always provide a process flowchart—it is more intuitive to use images to describe a process than to describe it with words.

Conclusion

A good API is composed of several factors, which together bring several advantages such as easy integration, fast development, easy maintenance, etc.

In this article, we covered seven topics with tips for creating professional and quality Web APIs. So, when creating a new API, it’s worth making sure you’re not forgetting any of them.


assis-zang-bio
About the Author

Assis Zang

Assis Zang is a software developer from Brazil, developing in the .NET platform since 2017. In his free time, he enjoys playing video games and reading good books. You can follow him at: LinkedIn and Github.

Related Posts

Comments

Comments are disabled in preview mode.