Slide 1

Slide 1 text

Build RESTful APIs easily with Symfony Sarah Khalil February 20th, 2015

Slide 2

Slide 2 text

Who am I? ❖ Trainer ❖ Developer ❖ Enjoying sharer France

Slide 3

Slide 3 text

What is REST ?

Slide 4

Slide 4 text

What is REST? ❖ REpresentational State Transfer ❖ Architecture ❖ Associated to Service Oriented Architecture ❖ Management of resources

Slide 5

Slide 5 text

Constraints of REST

Slide 6

Slide 6 text

Client - Server (1/6) ❖ Separation of concerns ❖ Portability

Slide 7

Slide 7 text

Stateless (2/6) ❖ HTTP ❖ Session on the client side

Slide 8

Slide 8 text

Cacheable (3/6) ❖ Avoid useless requests

Slide 9

Slide 9 text

Layered System (4/6) ❖The client requests a resource but it does not know the system behind.

Slide 10

Slide 10 text

Uniform interface (5/6) ❖ Each resource has its identifier: URI. ❖ Each resource has its representation. ❖ Auto-descripted message: if it’s JSON, the HTTP Response has a content-type header saying so.

Slide 11

Slide 11 text

Code on demand (optional) (6/6) ❖ The client could execute some code coming from the server, to avoid more work on the server side.

Slide 12

Slide 12 text

HTTP Methods ❖ GET ❖ POST ❖ PUT ❖ DELETE ❖ OPTIONS ❖ HEAD ❖ …

Slide 13

Slide 13 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 14

Slide 14 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

REQUEST

Slide 17

Slide 17 text

GET /users HTTP/1.1 User-Agent: curl/7.37.1 Host: 127.0.0.1:8000 Accept: */*

Slide 18

Slide 18 text

Symfony\Component\HttpFoundation\Request

Slide 19

Slide 19 text

RESPONSE

Slide 20

Slide 20 text

HTTP/1.1 200 OK Host: 127.0.0.1:8000 Connection: close X-Powered-By: PHP/5.6.3 Set-Cookie: PHPSESSID=ecdadm6o2d5v9ei0m181j8gh96; path=/ Cache-Control: no-cacheDate: Wed, 11 Feb 2015 23:42:50 GMT Content-Type: text/html; charset=UTF-8 X-Debug-Token: 32860d X-Debug-Token-Link: /_profiler/32860d

Slide 21

Slide 21 text

Symfony\Component\HttpFoundation\Response

Slide 22

Slide 22 text

RESTful Application Programming Interface

Slide 23

Slide 23 text

Keep’em in mind ❖JSON, XML, HTML… ❖By developers, for developers. ❖ Scalability ❖ Latency ❖ Security

Slide 24

Slide 24 text

Levels http://martinfowler.com

Slide 25

Slide 25 text

HATEOAS ❖ Hypermedia as the Engine of Application State ❖ Discoverability

Slide 26

Slide 26 text

How to build all of that?

Slide 27

Slide 27 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 28

Slide 28 text

JMSSerializerBundle Work with resources

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Configuration ❖ XML, YAML and Annotation ❖ http://jmsyst.com/libs/serializer/master/reference ❖ Helper to serialize properties the way you want

Slide 31

Slide 31 text

Usage class DefaultController { public function indexAction() { $obj = new \StdClass(); $obj->latitude = -33.87087; $obj->longitude = 151.225459; $data = $this->get(‘jms_serializer’) ->serialize($obj, ’json’); return new Response( $data, 200, array(‘Content-Type’=>’application/json’) ); } } HTTP/1.1 200 OK Host: 127.0.0.1 X-Powered-By: PHP/5.6.3 Content-Type: application/json { "latitude": -33.87087, "longitude": 151.225459 }

Slide 32

Slide 32 text

Exclusion strategy /** * @JMS\ExclusionPolicy("all") */ class Product { /** * @JMS\Expose() */ private $name; private $users; } ❖ Visitor pattern ❖ Object graph ❖ Relations (xToMany)

Slide 33

Slide 33 text

Serializer Handler ❖ Special needs ❖ implements JMS\Serializer\Handler\SubscribingHandlerInterface ❖

Slide 34

Slide 34 text

public static function getSubscribingMethods() { return [ [ 'direction' => GraphNavigator::DIRECTION_SERIALIZATION, 'format' => ‘json', 'type' => ‘AppBundle\MyClass’, 'method' => ‘serialize', ], [ 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION, 'format' => ‘json', 'type' => ‘AppBundle\MyClass', 'method' => ‘deserialize', ], ]; }

Slide 35

Slide 35 text

public function serialize( JsonSerializationVisitor $visitor, MyClass $object, array $type, Context $context ) { $data = $object->toArray(); return $data; } public function deserialize(JsonDeserializationVisitor $visitor, $data) { $object = new AppBundle\MyClass($data); return $object; }

Slide 36

Slide 36 text

Event Subscriber ❖ http://jmsyst.com/libs/serializer/master/event_system ❖ serializer.pre_serialize, serializer.post_serialize, serializer.pre_deserialize, serializer.post_deserialize ❖ implements JMS\Serializer\EventDispatcher\EventSubscriberInterface ❖

Slide 37

Slide 37 text

class PostSerializeMediaListener implements EventSubscriberInterface { public function onPostSerialize(ObjectEvent $event) { $myObject = $event->getObject(); $event->getVisitor()->addData('newElementInJson', 'information’); } public static function getSubscribedEvents() { return array( array( 'event' => Events::POST_SERIALIZE, 'format' => ‘json', 'class' => ‘AppBundle\MyClass', 'method' => ‘onPostSerialize', ), ); } }

Slide 38

Slide 38 text

Other features ❖ Serialization Group ❖ Versioning

Slide 39

Slide 39 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 40

Slide 40 text

FOS Rest Bundle Setup your Symfony application

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

Controller ❖ Return view / data # app/config/config.yml fos_rest: view: view_response_listener: force sensio_framework_extra: view: { annotations: false } /** * @View() */ public function getUsersAction() { … return $data; }

Slide 43

Slide 43 text

Content type negotiation ❖ Request = > Accept ❖ priorities of media type(s) that your application is ready to get ❖ Response => Content-Type ❖ the media type of the response (html, json, png…)

Slide 44

Slide 44 text

Content type negotiation ❖ Priorities define the order of media types as the application prefers # app/config/config.yml fos_rest: format_listener: rules: - { path: '^/', priorities: ['json'], fallback_format: html } ❖ Request: ❖ Accept text/json;q=0.9,*/*;q=0.8,text/html

Slide 45

Slide 45 text

Request Body Converter ❖ Decoder ❖ ParamConverter # app/config/config.yml sensio_framework_extra: request: { converters: true } fos_rest: body_converter: enabled: true /** * @ParamConverter("post", converter="fos_rest.request_body") */ public function putPostAction(Post $post) { }

Slide 46

Slide 46 text

Validation (with the Symfony Validator Component) fos_rest: body_converter: enabled: true validate: true validation_errors_argument: validationErrors /** * @ParamConverter("post", converter="fos_rest.request_body") */ public function putPostAction(Post $post, ConstraintViolationListInterface $validationErrors) { if (count($validationErrors) > 0) { // Handle validation errors } }

Slide 47

Slide 47 text

Param Fetcher

Slide 48

Slide 48 text

/** * @QueryParam( * name="sort", * requirements="(asc|desc)", * allowBlank=false, * default="asc", * description="Sort direction" * ) * @RequestParam( * name="firstname", * requirements="[a-z]+" * ) * @QueryParam( * array=true, * name="filters", * requirements=@MyComplexConstraint * ) */ public function getArticlesAction(ParamFetcher $paramFetcher) {} http://mydomain.com/resources?sort=desc In $request->request ($_POST) fos_rest.param_fetcher_listener: true

Slide 49

Slide 49 text

/** * @QueryParam(name="page", requirements="\d+", default="1") */ public function getArticlesAction($page) {} fos_rest.param_fetcher_listener: force

Slide 50

Slide 50 text

Error handling fos_rest: codes: 'Symfony\Component\Routing\Exception \ResourceNotFoundException': 404 'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT

Slide 51

Slide 51 text

Error handling fos_rest: view: exception_wrapper_handler: My\Bundle\Handler\MyExceptionWrapperHandler ❖ implements FOS\RestBundle\ViewExceptionWrapperHandlerInterface public function wrap($data) { return new MyExceptionWrapper($data); } ❖ Take example on FOS\RestBundle\Util\ExceptionWrapper

Slide 52

Slide 52 text

Other Features ❖ Automatic generation of routes ❖ Consistency ❖ Versioning ❖ url ❖ mime type ❖ Take a look at the documentation : ❖ http://symfony.com/doc/master/bundles/FOSRestBundle/index.html

Slide 53

Slide 53 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 54

Slide 54 text

BazingaHateoasBundle Unlock the level 3 of the Richardson’s model

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

HATEOAS ❖ Hypermedia As The Engine Of Application State ❖ Linking resources ❖ Self discovery

Slide 57

Slide 57 text

HAL ❖ Hypertext Application Language ❖ _links - relations ❖ _embedded - related resource(s) ❖ self ❖ Specifications: http://stateless.co/hal_specification.html

Slide 58

Slide 58 text

JSON Api ❖ Similar to HAL ❖ meta - meta info such as pagination… ❖ links - Relation (xToOne, xToMany…) ❖ linked - Resource(s) linked ❖ Specifications: http://jsonapi.org/format/

Slide 59

Slide 59 text

Configuration ❖ Annotation, XML and YML ❖ Relies on Expression Language (since Symfony 2.4)

Slide 60

Slide 60 text

/** * @Hateoas\Relation( * "self", * href = @Hateoas\Route( * "get_notification", * absolute= true, * parameters = { * "id" = "expr(object.getId())", * "user" = "expr(service(‘security.context’).getToken().getUser().getUuid())" * } * ) * ) */ class Notification

Slide 61

Slide 61 text

Result { "id": 1, "_links": { "self": { "href": "http://domaine.name/users/eh1754329986/notifications/1/" } } }

Slide 62

Slide 62 text

/** * … * @Hateoas\Relation( * "activity", * href = "expr(‘/activity/'~object.getActivity().getId()", * embedded = "expr(object.getActivity())", * exclusion = @Hateoas\Exclusion(excludeIf = "expr(object.getActivity() === null)") * ) */ class Notification

Slide 63

Slide 63 text

Result { "id": 1, "_embedded": { "activity": { "id": 3, "content": "Activity content", "_links": { "self": { "href": "/activities/3" } } } } }

Slide 64

Slide 64 text

Representation ❖ Use representation to decorate your resource ❖ Hateoas\Representation\PaginatedRepresentation ❖ And many more !

Slide 65

Slide 65 text

"page": 1, "limit": 10, "pages": "100", "total": 1000, "_links": { "self": { "href": "http://domain.name/notifcations?page=1&limit=10" }, "first": { "href": "http://domain.name/notifcations?page=1&limit=1" }, "last": { "href": "http://domain.name/notifcations?page=100&limit=1" }, "next": { "href": "http://domain.name/notifcations?page=2&limit=1" } }

Slide 66

Slide 66 text

Representation ❖ Any representation you want ! ❖ A class with the properties you need ❖ Let the serializer do its job

Slide 67

Slide 67 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 68

Slide 68 text

Guzzle Communicate between micro services

Slide 69

Slide 69 text

Frontend Service 1 Service 2 Service 3 Service 4 Private network Client

Slide 70

Slide 70 text

Guzzle Bundle ❖ HTTP client ❖ Request ❖ Response ❖ HTTPS ❖ Error management ❖ https://github.com/misd-service- development/guzzle-bundle ❖ https://github.com/csarrazi/ CsaGuzzleBundle

Slide 71

Slide 71 text

Security

Slide 72

Slide 72 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHatoasBundle Guzzle NelmioApiDocBundle

Slide 73

Slide 73 text

Authentication ❖ Authorization header : Authorization: Bearer ❖ https://github.com/poledev/katas/tree/kata-authentication ❖ http://symfony.com/doc/current/cookbook/security/ api_key_authentication.html ❖ Make it stateless

Slide 74

Slide 74 text

Authorisation ❖ access_control ❖ $this->get(‘security.authorization_checker')->isGranted('ROLE_ADMIN') ❖ {% if is_granted(‘ROLE_ADMIN’) %} … ❖ Voter ❖ ACL ❖ …

Slide 75

Slide 75 text

FosHttpCacheBundle keep it in mind

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

One last thing…

Slide 78

Slide 78 text

Symfony Full Stack JMSSerializerBundle FOSRestBundle BazingaHateoasBundle Guzzle NelmioApiDocBundle

Slide 79

Slide 79 text

NelmioApiDocBundle Documentation

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

Many tools, pick what you need!

Slide 83

Slide 83 text

https://www.flickr.com/photos/jeffclow @Saro0h http://joind.in/talk/view/13387

Slide 84

Slide 84 text

Some references ❖ Symfony Rest Edition: https://github.com/gimler/symfony-rest-edition ❖ Lukas Kahwe Smith & William Durand - Build Awesome REST APIs With Symfony2: https://www.youtube.com/watch?v=AcLHvOT5Ekg ❖ REST+JSON API Design - Best Practices for Developers: https://www.youtube.com/watch? v=hdSrT4yjS1g ❖ http://json-ld.org/