Skip to content

API Mode

Laravel AutoCrud can expose the same CRUD engine through two independent interfaces:

  • Web/Inertia interface: enabled by default and used by the published Vue/Inertia components.
  • JSON API interface: optional and intended for mobile apps, SPAs, admin panels, or any external frontend.

Both interfaces share the same internal services, validation rules, model metadata, relationships, custom fields, file handling, forbidden actions, records, and events.

Publish the package config if you have not done it yet:

Terminal window
php artisan vendor:publish --tag=laravel-auto-crud-config --force

Then enable the API routes in config/laravel-auto-crud.php:

return [
'timezone' => 'Atlantic/Canary',
'web' => [
'enabled' => true,
'prefix' => 'laravel-auto-crud',
'middleware' => ['web'],
'protected_middleware' => ['auth', 'checkForbiddenActions'],
],
'api' => [
'enabled' => true,
'prefix' => 'api/laravel-auto-crud',
'middleware' => ['forceJsonResponse', 'api', 'auth:sanctum', 'checkForbiddenActions'],
'public_middleware' => null,
],
];

After changing package config in a cached application, refresh Laravel’s cache:

Terminal window
php artisan config:clear
php artisan route:clear
OptionDefaultDescription
api.enabledfalseEnables or disables API routes.
api.prefixapi/laravel-auto-crudPrefix for all API endpoints.
api.middleware['forceJsonResponse', 'api', 'auth:sanctum', 'checkForbiddenActions']Middleware used by protected API endpoints.
api.public_middlewarenullMiddleware used by public file/image endpoints. When null, it is derived from api.middleware without auth and forbidden-action middleware.

The forceJsonResponse middleware sets Accept: application/json on API requests before Laravel handles validation or authentication. This prevents HTML redirects for validation failures or unauthenticated requests.

The default API middleware uses auth:sanctum. This is a safe default for external frontends, but you can replace it:

'api' => [
'enabled' => true,
'prefix' => 'api/laravel-auto-crud',
'middleware' => ['forceJsonResponse', 'api', 'auth:api', 'checkForbiddenActions'],
],

For a token-based client, send the bearer token with every protected request:

Authorization: Bearer <token>
Accept: application/json

The package still forces JSON through middleware, but external clients should send Accept: application/json explicitly.

The {model} route parameter is resolved with Str::studly() against App\Models.

URL parameterResolved model
productApp\Models\Product
user_profileApp\Models\UserProfile
reservation_vehicleApp\Models\ReservationVehicle

Successful API responses use this shape:

{
"success": true,
"message": "Elemento creado.",
"data": {},
"meta": {}
}

message and meta are included only when relevant.

Error responses use this shape when generated by the package:

{
"success": false,
"message": "No tienes permiso para realizar esta acción.",
"errors": {}
}

Laravel validation errors keep Laravel’s standard JSON validation format unless your application customizes the exception handler.

External frontends should start by loading the schema for each model:

GET /api/laravel-auto-crud/{model}/schema

Example:

GET /api/laravel-auto-crud/product/schema

Response:

{
"success": true,
"data": {
"endPoint": "/api/laravel-auto-crud/product",
"endPoints": {
"web": "/laravel-auto-crud/product",
"api": "/api/laravel-auto-crud/product"
},
"formFields": [],
"tableHeaders": [],
"externalRelations": [],
"forbiddenActions": [],
"calendarFields": [],
"customFieldsEnabled": false
}
}

Use formFields to render forms, tableHeaders to render tables, externalRelations to render related resources, and endPoint for subsequent API calls.

MethodEndpointDescription
GET/{model}/schemaReturns model metadata for external frontends.
POST/{model}/load-itemsReturns paginated table data.
GET/{model}/allReturns all records with includes.
GET/{model}/{id}Returns one record with includes and custom fields.
POST/{model}Creates a record.
PUT/{model}/{id}Updates a record.
PATCH/{model}/{id}Updates a record.
POST/{model}/{id}Updates a record, useful for multipart/form-data with _method.
DELETE/{model}/{id}Soft deletes a record.
POST/{model}/{id}/destroySoft deletes a record, compatibility alternative.
POST/{model}/{id}/restoreRestores a soft-deleted record.
DELETE/{model}/{id}/forcePermanently deletes a soft-deleted record.
POST/{model}/{id}/permanentPermanently deletes a soft-deleted record, compatibility alternative.
GET/{model}/export-excelReturns all model items in the existing export payload.

All endpoints in this table are relative to the configured API prefix. With the default prefix, /{model}/schema becomes /api/laravel-auto-crud/{model}/schema.

The API uses the same server-side table engine as the Inertia components.

POST /api/laravel-auto-crud/product/load-items
Content-Type: application/json

Payload:

{
"page": 1,
"itemsPerPage": 10,
"sortBy": "[{\"key\":\"name\",\"order\":\"asc\"}]",
"search": "{\"name\":\"chair\"}",
"exactFilters": "{\"category_id\":2}",
"deleted": false
}

Response:

{
"success": true,
"data": {
"items": [],
"itemsLength": 0,
"itemsPerPage": 10,
"page": 1,
"sortBy": [],
"search": {},
"deleted": false
},
"meta": {
"schema": {}
}
}

Notes:

  • sortBy, search, and exactFilters are JSON-encoded strings for compatibility with the existing table engine.
  • Set itemsPerPage to -1 to request all matching rows.
  • Set deleted to true to load soft-deleted rows when the model uses SoftDeletes.
POST /api/laravel-auto-crud/product
Content-Type: application/json

Payload:

{
"name": "Chair",
"price": 49,
"is_active": true
}

Response:

{
"success": true,
"message": "Elemento creado.",
"data": {
"id": 1,
"name": "Chair",
"price": "49",
"is_active": true
}
}

The same DynamicFormRequest rules used by the web interface are applied to API create requests.

PUT /api/laravel-auto-crud/product/1
Content-Type: application/json

Payload:

{
"name": "Office chair",
"price": 59,
"is_active": true
}

For file uploads, use POST with _method=PUT and multipart/form-data:

POST /api/laravel-auto-crud/product/1
Content-Type: multipart/form-data

Form fields:

_method=PUT
name=Office chair
image=<binary file>

Password fields are ignored during update when they are empty, matching the web behavior.

AutoCrud supports public and private files/images defined in model fields.

MethodEndpointDescription
GET/public/images/{model}/{field}/{id}Serves a public image.
GET/public/files/{model}/{field}/{id}Serves a public file.
GET/private/images/{model}/{field}/{id}Serves a private image through protected API middleware.
GET/private/files/{model}/{field}/{id}Serves a private file through protected API middleware.

These endpoints are relative to api.prefix. With the default prefix, public images are served from /api/laravel-auto-crud/public/images/{model}/{field}/{id}.

By default, public file/image endpoints use api.public_middleware. If it is null, the package derives it from api.middleware by removing auth, auth:sanctum, and checkForbiddenActions.

If you use a custom auth middleware such as auth:api, set api.public_middleware explicitly so public file/image endpoints do not require authentication unless that is your intention.

Server-side autocomplete:

POST /api/laravel-auto-crud/{model}/load-autocomplete-items
Content-Type: application/json

Payload:

{
"search": "john",
"key": "user_id"
}

Response:

{
"success": true,
"data": []
}

Load all related options:

GET /api/laravel-auto-crud/{model}/all
POST /api/laravel-auto-crud/{model}/load-calendar-events
Content-Type: application/json

Payload:

{
"start": "2026-01-01",
"end": "2026-01-31"
}

Response:

{
"success": true,
"data": {
"items": []
}
}

The model must define $calendarFields for useful calendar output.

AutoCrud exposes relation actions for external relations declared in $externalRelations.

MethodEndpointDescription
POST/{model}/{id}/bind/{externalRelation}/{item}Attaches an item to a relation.
PUT/{model}/{id}/pivot/{externalRelation}/{item}Updates pivot fields.
PATCH/{model}/{id}/pivot/{externalRelation}/{item}Updates pivot fields.
POST/{model}/{id}/pivot/{externalRelation}/{item}Updates pivot fields, compatibility alternative.
DELETE/{model}/{id}/unbind/{externalRelation}/{item}Detaches an item from a relation.
POST/{model}/{id}/unbind/{externalRelation}/{item}Detaches an item from a relation, compatibility alternative.

Bind example:

POST /api/laravel-auto-crud/reservation/1/bind/vehicles/5
Content-Type: application/json

Payload with pivot fields:

{
"driver": true,
"notes": "Primary vehicle"
}

Pivot fields are validated with the same rules defined in the relation metadata.

Custom fields can be managed through API endpoints when your model has custom fields enabled.

MethodEndpointDescription
GET/custom-fields-typesReturns available custom field types.
GET/custom-fields/{model}Lists custom field definitions for a model.
POST/custom-fields/{model}Creates a custom field definition.
PUT/custom-fields/{model}/{id}Updates a custom field definition.
PATCH/custom-fields/{model}/{id}Updates a custom field definition.
DELETE/custom-fields/{model}/{id}Deletes a custom field definition.
POST/custom-fields/{model}/{id}/destroyDeletes a custom field definition, compatibility alternative.
POST/custom-fields/{model}/reorderReorders custom field definitions.

Custom field update, destroy, and reorder operations are scoped to the model in the URL. An ID from another model will not be accepted.

Create example:

POST /api/laravel-auto-crud/custom-fields/product
Content-Type: application/json

Payload:

{
"label": "Internal Code",
"name": "internal_code",
"type": "string",
"show_in_table": true,
"is_active": true
}

Reorder example:

POST /api/laravel-auto-crud/custom-fields/product/reorder
Content-Type: application/json

Payload:

{
"order": [3, 1, 2]
}

The API uses the same checkForbiddenActions middleware as the web interface. Forbidden API actions return JSON:

{
"success": false,
"message": "No tienes permiso para realizar esta acción."
}

The HTTP status code is 403.

The AutoCrud trait now supports endpoint context.

Product::getEndpoint(); // "/laravel-auto-crud/product"
Product::getEndpoint(null, 'web'); // "/laravel-auto-crud/product"
Product::getEndpoint(null, 'api'); // "/api/laravel-auto-crud/product"
Product::getEndpoints();
// [
// 'web' => '/laravel-auto-crud/product',
// 'api' => '/api/laravel-auto-crud/product',
// ]
Product::getModel([], 'api');

When the schema endpoint is requested through API, relation endpoints inside formFields and externalRelations are generated with the API prefix.

  1. Authenticate the user and store the token or session according to your auth driver.
  2. Fetch GET /api/laravel-auto-crud/{model}/schema for each AutoCrud model used by the frontend.
  3. Render forms from formFields and tables from tableHeaders.
  4. Use POST /{model}/load-items for paginated tables.
  5. Use POST /{model} and PUT /{model}/{id} for mutations.
  6. Use relation endpoints from the schema instead of hardcoding related model URLs.
  7. Handle 422, 401, 403, and 404 as JSON responses.

Using The Published Vue Components Without Inertia

Section titled “Using The Published Vue Components Without Inertia”

The published Vue components can run against either Inertia or the JSON API through an adapter plugin.

In an external Vue/Vuetify frontend, install the API adapter once when bootstrapping the app:

import axios from "axios"
import { createApp } from "vue"
import {
createApiAutoCrudAdapter,
createAutoCrudPlugin,
} from "./Adapters/LaravelAutoCrud"
const app = createApp(App)
app.use(
createAutoCrudPlugin({
adapter: createApiAutoCrudAdapter({
baseUrl: "/api/laravel-auto-crud",
axios,
getAuthUser: () => auth.user,
}),
}),
)

Then load the schema and pass it to the same components used by Inertia apps:

<script setup>
import { onMounted, ref } from "vue"
import AutoTable from "./Components/LaravelAutoCrud/AutoTable.vue"
import { useAutoCrud } from "./Adapters/LaravelAutoCrud"
const autoCrud = useAutoCrud()
const model = ref(null)
onMounted(async () => {
model.value = await autoCrud.loadSchema("product")
})
</script>
<template>
<auto-table v-if="model" title="Products" :model="model" />
</template>

For existing Inertia apps, install the Inertia adapter:

import {
createAutoCrudPlugin,
createInertiaAutoCrudAdapter,
} from "./Adapters/LaravelAutoCrud"
app.use(
createAutoCrudPlugin({
adapter: createInertiaAutoCrudAdapter(),
}),
)

Components no longer import @inertiajs/vue3 directly. Only inertiaAdapter.js depends on Inertia, so external frontends can use apiAdapter.js without installing Inertia.

  • API mode is disabled by default, so existing Inertia installations keep their current behavior.
  • Web and API routes can use different prefixes and middleware stacks.
  • The published Vue components require an AutoCrud adapter plugin. Use createInertiaAutoCrudAdapter() for Inertia apps and createApiAutoCrudAdapter() for external frontends.
  • The API is designed for external frontends; it does not require Inertia.
  • File updates with multipart/form-data should use POST plus _method=PUT for maximum compatibility.