JSON:API (https://jsonapi.org/ 234,846 ) is a published format, so generic clients can read it. Every object has id (a string), type and attributes. Relations are identifier pairs, and related records travel once in a top-level included array. Laravel 13 2,157 ships JsonApiResource, generated by make:resource --json-api. The bookshop's v2 uses it:
class ProductResource extends JsonApiResource
{
public $attributes = ['sku', 'name', 'price', 'stock'];
public $relationships = ['category' => CategoryResource::class];
}$ curl -gs "$B/api/v2/products/2?include=category&fields[products]=name,price" \
| jq -c '.data.type, .data.attributes, .data.relationships, .included[]'
"products"
{"name":"Modern PHP","price":34.5}
{"category":{"data":{"id":"1","type":"categories"}}}
{"id":"1","type":"categories","attributes":{"name":"Computing","slug":"computing"}}The response is application/vnd.api+json, and the client chose its shape: include=category added the relation, and the sparse fieldset fields[products] cut the attributes to two. The type comes from the class name (a first draft called ProductApiResource served product_apis), so use a V2 namespace or override toType(). For JSON:API filter and sort, add spatie/laravel-query-builder.