gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
The amazing interactive documentation that we've stumbled across is not something from API platform! Nope, it's actually an open-source API documentation library called Swagger UI. And the really cool thing about Swagger UI is that, if someone create a file that describes any API, then that API can get all of this for free! I love free stuff! We get Swagger UI because API platform provides that description file out of the box. But more on that in a minute.
Let's play around with this. Use the POST endpoint to create a new DragonTreasure
. We've recently plundered some "Gold coins"... which we got from "Scrooge McDuck". He's mad. For our purposes, none of the other fields really matter. Down here, hit " Execute" and... boom! When you scroll down, you can see that this made a POST request to /api/dragon_treasures
and sent all of that data as JSON! Then, our API returned a "201" status code. A 201 status means that the request was successful and a resource was created. Then it returned this JSON, which includes an id
of 1
. So, as I said, this isn't just documentation: we really do have a working API! There are a few extra fields here too: @context
, @id
, and @type
We'll talk about those soon.
Now that we have a DragonTreasure
to work with, open up this "GET" endpoint, click "Try it Out", then "Execute". Oh, I love it. Swagger just made a GET
request to /api/dragon_treasures
- this ?page=1
is optional. Our API returned information inside something called hydra:member
, which isn't particularly important yet. What matters is that our API did return a list of all of the DragonTreasures
we currently have, which is just this one.
So in just a few minutes of work, we have a fully featured API for our Doctrine entity. That is cool.
Copy the URL to the API endpoint, open a new tab, and paste that in. Whoa! This... returned HTML? But a second ago, Swagger said that it made a GET
request to that URL... and it returned JSON. What's going on?
One feature of API Platform is called "Content Negotiation". It means that our API can return the same resource - like DragonTreasure
- in multiple formats, like JSON, or HTML... or even things like CSV. Oh, an ASCII format would be awesome. Anyways, we tell API Platform which format we want by passing an Accept
header in the request. When we use the interactive docs, it passes this Accept
header for us set to application/ld+json
. We'll talk about the ld+json
part soon... but, thanks to this, our API returns JSON!
And even though we don't see it here, when you go to a page in your browser, your browser automatically sends an Accept
header that says we want text/html
. So, this is API Platform showing us the "HTML representation" of our dragon treasures..., which is just the documentation. Watch: when I open the endpoint this URL is for, it automatically executed it.
The point is: if we want to see the JSON representation of our dragon treasures, we need to pass this Accept
header... which is super easy, for example, if you're writing JavaScript.
But passing a custom Accept
header isn't so easy in a browser... and it would be nice to be able to see the JSON version of this. Fortunately, API Platform gives us a way to cheat. Remove the ?page=1
to simplify things. Then, at the end of any endpoint, you can add .
followed by the extension of the format you want: like .jsonld
.
Now we see the DragonTreasure
resource in that format. API Platform also supports normal JSON out of the box, so we can see the same thing, but in pure, standard JSON.
The fact that all of this works means that... we apparently have a new route for /api
as well as a bunch of other new routes for each operation - like GET /api/dragon_treasures
. But... where did these come from? How are they being dynamically added to our app?
To answer that, spin over to your terminal and run:
./bin/console debug:router
I'll make this a bit smaller so we can see everything. Yup! Each endpoint is represented by a normal, traditional route. How are these being added? When we installed API Platform, its recipe added a config/routes/api_platform.yaml
file.
api_platform: | |
resource: . | |
type: api_platform | |
prefix: /api |
This is actually a route import. It looks a little weird, but it activates API Platform when the routing system is loading. API Platform then finds all of the API resources in our app and generates a route for every endpoint.
The point is that all we need to focus on is creating these beautiful PHP classes and decorating them with ApiResource
. API Platform takes care of all the heavy lifting of hooking up those endpoints. Of course, we'll need to tweak the configuration and talk about more advanced things, but hey! That's the point of this tutorial. And we're already off to an epic start.
Next: I want to talk about the secret behind how this Swagger UI documentation is generated. It's called OpenAPI.
Hey Radu!
Hmm. Do you have a docker-compose.yml
file? If you do and you have Docker running AND you have a container called database
, then when you access the site, the symfony
binary is overriding the DATABASE_URL
env var and setting it to point at that Docker container. If you're setting up your database locally (without Docker), just don't bother starting Docker and delete the docker-compose.yml
file entirely (or at least the matching service).
If my guess is incorrect, let me know!
Cheers!
Hey @weaverryan,
Thanks for the quick reply! You were indeed correct! I've recreated my local container after renaming the database service in the docker-compose.yml file to my_app_database and the issue went away! :)
Cheers,
Radu
// 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
}
}
Hi Ryan,
I have an issue. I'm using mysql instead of postgresql (created a docker standard mysql 8 container from mysql:latest).
I was able to create the entity with the maker bundle, the migration, create the database (./bin/console doctrine:database:create) and execute the migration using CLI commands (./bin/console doctrine:migrations:migrate). The database was created and the tables as well.
All steps went successfully until I tried to submit a post request from the https://127.0.0.1:8000/api. I get the following error:
The problem is that for some reason it is trying to query a database named root, when in fact the database that was earlier created is called app.
In my .env file I have:
Any thoughts on this one?
Thanks,
Radu