Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

API Platform 3 Part 1: Mythically Good RESTful APIs

2:51:42

What you'll be learning

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "api-platform/core": "^3.0", // v3.0.8
        "doctrine/annotations": "^1.0", // 1.14.2
        "doctrine/doctrine-bundle": "^2.8", // 2.8.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.14", // 2.14.0
        "nelmio/cors-bundle": "^2.2", // 2.2.0
        "nesbot/carbon": "^2.64", // 2.64.1
        "phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
        "phpstan/phpdoc-parser": "^1.15", // 1.15.3
        "symfony/asset": "6.2.*", // v6.2.0
        "symfony/console": "6.2.*", // v6.2.3
        "symfony/dotenv": "6.2.*", // v6.2.0
        "symfony/expression-language": "6.2.*", // v6.2.2
        "symfony/flex": "^2", // v2.2.4
        "symfony/framework-bundle": "6.2.*", // v6.2.3
        "symfony/property-access": "6.2.*", // v6.2.3
        "symfony/property-info": "6.2.*", // v6.2.3
        "symfony/runtime": "6.2.*", // v6.2.0
        "symfony/security-bundle": "6.2.*", // v6.2.3
        "symfony/serializer": "6.2.*", // v6.2.3
        "symfony/twig-bundle": "6.2.*", // v6.2.3
        "symfony/ux-react": "^2.6", // v2.6.1
        "symfony/validator": "6.2.*", // v6.2.3
        "symfony/webpack-encore-bundle": "^1.16", // v1.16.0
        "symfony/yaml": "6.2.*" // v6.2.2
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "6.2.*", // v6.2.1
        "symfony/maker-bundle": "^1.48", // v1.48.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/stopwatch": "6.2.*", // v6.2.0
        "symfony/web-profiler-bundle": "6.2.*", // v6.2.4
        "zenstruck/foundry": "^1.26" // v1.26.0
    }
}
// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "api-platform/core": "^3.0", // v3.0.8
        "doctrine/annotations": "^1.0", // 1.14.2
        "doctrine/doctrine-bundle": "^2.8", // 2.8.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.14", // 2.14.0
        "nelmio/cors-bundle": "^2.2", // 2.2.0
        "nesbot/carbon": "^2.64", // 2.64.1
        "phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
        "phpstan/phpdoc-parser": "^1.15", // 1.15.3
        "symfony/asset": "6.2.*", // v6.2.0
        "symfony/console": "6.2.*", // v6.2.3
        "symfony/dotenv": "6.2.*", // v6.2.0
        "symfony/expression-language": "6.2.*", // v6.2.2
        "symfony/flex": "^2", // v2.2.4
        "symfony/framework-bundle": "6.2.*", // v6.2.3
        "symfony/property-access": "6.2.*", // v6.2.3
        "symfony/property-info": "6.2.*", // v6.2.3
        "symfony/runtime": "6.2.*", // v6.2.0
        "symfony/security-bundle": "6.2.*", // v6.2.3
        "symfony/serializer": "6.2.*", // v6.2.3
        "symfony/twig-bundle": "6.2.*", // v6.2.3
        "symfony/ux-react": "^2.6", // v2.6.1
        "symfony/validator": "6.2.*", // v6.2.3
        "symfony/webpack-encore-bundle": "^1.16", // v1.16.0
        "symfony/yaml": "6.2.*" // v6.2.2
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "6.2.*", // v6.2.1
        "symfony/maker-bundle": "^1.48", // v1.48.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/stopwatch": "6.2.*", // v6.2.0
        "symfony/web-profiler-bundle": "6.2.*", // v6.2.4
        "zenstruck/foundry": "^1.26" // v1.26.0
    }
}

Need to build an API and love the process? You've come to the right place.

Built on top of Symfony, API Platform enables you to build a rich, JSON-LD-powered, hypermedia API... pretty much instantly (we'll even teach you what those buzzwords mean). In this tutorial, we'll build a real app including:

  • Setting up API Platform in a Symfony app
  • Buzzwords: Swagger, OpenAPI & JSON-LD+Hydra
  • "Exposing" a Class to your API via ApiResource
  • Customizing operations
  • All about serialization and controlling input/output fields
  • IRIs & how to control them
  • Related resources
  • Embedded resources & Subresources
  • Filters
  • Pagination
  • Validation
  • Formats & Content-Type Negotiation
  • Bonus: React Admin

Your Guides

Ryan Weaver

Buy Access

Join the Conversation?

29
Login or Register to join the conversation
Evgeny Avatar

Great lessons, thanks! Is there a video on how to use GraphQl based on Api-Platform?

1 Reply

Hey @Evgeny

We do not cover QraphQL in these videos but ApiPlatform docs are very good, you may want to give it a read https://api-platform.com/docs/core/graphql/

Cheers!

1 Reply
Evgeny Avatar

It's a pity, as always, not everything is clear from the documentation, for example, how can I return DTO instead an entity object. It would be great if you recorded additional videos on graphql.

Reply

Thanks for your feedback, I'll pass your message to the team. We love to know what our users want to learn. In the meantime, I can recommend you to watch these videos where we introduce a DTO into an ApiPlatform project, but it's written for version 2, however, the logic is still relevant but the code will change for version 3
https://symfonycasts.com/screencast/api-platform-extending/output-class

Oh, and here are the docs about DTOs https://api-platform.com/docs/core/dto/

Cheers!

Reply

Oh, and also, I'm literally coding up episode 3 right now - https://symfonycasts.com/screencast/api-platform3-extending - where we go extensively into DTO's :)

1 Reply
Evgeny Avatar

Thanks for the answer! Will annotations with api-platform settings work for a graphql?

Reply

I'm not sure about that. As far as I know, ApiPlatform 3 moved from annotations to PHP attributes, which are faster and more reliable.

Reply
Pawel_Liszka Avatar
Pawel_Liszka Avatar Pawel_Liszka | posted 10 months ago

Thank you very much, Ryan - the guide fell from heaven! A few days ago I was just watching the previous version of the guide to the API Platform but with symfony 6.2 it worked quite averagely! I'm waiting for the next episodes! :)

1 Reply

Haha, quite "averagely", I would agree! Looking at all the old syntax - the annotations, etc - no fun. We're going to get these out as quickly as we can - I've nearly got all of the audio recorded (and all the video is already done).

Cheers!

4 Reply
Yangzhi Avatar
Yangzhi Avatar Yangzhi | posted 17 days ago | edited
        new Post(
            '/share_files/upload.{_format}',
            inputFormats: array('multipart' => 'multipart/form-data'),
            deserialize: false,
            processor: UploadFileProcessor::class
        ),

hi,use api upload file,can not use processor,will get

"Could not resolve argument $data of \"api_platform.action.placeholder::__invoke()\", maybe you forgot to register the controller as a service or missed tagging it with the \"controller.service_arguments\"?"

only working by custom controller。
right?
thanks

Reply

Hey @Yangzhi!

Sorry for my very slow reply! Ok, on a very low level, the error means that you're missing a "data" request attribute. This is normally set via one of the listeners. I believe, in the case of a POST, that it's set right here - https://github.com/api-platform/core/blob/main/src/Symfony/EventListener/DeserializeListener.php#L102-L106

Assuming I'm correct, the question is: why does that listener exit before setting the request attribute? Based on tour deserialize: false - I'm guessing it's this line - https://github.com/api-platform/core/blob/main/src/Symfony/EventListener/DeserializeListener.php#L84 - but I know why you have deserialize: false. On the docs - https://api-platform.com/docs/core/file-upload/ - they also show setting a custom controller for this (which would not require that data attribute). You might just be missing that part?

Cheers!

Reply
Yangzhi Avatar

hi,the post operation need return one object,but sometimes i want return collection by post, and i created customer controller and uritemplate,can you teach how to return collection please.thanks

Reply

Hi @Yangzhi!

Hmm. I'm honestly not sure how to do this - and I would avoid it if possible. It's very not RESTful (the POST endpoint is supposed to be a singular endpoint, but in creating a single item and returning a single item), which is fine (you don't always need to follow the result), but as API Platform tries to be RESTful, doing something like this is quite custom. There MAY be some trick to do it, but I'm not sure. Again, I'd try to avoid this... but you could perhaps create a totally custom operation via a controller to try to do this.

Good luck and sorry I can't be more helpful!

Reply
Yangzhi Avatar

how to return exception message on production environment?

Reply

Hey @Yangzhi

You need to catch the exception, probably in your controller, and just return a response as always, you can set the response with the exception error message, and also set the HTTP status code accordingly

Cheers!

Reply
May-S Avatar

Hello!
Thank you very for the tutos.
At the moment I'm trying to set an offset (using offset instead of page), but I cannot override the actual pagination (I cannot setFirstResults of the queries), any ideas plz ?

Reply

Hey @May-S

Thanks for your kind words. About your question, I'm not sure what you mean by "using an offset" but have you read the last topics of the documentation? https://api-platform.com/docs/core/pagination/#controlling-the-behavior-of-the-doctrine-orm-paginator
I believe you'll need to extend or create a custom paginator

Cheers!

Reply
May-S Avatar
May-S Avatar May-S | MolloKhan | posted 7 months ago | edited

Hey @MolloKhan thanks for your reply.
I mean by my question, that I can pass a param named 'offset' or 'started_from' in the query and then this offset which gonna be set in the first result of the query. It's the same aspect searched here https://github.com/api-platform/core/issues/4672.
I've tried to create an extension for the paginator but the class https://github.com/api-platform/core/blob/main/src/Doctrine/Orm/Extension/PaginationExtension.php is final. Have any ideas plz ?

Reply

Ohh, it seems like they never implemented that feature. You'll have to decorate the PaginationExtension class then, or create a custom paginator. Here's an example about how to decorate an ApiPlatform service https://api-platform.com/docs/core/state-processors/#creating-a-custom-state-processor

I hope it helps. Cheers!

Reply
May-S Avatar

Hello!
Thanks, but It does not solve the problem.
However I've set a solution, for anybody who need it https://github.com/api-platform/core/issues/4672#issuecomment-1557480806.

1 Reply
Rokas-P Avatar

Thanks. Though why no information about new state providers?

At the moment I'm struggling to use Custom DataProvider with DTO to GetCollection data.
I manage to get the data, but the pagination info is gone :(

Reply

Hey @Rokas-P!

Unfortunately, custom data providers will be covered, but not until episode 3. If you're trying to customize how a collection of entities is returned, then you should use a query extension instead. If this is a more custom source, I haven't done this yet, but you can look at how the ORM pagination works. The process is:

A) CollectionProvider is the provider used for the ORM: https://github.com/api-platform/core/blob/main/src/Doctrine/Orm/State/CollectionProvider.php

B) At first, it looks like there is nothing related to pagination, and this returns the final result on the bottom. BUT, just above the bottom, it checks to see if any extensions implement QueryResultCollectionExtensionInterface. And they do, it calls them and returns THAT result instead. This is a sneaky way of calling PaginationExtension: https://github.com/api-platform/core/blob/main/src/Doctrine/Orm/Extension/PaginationExtension.php

C) PaginationExtension::getResult() returns a Paginator object. THAT is the key.

So the long story is that, if you want your custom data provider to allow pagination, instead of returning the array of results, you should return a Paginator. Well, not THAT Paginator, as that's from the ORM. I think you'll need to create a custom class that implements PaginatorInterface (or more likely extends AbstractPaginator).

Let me know if this helps :).

Cheers!

Reply
Ousmane-N Avatar
Ousmane-N Avatar Ousmane-N | posted 9 months ago

Thanks Ryan, nice start !
Any chance to see a tutorial about client generator for nuxt 3 ?

Reply

Hey,

Thanks for feedback and course idea!

Cheers!

Reply
Martin-M Avatar

Very nice! Thanks for the good work! Is there any chance you'll be doing an upgrade guide from 2.6/2.7 to >3 like you did for Symfony 5.* to 6? That one helped me a loooot :)

Reply

Hey Martin!

Hmm, maybe! I have one other friend that is doing the upgrade on a pretty big project and has been hitting some issues. Are there any specific things with upgrading that you're interested in? Or just a general request because you'll need to upgrade an app at some point?

Cheers!

Reply
Martin-M Avatar

Hy Ryan,

sorry for the late answer.

I'm working on a project with multiple Microservices which are based on Symfony 5.4 and Api-platform 2.7. These will have to be upgraded to Symfony6 and Apip3 sooner or later. In my opinion better sooner than later. The heavy lifting was done in the upgrade from 2.6. to 2.7 with the new syntax, but there are some deprecations I just don't seem to get rid off.
And that's the point where your upgrading symfony course really helped me and I would have the hopes, that upgrading apip would do the same :D

for example:

Since api-platform/core 2.7: The $exceptionOnNoToken parameter in "ApiPlatform\Symfony\Security\ResourceAccessChecker::__construct()" is deprecated and will always be false in 3.0, you should stop using it.
Reply
Martin-M Avatar
Martin-M Avatar Martin-M | Martin-M | posted 9 months ago | edited

Ignore that deprecation in my last comment. I upgraded to Symfony 6.2 and apip 3.1 just to see if it works and this and another deprecation just went away. A bit jarring for me, because I don't have that much Symfony experience, but sometimes you just have to try.

Reply

Nice work Martin! That deprecation indeed looks odd. The deprecations system in Symfony is GREAT, but sometimes they're a bit internal and it's not obvious what's needed :)

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice