api-platform/core
API Platform Core lets you quickly build hypermedia-driven REST and GraphQL APIs in PHP/Symfony. Supports JSON-LD, Hydra, OpenAPI v2/v3, JSON:API, HAL, and RFC7807. Extensible, high-performance, API-first.
## Getting Started
### Minimal Setup in Laravel
1. **Installation**
```bash
composer require api-platform/core
composer require api-platform/laravel
Add ApiPlatform\Laravel\ApiPlatformServiceProvider::class to config/app.php providers.
First Resource
Annotate an Eloquent model with #[ApiResource]:
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
#[ApiResource(
operations: [
new Get(),
new GetCollection(),
],
normalizationContext: ['groups' => ['read']],
denormalizationContext: ['groups' => ['write']],
)]
class Book extends Model
{
// ...
}
Run migrations and test with:
php artisan api:context:generate
php artisan serve
Access /api/books to see auto-generated API.
First Use Case
#[ApiResource] classes./api/books?page=2)./api/docs.Resource Design
#[ApiResource] with operations (e.g., #[Post], #[Put]) and #[ApiProperty] for fine-grained control.#[ApiResource(input: BookInput::class)].#[Groups(['read'])] on properties.Filtering & Sorting
#[ApiFilter(SearchFilter::class)] or #[ApiFilter(ComparisonFilter::class)].FilterInterface:
use ApiPlatform\Metadata\OperationFilter;
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
#[ApiFilter(BookFilter::class)]
class Book extends Model {}
class BookFilter extends AbstractFilter
{
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, string $resourceClass, Operation $operation = null, array $context = []): void
{
if ($property === 'published') {
$queryBuilder->andWhere('b.published_at >= :date')
->setParameter('date', $value);
}
}
}
State Providers
StateProviderInterface for complex operations:
use ApiPlatform\State\ProviderInterface;
class BookStateProvider implements ProviderInterface
{
public function process($data, Operation $operation, array $uriVariables = [], array $context = []): void
{
if ($operation->getMethod() === 'POST') {
$data->setAuthorId(auth()->id());
}
}
}
#[ApiResource(state: BookStateProvider::class)].GraphQL Integration
#[ApiResource(graphql: ['query' => true, 'mutation' => true])].#[GraphQLQuery] or #[GraphQLMutation] for custom operations.Validation
#[Assert\...] or #[ApiProperty(validationConstraints: [new Assert\NotBlank()])].denormalizationContext:
#[ApiResource(
denormalizationContext: ['validation_groups' => ['partial']]
)]
Security
#[ApiResource(security: "is_granted('ROLE_ADMIN')")].#[ApiResource(security: "is_granted('ROLE_USER')")] and middleware.Performance
#[ApiResource(paginationItemsPerPage: 20)].#[ApiResource(cacheHeaders: ['max-age' => 3600])].#[ApiResource(iriConverter: BookIriConverter::class)] for custom IRIs.Laravel-Specific
#[ApiResource] directly on models (no need for separate DTOs for simple CRUD).#[ApiResource(security: 'authorize:book')].ApiPlatform\EventListener\Event (e.g., PRE_WRITE, POST_READ).Symfony Interop
api_platform.iri_converter, api_platform.state_provider, etc.ApiPlatform\State\ProviderInterface for async processing.Testing
ApiPlatform\Symfony\Tests\Functional\TestTrait:
use ApiPlatform\Symfony\Tests\Functional\TestTrait;
class BookTest extends TestCase
{
use TestTrait;
public function testGetCollection(): void
{
$response = static::createClient()->request('GET', '/api/books');
$this->assertResponseIsSuccessful();
}
}
ApiPlatform\Symfony\Bundle\Test\Fixtures\Loader\DoctrineFixturesLoader.IRI Generation
iriConverter may break pagination or relations.IriConverterInterface implements getIriFromResource() and getResourceFromIri() correctly.api_platform.iri_converter service for collisions.Circular References
#[ApiProperty(serialize: false)] or configure normalizationContext:
#[ApiResource(
normalizationContext: ['enable_max_depth' => true, 'max_depth' => 3]
)]
Filter Conflicts
SearchFilter + ComparisonFilter).#[ApiFilter(SearchFilter::class, properties: ['title'])] to scope properties.GraphQL Schema Conflicts
#[GraphQLField] or use #[ApiProperty(graphql: ['name' => 'custom_name'])].Laravel-Specific Quirks
#[ApiResource] on abstract models may cause metadata errors.#[ApiResource(shortName: 'concrete_model')].#[ApiProperty(denormalizationContext: ['groups' => ['cast']])] or implement a custom StateProvider.Performance
#[ApiResource(iriConverter: CustomIriConverter::class)] with eager loading or #[ApiProperty(serialize: false)] for heavy relations.$this->container->get('doctrine')->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Caching Headers
ETag or Last-Modified headers.CacheHeadersProviderInterface or use #[ApiResource(cachePublic: true)].Metadata Dumps
$metadata = $this->container->get('api_platform.metadata.resource.metadata_factory')->getMetadataForClass(Book::class);
dd($metadata);
Event Listeners
use ApiPlatform\EventListener\Event;
$event->setResult($modifiedData); // In PRE_WRITE
$event->setResponseHeader('X-Custom-Header', 'value'); // In POST_READ
GraphQL Debugging
/api/graphql and inspect queries.api_platform.graphql.schema_factory for schema generation issues.Validation Errors
ValidationException for detailed error messages:
throw new ValidationException($validator->getErrors());
Symfony Profiler
api_platform.profiler toHow can I help you explore Laravel packages today?