From laravel-api-tool-kit
Adds a complete CRUD endpoint to Laravel projects (standard or DDD), via ordered steps for model, migration, filter, controller, policy, resources, and more.
How this command is triggered — by the user, by Claude, or both
Slash command
/laravel-api-tool-kit:new-endpointlaravel-api-tool-kit/workflows/The summary Claude sees in its command listing — used to decide when to auto-load this command
# Workflow: Add a New CRUD Endpoint Follow these steps in order. Do not skip steps or reorder them. --- ## Step 0 — Gather Requirements Before creating any file, confirm: - What is the resource name? (e.g. `Car`) - What columns does it have and what are their types? - Does it need soft deletes? - Does it need file uploads? - Which routes are needed? (all CRUD, or subset?) - Is authentication required? Which guard? ### Detect the project structure first - If you see `Domain/` → **DDD structure** — check `ls app/Domain/` to confirm folder naming conventions used in the project - If yo...
Follow these steps in order. Do not skip steps or reorder them.
Before creating any file, confirm:
Car)ls app/
Domain/ → DDD structure — check ls app/Domain/ to confirm folder naming conventions used in the projectModels/, Http/ only → Standard Laravel structure| Component | Standard Laravel | DDD |
|---|---|---|
| Model | app/Models/Car.php | app/Domain/Car/Models/Car.php |
| Filter | app/Filters/CarFilters.php | app/Domain/Car/Filters/CarFilters.php |
| Enum | app/Enums/CarStatusEnum.php | app/Domain/Car/Enums/CarStatusEnum.php |
| Action | app/Actions/CreateCarAction.php | app/Domain/Car/Actions/CreateCarAction.php |
| Policy | app/Policies/CarPolicy.php | app/Domain/Car/Policies/CarPolicy.php |
| Form Request | app/Http/Requests/Car/ | app/Http/Requests/Application/Car/ (or Dashboard/Car/) |
| Resource | app/Http/Resources/Car/CarResource.php | app/Http/Resources/Application/Car/CarResource.php |
| Controller | app/Http/Controllers/CarController.php | app/Http/Controllers/API/Application/Car/CarController.php |
Note: Controllers, Requests, and Resources always stay in
app/Http/regardless of structure — only domain logic moves intoapp/Domain/.
Follow rules/models.md for the correct structure.
$fillable$casts for booleans, enums, and arraysSoftDeletes if neededHasUlids trait + $keyType = 'string' + $incrementing = false$default_filters yet — do that after creating the Filter (Step 3)File: app/Models/Car.php — DDD: app/Domain/Car/Models/Car.php
Create a standard Laravel migration:
Use the project's primary key convention from SKILL.md Project Defaults:
// ULID project
Schema::create('cars', function (Blueprint $table) {
$table->ulid('id')->primary();
$table->string('name');
$table->string('color');
$table->boolean('is_active')->default(true);
$table->foreignUlid('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
// Auto-increment project
Schema::create('cars', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('color');
$table->boolean('is_active')->default(true);
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->timestamps();
});
Follow rules/filters.md for the correct structure.
$allowedFilters with columns clients can filter by equality$allowedSorts with sortable columns (always include created_at)$columnSearch if text search is needed$allowedIncludes for relationships clients can eager-loadFile: app/Filters/CarFilters.php — DDD: app/Domain/Car/Filters/CarFilters.php
Then go back to the Model and bind it:
protected string $default_filters = CarFilters::class;
Follow rules/enums.md for the correct structure. Create one for any fixed-value column (status, type, etc.).
File: app/Enums/CarStatusEnum.php — DDD: app/Domain/Car/Enums/CarStatusEnum.php
Cast it in the Model:
protected $casts = [
'status' => CarStatusEnum::class,
];
Use it in validation:
'status' => ['required', Rule::in(CarStatusEnum::values())],
Follow rules/requests.md for the correct structure.
sometimes prefix for partial updatesFiles:
Standard: app/Http/Requests/Car/CreateCarRequest.php
app/Http/Requests/Car/UpdateCarRequest.php
DDD: app/Http/Requests/Application/Car/CreateCarRequest.php
app/Http/Requests/Application/Car/UpdateCarRequest.php
(use Dashboard/Car/ for admin-facing requests)
Follow rules/resources.md for the correct structure.
$this->whenLoaded('relation') for every relationshipdateTimeFormat() for all timestampstoArray()File: app/Http/Resources/Car/CarResource.php — DDD: app/Http/Resources/Application/Car/CarResource.php
Follow rules/authorization.md. Create a Policy if any endpoint needs ownership or role checks.
Include all applicable methods: viewAny, view, create, update, delete.
File: app/Policies/CarPolicy.php — DDD: app/Domain/Car/Policies/CarPolicy.php
Register in AppServiceProvider or via automatic discovery (Laravel auto-discovers policies by convention).
Follow rules/actions.md for the correct structure. Create one only if the operation:
If it's a simple Model::create($data) — do it directly in the controller. No Action needed.
For external 3rd-party integrations (SMS, payment, etc.), see rules/services.md.
File: app/Actions/CreateCarAction.php — DDD: app/Domain/Car/Actions/CreateCarAction.php
Follow rules/controllers.md for the correct structure. Use the pattern with a constructor-injected Action/Service/Repository if you created one in Step 8.
$this->authorize() on methods that need policy checksFile: app/Http/Controllers/CarController.php — DDD: app/Http/Controllers/API/Application/Car/CarController.php
Since all user-facing strings MUST use trans(), create translation keys before writing messages in the controller.
// lang/en/car.php
return [
'created' => 'Car created successfully.',
'updated' => 'Car updated successfully.',
'deleted' => 'Car deleted successfully.',
];
Register the resource route in routes/api.php:
// Full CRUD
Route::apiResource('cars', CarController::class)->middleware('auth:sanctum');
// Or manual registration for partial CRUD
Route::middleware('auth:sanctum')->group(function () {
Route::get('cars', [CarController::class, 'index']);
Route::post('cars', [CarController::class, 'store'])->middleware('throttle:10,1');
Route::get('cars/{car}', [CarController::class, 'show']);
Route::put('cars/{car}', [CarController::class, 'update']);
Route::delete('cars/{car}', [CarController::class, 'destroy']);
});
Create a factory:
// database/factories/CarFactory.php
public function definition(): array
{
return [
'name' => fake()->word(),
'color' => fake()->safeColorName(),
'is_active' => true,
'user_id' => User::factory(),
];
}
Write a feature test covering index, store, show, update, destroy.
Before marking the feature done, verify (paths per Step 0 path map):
$fillable is populated$casts covers all booleans and enums$allowedFilters and $allowedSorts$request->validated()trans()dateTimeFormat()whenLoaded()declare(strict_types=1) on every new filenpx claudepluginhub ahmedesa/laravel-api-tool-kit/endpointGenerates complete FastAPI CRUD endpoint for MongoDB using Beanie, with Pydantic schemas, models, service layer, router, and tests. Prompts for resource name, fields, auth, caching.
/blazor-apiGenerates ASP.NET Core API endpoints for an entity using minimal APIs or controllers, including full CRUD, validation, authorization, and OpenAPI docs.
/laravelBuilds, configures, and optimizes Laravel apps with Eloquent models, service layers, queues, events, broadcasting, Sanctum/Passport auth, and Pest tests. Supports API, auth, queue, model gen, optimization, upgrade, audit flags.