> APIs >

Course Overview

Login to bookmark this course

Symfony RESTful API: Course 1

Get hands-on with Symfony to build a working API. Learn about forms, test writing, handling endpoints, and leveraging the JMS Serializer.

  • 5148 students
  • EN Captions
  • EN Script
  • Certificate of Completion

Your Guides

About this course

This tutorial uses an older version of Symfony. The concepts of REST are still valid, but I recommend using API Platform in new Symfony apps.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.6.*", // v2.6.11
        "doctrine/orm": "~2.2,>=2.2.3,<2.5", // v2.4.7
        "doctrine/dbal": "<2.5", // v2.4.4
        "doctrine/doctrine-bundle": "~1.2", // v1.4.0
        "twig/extensions": "~1.0", // v1.2.0
        "symfony/assetic-bundle": "~2.3", // v2.6.1
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.8
        "symfony/monolog-bundle": "~2.4", // v2.7.1
        "sensio/distribution-bundle": "~3.0,>=3.0.12", // v3.0.21
        "sensio/framework-extra-bundle": "~3.0,>=3.0.2", // v3.0.7
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "hautelook/alice-bundle": "0.2.*", // 0.2
        "jms/serializer-bundle": "0.13.*" // 0.13.0
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3", // v2.5.3
        "behat/behat": "~3.0", // v3.0.15
        "behat/mink-extension": "~2.0.1", // v2.0.1
        "behat/mink-goutte-driver": "~1.1.0", // v1.1.0
        "behat/mink-selenium2-driver": "~1.2.0", // v1.2.0
        "phpunit/phpunit": "~4.6.0" // 4.6.4
    }
}

In this series, we get to work: by building the same API in the REST series, but leveraging all the amazing tools and libraries of the Symfony framework. If terminology like representations, resources, and hypermedia are new to you, start with the REST series and then come back. And get ready to get your (API) mind blown.

In Course 1, we'll start quick with:

  • Build your first functional POST endpoint
  • Decoding the Request body
  • Using forms to handle incoming data
  • Writing tests for your API
  • Handling data "seeding" for your tests
  • GET, PUT, DELETE and PATCH endpoints
  • Using the JMS Serializer

Next courses in the APIs: REST in Symfony 2 & 3 section of the APIs Track!

49 Comments

Sort By
Login or Register to join the conversation
Tac-Tacelosky avatar Tac-Tacelosky 6 years ago

+1 for a tutorial that uses FOSRestBundle and Angular.

10 | Reply |

Is there a plan to release a course on using API Platform to develop a REST (or GraphQL) API?

2 | Reply |

Hey Mario,

We do want to release this course, you can see it here: https://knpuniversity.userv... - but unfortunately we do not have any estimations about its release date yet, sorry. Thanks for your patience!

Cheers!

| Reply |
Daniel G. avatar Daniel G. Victor 5 years ago

This link shows: "This UserVoice instance does not exist.". I would be very happy if API platform course would be created.

| Reply |

Hey Daniel,

Awesome news for you! We've started working on ApiPlatform course! Well, it's on the planning stage yet, but I think it will be available in a month or two. And yeah, it's a *top* secret! So, shhhhhhh, don't tell anyone ;)

P.S. And the correct link is: https://symfonycasts.uservo...

Cheers!

| Reply |
Daniel G. avatar Daniel G. Victor 5 years ago

Wow!

Your voting system is totally awesome.😍 Now I feel that I have influence on topics that you will presents.
I have to tell all my friends 🧑👨👧🎃 to subscribe Symfony Cast and vote like me as soon as possible :D 😇

Thank You!

| Reply |

Hi Victor, I need to start a new project with an API. If I plan to go with API Platform which tutorial should I follow here on SFcast ? Does the Symfony RESTful API: Course will help or it's unrelated ? Thx.

| Reply |

Hey Kaizoku

You can watch our courses about REST API's, it will help you understanding how to develop your own API, how to test it, and some best practices, but we do not talk about "API Platform" on those courses.

Cheers!

| Reply |

Thanks, that's good to know. Will be looking out for it.

| Reply |
Default user avatar Filipe Moraes 6 years ago

Why the methods "processForm" , "getErrorsFromForm" and "throwApiProblemValidationException" are not within the class "BaseController"? These methods are common to all controllers.

2 | Reply |

Hey Filipe!

You're absolutely right - and we move them into the BaseController eventually (once we actually need them in multiple places - actually it's something we do "offline" between episodes 4 and 5). Of course, many people - like you - will already know this will need to happen, so you can put it directly into BaseController :).

Cheers!

1 | Reply |
Default user avatar Lukasz 5 years ago

FOSRestBundle is not a good idea https://github.com/tulik/sy...

1 | Reply |
Default user avatar Hugo do Carmo 6 years ago edited

Hi there, if you're interested to see how this course would look in the new Symfony 4 (with Flex) architecture, you can take a look in my repository ( https://github.com/spelcast... ). I've also used Guzzle 6 to test the api controller.

I had to refactor a few things to get it working, some of them were big changes and were not that intuitive, and others were very easy to accomplish.

I had a hard time to separate the test environment, fortunately, there is an open PR by there that should fix or at least mitigate the problem (I recommend you to visit the link to understand the problem https://github.com/symfony/... ).

I hope you all enjoy, cya.

1 | Reply |

Very cool Hugo do Carmo! Thanks for sharing this - awesome! And yea... hopefully we'll get that PR merged soon - I'm still trying to decide about that last part - if we should add a system to also be able to load .env.ENVIRONMENT files (e.g. .env.test).

Cheers!

1 | Reply |
joanquilez avatar joanquilez 5 years ago

Hello,
I'm trying to follow this tutorial but I can't find a way to do it. My first option was to do it 'as it is', although I know it is very old. I thought "first I will learn how everything works and, later, I will update my knowledge to the new symfony versions". But soon I discovered that I couldn't do that because my system has PHP 7 and I can't go beyond the 3rd video if I don't make some updates in code and dependencies (reverting to PHP 5 is not an option!). I've been struggling for hours with several ways to do it with no success. I always end up with some problem related to unresolvable dependencies (as I am very far from an expert on this, I feel myself as I were crashing against a wall over and over). So, at this point, I'm wondering... what would you advise me to do?

| Reply |

Hey joanquilez

Thank you for your honest feedback. We're working on a way to reduce this kind of problems (related to old dependencies) but it will take us some time to deliver it. What I recommend you in the meanwhile is to follow this course on Symfony4. it may cause you some troubles because the architecture changed but it's not that hard, and if you have problems, you can always leave us a message.
Oh, and about the dependencies, I think you will have problems with Guzzle only, but you can install the latest version and adjust the code, the library didn't change too much (at least the functions we use in the tutorial)

Cheers!

1 | Reply |

Thanks a lot! Following it with Symfony4 is what I'm doing since I wrote the message, but with your answer I am more comfortable with my decision!

1 | Reply |
Default user avatar ibn mrabet achraf 6 years ago

hey guys,
how to get a twig.html page as response in api Controller for example: I get information from the user then i want to display a html page containing those information !

| Reply |

Hi there!

You absolutely can just return HTML in your API instead of JSON - it's often an easy shortcut to make updating your HTML page with AJAX really easy. Here's how it would look:


public function someAction()
{
    $html = $this->renderView('mypage/some_template.html.twig', array('twigVar' => 'twigVal'));

    // return pure HTML
    return new Response($html);
    // or return HTML inside JSON, under an "html" key (but this key could be anything).
    return new JsonResponse(array('html' => $html));
}

Cheers!

| Reply |
Default user avatar Henri Tompodung 6 years ago

Hi Ryan, i get an error when i want to install it..

[Doctrine\Common\Annotations\AnnotationException]
You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.

Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the post-install-cmd event terminated with an exception

[RuntimeException]
An error occurred when executing the "'cache:clear --no-warmup'" command.

Thx anyway..

| Reply |

Hey Henri,

Ah, looks like this issue was already solved - could you follow the http://knpuniversity.com/sc... thread to get it worked? Let me know if it helps.

Cheers!

| Reply |
Default user avatar Henri Tompodung Victor 6 years ago

It's work! Thanks Victor :)

| Reply |
Paweł C. avatar Paweł C. 6 years ago

Do I need before Symfony RESTful 5-pack courses first go with these two courses? https://knpuniversity.com/s... https://knpuniversity.com/s...

| Reply |

Hey Paweł C.!

You do not need to go through those other REST courses before going through this course :). Those courses give deeper explanations of REST concepts, but I refer to them in this screencast. So, if there is some REST idea that is still not clear in this tutorial, you can quickly look at the other tutorials *if* you want deeper explanation.

I hope that helps! Cheers!

1 | Reply |

thanks.

| Reply |
Default user avatar David Song 6 years ago edited

Hey Ryan! How are you doing? In Chapter 13, in ApiTestCase's setUpBeforeClass(), you are using Guzzle 5's emitter->on('before') to rewrite the target Path of the request (prepending the app_test.php to every request). I've had to modify your code since that is no longer supported in Guzzle 6. So far, I've got it correctly rewriting the "requestTarget" in the Request object but the client still goes to the unmodified target, here is the relevant excerpt from my setUpBeforeClass():


        $testEnvMapper = Middleware::mapRequest(function (RequestInterface $request) {
            $path = $request->getRequestTarget();
            $modified_request = $request->withRequestTarget('/app_test.php'.$path);
            var_dump($modified_request->getRequestTarget());
            return $modified_request;
        });
        $handler->push($testEnvMapper);
        self::$staticClient = new Client([
            'base_uri' => 'http://localhost:8000',
            'handler' => $handler,
            'http_errors' => false
        ]);

Here is the modified request from the captured history:
["method":"GuzzleHttp\Psr7\Request":private]=>
string(3) "PUT"
["requestTarget":"GuzzleHttp\Psr7\Request":private]=>
string(24) "/app_test.php/customer/2"
["uri":"GuzzleHttp\Psr7\Request":private]=>
object(GuzzleHttp\Psr7\Uri)#284 (7) {

["scheme":"GuzzleHttp\Psr7\Uri":private]=>
string(4) "http"
["userInfo":"GuzzleHttp\Psr7\Uri":private]=>
string(0) ""
["host":"GuzzleHttp\Psr7\Uri":private]=>
string(9) "localhost"
["port":"GuzzleHttp\Psr7\Uri":private]=>
int(8000)
["path":"GuzzleHttp\Psr7\Uri":private]=>
string(11) "/customer/2"
["query":"GuzzleHttp\Psr7\Uri":private]=>
string(0) ""
["fragment":"GuzzleHttp\Psr7\Uri":private]=>
string(0) ""

}



As you can see "requestTarget" has been correctly modified, but the Uri Object's "path" field is still the original.
All my tests use the guzzle client like this:
    $response = $this->client->put('customer/2', [
        'body' => json_encode($data)
    ]);


And they are all hitting the unmodified target.  Any insights? I've scoured resources online to no avail.
| Reply |

Hey David Song!

Sorry for the slow reply - somehow your message landed in SPAM at first >:( - but we rescued it :). In episode 4 of this tutorial, we actually upgraded to Guzzle 6 and made this change. So, I'm happy to say that you can see some working code there: http://knpuniversity.com/sc.... You can get the code by downloading it, or I have a copy of it here: https://gist.github.com/wea...

Let me know if that helps!

| Reply |

Thanks Ryan! withUri() instead of withRequestTarget()!

1 | Reply |

Hey Ryan! Do I need to finished "RESTful APIs in the Real World" courses first to start this course for Symfony? Does this courses closely connected?

| Reply |

Hey Bocharsky!

You can start right with this one :). If you're quite new to REST/API stuff and really want to go deep, I'd watch the other one - though in this screencast, we occasionally say "if you want more info, go to this URL to watch part of the REST series" - to help out with this.

Cheers!

| Reply |

There is a lot of information about REST on KnpU. I think I better to start from scratch and I'm almost sure there I discover a useful information for me too. I always open something new for myself with your screencasts. They're a very cognitive and helps to improve the understanding of the subject area! Thanks!

| Reply |

Hey Ryan, great stuff!!! I have some questions. Couldn't we use Symfony\Bundle\FrameworkBundle\Client instead of Guzzle? Like the Symfony documentation says in the functional tests section. Don't those tools supposed to do the same thing? Is there any advantage of using Guzzle instead of Symfony Client??

| Reply |

Hey Achilles,

Yes, you could! It's a matter of taste. But both do the same things. Actually, Symfony client is even simpler because it's already pre-installed in Symfony SE, i.e. you don't need to require it manually with Composer. So just use the one you like the most ;)

Cheers!

| Reply |

Thanks :)

| Reply |

Any estimate? Will you use angular and fosrestbundle for this?

| Reply |

Hey Nick!

We haven't started on this yet, but it's up next - so it's still at least several weeks away. YES, it will use FOSRestBundle. I'm not sure about angular yet - but I think it might be a good idea - after all, that's what a lot of people are using to consume those APIs.

Cheers!

1 | Reply |

Ye Angular would be great! Any news on that front? And also when you are planning to publish the series? Cheers:)

| Reply |

Hey!

On Angular, no plan yet - but I'm going to see if anything angular-specific might be nice to cover as an addendum to this series. I mean, you'll be rocking a very solid API after this series, but if there are any tweaks needed to play even nicer with Angular, maybe we can add something for that after.

The good news is that we're releasing this series right now - expect more next week.

Cheers!

1 | Reply |
Default user avatar twomartoe Nick 6 years ago

Eagerly awaiting this too, in the meantime, check this out

https://codereviewvideos.co...

| Reply |
Default user avatar Martin Bittner 6 years ago

How do you manage an entity with a collection of object?

Say a customer entity may have multiple addresses. Would you have /api/customers/123 (123 being an id), display all customer info and its addresses? Or you would have /api/customers/123/addresses

Same when you create/update, would you accept json (or xml or other) with customer info and its addresses and then code logic in the controller that would instanciate a customer object, and then loop through addresses to create an address entity, link it to the customer and save everything?

Thx

| Reply |

Hey Martin,

Actually, it's completely up to you... or probably your API users. Think about your customers, do they want to send another request for fetching addresses or would like to get them in the customer request. My point here is that if API users need all this info at once, I mean fetching a customer info without addresses is pointless for them, so they always will send another request to get missing addresses, then better to include addresses into the /api/customers/123 endpoint response. But if API users need addresses really rare (they are just interested in basic customer info without addresses), then better to make another endpoint like /api/customers/123/addresses to allow fetching this info only when needed. That's it, I really don't see any significant disadvantages in both approaches.

Cheers!

| Reply |
Default user avatar Ghulam Kibria Ali 6 years ago

Can't wait for this course! :D

| Reply |
Default user avatar prasath_s 6 years ago

Cant wait either:) Hope that more of this series is soon to be released!!:)

| Reply |

Hey! Why not use FosRestBundle? Or maybe this will be in episode 2?

| Reply |

Hey Nick!

Good question! The FosRestBundle is basically a set of various tools - like helping you decode the JSON request, helping serialize the Form errors, doing content-type negotiation and adding a view layer. These are all peripheral tools - and when you get building, you find that you need probably only a few of those. I don't use it in the screencast, though I could have for JSON deserialization and a few other minor things.

Cheers!

| Reply |

Thanks for reply, for a real application though is FosRestBundle and
BazingaHateoasBundle recommended or you think without those you can create a real life professional application without issues and clean enough?

| Reply |

I think FOSRestBundle is a bit like FOSUserBundle - you can use it, or you can fairly easily build that same stuff yourself and get more flexibility. No one option is best - using the bundle gets you setup faster of course :).

BazingaHateoasBundle is *wonderful*, and we will use it later in the series. The question here is, do you really need links in your API? They're all the rage, but it depends on who you're building your API for (e.g. if your API is for your JS frontend, maybe you don't need links, or need very few).

Cheers!

| Reply |
Default user avatar Marcinho Mascarenhas 6 years ago

Hello
Suggestion. Could update this course and launch one with symfony 4

| Reply |

Hey Marcinho,

Good question! Well, it won't be the next tutorial we're released, but we're definitely going to talk about API with Symfony 4.

Cheers!

| Reply |

Delete comment?

Share this comment

astronaut with balloons in space

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