Published
5/30/2024
Categories
Web Development, Web Design
Tags
Symfony

The Power of API Platform Operations

Woman writes on white board.

Why Continuing to Test Multiple Browsers is Important

Generally in web development we take a code first approach to creating our API’s. This means that we code our application’s API layer and then document after. Creating an API using this methodology will allow developers and managers to release and deploy an application relatively quickly. The downside to this approach is that down the line, developers, testers, and other professionals that dig into the code later may have a tough time understanding or documenting the API. 

The other school of thought with developing an API is a design first methodology. By approaching API development this way, developers can clearly define the roles of both the general API layer and the specific components of the application API. As the name implies, we are designing our API before we code. A key benefit of this approach is that developers, testers, and managers are on the same page about what the API or API component is meant to accomplish. This allows a development team to ensure the goal is achieved, test for the goal once built, and could expedite the release of an application. 

We discuss these points to guide the development of a software system that encourages developers to adopt the latter approach. Specifically, we refer to API Platform—a minimalistic software system that includes a Symfony API, React Admin, and a Next.js frontend application out of the box.  

On the Surface

API platform strongly encourages developers to take the design first approach.The key feature that API Platform introduces to achieve this are Operations. Operations are a tool we use to connect our resource or entity to a route and a controller. When creating a resource with API platform and we annotate it as an API Resource, a Get, GetCollection, Put, Post, and Delete methods will be auto generated. We can also annotate the resource with each of these methods. These are simply annotations that allow us to define how each of the HTTP methods will behave in our application for a given resource. When initialized they simply allow the basic CRUD operations when the resource endpoint is requested with a given method. This is where we begin to leverage the power of the API platform operations. 

First thing is first, we need to define what our resource is for and what each operation will need to do when requested. Each operation can be configured to cater to our design. We can firstly give a description and summary of our operation by using the “openapiContext” configuration. This allows us to give documentation directly on the operation and in the swagger UI so that a user may understand what the operation is for. The route can also be configured by editing the “uriTemplate” and “routePrefix” configuration.

Put Your Design into Action

These are just some of the surface level configurations to customize the operation. Now we can move onto more powerful configurations that will hold the logic that is defined by our design. One of the most powerful configurations is the “processor.” A processor is what will house our custom logic to accomplish our design requirements for Put, Post, and Delete operations. A processor can extend the default logic to simply call an event listener or it can outright replace the logic of the default system and complete a request. Typically a processor will be used in conjunction with a Data Transfer Object or DTO. A DTO is intended to define the structure of the data that an operation should expect in order to complete a request. We first create a DTO object and then we use the “input” configuration to assign it to the operation. Once a DTO is assigned then our processor will have the data object in the shape of that DTO to then complete the request. 

Before we can do a Put or Delete on a record of a specific resource, we need to provide the data to our other application layers that will operate on or transform the data. To accomplish this we have the “provider” configuration. A provider will allow us to deliver the data to the other layers of our application in the shape that fits the needs of those layers. Typically these will be used by the Get and GetCollection operation. A provider will allow us to query for data from our database and combine data from multiple resources to then create a specific shape. 

Consider the Power of Configurations

When we put a provider, DTO, and a processor together, you can unlock a variety of possible ways the different layers of an application will interact with one another and the data. A provider can combine a multitude of resources and allow a layer of the application to alter the data to match a DTO and then a provider will process the data that may or may not alter the data in your database. These different tools can ultimately reduce the amount of code we write and allow us to spend more time clearly defining our requirements. 

Along with setting security rules by defining the minimum role required to access an operation, you can also remove the operation entirely and the method will return a “400.” If a resource is meant to be publicly accessible and allow CRUD operations from any origin you can simply annotate it with the APIResource annotation and all Get, GetCollection, Put, Post, and Delete methods will be allowed. 

There are a plethora of configurations that you can set to make sure your operations fit the overall design of your application layers. We recommend you check out the API Operation Documentation to review the possibilities.