a couple api tips
- "agreement" with yourself about response structure like
// success response
{
"success": true,
"data": data
}
// fail response
{
"success": false,
"error": error
}
u can add more specific structure like "notification". all requests, in addition to primary data, contain a toast notification data, if necessary (for example, "you have new answers on your topic")
- dont forget about versioning api with backward compatibility. there are several techniques, but here, for example, how I do
// file structure
- app/
-- Http/
---- Controllers/
------ v1/
-------- UserController.php
------ v2/
-------- UserController.php
------ Controller.php
// Controller.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
final protected function success($result, int $code = 200): JsonResponse
{
$response = [
'success' => true,
'data' => $result,
];
return response()->json($response, $code);
}
final protected function error($error, int $code = 404): JsonResponse
{
$response = [
'success' => false,
'error' => $error,
];
return response()->json($response, $code);
}
// v1/UserController.php
<?php
namespace App\Http\Controllers\v1;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\Models\User;
class UserController extends Controller
{
public function logout()
{
try {
auth('api')->logout();
return $this->success(true);
} catch (JWTException $exception) {
return $this->error($exception, 401);
}
}
public function info()
{
$user = auth()->user();
return $this->success(new UserResource($user));
}
// v2/UserController.php
<?php
namespace App\Http\Controllers\v2;
use App\Http\Resources\UserResource;
use App\Models\User;
class UserController extends \App\Http\Controllers\v1\UserController
{
public function info()
{
$user = auth()->user();
$user->additionalDataAndRelations();
return $this->success(new UserResource($user));
}
// route/api/v1api.php
<?php
namespace App\Http\Controllers\v1;
use Illuminate\Support\Facades\Route;
Route::group([
'prefix' => '/v1',
], function ($router) {
Route::middleware(['auth:api'])->group(function () {
Route::group(['prefix' => 'user'], function () {
Route::get('logout', [UserController::class, 'logout']);
Route::get('info', [UserController::class, 'info']);
});
});
});
// route/api/v2/api.php
<?php
namespace App\Http\Controllers\v2;
use Illuminate\Support\Facades\Route;
Route::group([
'prefix' => '/v2',
], function ($router) {
Route::namespace('v2')->group(function () {
Route::middleware(['auth:api'])->group(function () {
Route::group(['prefix' => 'user'], function () {
Route::get('logout', [UserController::class, 'logout']);
Route::get('info', [UserController::class, 'info']);
});
});
});
});
at all u have routing
/api/v1/user/logout
/api/v1/user/info
/api/v2/user/logout
/api/v2/user/info
when u request to /api/v1/user/logout, u call v1/UserController.php@logout. when u request to /api/v2/user/logout, u call v1/UserController.php@logout too. because v2/UserController.php extends v1/UserController.php and not override logout method. and if request to /api/v2/user/info, u get user info with additional data or/and relations. if request to /api/v1/user/info -- only user data.
it is often need to backward compatibility for old api user, like old mobile app etc.
- often u need special route like /api/v1/summary, who return
{
"success": true,
"data": {
name: "my best app",
"minimal": {
"ios": 2.1,
"android": 3,
"admin": 1
}
}
}
it is for notification old mobile client about need to update. or complete blocking old app