In the world of PHP, Laravel is the most popular framework right now. It allows developers to do rapid development because it has a fine balance of being minimalist at start by writing simple code and not worrying too much about the structure, and when the requirements grow or become complex, you can refactor and architect it in a way which is more manageable and easy to extend. Personally, I feel this is a great thing where the structure doesn’t dictate the development.
Apart from that, the Laravel community is also very active.
- Help can be easily found on mediums like Stackover and Laracasts.
- There are popular developer blogs where people share their personal experiences that are helpful to develop skills and know what challenges were faced by them and how they overcame them.
These mediums ensure a quick learning curve and getting productive quickly. Additionally, we can also find a lot of packages through the composer package repository which can reduce the development time significantly.
In this blog post, I am going to share my experience working with a team on a Laravel-based API for a mobile app built on React Native.
Based on my experience, below are some of the key factors which gave us a significant advantage in terms of development, and the tooling helped us in our analysis while making a lot of important decisions:
- Security and timely updates
- A strong and easy-to-use ORM
- Caching with Redis
- Working with Responses and Middlewares
- Queues and events
- A lot of packages to speed up development
Let me elaborate a bit on these.
Security and timely updates
A big advantage of Laravel is it’s updates. The framework receives regular updates which means that if there are any security vulnerabilities, then chances of getting a quick update is more likely. I remember one of the projects where I was working with CodeIgniter. It had a bug in the core session module and the community had fixed it by adding a patch. However, the core framework never took that patch and hence every developer had to manually add that and the main framework continued to have that issue in a core module. Hence, this is one big reason why going with Laravel gives me a lot of confidence in terms of security.
A strong and easy-to-use ORM
Laravel’s Eloquent ORM allows us to setup relationships and then querying the database is just a walk in the park. In our social app, there are users who create posts that have actions and comments. Apart from that, every post also supports user interactions like “Like”, “Favourite” so on and so forth. The Eloquent ORM works beautifully in getting all these relationships in place and when we are pulling data, the queries are easy and the mobile app can rely on the response structure because it is going to be consistent with the underlying model. I can hide certain columns which are not required in the responses or tweak certain attributes through the model before returning a response. Because of these features, communication with databases becomes very easy.
Caching with Redis
As part of the tooling available out of the box, caching is a crucial piece. And the Redis implementation is so easy to work with in Laravel. Our intention was to provide data through Redis as much as possible and avoid frequent database queries. (Redis is an In-Memory data structure storage which stores data as key value pair and retrieval is very fast. The entire storage is in memory, which means in the RAM of the server and hence the retrieval is a lot faster than a database). An 8GB Redis storage ensures that we are sending back the response quickly by avoiding database queries in a lot of instances. And with Redis eviction policy set to LRU (Least Frequently Used), we are able to generally cache most of the post objects inside Redis and the records which are not used very frequently will be removed from the storage once we start consuming the entire allocated memory for Redis.
Working with Responses and Middlewares
Sending responses and status codes to the client in a consistent way is very important for the front end developers. Based on our response status codes, the mobile app services classes will handle the responses and behave in a particular way. For example, while creating a post if the app doesn’t receive a 201 status code but instead gets a 422, then the app will know that are some validation issues and accordingly the app will show a pop up which is configured at a navigation level and hence this level of code is not required on every screen. Similarly, our app can at a certain point get 500 status code. Which means, for some reason the API failed. So, we track that and the client app will send this debugging information to the API server for us to debug later. Hence, a stable and consistent status code response is important and Laravel’s response class does that out of the box.
We also modified the response class because it’s “Macroable” and added two functions – response.success and response.error which allows the API to send the response in a pre-defined way which the app expects. For example, response.error will always have a message key which the app can use to show to the end user. The success will always data a data field along with a message which the app can use. This way, we were able to standardise the responses even more. For more details on how we modified that, you can read a blog post by my colleague Manish Manghwani on “Customising the Laravel response”.
Now, we spoke so much about responses, how can we not talk about Middlewares? In the entire response pipeline, Middlewares play a very critical role. The entire authentication was handled through middleware which makes it so easy to maintain the routes of our application. If the route is inside the middleware group, then we know that the piece of logic will be executed. Apart from the basic authentication, we also used Middlewares to ensure the basic checks we want like a user is logged in or the user has an active subscription etc.
Queues and events
Sending a quick response to the client is crucial. The user should feel that the app is fast, and the API plays a key role in that. The API should be able to send a response back to the client based on the action taken as quick as possible. However, there are many situations, where one action can trigger many other sub tasks required for the application to perform properly. However, we don’t want the user to wait for those actions to get over.
Sounds complicated? Let me explain with a real life scenario. When the user creates a post, we make an entry to the post table. However, after that a lot of other things will happen. For example, we trigger an API call for content moderation. We make an entry to the database about the last activity of the user. We make notification entries based on how users are following the current user so that they know about this new post and we also update our search index. Now, if we analyse the entire set of actions then we will see that the end user doesn’t care what is going on in the back end. We can send a confirmation back to the user, the moment the post tabel entry is made. So, we use queues. A database entry is made, we trigger an event and then a successful response is sent to the user. Now, it is the responsibility of the queue worker to listen to that event and process all the listeners and action. Hence, all the tasks that we spoke about when the post was created are done by the queue worker running on a separate thread. Below is a diagram to show you the entire flow:
So, these tools really helped us a lot in the development of the API and not only made it easy and fast but also ensured the implementation is done in the best possible way. Although in today’s situation where the technology is changing fast, it is very important to keep pace with the changes, a matured framework takes away a lot of burden away from the developers. It allows them to focus on building features while the framework and it’s tools help them in all possible ways.