API design best practices
Stop exposing your domain models
Returning only the necessary data to your API clients
Hi, multicellular organisms!
Today I want to talk about a very important topic regarding API designs. To not expose your domain models or your entities in your API endpoints.
Note that I’ll show examples in .NET but this is valid for any programming language.
Very often I see an endpoint like this:
This is a .NET controller where you fetch information from data storage and return it directly to your endpoint.
What is the problem with this design?
By allowing your API to return your domain models you are exposing it to some issues.
Domain models change more often than API resources
Your API return models are your API contracts, which determine the resources and endpoint receives and/or returns. They shouldn’t change as much as your domain models.
By doing so, you reduce the amount of breaking changes your API clients have to handle every time an update is released.
Security
Your domain models are the representation of your application logic, sometimes your database structure, depending on your architecture.
When you return them directly you are exposing this structure to any client that has access to your API.
Unintentional behaviors
Some unexpected behaviors can happen when returning your domain models directly.
A common scenario with entity framework is to have circular references between models, having a two-way reference between models. If you return them directly, you will probably face a self reference loop exception
. This happens because the two models have a reference to one another and when it is being serialized to an JSON
object, it faces an infinity loop. You can see an example below:
public class FirstModel
{
public int Id { get; set; } public SecondModel SecondModel { get; set; }
}public class SecondModel
{
public int Id { get; set; } public FirstModel FirstModel { get; set; }
}
You can configure your JSON
serialization to avoid these situations, but then you might get some strange behaviors after serialization that might not be what your clients are looking for.
What to return then?
The best practice is to have dedicated API models which, before returning a response, you’ll map from your domain models. This will ensure that you are only returning the necessary data your client needs.
An API model must be a contract with your client and it should be the most stable as possible to not break any calls that all your clients make to your API.
An example in .NET would be:
public Task<ActionResult<IEnumerable<ProductResource>> GetProducts()
{
var products = await _service.GetProducts(); var productResources = _mapper.Map<IEnumerable<ProductResource>>(products); return Ok(productResources);
}
This way you have more control over what you are returning by decoupling your responses from your domain and making sure that you’ll only return the necessary information required by your client.
Conclusion
Returning directly to your domain models and exposing them to your clients is considered a bad API design for many reasons.
You force your clients to be constantly updating their implementations that are dependent on your API.
You also are prone to having unexpected behaviors such as the one with circular references with .NET entity framework.
And another one is that it might expose some security issues as you might be exposing not only your models but also your database (if your domains models reflect your database entities).
So the best and most recommended practice is to decouple your domain from your API by having dedicated API models. Then you’ll map your domain models to your API models which you’ll return as the response.
Happy coding!