Symfony RESTful API: Course 1

Buy Access

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

Your Guides

Ryan Weaver Leanna Pelham

Questions? Conversation?

  • 2017-04-14 Victor Bocharsky

    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!

  • 2017-04-12 David Song

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

  • 2017-04-12 Martin Bittner

    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

  • 2017-04-11 weaverryan

    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!

  • 2017-04-06 David Song

    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.

  • 2017-02-27 Achilles Kaloeridis

    Thanks :)

  • 2017-02-27 Victor Bocharsky

    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!

  • 2017-02-26 Achilles Kaloeridis

    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??

  • 2017-01-19 Henri Tompodung

    It's work! Thanks Victor :)

  • 2017-01-18 Victor Bocharsky

    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!

  • 2017-01-18 Henri Tompodung

    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..

  • 2016-06-18 weaverryan

    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!

  • 2016-06-16 Filipe Moraes

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

  • 2016-02-29 weaverryan

    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!

  • 2016-02-29 ibn mrabet achraf

    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 !

  • 2015-08-25 Michael Tacelosky

    +1 for a tutorial that uses FOSRestBundle and Angular.

  • 2015-06-22 Victor Bocharsky

    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!

  • 2015-06-22 weaverryan

    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!

  • 2015-06-22 Victor Bocharsky

    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?

  • 2015-05-21 weaverryan

    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!

  • 2015-05-21 Nick

    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?

  • 2015-05-21 weaverryan

    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!

  • 2015-05-21 Nick

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

  • 2015-05-16 weaverryan

    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!

  • 2015-05-15 prasath_s

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

  • 2015-05-13 prasath_s

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

  • 2015-04-23 Ghulam Kibria Ali

    Can't wait for this course! :D

  • 2015-03-25 twomartoe

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

    https://codereviewvideos.co...

  • 2015-03-19 weaverryan

    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!

  • 2015-03-19 Nick

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