
Cómo montar una API en tu proyecto Laravel 12 existente (y crear un CRUD de clientes) — Guía en 5 minutos
Prerrequisitos rápidos
PHP 8.2+ y Composer.
Proyecto Laravel 12 ya funcionando.
DB configurada en
..env
Si estas trabajando con una versióno de laravel mayor a la 10 es necesario ejecutar esta linea
php artisan install:api
ya que las versiones posteriores de laravel no incluyen el móduloRecuerda revisar este link, para descargar el Modelo, la Migración y el .CSV de clientes
1) Instala y configura Sanctum (opcional pero muy recomendado)
composer require laravel/sanctum php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" php artisan migrate
En
app/Http/Kernel.php
agrega el middleware para API si no está:protected $middlewareGroups = [ 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
En
config/sanctum.php
, si tu frontend vive en otro dominio, añade su URL en stateful
.Tokens personales: para pruebas, puedes generar un token:
// En un tinker o seeder: $user = \App\Models\User::first(); $token = $user->createToken('api-token')->plainTextToken; // Usa "Authorization: Bearer $token" en tus requests
2) Crea la migración y el modelo de clientes
2.1 Migración
php artisan make:model Cliente -m
Edita
database/migrations/xxxx_xx_xx_create_clientes_table.php
:public function up(): void { Schema::create('clientes', function (Blueprint $table) { $table->id(); $table->string('nombre', 120); $table->string('apellido_paterno', 120)->nullable(); $table->string('apellido_materno', 120)->nullable(); $table->string('email')->unique(); $table->string('telefono', 20)->nullable(); $table->timestamps(); $table->softDeletes(); }); }
2.2 Modelo
En
app/Models/Cliente.php
:namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Cliente extends Model { use SoftDeletes; protected $fillable = [ 'nombre', 'apellido_paterno', 'apellido_materno', 'email', 'telefono' ]; protected $appends = ['nombre_completo']; public function getNombreCompletoAttribute(): string { return trim($this->nombre.' '.$this->apellido_paterno.' '.$this->apellido_materno); } }
Aplica migraciones:
php artisan migrate
3) (BONUS) Respuestas consistentes para tu API
Crea
app/Http/Responses/ApiResponse.php
:namespace App\Http\Responses; class ApiResponse { public static function success($data = null, string $message = null, int $code = 200) { return response()->json([ 'success' => true, 'message' => $message, 'data' => $data, 'errors' => null, ], $code); } public static function error(string $message, $errors = null, int $code = 422) { return response()->json([ 'success' => false, 'message' => $message, 'data' => null, 'errors' => $errors, ], $code); } }
4) Requests de validación
php artisan make:request StoreClienteRequest php artisan make:request UpdateClienteRequest
app/Http/Requests/StoreClienteRequest.php
:public function rules(): array { return [ 'nombre' => ['required','string','max:120'], 'apellido_paterno' => ['nullable','string','max:120'], 'apellido_materno' => ['nullable','string','max:120'], 'email' => ['required','email','unique:clientes,email'], 'telefono' => ['nullable','string','max:20'], ]; }
app/Http/Requests/UpdateClienteRequest.php
:public function rules(): array { $id = $this->route('cliente'); // viene del binding return [ 'nombre' => ['sometimes','string','max:120'], 'apellido_paterno' => ['sometimes','nullable','string','max:120'], 'apellido_materno' => ['sometimes','nullable','string','max:120'], 'email' => ['sometimes','email',"unique:clientes,email,{$id}"], 'telefono' => ['sometimes','nullable','string','max:20'], ]; }
5) Controlador API (CRUD)
php artisan make:controller Api/ClienteController --api
app/Http/Controllers/Api/ClienteController.php
:namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Requests\StoreClienteRequest; use App\Http\Requests\UpdateClienteRequest; use App\Http\Responses\ApiResponse; use App\Models\Cliente; use Illuminate\Http\Request; class ClienteController extends Controller { public function index(Request $request) { $q = $request->get('q'); $clientes = Cliente::query() ->when($q, fn($qq) => $qq->where('nombre', 'like', "%$q%") ->orWhere('apellido_paterno','like',"%$q%") ->orWhere('apellido_materno','like',"%$q%") ->orWhere('email','like',"%$q%") ) ->latest() ->paginate(10); return ApiResponse::success($clientes); } public function store(StoreClienteRequest $request) { $cliente = Cliente::create($request->validated()); return ApiResponse::success($cliente, 'Cliente creado', 201); } public function show(Cliente $cliente) { return ApiResponse::success($cliente); } public function update(UpdateClienteRequest $request, Cliente $cliente) { $cliente->update($request->validated()); return ApiResponse::success($cliente, 'Cliente actualizado'); } public function destroy(Cliente $cliente) { $cliente->delete(); return ApiResponse::success(null, 'Cliente eliminado', 204); } }
Route Model Binding: al tipar
, Laravel resuelve el ID automáticamente.Cliente $cliente
6) Rutas de la API
En
routes/api.php
:use App\Http\Controllers\Api\ClienteController; Route::middleware(['auth:sanctum'])->group(function () { Route::apiResource('clientes', ClienteController::class); });
Para pruebas sin auth, elimina el middleware (no recomendado en producción).
7) Probar rápido (curl / Postman)
Crear
curl -X POST https://tudominio.test/api/clientes \ -H "Authorization: Bearer TU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "nombre":"Manuel", "apellido_paterno":"Bravo", "email":"manuel@example.com", "telefono":"771-000-0000" }'
Listar con búsqueda y paginación
curl -H "Authorization: Bearer TU_TOKEN" \ "https://tudominio.test/api/clientes?q=manu&page=1"
Ver detalle
curl -H "Authorization: Bearer TU_TOKEN" \ "https://tudominio.test/api/clientes/1"
Actualizar
curl -X PUT https://tudominio.test/api/clientes/1 \ -H "Authorization: Bearer TU_TOKEN" \ -H "Content-Type: application/json" \ -d '{"telefono":"771-123-4567"}'
Eliminar
curl -X DELETE https://tudominio.test/api/clientes/1 \ -H "Authorization: Bearer TU_TOKEN"
8) Buenas prácticas rápidas
Versiona tus rutas (
) para cambios futuros./api/v1/clientes
Capa de presentación: usa Resources si necesitas transformar campos:
php artisan make:resource ClienteResource
Logs y excepciones: centraliza errores con
.App\Exceptions\Handler
CORS: configura
si consumirás desde otro dominio.config/cors.php
Rate limiting: ajusta límites en
si hace falta.RouteServiceProvider
9) Checklist de producción
✅ HTTPS habilitado
✅ Tokens rotados y revocados cuando corresponda
✅ Backups de DB
✅ Monitoreo (Laravel Telescope opcional en entornos controlados)
✅ Documentación de endpoints (OpenAPI/Swagger con darkaonline/l5-swagger, opcional)