Finishing POST [API Platform]
In this video we are continuing with our POST
implementation. We're going to start by tackling the two problems we encountered towards the end of the previous video:
- The
releaseDate
should not benull
- We're expecting
releaseDate
, notrelease_date
The first of these two issues is with our current entity setup.
We either need to explicitly allow the releaseDate
property to be nullable
:
/**
* @var \DateTime|null
- * @ORM\column(type="datetime")
+ * @ORM\column(type="datetime", nullable=true)
*/
private $releaseDate;
Which is not what I want to do.
Or we need to add an assertion on to this property to throw a validation exception (a 400
error code), rather than trying to save to the database and failing (a 500
error code).
/**
* @var \DateTime|null
+ * @Assert\NotNull()
* @ORM\column(type="datetime", nullable=true)
*/
private $releaseDate;
Ok, now if we send in our test:
vendor/bin/behat features/album.feature --tags=t
Feature: Provide a consistent standard JSON API endpoint
In order to build interchangeable front ends
As a JSON API developer
I need to allow Create, Read, Update, and Delete functionality
Background: # features/album.feature:7
Given there are Albums with the following details: # FeatureContext::thereAreAlbumsWithTheFollowingDetails()
| title | track_count | release_date |
| some fake album name | 12 | 2020-01-08T00:00:00+00:00 |
| another great album | 9 | 2019-01-07T23:22:21+00:00 |
| now that's what I call Album vol 2 | 23 | 2018-02-06T11:10:09+00:00 |
And the "Content-Type" request header is "application/json" # Imbo\BehatApiExtension\Context\ApiContext::setRequestHeader()
@t
Scenario: Can add a new Album # features/album.feature:30
Given the request body is: # Imbo\BehatApiExtension\Context\ApiContext::setRequestBody()
"""
{
"title": "Awesome new Album",
"track_count": 7,
"release_date": "2030-12-05T01:02:03+00:00"
}
"""
When I request "/album" using HTTP POST # Imbo\BehatApiExtension\Context\ApiContext::requestPath()
Then the response code is 201 # Imbo\BehatApiExtension\Context\ApiContext::assertResponseCodeIs()
Expected response code 201, got 400. (Imbo\BehatApiExtension\Exception\AssertionFailedException)
--- Failed scenarios:
features/album.feature:30
1 scenario (1 failed)
5 steps (4 passed, 1 failed)
0m0.11s (9.96Mb)
Great Success!
We switched out our 500
for a 400
. It's not great, but the error message itself is much nicer:
{
"type": "https://tools.ietf.org/html/rfc2616#section-10",
"title": "An error occurred",
"detail": "releaseDate: This value should not be null.",
"violations": [
{
"propertyPath": "releaseDate",
"message": "This value should not be null."
}
]
}
If you've been following along with either the Symfony 4 JSON API, or Symfony 4 FOSRESTBundle API, you will likely be thinking... wow that's a nicer looking error message :) I agree.
Ok, but 400
or 500
, it's still not a 201
, which is what we want.
What The null
?
Looking at the violation we can see that releaseDate
is null
. And it should not be null
.
Well, releaseDate
is null
because we're using release_date
.
In order to fix this, just follow the docs:
# api/config/services.yaml
services:
# ... other stuff ...
Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter: ~
(Watch the video for a little more on this.)
And also:
# api/config/packages/api_platform.yaml
api_platform:
# ... other stuff ...
name_converter: 'Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter'
Honestly there's not a whole lot to say about this process.
It's a piece of functionality provided by Symfony's Serializer. You can use this independently of API Platform.
There's an additional piece of documentation about this here.
You can also implement your own name_convertor
by writing your own class that implements Symfony\Component\Serializer\NameConverter\NameConverterInterface
, then using that instead.
Ok cool, and now we have a passing test:
vendor/bin/behat features/album.feature --tags=t
Feature: Provide a consistent standard JSON API endpoint
In order to build interchangeable front ends
As a JSON API developer
I need to allow Create, Read, Update, and Delete functionality
Background: # features/album.feature:7
Given there are Albums with the following details: # FeatureContext::thereAreAlbumsWithTheFollowingDetails()
| title | track_count | release_date |
| some fake album name | 12 | 2020-01-08T00:00:00+00:00 |
| another great album | 9 | 2019-01-07T23:22:21+00:00 |
| now that's what I call Album vol 2 | 23 | 2018-02-06T11:10:09+00:00 |
And the "Content-Type" request header is "application/json" # Imbo\BehatApiExtension\Context\ApiContext::setRequestHeader()
@t
Scenario: Can add a new Album # features/album.feature:30
Given the request body is: # Imbo\BehatApiExtension\Context\ApiContext::setRequestBody()
"""
{
"title": "Awesome new Album",
"track_count": 7,
"release_date": "2030-12-05T01:02:03+00:00"
}
"""
When I request "/album" using HTTP POST # Imbo\BehatApiExtension\Context\ApiContext::requestPath()
Then the response code is 201 # Imbo\BehatApiExtension\Context\ApiContext::assertResponseCodeIs()
1 scenario (1 passed)
5 steps (5 passed)
0m0.17s (9.65Mb)
And we know that if we can POST
then our Background
steps are also working.
This is good. It means we can get on with the process of testing all our other endpoints. And we will do just this in the very next video.