If you handle API development for mobile app like I do. I hope you can share me in comment about about it, because there is a problem that give me lots of headaches.
How do you make the mobile app backward compatible when old version of api will be deprecated when we toggle it in our config?
Other than this, this is also what is required to be achieved.
The older version of mobile app only need to be force update when the updated endpoint was required by this feature. Therefore, changes only can impact on its features and not the whole mobile app.
Trial and Error
The first solution that came out from my mind is wrapping the data inside a version attribute.
{
"today_temperature": "23 ", // use by app v1.0.0, not wrapped
"v2": { // use by app v1.1.0
"today": {
"temperature_val": 22,
"temperature_unit": "Celsius",
"symbol":"°C"
},
"tomorrow":{
"temperature_val": 29,
"temperature_unit": "Celsius",
"symbol":"°C"
},
}
}
This solution is good to go for when the older version doesn’t need to be deprecated. It can maintain old version of mobile app, but this doesn’t allow to deprecate the specific version. Besides, return data of all versions in 1 endpoint is making API heavier.
Approach that works
Make a new version for the endpoint. Example routes:
Route::group(['prefix' =>'/weather'], function() { Route::get('/', '\App\Http\Controllers\WeatherController@index'); Route::get('/v2', '\App\Http\Controllers\V2\WeatherController@index');});
In this solution, older version of app is still using the version 1 endpoint while the latest version is using the v2 endpoint. The has allow us to make whatever changes on the new endpoint without concerning on its affect on older one.
Besides that, we can now deprecate the older endpoint anytime we want to. In my case, I usually it is after the new version of (Android and iOS ) app has been reviewed and released to PlayStore/AppStore. Mobile App is always allowed to use the latest API and the existing app won’t be affected.
When older version of API has to be deprecated. We can use a fundamental component that standardise our error response. If did not read my previous article about standardise error response in Laravel, you can read it here.
We have a predefined exception for the error.
<?php
namespace App\Exceptions;
class UpdateRequiredException extends CustomException
{
public function __construct()
{
parent::__construct(trans('messages.update_required'), 99);
}
}
In our function for v1 api, an exception will be thrown.
public function index()
{
if (config('app.weather_v1_deprecated'), UpdateRequiredException::class);
// original codes and logic
}
This is a toggle that we can change the value at anytime we want to do. In this example, it is the environment variable with default false value. config('app.weather_v1_deprecated')
When we set this value as true, the v1 endpoint will return error response and let mobile app to handle update app. Example response:
{
"code": 99,
"message": "Please update app to continue."
}
In mobile side, it has logic that can handle this response and it will show the message and redirect to PlayStore/AppStore and let user to update app.
After the release, the toggle for this release can be removed. Routes for deprecated endpoint can be replace to a controller with an __invoke
function that only throw UpdateAppException.
Route::group(['prefix' =>'/weather'], function() {Route::get('/', UpdateAppController::class); // Deprecated EndpointRoute::get('/v2', '\App\Http\Controllers\V2\WeatherController@index');});
Conclusion
In my experiences, this is an effective way to reduce the rate to force update on our mobile application when there is a backward incompatible changes on only a specific part of the application.
While the pitfall of this approach was obvious, which is adding additional complexity to codebase on both backend and mobile. Besides that, it required good cross team communication and documentation to maintain good user experience and codebase clean.
Please let me know in comment section if there is better solution that can help me to do better in handling this challenge.