Laravel API Development Reference
A Laravel API development reference: setting up Sanctum, issuing API tokens, and building JSON API resource controllers.
Laravel Reference
Installing the API scaffolding
Since Laravel 11, a fresh project has no API routes file by default. This one command adds Sanctum, publishes its config/migration, and creates routes/api.php wired up in bootstrap/app.php.
php artisan install:api
Enable token issuing on the User model:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
}
php artisan migrate
Issuing tokens
Sanctum tokens are simple, database-backed API keys — no OAuth server needed. Typically issued right after a normal login, and given straight back to the client to store and send on future requests.
// e.g. in an API login controller
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return response()->json([
'token' => $user->createToken('mobile-app')->plainTextToken,
]);
}
The plain-text token is only ever shown once, at creation time — only its hash is stored in the personal_access_tokens table.
Token abilities (scopes)
// Restrict what this token is allowed to do
$token = $user->createToken('read-only', ['posts:read'])->plainTextToken;
// In a route or policy
if ($request->user()->tokenCan('posts:read')) {
// ...
}
Protecting API routes
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn (Request $request) => $request->user());
Route::apiResource('posts', Api\PostController::class);
});
The client sends the token on every request as a bearer token:
Authorization: Bearer 1|f8b3c9d2e1a4...
Revoking tokens
// Revoke the token used for the current request (e.g. logout)
$request->user()->currentAccessToken()->delete();
// Revoke every token for a user
$user->tokens()->delete();
API resource controllers
apiResource registers the same seven conventional actions as resource, minus the two that only make sense for an HTML form (create and edit), since an API client doesn't need a route that just returns a form.
php artisan make:controller Api/PostController --api --model=Post
Route::apiResource('posts', Api\PostController::class);
| Verb | URI | Action |
|---|---|---|
| GET | /posts | index |
| POST | /posts | store |
| GET | /posts/{post} | show |
| PUT/PATCH | /posts/{post} | update |
| DELETE | /posts/{post} | destroy |
Shaping JSON with API resources
An Eloquent model serialized directly to JSON exposes every column, in whatever shape the database happens to use. A resource class controls exactly what the API returns instead.
php artisan make:resource PostResource
class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'excerpt' => Str::limit($this->body, 100),
'author' => $this->whenLoaded('user', fn () => $this->user->name),
'published_at' => $this->published_at?->toIso8601String(),
];
}
}
public function show(Post $post)
{
return new PostResource($post);
}
public function index()
{
return PostResource::collection(Post::with('user')->paginate());
}
Rate limiting
API routes get a named api throttle by default (60 requests/minute per user or IP). Define your own limiter for finer control:
// bootstrap/app.php or a service provider
RateLimiter::for('uploads', function (Request $request) {
return Limit::perMinute(10)->by($request->user()->id);
});
Route::middleware('throttle:uploads')->post('/uploads', ...);