Skip to content

Commit 7e8445c

Browse files
committed
docs(jsonapi): document opt-in client-generated IDs
Add a "Client-Generated IDs" section to core/jsonapi.md covering the JSON:API spec allowance, the default refusal (security footgun), and how to opt in globally (Symfony YAML / Laravel config) or per-operation via denormalizationContext. References api-platform/core#7930 and spec §crud-creating-client-ids.
1 parent 925f3b6 commit 7e8445c

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

core/jsonapi.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,86 @@ class Tag
8080
This allows a parent resource to reference `Tag` objects in its relationships while `Tag` itself has
8181
no public item endpoint. The `NotExposed` operation requires a `uriTemplate` with a single URI
8282
variable.
83+
84+
## Client-Generated IDs
85+
86+
The
87+
[JSON:API specification allows clients to supply their own `id` on `POST`](https://jsonapi.org/format/#crud-creating-client-ids)
88+
when the server agrees to accept it. This is useful when the client generates a UUID before sending
89+
the request and needs the server to persist it as-is.
90+
91+
By default, API Platform rejects client-supplied `data.id` on `POST` with a `400` response. Enable
92+
it explicitly when needed.
93+
94+
### Enabling Globally
95+
96+
**Symfony:**
97+
98+
```yaml
99+
# config/packages/api_platform.yaml
100+
api_platform:
101+
jsonapi:
102+
allow_client_generated_id: true
103+
```
104+
105+
**Laravel** (`config/api-platform.php`):
106+
107+
```php
108+
'jsonapi' => [
109+
'allow_client_generated_id' => true,
110+
],
111+
```
112+
113+
### Enabling Per Operation
114+
115+
Use the `denormalizationContext` on the `#[Post]` operation to enable the feature for a single
116+
endpoint without affecting the rest of the API:
117+
118+
```php
119+
<?php
120+
121+
namespace App\ApiResource;
122+
123+
use ApiPlatform\JsonApi\Serializer\ItemNormalizer;
124+
use ApiPlatform\Metadata\ApiResource;
125+
use ApiPlatform\Metadata\Get;
126+
use ApiPlatform\Metadata\Post;
127+
128+
#[ApiResource(
129+
formats: ['jsonapi' => ['application/vnd.api+json']],
130+
operations: [
131+
new Get(uriTemplate: '/books/{id}'),
132+
new Post(
133+
uriTemplate: '/books',
134+
denormalizationContext: [ItemNormalizer::ALLOW_CLIENT_GENERATED_ID => true],
135+
),
136+
],
137+
)]
138+
class Book
139+
{
140+
public ?string $id = null;
141+
public string $title = '';
142+
}
143+
```
144+
145+
A request that supplies `data.id` is then accepted:
146+
147+
```http
148+
POST /api/books HTTP/1.1
149+
Accept: application/vnd.api+json
150+
Content-Type: application/vnd.api+json
151+
152+
{
153+
"data": {
154+
"type": "Book",
155+
"id": "01932b4c-a3f1-7b7e-9e5b-3d8f1c2e4a6d",
156+
"attributes": {
157+
"title": "Hyperion"
158+
}
159+
}
160+
}
161+
```
162+
163+
The supplied `id` is passed to the entity's `id` setter. The processor is responsible for persisting
164+
it. The response output schema still requires `id`; only the `POST` input schema marks it as
165+
optional.

0 commit comments

Comments
 (0)