API Platform: The Pragmatic API framework

soyuka1 637 views 57 slides Sep 23, 2024
Slide 1
Slide 1 of 57
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57

About This Presentation

API Platform 4.0 got released. Discover Pragmatic API development with API Platform.


Slide Content

The pragmatic API framework API PLATFORM CON 2024 - LILLE, FRANCE & ONLINE

API PLATFORM CON 2023 - LILLE, FRANCE & ONLINE API PLATFORM 4

About Me API Platform release manager CTO at Les-Tilleuls.coop Developer, biker, builder Recent father Antoine Bluchet soyuka @phpc.social @s0yuka soyuka .me

with API Platform Pragmatic API development

Pragmatic API development Enable (or disable) the features you (don’t) need Decorate our extension points (IRI Converter, providers , processors, etc. ) API Platform segregates commands and queries It’s not always CRUD: u se operations to create RPC endpoints Don’t fight the framework

Enable / Disable Features use ApiPlatform\Metadata\Post; #[ Post ( read : true , // to call the provider write : true , // to call the processor deserialize : false , // transforms the request body to this resource's class serialize : false , // transforms the provider's response to JSON validate : false , // validates the user input queryParameterValidationEnabled : false , // validates query parameters output : false , // using false will return nothing openapi : false , // disables the OpenAPI documentation )] class Book {}

use ApiPlatform\Metadata\ApiResource; use App\State\BookProvider; #[ Put ( uriTemplate : '/books/{id}' , allowCreate : true , // create a new value through a PUT operation provider : BookProvider :: class , processor : BookProcessor :: class , )] class Book { } State Provider / Processor

use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProviderInterface; use ApiPlatform\Laravel\Eloquent\State\ItemProvider; use ApiPlatform\Doctrine\Orm\State\ItemProvider; final readonly class BookProvider implements ProviderInterface { public function __construct( private ItemProvider $itemProvider ) {} public function provide( Operation $operation , array $uriVariables = [], array $context … { return $this -> itemProvider ->provide( $operation , $uriVariables , $context ); } } Decorate the state providers

use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProviderInterface; final class BookProvider implements ProviderInterface { public function provide( Operation $operation , array $uriVariables = [], array $context … { $dbh = new \ PDO ( 'sqlite:/db.sqlite' ); $sth = $dbh ->prepare( 'SELECT * FROM book WHERE id = :id' ); $sth ->bindParam( 'id' , $uriVariables [ 'id' ], \ PDO :: PARAM_STR ); $sth ->execute(); return !( $result = $sth ->fetch()) ? null : $result ; } } Any data source

use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProviderInterface; final class BookProvider implements ProviderInterface { public function provide( Operation $operation , array $uriVariables = [], array $context … { return json_decode( file_get_contents( 'https://demo.api-platform.com/api/books.jsonld' ) ); } } Any data source

Use your favorite query builder use ApiPlatform\Laravel\Eloquent\State\Options; use Illuminate\Database\Eloquent\Builder; #[ GetCollection ( uriTemplate : '/author/{author}/books' , uriVariables : [ 'author' ], stateOptions : new Options ( handleLinks : [ self :: class , 'handleLinks' ] ) )] class Employee { public static function handleLinks( Builder $builder , array $uriVariables , array $context ): Builder { $builder ->where( 'books.author_id' , $uriVariables [ 'author' ]); } } AVAILABLE FOR Doctrine ORM Doctrine ODM Laravel Eloquent Elasticsearch ESQL

use ApiPlatform\Metadata\Post; #[ Post ( uriTemplate : '/rpc' , processor : [ self :: class , 'process' ])] final readonly class PostContent { public function __construct( public string $name ) {} public static function process( mixed $data ) { // do something with $data } } RPC endpoints

Not convinced?

Why you should use API Platform commit f55cfce9c014655dd5343a5f20dfaf9ec5df2da0 Author: Kévin Dunglas < dunglas @ gmail . com > Date: Tue Jan 20 00:16:57 2015 + 0100 first commit REST oriented JSON-LD , JSON:API serialization OpenAPI, Hydra, JSON Schema documentation URI based mechanics (cache, live updates, relations etc.) Embedded content negotiation, validation and authorization 10 years of existence in January

The power of Edge Side APIs

1 2 3 4 5 Atomic Resource Pre-generate HTTP Cache Preload Push edge-side-api.rocks

Embedding...

@api-platform/ld { "@id" : "/books/1" , "@type" : [ "https://schema.org/Book" ], "title" : "Hyperion" , "author" : "https://localhost/authors/1" } fetch ( ' /books/1 ' )

@api-platform/ld { "@id" : "/books/1" , "@type" : [ "https://schema.org/Book" ], "title" : "Hyperion" , "author" : " https://localhost/authors/1 " } fetch ( ' /authors/1 ' )

@api-platform/ld { "@id" : "/books/1" , "@type" : [ "https://schema.org/Book" ], "title" : "Hyperion" , "author" : " https://localhost/authors/1 " } fetch ( ' /authors/1 ' )

@api-platform/ld { "@id" : "/books/1" , "@type" : [ "https://schema.org/Book" ], "title" : "Hyperion" , "author" : " https://localhost/authors/1 " } import ld from '@api-platform/ld' await ld( '/books' , { urlPattern : new URLPattern ( "/authors/:id" , "https://localhost" ) }) console .log(books.author?.name)

@api-platform/mercure import mercure, { close } from "@api-platform/mercure" ; await mercure( 'https://localhost/authors/1' , { onUpdate: (author) => console .log(author) }) Link: </.well-known/mercure>; rel="mercure"

Mercure Resource live updates Compatible with Laravel Echo High Availability as a service https://mercure.rocks

@api-platform/ld + @api-platform/mercure const fetchFn = (url) => { return mercure(url).then(d => d.json()) } ld( '/books' , { fetchFn : fetchFn, urlPattern : pattern }) .then(books => { // Do something with books })

Client tools Admin generator @api-platform/admin Manage topics on a single EventSource connection @api-platform/mercure Replace fetch by @api-platform/ld for linked data And more at api-platform.com

Atomic resources? Small and atomic documents Don’t embed resources, use URIs instead Better browser and share cache hit rate Clients fetch only what they initially need and update only small chunks of data Hypermedia is at the heart of Edge Side APIs (JSON-LD, Hydra, JSON:API) Read our white paper !

IRIs collection GET /books Content-Type : application/ld+json { "@context" : "/contexts/Book" , "@id" : "/books" , "@type" : "Collection" , "totalItems" : 4 , "member" : [ "/books/1" , "/books/2" , "/books/3" , "/books/4" ] } GET / books Content - Type : application / ld + json { "@context" : "/contexts/Book" , "@id" : "/books" , "@type" : "Collection" , "totalItems" : 4 , "member" : [ { "@id" : "/books/1" , "title" : "API Platform" , "author" : "/authors/1" }, { "@id" : "/books/2" , ... }, ] }

use ApiPlatform\Metadata\ApiResource; #[ ApiResource ( normalizationContext : [ 'iri_only' => true ], )] final readonly class Book { } IRI Only GET /books Content-Type : application/ld+json { "@context" : "/contexts/Book" , "@id" : "/books" , "@type" : "Collection" , "totalItems" : 4 , "member" : [ "/books/73" , "/books/74" , "/books/75" , "/books/76" ] }

use ApiPlatform\Metadata\GetCollection; #[ ApiResource ( uriTemplate : ' /authors/{author}/books ' , uriVariables : [ 'author' => new Link ( fromClass : Author :: class , toProperty : 'author' ) ], normalizationContext : [ 'iri_only' => true ], )] final readonly class Book { } Group data with IRIs

use ApiPlatform\Metadata\ApiProperty; #[ ApiProperty ( property : 'books' , uriTemplate : '/authors/{author}/books' )] final readonly class Author { } GET /authors/1 Content-Type : application/ld+json { "@context" : "/contexts/Book" , "@id" : "/authors/1" , "@type" : "Author" , " books " : " /authors/1/books " } Group data with IRIs

Preload Preload resources Server push Early Hints Caddy module https://vulcain.rocks

API Platform 3.4/4.0 What’s new ?

Query parameters use ApiPlatform\Laravel\Eloquent\Filter\PartialSearchFilter; use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\QueryParameter; #[ ApiResource ] #[ QueryParameter ( key : 'sort[:property]' , filter : OrderFilter :: class )] #[ QueryParameter ( key : ':property' , filter : PartialSearchFilter :: class )] class Book extends Model { }

Query parameters

Query parameters Header / Query parameter JSON Schema validation OpenAPI and Hydra compatible :property placeholder for better DX Better filter composition (OrFilter, decorate D octrine filters etc.) Filter aliasing

P arameters Header / Query parameter JSON Schema validation OpenAPI and Hydra compatible :property placeholder for better DX Better filter composition (OrFilter, decorate Doctrine filters etc.) Filter aliasing

Error management use ApiPlatform\Metadata\GetCollection; use App\Exception\MyDomainException; #[ GetCollection ( errors : [ MyDomainException :: class ])] class Book { }

Error management Exception can be API resources using ApiPlatform\Metadata\Error Specify errors in an operation to document them with OpenAPI Read our guides: api-platform.com/docs/guides/error-provider/ api-platform.com/docs/guides/error-resource/ Exception are automatically rendered using the Problem details specification (RFC 7807)!

Laravel support Most of our existing features are useable in a Laravel project Authorization and validation use Laravel FormRequest and policies Read the documentation: a pi-platform.com/docs/laravel/ Send feedbacks!

And way more… Doctrine ENUM filter Serializer default context fixes with an option to change the deserializer type Static headers Inflector is now a service Configure whether to override OpenAPI responses Read the CHANGELOG !

From v3 to v4 How to update?

$ composer require api-platform/core:^3.4 Update to API Platform 3.4 api-platform.com/docs/core/upgrade-guide/

FIX DEPRECATIONS!

$ composer update // composer.json - "api-platform/core": "^3.4", + "api-platform/symfony": "^3.4", + "api-platform/doctrine-orm": "^3.4", Step 1 Step 2 Step 3 $ phpunit Update to API Platform 3.4

api-platform/symfony Symfony API Platform integration api-platform/graphql Build GraphQL enpoints api-platform/laravel Laravel API Platform integration And more… Hydra, JSON-LD, OpenAPI, JSON Schema etc. api-platform/doctrine-orm Doctrine ORM bridge api-platform/doctrine-odm Doctrine ODM bridge Choose your components

api_platform: - use_symfony_listeners: true + use_symfony_listeners: false defaults: operations: - ApiPlatform\Metadata\Get - ApiPlatform\Metadata\GetCollection - ApiPlatform\Metadata\Post - - ApiPlatform\Metadata\Put - ApiPlatform\Metadata\Patch - ApiPlatform\Metadata\Delete serializer: - hydra_prefix: true + hydra_prefix: false What changed?

What changed? No more PUT operation by default Hydra prefix disabled ApiPlatform/{Api,Exception} moved to ApiPlatform/Metadata Listeners are no longer used* * only useful when using controllers on API Resources

Hydra prefix? GET /books Content-Type : application/ld+json { "@context" : "/contexts/Book" , "@id" : "/books" , "@type" : "Collection" , "totalItems" : 4 , "member" : [ "/books/73" , "/books/74" , "/books/75" , "/books/76" ] } GET /books Content-Type : application/ld+json { "@context" : "/contexts/Book" , "@id" : "/books" , "@type" : " hydra: Collection" , " hydra: totalItems" : 4 , " hydra: member" : [ "/books/73" , "/books/74" , "/books/75" , "/books/76" ] }

composer require api-platform/symfony:^4.0.0 Install API Platform 4 composer require api-platform/laravel

Thanks! SPONSOR ME! github.com/soyuka

Thanks! Sponsor Me! github.com/soyuka Buy or merch! | 10 000 € PHYSICAL COVERAGE

Dos / don’t Errors openapi + defaults ?

Error management Static HTTP Headers Denormalize type Query Parameter New in API Platform 3.4 / 4.0

Error management Static HTTP Headers Denormalize type Query Parameter New in API Platform 3.4 / 4.0

New in API Platform 3.4 / 4.0 POST /import HTTP/2 Content-Type : application/json [{ "name" : "Kévin" }, { "name" : "Antoine" }]

use ApiPlatform \ Metadata \ Post ; class ValueObject { public string $name ; } #[ Post ( uriTemplate : '/import' , denormalizationContext : [ 'deserializer_type' : 'ValueObject[]' ], validate : false , // we don't validate lists processor : [ self :: class , 'process' ] )] class Book { public static function process ( mixed $data ) { // Do something with the list of ValueObject } } New in API Platform 3.4 / 4.0 custom validation => processor

ESA (frontend tools + mercure powerful) Handle links Parameters Iri Converter Subtree split ? Allow create ? NoCode version xD Don’t fight, win. Parameter validation + security + docs + filter aliases News: doctrine enum filter, hydra prefix, event listeners Backed enum resources + doctrine filter Openapi_api_override_response Error custom feature