Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Let's get to work customizing our API. A RESTful API is all about resources. We have one resource - our CheeseListing - and, by default, API Platform generated 5 endpoints for it. These are called "operations".

Collection and Item Operations

Operations are divided into two categories. First, "collection" operations. These are the URLs that don't include {id} and where the "resource" you're operating on is technically the "collection of cheese listings". For example, you're "getting" the collection or you're "adding" to the collection with POST.

And second - "item" operations. These are the URLs that do have the {id} part, when you're "operating" on a single cheese listing resource.

The first thing we can customize is which operations we actually want! Above CheeseListing, inside the annotation, add collectionOperations={} with "get" and "post" inside. Then itemOperations with {"get", "put", "delete"}.

... lines 1 - 7
* @ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get", "put", "delete"}
* )
... line 13
class CheeseListing
... lines 16 - 116

A lot of mastering API Platform comes down to learning about what options you can pass inside this annotation. This is basically the default configuration: we want all five operations. So not surprisingly, when we refresh, we see absolutely no changes. But what if we don't want to allow users to delete a cheese listing? Maybe instead, in the future, we'll add a way to "archive" them. Remove "delete".

... lines 1 - 7
* @ApiResource(
... line 10
* itemOperations={"get", "put"}
* )
... line 13
... lines 15 - 116

As soon as we do that... boom! It's gone from our documentation. Simple, right? Yep! But a bunch of cool things just happened. Remember that, behind the scenes, the Swagger UI is built off of an Open API spec document, which you can see at /api/docs.json. The reason the "delete" endpoint disappeared from Swagger is that it disappeared from here. API Platform is keeping our "spec" document up to date. If you looked at the JSON-LD spec doc, you'd see the same thing.

And of course, it also completely removed the endpoint - you can see that by running:

php bin/console debug:router

Yep, just GET, POST, GET and PUT.

Customizing the Resource URL (shortName)

Hmm, now that I'm looking at this, I don't love the cheese_listings part of the URLs... API Platform generates this from the class name. And really, in an API, you shouldn't obsess about how your URLs look - it's just not important, especially - as you'll see - when your API responses include links to other resources. But... we can control this.

Flip back over and add another option: shortName set to cheeses.

... lines 1 - 7
* @ApiResource(
... lines 10 - 11
* shortName="cheeses"
... lines 13 - 14
... lines 16 - 117

Now run debug:router again:

php bin/console debug:router

Hey! /api/cheeses! Much better! And we see the same thing now on our API docs.

Customizing Operation Route Details

Ok: so we can control which operations we want on a resource. And later, we'll learn how to add custom operations. But we can also control quite a lot about the individual operations.

We know that each operation generates a route, and API Platform gives you full control over how that route looks. Check it out: break itemOperations onto multiple lines. Then, instead of just saying "get", we can say "get"={} and pass this extra configuration.

Try "path"= set to, I don't know, "/i❤️️cheeses/{id}".

... lines 1 - 7
* @ApiResource(
... line 10
* itemOperations={
* "get"={"path"="/i❤️cheeses/{id}"},
* "put"
* },
... line 15
* )
... line 17
... lines 19 - 120

Go check out the docs! Ha! That works! What else can you put here? Quite a lot! To start, anything that can be defined on a route, can be added here - like method, hosts, etc.

What else? Well, along the way, we'll learn about other, API-Platform-specific stuff that you can put here, like access_control for security and ways to control the serialization process.

In fact, let's learn about that process right now! How does API Platform transform our CheeseListing object - with all these private properties - into the JSON that we've been seeing? And when we create a new CheeseListing, how is it converting our input JSON into a CheeseListing object?

Understanding the serialization process may be the most important piece to unlocking API Platform.

Leave a comment!

This tutorial works great for Symfony 5 and API Platform 2.5.

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.1.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "api-platform/api-pack": "^1.2", // v1.2.0
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-migrations-bundle": "^2.0", // v2.0.0
        "nesbot/carbon": "^2.17", // 2.19.2
        "symfony/console": "4.2.*", // v4.2.9
        "symfony/dotenv": "4.2.*", // v4.2.9
        "symfony/flex": "^1.1", // v1.9.10
        "symfony/framework-bundle": "4.2.*", // v4.2.9
        "symfony/yaml": "4.2.*" // v4.2.9
    "require-dev": {
        "symfony/maker-bundle": "^1.11", // v1.11.6
        "symfony/profiler-pack": "^1.0" // v1.0.4