Conclusion– REST APIs
A data transfer object (DTO) allows us to design an API endpoint with specialized input and output instead of exposing the domain or data model. Those DTOs shield our internal business logic, which improves our ability to design our APIs and also helps us make them more secure.
By defining DTOs, we can avoid a malicious actor trying to bind data that he should not have access to. For example, when using an input “Login DTO” that only contains a username and password properties, a malicious user could not try to bind the IsAdmin field available in our domain and database. There are other ways to mitigate this, but they are out of the scope of this chapter, yet, a DTO is a great candidate to mitigate this attack vector.
This separation between the presentation and the domain is a crucial element that leads to having multiple independent components instead of a bigger, more fragile one or leaking the internal data structure to the clients consuming the API.We explore building APIs in the next few chapters and explore some topics more in-depth in Section 4, Designing at Application Scale.Using the DTO pattern helps us follow the SOLID principles in the following ways:
- S: A DTO adds clear boundaries between the domain logic or the data and the API contract, dividing one model into several distinct responsibilities to help keep things isolated.
- O: N/A
- L: N/A
- I: A DTO is a smaller, specifically crafted model that serves a clear purpose. With a DTO, we now have two models (domain and API contract) and several classes (input DTO, output DTO, and domain or data entities) instead of a generic one (only the domain or data entity).
- D: N/A
Next, we look at how we can glue the pieces that we explored so far into API contracts.
API Contracts serve as an essential blueprint, outlining the rules of engagement between your API and its consumers. This includes available endpoints, HTTP methods they support, expected request formats, and potential response structures, including HTTP status codes.These contracts provide clarity, robustness, consistency, and interoperability, facilitating seamless system interactions, no matter the language they are built with. Moreover, well-documented API contracts are a reliable reference guide, helping developers understand and utilize your API effectively. Thus, designing comprehensive and clear API contracts is critical in building high-quality, maintainable, and user-friendly APIs.An API contract describes a REST API, so a consumer should know how to call an endpoint and what to expect from it in return. What an endpoint does or the capability it provides should be clear just by reading its contract.Each endpoint in a REST API should provide at least the following signature:
- A Uniform Resource Identifier (URI) that indicates where to access it.
- An HTTP method that describes the type of operation it does.
- An input that defines what is needed for the operation to happen. For example, the input can be the HTTP body, URL parameters, query parameters, HTTP headers, or even a combination.
- An output that defines what the client should expect. A client should expect multiple output definitions since an endpoint will not return the same information if the request succeeds or fails.
The input and output of an endpoint are often DTOs, making DTOs even more important.
There are multiple ways to define API contracts. For example, to define an API contract, we could do the following:
- Open any text editor, such as MS Word or Notepad, and start writing a document describing our web APIs; this is probably the most tedious and least flexible way. I do not recommend this for many reasons.
- Writing specifications in Markdown files and saving those files within your project Git repository for easy discoverability. Very similar to MS Word, but more accessible for all team members to consume. This approach is better than Word, yet not optimal since you need to manually update those files when the API changes.
- Use an existing standard, such as the OpenAPI specification (formerly Swagger). This technique implies a learning curve, but the result should be easier to consume. Moreover, many tools allow us to create automation using the OpenAPI specs. This approach is starting to remove the need for manual intervention.
- Use a code-first approach and ASP.NET Core tooling to extract the OpenAPI specs from your code.
- Use any other tools that fit our requirements.