laravel-nuxt
Skill0 starsChanged 29 days agoMITView on GitHub
API Laravel 13 optimisée pour Nuxt.js. Routes API, Sanctum SPA auth, SSR-friendly responses, typed endpoints, JSON:API. Utiliser quand on crée une API Laravel destinée à un frontend Nuxt, on configure l'auth entre Laravel et Nuxt, ou on structure les endpoints pour la consommation Nuxt.
In this repository29 other files it gives its agents.
- AGENTS.mdAGENTS.md
- skills/laravel-core/templates/AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- dokploy-deploySkill
- laravel-adminSkill
- laravel-aiSkill
- laravel-apiSkill
- laravel-coreSkill
- laravel-deploySkill
- laravel-inertiaSkill
- laravel-livewireSkill
- laravel-mcpSkill
- laravel-microserviceSkill
- laravel-multi-tenancySkill
- laravel-performanceSkill
- laravel-queueSkill
- laravel-securitySkill
- laravel-testingSkill
- laravel-websocketSkill
- nuxt-coreSkill
- nuxt-uiSkill
- pestSkill
- project-definerSkill
- scribeSkill
- searxng-mcpSkill
- sentrySkill
- skill-creatorSkill
- skill-health-checkerSkill
- spatie-query-builderSkill
The file
Files
Laravel Nuxt — API Laravel pour Nuxt.js
Tu es un expert en APIs Laravel optimisées pour Nuxt.js. Ton rôle est de
concevoir des endpoints qui s'intègrent parfaitement avec les composables
Nuxt (useFetch, useAsyncData, $fetch).
Quand utiliser
- Créer une API Laravel pour un frontend Nuxt
- Configurer Sanctum SPA auth entre Laravel et Nuxt
- Structurer les endpoints pour SSR-friendly responses
- Typer les réponses API pour TypeScript
- Optimiser les appels réseau pour SSR/CSR
Quand NE PAS utiliser
- API purement interne (pas de frontend Nuxt)
- Application monolithe Blade sans Nuxt
- Microservice sans consommation Nuxt
Architecture
Laravel (API) Nuxt (Frontend)
├── routes/api.php ├── composables/useApi.ts
├── app/Http/Controllers/Api/ ├── plugins/api.ts
├── app/Http/Resources/ ├── types/api.ts
├── app/Http/Middleware/ └── middleware/auth.ts
└── config/cors.php1. Configuration CORS
// config/cors.php
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => [
env('NUXT_URL', 'http://localhost:3000'),
],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true, // REQUIS pour Sanctum SPA
];2. Sanctum SPA Auth
// routes/api.php
use Illuminate\Support\Facades\Route;
// Public routes
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
Route::post('/forgot-password', [PasswordResetController::class, 'store']);
// CSRF cookie pour Nuxt
Route::get('/sanctum/csrf-cookie', function () {
return response()->json(['status' => 'ok']);
});
// Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn (Request $r) => new UserResource($r->user()));
Route::post('/logout', [AuthController::class, 'logout']);
Route::apiResource('posts', PostController::class);
});// app/Http/Controllers/Api/AuthController.php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
public function login(Request $request): JsonResponse
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required|string',
]);
$user = \App\Models\User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Les identifiants sont incorrects.'],
]);
}
$token = $user->createToken($request->device_name)->plainTextToken;
return response()->json([
'user' => new UserResource($user),
'token' => $token,
]);
}
public function logout(Request $request): JsonResponse
{
$request->user()->currentAccessToken()->delete();
return response()->json(null, 204);
}
}3. Controller API typé
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Http\Resources\PostCollection;
use App\Http\Resources\PostResource;
use App\Models\Post;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class PostController extends Controller
{
// GET /api/posts — compatible avec useFetch/useAsyncData
public function index(Request $request): PostCollection
{
$posts = Post::query()
->with('user:id,name,avatar')
->when($request->search, fn ($q, $s) =>
$q->where('title', 'LIKE', "%{$s}%")
)
->when($request->category, fn ($q, $c) =>
$q->where('category', $c)
)
->latest()
->paginate($request->per_page ?? 15);
return new PostCollection($posts);
}
// GET /api/posts/{post}
public function show(Post $post): PostResource
{
$post->load(['user:id,name,avatar', 'comments.user:id,name,avatar']);
return new PostResource($post);
}
// POST /api/posts
public function store(StorePostRequest $request): JsonResponse
{
$post = Post::create([
...$request->validated(),
'user_id' => $request->user()->id,
]);
return (new PostResource($post->load('user')))
->response()
->setStatusCode(201);
}
// PUT /api/posts/{post}
public function update(UpdatePostRequest $request, Post $post): PostResource
{
$this->authorize('update', $post);
$post->update($request->validated());
return new PostResource($post->fresh()->load('user'));
}
// DELETE /api/posts/{post}
public function destroy(Post $post): JsonResponse
{
$this->authorize('delete', $post);
$post->delete();
return response()->json(null, 204);
}
}4. API Resource pour Nuxt
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'content' => $this->content,
'excerpt' => $this->excerpt,
'is_published' => $this->is_published,
'published_at' => $this->published_at?->toISOString(),
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
// Relations (seulement si chargées)
'user' => new UserResource($this->whenLoaded('user')),
'comments' => CommentResource::collection($this->whenLoaded('comments')),
'comments_count' => $this->whenCounted('comments'),
// Links pour la navigation Nuxt
'links' => [
'self' => route('api.posts.show', $this->id),
'author' => route('api.users.show', $this->user_id),
],
];
}
}5. Réponses standardisées
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PostCollection extends ResourceCollection
{
public $collects = PostResource::class;
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
'meta' => [
'current_page' => $this->resource->currentPage(),
'last_page' => $this->resource->lastPage(),
'per_page' => $this->resource->perPage(),
'total' => $this->resource->total(),
],
'links' => [
'self' => $this->resource->url($this->resource->currentPage()),
'next' => $this->resource->nextPageUrl(),
'prev' => $this->resource->previousPageUrl(),
'first' => $this->resource->url(1),
'last' => $this->resource->url($this->resource->lastPage()),
],
];
}
}6. Rate limiting
<?php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
// AppServiceProvider.php
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(
$request->user()?->id ?: $request->ip()
);
});
// Rate limiting spécifique pour l'auth
RateLimiter::for('auth', function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
}7. Routes API versionnées
<?php
// routes/api.php
use Illuminate\Support\Facades\Route;
Route::prefix('v1')->group(function () {
// Public
Route::post('/login', [AuthController::class, 'login'])
->middleware('throttle:auth');
Route::post('/register', [AuthController::class, 'register']);
Route::get('/posts', [PostController::class, 'index'])
->middleware('cache:60'); // Cache 60 secondes
// Protected
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', fn ($r) => new UserResource($r->user()));
Route::apiResource('posts', PostController::class)
->except('index', 'show');
});
});8. Error responses pour Nuxt
<?php
namespace App\Exceptions;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ApiExceptionHandler
{
public function render($request, \Throwable $e)
{
if ($request->expectsJson() || $request->is('api/*')) {
return match (true) {
$e instanceof ValidationException => response()->json([
'message' => 'Erreur de validation',
'errors' => $e->errors(),
], 422),
$e instanceof AuthenticationException => response()->json([
'message' => 'Non authentifié',
], 401),
$e instanceof NotFoundHttpException => response()->json([
'message' => 'Ressource non trouvée',
], 404),
default => response()->json([
'message' => $e->getMessage(),
], 500),
};
}
return parent::render($request, $e);
}
}Bonnes pratiques
- Sanctum SPA auth — Token dans les cookies,
withCredentials: true - Réponses typées — API Resources avec structure cohérente
- Pagination — Toujours paginer,
meta+links - Loading states —
pendingnatif dansuseFetch - Error handling — Format
{ message, errors }pour NuxtuseError() - Caching — Routes GET cachées côté serveur et client
- Rate limiting — Différent pour auth vs API
- CORS —
supports_credentials: trueobligatoire - Device name — Requis pour Sanctum multi-device tokens
- Relations chargées —
with()pour éviter le N+1
Validation
- CORS configuré avec
supports_credentials: true - Sanctum SPA auth fonctionnelle
- API Resources pour tous les modèles
- Pagination avec
meta+links - Error responses formatées
- Rate limiting configuré
- Routes versionnées (
/api/v1/...)
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

