Подготовка:
Начиная с Laravel 11 файла routes/api.php в свежем проекте нет, его подключают отдельной командой. Она же ставит Sanctum, пакет для токенной аутентификации, и регистрирует всё в bootstrap/app.php. Контроллер генерируем с флагом --api: получим те же методы, что у resource-контроллера из главы 2, но без create и edit, ведь формы теперь рисует клиент, а не сервер.
Код: Выделить всё
php artisan install:api
php artisan make:controller Api/ArticleController --api --model=Article
php artisan make:resource ArticleResourceКод: Выделить всё
use App\Http\Controllers\Api\ArticleController;
Route::prefix('v1')->group(function () {
Route::apiResource('articles', ArticleController::class)
->only(['index', 'show']);
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('articles', ArticleController::class)
->except(['index', 'show']);
});
});Можно вернуть из контроллера return Article::all(), и Laravel честно отдаст JSON. Не делайте так. Формат ответа намертво привяжется к структуре таблицы, и первое же переименование колонки сломает мобильное приложение. Нужна прослойка, она называется API Resource.
Код: Выделить всё
// app/Http/Resources/ArticleResource.php
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'author' => $this->whenLoaded('user', fn () => $this->user->name),
'created_at' => $this->created_at->toIso8601String(),
];
}
// app/Http/Controllers/Api/ArticleController.php
public function index()
{
return ArticleResource::collection(
Article::with('user')->latest()->paginate(20)
);
}
public function store(StoreArticleRequest $request)
{
$article = $request->user()->articles()->create($request->validated());
return ArticleResource::make($article)
->response()
->setStatusCode(201);
}Токены через Sanctum:
Сессии для API не подходят. Клиент один раз обменивает email и пароль на токен, а дальше шлет его в каждом запросе в заголовке Authorization: Bearer.
Код: Выделить всё
Route::post('/v1/login', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Неверный email или пароль'], 401);
}
return ['token' => $user->createToken('mobile')->plainTextToken];
});Типичные грабли:
Первые и самые частые: забытый заголовок Accept: application/json. Без него Laravel считает клиента браузером. При ошибке валидации он делает редирект назад вместо 422, а на запрос без токена пытается отправить на страницу логина и падает с ошибкой Route [login] not defined. Приучите клиента слать этот заголовок всегда, а на сервере подстрахуйтесь в bootstrap/app.php через shouldRenderJsonWhen для путей api/*.
Вторые: N+1 из главы 6. Сам по себе whenLoaded связь не грузит, with('user') в контроллере обязателен, иначе список из 20 статей породит 21 запрос к базе.
Третьи: отдача всей таблицы разом. Только paginate(), никаких all() в index. Первый же клиент с парой сотен тысяч записей упрется в лимит памяти PHP, и хорошо если на тестовом сервере, а не в проде.
Что усвоили:
API в Laravel это те же кирпичи, что и раньше: маршруты, контроллеры, Eloquent, form request-ы, middleware. Новое здесь только ресурсы как контракт формата и Sanctum вместо сессий. На этом курс закончен, но не проект: добавьте rate limiting через throttle, напишите тесты на эндпоинты, вынесите тяжелые операции в очереди из главы 10. Фундамент у вас уже есть.