alexfigures/symfony-jsonapi-bundle
Production-ready JSON:API 1.1 implementation for Symfony with complete filtering, automatic eager loading, and zero N+1 queries.
composer require alexfigures/symfony-jsonapi-bundle
Requirements:
config/bundles.php:return [
AlexFigures\Symfony\Bridge\Symfony\Bundle\JsonApiBundle::class => ['all' => true],
];
config/packages/jsonapi.yaml:jsonapi:
route_prefix: '/api'
pagination:
default_size: 25
max_size: 100
use AlexFigures\Symfony\Resource\Attribute\JsonApiResource;
use AlexFigures\Symfony\Resource\Attribute\Id;
use AlexFigures\Symfony\Resource\Attribute\Attribute;
use AlexFigures\Symfony\Resource\Attribute\Relationship;
#[JsonApiResource(type: 'articles')]
final class Article
{
#[Id]
#[Attribute]
public string $id;
#[Attribute]
public string $title;
#[Relationship(toMany: true, targetType: 'comments')]
public array $comments = [];
}
Implement data layer (see Doctrine Integration Guide)
Start using your API:
# Get all articles
curl http://localhost:8000/api/articles
# Filter, sort, and include relationships (no N+1 queries!)
curl "http://localhost:8000/api/articles?filter[status][eq]=published&sort=-createdAt&include=author,tags"
# Advanced filtering with multiple conditions
curl "http://localhost:8000/api/articles?filter[and][0][status][eq]=published&filter[and][1][viewCount][gte]=100"
# Create new article
curl -X POST \
-H "Content-Type: application/vnd.api+json" \
-d '{"data": {"type": "articles", "attributes": {"title": "Hello"}}}' \
http://localhost:8000/api/articles
# Interactive API documentation
open http://localhost:8000/_jsonapi/docs
📖 Complete Getting Started Guide → 📊 Interactive API Docs →
| JsonApiBundle | PHP | Symfony | Doctrine ORM |
|---|---|---|---|
main branch |
8.2 · 8.3 · 8.4 | 7.1 · 7.2 · 7.3 | 3.0+ |
| Latest release | 8.2+ | 7.1+ | 3.0+ |
CI runs the full test suite across PHP 8.2–8.4 with both stable and lowest-dependency sets to guarantee forwards and backwards compatibility inside each supported Symfony minor.
Tested Databases:
eq, ne, gt, gte, lt, lte, in, nin, like, ilike operatorsand, or, not for complex queriesfilter[author.name]=John#[FilterableFields]# Complex filtering example
curl "api/articles?filter[and][0][status][eq]=published&filter[and][1][or][0][viewCount][gte]=100&filter[and][1][or][1][featured][eq]=true"
include=author.company,tagsVERIFY, ALLOW_ORPHANS)#[Relationship(
toMany: true,
targetType: 'tags',
propertyPath: 'articleTags.tag', // Clean API: specialTags → complex path
linkingPolicy: RelationshipLinkingPolicy::VERIFY
)]
private Collection $specialTags;
sort=title,-createdAt,author.nameALLOW_EXTRA_ATTRIBUTES=false)#[JsonApiResource(type: 'articles')]
#[FilterableFields(['title', new FilterableField('author', inherit: true)])]
#[SortableFields(['title', 'createdAt', new SortableField('author', inherit: true)])]
final class Article
{
#[Id] #[Attribute] public string $id;
#[Attribute] public string $title;
#[Relationship(targetType: 'authors')] public Author $author;
}
📖 Complete Documentation Index →
propertyPath parameter
#[Relationship(
toMany: true,
targetType: 'tags',
propertyPath: 'articleTags.tag' // API: specialTags → Doctrine: articleTags.tag
)]
private Collection $specialTags;
#[JsonApiCustomRoute] attribute (docs)Run tests with:
make test # Unit and functional tests
make docker-test # Integration tests in Docker
make qa-full # Full QA suite (tests, static analysis, mutation testing)
See TESTING.md for complete testing documentation.
✅ Complete Filtering System - All operators (eq, ne, lt, lte, gt, gte, like, in, isnull, between) with SQL injection protection ✅ Automatic Eager Loading - Zero N+1 queries with automatic JOINs for includes ✅ Generic Doctrine Repository - Works out of the box, no custom code needed ✅ Relationship Pagination - Proper pagination for all relationship endpoints ✅ PostgreSQL Optimized - Tested and optimized for PostgreSQL ✅ Custom Route Handlers - Build custom endpoints with automatic transaction management and JSON:API formatting
✅ JSON:API 1.1 Compliance - 97.8% specification coverage (132/135 requirements)
✅ Attribute-Driven - No XML/YAML configuration needed
✅ Auto-Generated Endpoints - No controller boilerplate
✅ Configurable Route Naming - Choose between snake_case and kebab-case
✅ Custom Route Attributes - Define custom endpoints with PHP attributes
✅ Query Parameters - include, fields, sort, page, filter
✅ Relationships - To-one and to-many with full CRUD
✅ Write Operations - POST, PATCH, DELETE with validation
✅ Atomic Operations - Batch operations in single transaction
✅ Interactive Docs - Swagger UI / Redoc integration
✅ Response Factory - Build JSON:API responses in custom controllers
GET /api/{type} - Collection with pagination, sorting, filteringGET /api/{type}/{id} - Single resource with sparse fieldsetsGET /api/{type}/{id}/relationships/{rel} - Relationship linkageGET /api/{type}/{id}/{rel} - Related resourcesinclude, fields[TYPE], sort, page[number], page[size]self, first, prev, next, last linksincluded arrayPOST /api/{type} → 201 Created with Location headerPATCH /api/{type}/{id} → 200 OK with updated resourceDELETE /api/{type}/{id} → 204 No ContentTransactionManagerJsonApiBundle follows Semantic Versioning:
The following are guaranteed to maintain backward compatibility:
src/Contract/) - Data layer contractssrc/Resource/Attribute/) - #[JsonApiResource], #[Attribute], etc.jsonapi: configuration options📖 Public API Reference → 📖 BC Policy → 📖 Upgrade Guide →
⚠️ Versions 0.x may introduce breaking changes in MINOR versions. Pin to exact MINOR version:
{
"require": {
"jsonapi/symfony-jsonapi-bundle": "~0.1.0"
}
}
The bundle provides automatic OpenAPI 3.1 documentation with interactive UI:
Swagger UI (Interactive):
http://localhost:8000/_jsonapi/docs
OpenAPI Specification (JSON):
http://localhost:8000/_jsonapi/openapi.json
# config/packages/jsonapi.yaml
jsonapi:
docs:
generator:
openapi:
enabled: true
title: 'My API'
version: '1.0.0'
ui:
enabled: true
route: '/_jsonapi/docs'
theme: 'swagger' # or 'redoc'
Production: Disable in config/packages/prod/jsonapi.yaml:
jsonapi:
docs:
ui:
enabled: false
{
"jsonapi": { "version": "1.1" },
"links": {
"self": "http://localhost/api/articles?page[number]=1&page[size]=10",
"first": "http://localhost/api/articles?page[number]=1&page[size]=10",
"last": "http://localhost/api/articles?page[number]=3&page[size]=10",
"next": "http://localhost/api/articles?page[number]=2&page[size]=10"
},
"data": [
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Getting Started with JSON:API",
"createdAt": "2025-10-07T10:00:00+00:00"
},
"relationships": {
"author": {
"links": {
"self": "http://localhost/api/articles/1/relationships/author",
"related": "http://localhost/api/articles/1/author"
},
"data": { "type": "authors", "id": "1" }
}
},
"links": {
"self": "http://localhost/api/articles/1"
}
}
],
"included": [
{
"type": "authors",
"id": "1",
"attributes": { "name": "Alice" },
"links": { "self": "http://localhost/api/authors/1" }
}
],
"meta": {
"total": 25,
"page": 1,
"size": 10
}
}
# Install dependencies
composer install
# or
make install
# Run tests
make test # Unit and functional tests (no Docker required)
make docker-test # Integration tests with real databases
make test-all # All test suites
# Code quality
make stan # PHPStan static analysis (level 8)
make cs-fix # Fix code style (PSR-12)
make rector # Automated refactoring
make mutation # Mutation testing (70% MSI threshold)
make deptrac # Architecture rules validation
make bc-check # Backward compatibility check
# Full QA pipeline
make qa-full # Run all quality checks
See TESTING.md for detailed testing documentation.
How can I help you explore Laravel packages today?