Перейти к основному содержимому

Laravel API с Sanctum

Разработчику

Laravel API с Sanctum

Первая программа на Laravel строит классический сайт — формы, Blade, сессия в cookie. Современные проекты часто отдают JSON отдельному клиенту — SPA на Vue/React, мобильное приложение, другой сервис.

API (Application Programming Interface) здесь — набор URL, которые принимают и возвращают JSON вместо HTML.

Laravel Sanctum выдаёт пользователю API-токен — длинную строку, которую клиент передаёт в заголовке Authorization: Bearer … при каждом запросе. Это проще полноценного OAuth2 для "своего" фронта и мобильных приложений.

Обзор MVC: Laravel 143.md. Альтернатива в Symfony: Symfony.


Web и API — в чём разница

routes/web.phproutes/api.php
ОтветHTML (Blade)JSON
СессияCookie, CSRF-токен в формахОбычно без cookie-сессии
Префикс URL//api/… (Laravel добавляет автоматически)
Middlewareweb (CSRF, сессия)api (throttle, без CSRF для токена)

Ошибка 419 Page Expired при POST в API чаще всего значит, что запрос попал в web.php и Laravel ждёт CSRF-токен. API-маршруты держите в api.php.


Термины

ТерминЗначение
JSONТекстовый формат { "key": "value" } для обмена данными
Bearer tokenТокен в заголовке Authorization: Bearer abc123…
ResourceКласс, который задаёт, какие поля модели уходят в JSON
SanctumПакет Laravel для токенов и SPA-аутентификации
401 Unauthorized"Не знаю, кто ты" — нет или неверный токен
403 Forbidden"Знаю, но нельзя" — Policy запретила действие

Что получится в конце

ШагРезультат
GET /api/notes без токена401
POST /api/login с email и passwordJSON { "token": "…" }
Запросы с Authorization: Bearer …CRUD заметок

Модель Note

После учебного проекта из Первая программа на Laravel:

php artisan make:model Note -m

Разбор:

  • make:model Note -m создает модель Note и миграцию таблицы за одну команду.
  • Это ускоряет старт, потому что структура данных и ORM-слой появляются синхронно.
  • Дальше модель используется в контроллере и Resource как единый источник данных.

app/Models/Note.php:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Note extends Model
{
protected $fillable = ['text'];
}

В миграции — поле text (string). Затем:

php artisan migrate

Разбор:

  • migrate применяет все непримененные миграции и создает/обновляет таблицы.
  • Для Note это означает, что таблица физически появляется в базе и готова к CRUD.
  • Без этой команды API вернет ошибки уровня SQLSTATE при попытке чтения или записи.

$fillable разрешает массовое заполнение Note::create(['text' => '…']) — без этого Laravel проигнорирует поле из соображений безопасности.


Установка Sanctum

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

Разбор:

  • composer require добавляет пакет Sanctum в проект и подтягивает его зависимости.
  • vendor:publish копирует конфигурацию и миграции Sanctum в проект, чтобы их можно было контролировать.
  • Повторный migrate создает таблицы токенов (personal_access_tokens) для хранения хэшей ключей доступа.

Миграция создаёт таблицу personal_access_tokens — там хранятся хэши токенов.

app/Models/User.php:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}

Разбор:

  • Трейт HasApiTokens добавляет модели User методы для создания и управления API-токенами.
  • createToken(...) возвращает plainTextToken только один раз, поэтому клиент должен сохранить его сразу.
  • Это стандартная интеграция Sanctum для Bearer-аутентификации в API.

Трейт HasApiTokens добавляет метод $user->createToken('имя')->plainTextToken.

В Laravel 11+ маршруты API часто уже защищены middleware auth:sanctum в группе в routes/api.php.


API Resource — форма JSON

Модель Note в БД может иметь десятки полей. В API клиенту отдают только нужные — через Resource:

php artisan make:resource NoteResource

Разбор:

  • Команда создает класс Resource, который отвечает за JSON-представление модели.
  • Resource отделяет внутреннюю структуру БД от публичного API-контракта.
  • Это делает API стабильнее при будущих изменениях модели.

Код ITЗагрузка примера кода…

Разбор:

  • toArray(Request $request) определяет точный набор полей, которые попадут в JSON-ответ.
  • created_at?->toIso8601String() безопасно форматирует дату, даже если поле null.
  • Такой слой особенно полезен, когда нужно скрывать внутренние поля и отдавать только бизнес-значимые данные.

$this внутри Resource — обёртка над моделью Note. ?-> — безопасный вызов, если дата null.

NoteResource::collection($notes) превращает коллекцию в JSON-массив объектов одинаковой структуры.


Контроллер API

php artisan make:controller Api/NoteController --api

Разбор:

  • --api генерирует контроллер без HTML-методов create/edit, оставляя API-ориентированные действия.
  • Это подчеркивает разделение между веб-интерфейсом и JSON API.
  • Такой контроллер проще держать чистым и узкоспециализированным.

Флаг --api создаёт методы без create/edit для HTML-форм.

Код ITЗагрузка примера кода…

Разбор:

  • index() возвращает коллекцию через NoteResource::collection(...), сохраняя единый формат JSON.
  • store() валидирует вход, создает запись и отдает созданный объект в том же формате Resource.
  • destroy() удаляет запись и отвечает 204 No Content, что соответствует REST-практике для DELETE.
  • Типизация destroy(Note $note) использует route model binding и сокращает ручной код поиска.

validate при ошибке вернёт JSON 422 с описанием полей. noContent() — ответ 204 без тела (стандарт для DELETE).


Маршруты и логин

routes/api.php:

Код ITЗагрузка примера кода…

Разбор:

  • Публичный /login endpoint принимает email/password, валидирует и выдает токен при успешной проверке.
  • Hash::check(...) сравнивает введенный пароль с хэшем в базе, не раскрывая исходный пароль.
  • Группа Route::middleware('auth:sanctum') защищает CRUD-маршруты и требует Bearer-токен.
  • Route::apiResource(...)->only([...]) ограничивает набор операций и делает API более предсказуемым.

Разбор логина

  1. validate — email и password обязательны.
  2. User::where('email', …)->first() — ищем пользователя.
  3. Hash::check($plain, $user->password) — сравнение с хэшем в БД (пароль в открытом виде в БД не хранится).
  4. createToken('api') — новая запись в personal_access_tokens; plainTextToken показывается один раз клиенту.

Защищённая группа: middleware auth:sanctum читает Bearer-токен и подставляет $request->user().

Создайте пользователя: php artisan tinkerUser::factory()->create(['email' => 'user@example.com', 'password' => bcrypt('password')]).


Проверка через curl

curl -X POST http://127.0.0.1:8000/api/login \
-H "Content-Type: application/json" \
-d "{\"email\":\"user@example.com\",\"password\":\"password\"}"

Разбор:

  • Первый curl имитирует логин-клиент и получает токен доступа в JSON.
  • Заголовок Content-Type: application/json обязателен для корректного парсинга тела запроса.
  • Возвращенный токен затем используется как Bearer в остальных запросах.

Ответ: {"token":"1|abcdef…"}. Скопируйте значение token.

curl http://127.0.0.1:8000/api/notes \
-H "Authorization: Bearer 1|abcdef..."

curl -X POST http://127.0.0.1:8000/api/notes \
-H "Authorization: Bearer 1|abcdef..." \
-H "Content-Type: application/json" \
-d "{\"text\":\"Заметка из API\"}"

Разбор:

  • Заголовок Authorization: Bearer ... подтверждает личность клиента для защищенных endpoint-ов.
  • GET-запрос возвращает список заметок, доступных аутентифицированному пользователю.
  • POST-запрос создает новую заметку через тот же API-контракт и проверки валидации.
  • Такой набор команд удобно использовать как базовый smoke-тест API без Postman.

Префикс /api Laravel добавляет к маршрутам из routes/api.php автоматически.


Частые ошибки

СимптомПричина
419 / CSRFPOST на web.php вместо api.php
401 на все запросыНет заголовка Authorization: Bearer или опечатка в токене
Пустой JSONЗабыли обернуть в NoteResource
422 на storeНе передали text или превышен max:2000

Что попробовать дальше

  1. Feature-тест: $this->actingAs($user)->getJson('/api/notes') или withToken()PHPUnit.
  2. Policy: удалять заметку может только автор — Очереди и политики.
  3. SPA на Vue — хранить токен в localStorage и передавать в fetch.

Связанные материалы


Безопасное хранение токенов и отзыв доступа

Для мобильных и серверных клиентов токен хранится в защищенном хранилище платформы. Для SPA используют краткоживущие токены и понятный процесс выхода.


Отзыв токена при logout

Route::middleware('auth:sanctum')->post('/logout', function (Request $request) {
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Logged out']);
});

Разбор:

  • Endpoint доступен только аутентифицированному пользователю через auth:sanctum.
  • currentAccessToken()->delete() отзывает именно текущий токен клиента, а не все токены пользователя.
  • Это корректный logout для конкретной сессии устройства/приложения.
  • Ответ JSON подтверждает успешное завершение API-сессии.

Этот endpoint завершает текущую сессию API-клиента и снижает риск утечки долгоживущих токенов.


Версионирование API

При росте проекта удобно закладывать версию в URL:

  • /api/v1/notes
  • /api/v2/notes

Подход помогает выпускать новые поля и правила без резкого изменения старых клиентов.


Практика

  • Создайте группу Route::prefix('v1').
  • Добавьте NoteV1Resource и отдельные feature-тесты.
  • Подготовьте план миграции для клиентов.

Связанные темы: Livewire, Filament, тестирование в PHP.