dunglas/api-bundle
API Platform Core is a powerful Symfony bundle for building hypermedia-driven REST and GraphQL APIs. It supports JSON-LD, Hydra, OpenAPI, JSON:API, HAL and Problem Details, letting you create fully featured, high-performance API-first projects in minutes.
Install the Laravel bridge:
composer require api-platform/laravel
Publish its config so you can tweak defaults:
php artisan vendor:publish --tag=api-platform
Routes under the /api prefix (docs included) are registered automatically — no provider entry in config/app.php is needed in Laravel.
Declare your first resource on an Eloquent model:
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
#[ApiResource(
operations: [new Get(), new GetCollection(), new Post()],
normalizationContext: ['groups' => ['post:read']],
denormalizationContext: ['groups' => ['post:write']],
)]
class Post extends Model {}
First use case: hit /api/posts for the collection (Hydra/JSON:API depending on format), /api/posts/1 for an item, and /api/docs for the interaction-less OpenAPI UI. Confirm routes with php artisan route:list --path=api.
config/api-platform.php — formats, docs, defaults, cache wiring./api/docs — the generated OpenAPI is the fastest way to verify what an attribute actually produced.composer show api-platform/laravel — 5.0.4 is a patch on top of 5.0.3 with a large batch of metadata/JSON Schema fixes; check the version before debugging schema oddities.#[ApiResource] on the model and put per-operation tuning on the operation object (new Get(security: "..."), new GetCollection(paginationEnabled: false)).normalizationContext/denormalizationContext with #[Groups] on properties over hand-written resources; sparse fieldsets and JSON:API fields[] then work for free.#[ApiResource] on a plain class) when the API shape diverges from the table — v5 handles resources without an attribute on the backing class for shortName resolution, so uriTemplate/shortName stay predictable.with* helpers: Put::withAllowCreate(), Link::withFromProperty() now return a new instance (5.0.4). Always assign the return value; chained calls that relied on mutation silently lose the setting.use ApiPlatform\Laravel\Eloquent\Filter\SearchFilter;
use ApiPlatform\Laravel\Eloquent\Filter\OrFilter;
use ApiPlatform\Metadata\ApiFilter;
#[ApiFilter(SearchFilter::class, properties: ['title' => 'partial', 'status' => 'exact'])]
#[ApiFilter(OrFilter::class, properties: ['id', 'slug'])]
class Post extends Model {}
?id[]=1&id[]=2); OrFilter also accepts a single scalar (?id=1), so hand-written clients don't need to always send arrays.OrFilter now handles nested properties via a left join (fixed in 5.0.4 on the Doctrine side; the generator/parameter pipeline is shared) — properties: ['author.email'] style mappings behave like a top-level property instead of erroring or dropping the condition.?status=) now returns 422 (behaviour restored in 5.0.4). If you were relying on empty strings being coerced to null/ignored, strip them client-side or map them explicitly in a custom filter.new GetCollection(paginationEnabled: false) — but see the security gotcha below.SortDirection enums are accepted in ordering configuration (5.0.4) — passing the enum instead of the string no longer raises a TypeError.security on the operation, not the class, when read and write rules differ: new Get(security: "is_granted('ROLE_USER')").provider: on the operation; keep it thin and let Eloquent do the heavy lifting.processor:) rather than model events, so the behaviour is scoped to the API operation.provider: is no longer clobbered by the filter provider (5.0.4) — you can attach both a custom #[Parameter] provider and an #[ApiFilter] to the same property without one winning arbitrarily. Verify the resolved provider via a dd() in the provider if you used this combination before.#[Parameter(operations: [...])] accepts operation class names (operations: [Get::class, GetCollection::class]) as well as instances, and operation-level parameters now take precedence over #[Parameter] attributes declared on the property class.PartialDenormalizationException in a custom provider, note that 5.0.4 stopped calling the deprecated getErrors() internally; read the constraint violations directly rather than relying on that accessor.belongsTo relations now accept null on write (5.0.4, Laravel-specific). Before, a nullable FK sent as null could fail denormalization; if you had a custom setter or prepareForValidation shim for that, remove it and let the relation handle it.readableLink/writableLink survive on collections of non-resource classes (5.0.4) — useful when a collection returns value objects rather than entities.ON, put-create under a slug parent link, links accepting resolved resources). Don't go looking for those PRs when Eloquent links misbehave — the Laravel bridge has its own relation-based link handler....-validation_groups-...), so generated schemas are distinguishable per group.GroupSequence is supported as of 5.0.4 — use it when validation must short-circuit (e.g. "don't check uniqueness until the format is valid") instead of chaining constraints by hand.validationContext: ['groups' => ['post:patch']] on properties that must not be required on PATCH.#[Parameter] and an operation-level one, the operation wins (5.0.4) — don't debug the property attribute expecting it to apply.stateOptions and map are now forwarded to the QueryCollection parent constructor (5.0.4), so subclassed/extended collection queries keep their state configuration.#[ApiResource(operations: [...])]" guard threw for GraphQL ops too; that guard now correctly applies only to undeclared HTTP operations.mixed emits an empty schema, intersection types emit allOf, nested resources honour output DTOs, nested gen_id follows the property name, and operation groups survive into the schema context. Regenerate any client code — the diff will be non-trivial but correct.php artisan config:clear && php artisan cache:clear. In dev, point the metadata pool at the array store.queryParameterValidationEnabled now persists on ApiResource, contentNegotiation is copied by withOperation(), defaults that use can* getters, explicit readableLink/writableLink on non-resource collections). If a setting "doesn't take", clear the cache before opening an issue.use statement for the filter interface — if you generated a custom filter before 5.0.2, open the file and de-duplicate the import (a fatal "cannot use ... as ... because the name is already in use" otherwise).properties maps with strategies (exact, partial, start, end) — implicit behaviour differs per filter and shows up as confusing SQL.addcslashes in a custom filter, drop it — it will now be double-escaped.security expression on the first item, you can drop it.security on GetCollection does not filter rows — pair it with an extension/query scope for per-tenant visibility.fields[comments] finally trims sideloaded data. Expect payload-size changes if clients relied on full included objects.#[ApiResource(operations: [...])] throws instead of being ignored — a good thing, but it surfaces as a hard error on upgrade. GraphQL operations are exempt as of 5.0.4.ApiPlatform\Test\ApiTestCase and createOne() for for() relations (5.0.4, PHPUnit 13 compatibility). If you copied test scaffolding from older package examples, update the base test case import — the old path breaks under PHPUnit 13.php artisan route:list for the routing picture; /api/docs for the metadata picture; var_dump of the operation objects via a dd() in a state provider for the runtime picture.properties key doesn't match the attribute name.Patch release, no breaking changes or deprecations. Worth bumping for the Laravel-relevant fixes: nullable optional belongsTo writes, restored 422 on empty scalar params, 422 for backed enum errors, GroupSequence support, OrFilter nested properties, operation class names in #[Parameter(operations:)], operation-over-property parameter precedence, and a large batch of JSON Schema/OpenAPI correctness fixes. Behavioural changes to watch: empty scalar params return 422 again, schema output shifts for mixed/intersections/nested resources, and operation-level parameters now beat property-level ones. Doctrine-only fixes in this release (portable LIKE escape, ON joins for arbitrary links, put-create under a slug parent, resolved resources in links, ORM 3 column defaults) are listed here for completeness but won't change Eloquent behaviour. Clear config + cache after upgrading and regenerate any OpenAPI-derived clients.
How can I help you explore Laravel packages today?