Skip to content

Latest commit

 

History

History
228 lines (185 loc) · 6.25 KB

0002-resource-definition.md

File metadata and controls

228 lines (185 loc) · 6.25 KB

Resource definition

  • Status: accepted
  • Deciders: @dunglas, @soyuka, @vincentchalamon, @GregoireHebert

Context and Problem Statement

The API Platform @ApiResource annotation was initially created to represent a Resource as defined in Roy Fielding's dissertation about REST in correlation with RFC 7231 about HTTP Semantics. This annotation brings some confusion as it mixes concepts of resources and operations. Here we discussed how we could revamp API Platform's resource definition using PHP8 attributes, being as close as we can to Roy Fielding's thesis vocabulary.

Considered Options

  • Declare multiple ApiResource on a PHP Class see Subresources definition
  • Declare operations in conjunction with resources using two attributes: Resource and Operation
  • Use HTTP Verb to represent operations with a syntax sugar for collections (CGET?)

Decision Outcome

As Roy Fielding's thesis states:

REST uses a resource identifier to identify the particular resource involved in an interaction between components. REST connectors provide a generic interface for accessing and manipulating the value set of a resource, regardless of how the membership function is defined or the type of software that is handling the request.

In API Platform, this resource identifier is also named IRI (Internationalized Resource Identifiers). Following these recommendations, applied to PHP, we came up with the following PHP 8 attributes:

<?php

#[ApiResource]
class Users
{
    #[ApiProperty(types="hydra:member")]
    public iterable $member = [];

    public float $averageRate;
}

#[ApiResource("/companies/{companyId}/users/{id}", normalizationContext=["groups"= [....]]), operations={}]
#[ApiResource(normalizationContext=["groups"= [....]], operations=[
 new Get(),
 new Post(),
])]
class User
{
    #[ApiProperty(identifier=true)]
    public $id;
}

Under the hood, API Platform would declare two routes Representing the /users resource:

  • GET /users
  • POST /users

and three routes representing the /users/{id} resource:

  • GET /users/{id}
  • PUT /users/{id}
  • DELETE /users/{id}

For convenience and to ease the upgrade path, these would still be available on a single class:

<?php

#[ApiResource]
class User {}

corresponding to

<?php

#[Get]
#[GetCollection]
#[PostCollection]
#[Put]
#[Delete]
class User {}

Verbs declared on a PHP class define API Platform operations. The ApiResource attributes would become optional and the only thing needed is to specify at least a verb and an IRI representing the Resource. Some examples:

Code Operations
#[Get]
class User {}
            
  • GET /users/{id}
#[GetCollection]
class User {}
            
  • GET /users
#[Get("/users")]
class User {}
            
  • GET /users
#[Post("/users")]
#[Patch("/users/{id}")]
class User {}
            
  • POST /users
  • PATCH /users/{id}
// See these as aliases to the `/users/{id}` Resource:
#[Get("/companies/{companyId}/users/{id}")]
#[Delete("/companies/{companyId}/users/{id}")]
class User {}
            
  • GET /companies/{companyId}/users/{id}
  • DELETE /companies/{companyId}/users/{id}

To link these operations with identifiers, refer to URI Variables decision record, for example:

<?php
use Company;

#[Get(
    uriTemplate: "/companies/{companyId}/users/{id}", 
    uriVariables: [
        "companyId" => ["class" => Company::class, "identifiers" => ["id"], "property" => "user"], 
        "id" => ["class" => User::class, "identifiers" => ["id"]]
    ]
)]
class User {
  #[ApiProperty(identifier=true)]
  public $id;
  public Company $company;
}

The ApiResource attribute could be used to set defaults properties on operations:

<?php

#[ApiResource(normalizationContext=["groups"= [....]])]
#[Get("/users/{id}")]
class User {}

These properties can also be specified directly on the verb attribute:

<?php

#[Get("/users/{id}", normalizationContext=["group"])]
class User {}

Internally, HTTP verbs are aliases to the Resource Attribute holding a method and a default path. The ApiResource attribute is a reflection of the underlying metadata:

<?php

namespace ApiPlatform\Metadata;

class ApiResource {
    public string $iri;
    public bool $mercure;
    public string $security;
    public bool $paginationEnabled;
    // etc.

    /** this was named $attributes before **/
    public array $extraProperties;
}

For GraphQL, Query, Mutation and Subscription will be added.

Options declined

An Operation attribute was proposed, but we want to keep the code base small and decided verb attributes where sufficient.

The initially proposed CGet will be named GetCollection. It is only a shortcut to what used to be called collectionOperation on the GET verb. To remove confusion around collectionOperations and itemOperations, these terms will be deprecated in the code-base. To distant ourselves from the CRUD pattern, we also declined List and Create as we want to focus on Resource based architectures. RPC routes will be easy to add using the Post verb if required.