Skip to content

Conversation

@elfin-sbreuers
Copy link
Contributor

API platform uses IRIs to manage identity and applies it for linked data formats. JSON:API has different semantics.
This change aims at making JSON:API output spec-compliant without changing the internal identity model.

The current implementation returns resources as:

{
   "id": "api/role/3",
   "type": "role"
}

The resource identification of the JSON:API spec (https://jsonapi.org/format/#document-resource-object-identification) requires it to be:

{
   "id": "3",
   "type": "role"
}

The combination of type and id makes the resource unique.

The proposed implementation wants to keep the original behavior to be downward compatible but introduces a configuration flag, that allows to switch to the spec compliant version.

api_platform:
    jsonapi:
        resource_id_strategy: iri | identifiers

I would be grateful for any pointers that improves the pull request or its suitability to the concepts used in API platform.

The JSONAPI spec combines a type and an id to uniquely identify a
resource:

https://jsonapi.org/format/#document-resource-object-identification

The current api platform implementation uses the IRI as the ID which is
not JSONAPI compliant.
@soyuka
Copy link
Member

soyuka commented Jan 27, 2026

I like this a lot, I suggest however that we deprecate this at some point (in 4.4 with a removal in 5.0). I think that ideally our JSON:API representation should be like this:

{
    "links": {
        "self": "/related_dummies"
    },
    "meta": {
        "totalItems": 3,
        "itemsPerPage": 3,
        "currentPage": 1
    },
    "data": [
        {
            // 1. ID is now the raw unique identifier, not the IRI
            "id": "1",
            "type": "RelatedDummy",
            "attributes": {
                // 2. _id removed (redundant with top-level id)
                "name": "John Doe",
                "symfony": "symfony",
                "dummyDate": null,
                "dummyBoolean": null,
                "embeddedDummy": [],
                "age": 23
            },
            // 3. New 'links' object containing the IRI
            "links": {
                "self": "/related_dummies/1"
            },
            "relationships": {
                "thirdLevel": {
                    "data": {
                        "type": "ThirdLevel",
                        // 4. Relationship linkage uses the raw ID, not the IRI
                        "id": "1" 
                    }
                },
                "relatedToDummyFriend": {
                    "data": []
                }
            }
        },
        {
            "id": "2",
            "type": "RelatedDummy",
            "attributes": {
                "name": "RelatedDummy with no friends",
                "symfony": "symfony",
                "dummyDate": null,
                "dummyBoolean": null,
                "embeddedDummy": [],
                "age": null
            },
            "links": {
                "self": "/related_dummies/2"
            },
            "relationships": {
                "thirdLevel": {
                    "data": null
                },
                "relatedToDummyFriend": {
                    "data": []
                }
            }
        }
        // ... (Item 3 follows same pattern)
    ]
}

It's definitely possible to use type + id to get the resource, maybe that for this we could have another json:api specific interface (that would reuse the iri converter in the back).

I suggest that we use a boolean for this option and to add links.self node.

@elfin-sbreuers
Copy link
Contributor Author

Thanks for your response. At this point, I’m unsure how to proceed.

When you say “deprecate it,” do you mean the use of IRIs as resource identifiers? I’m not certain whether the approach in this PR is slightly over-engineered, or whether it is actually the correct way to support JSON:API in API Platform. My main goal was to respect what I understand to be the core concepts behind API Platform.

In my production application, my current workaround for the id == IRI situation is to hook into the serialization process and normalize the output produced by the API Platform item normalizer.

During normalization, I transform:

"id": "/api/role/3",
"type": "role"

into:

"id": "3",
"type": "role"

During denormalization, before passing the data back to the API Platform item normalizer, I reverse the process:

"id": "3",
"type": "role"

becomes:

"id": "/api/$type/$id",
"type": "role"

(for example: /api/role/3).

The rest of the document — including links, meta, and other JSON:API members — remains unchanged.

Given my requirements and system constraints (notably the use of Doctrine), this approach works well for me. However, I’m unsure whether this solution is transferable to all other ways API Platform can expose resources, or whether it is robust enough to be considered proper JSON:API support in a more general sense. In a way it would feel like a slimmer solution.

@soyuka
Copy link
Member

soyuka commented Jan 28, 2026

I think that for JSON:API the identifier should just be a scalar value not an IRI (id + type is enought information and fits well the specification). You're patch is nice I think that we should have a boolean as option and that in 5.0 the behavior you described should be the default one.

@elfin-sbreuers
Copy link
Contributor Author

Ok, thanks. I will modify the option to become a boolean.

@soyuka
Copy link
Member

soyuka commented Jan 29, 2026

I need to test this in details, it should target main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants