Symfony Montréal

Presented by

Welcome

Michaël Villeneuve

CTO @ ctrlweb

 

1 year

8 meetups in 2016

1 every ~6 weeks

- Docker et Symfony (16/11/2015)
- Symfony 3.0 (25/01/2016)

- TDD & BDD with Symfony - Steven Rosato (02/04/2016)
- Create an API with FOSRestBundle (Today!)
- Create a CMS with SonataAdminBundle
- How to optimise Doctrine
- Symfony caching system 
- SyliusBundle

Before we start...

- Anyone looking / offering for a job ?

- Summer break with Yuldev (06/08/2016) 

- Sponsors

Before we start...

Today

Objectives

  • RESTful principles
  • REST Standards

Schedule

19h00 Intro

19h05 RESTful principles

19h15 REST Standards

 

19h25 FOSRESTBundle

 

  • Config
  • Automatic routing 
  • Serialization
  • Versioning

API?

Application Program Interface

REST?

REpresentational State Transfer

In layman's terms, an API is an agreement between two people stating: "If you give me this instruction, I will perform this action, or return this information"

 

RE: the action of speaking or acting on behalf of someone.

 ST: Transfer the status (or the state) of said subject

RESTful principles

Definition (codeplanet)

  • Resource: A single instance of an object. For example, an animal.
  • Collection: A collection of homogeneous objects. For example, animals.
  • HTTP: A protocol for communicating over a network.
  • Consumer: A client computer application capable of making HTTP requests.
  • Server: An HTTP server/application accessible from a Consumer over a network.
  • Endpoint: An API URL on a Server which represents either a Resource or an entire Collection.

RESTful principles

Verbs (codeplanet)

  • GET (SELECT) - Fetch a resource from the server
  • POST (CREATE) - Create a resource from the server
  • PUT (UPDATE) - Update a resource from the server providing the entire resource
  • PATCH (UPDATE) - Update a resource from the server providing only the attributes                                             that have been changed
  • DELETE (DELETE) - I'll let you guess :)
  • HEAD - Wont be covered today - retreive a metadata about a resource
  • OPTIONS - Wont be covered today – Returns what methods are allowed with a                                      resource, required by CORS (Cross Origin Resource Sharing), useful for                                  the consumer

Versioning

  1. Your application will change
  2. It is your responsability to never break backward compatibility. (symfony.com/bc)

WHY?

Versioning

  1. Using an URL segment : https://mydomain.com/api/{version}/{resource}

  2. GET /foo HTTP/1.1
    Host: api.example.org
    Version: 1.2

HOW?

Standards

Endpoints

Root URL

https://mydomain.com/api/{version}/{resource}

https://api.mydomain.com/{version}/{resource}

Endpoints

GET /products : list of products 

POST /products : create a new product

GET /products/{id} : list a single product

POST /products/{id} : udpate a single product

DELETE /products/{id} : delete a single product

PUT

Status code

200 - OK

201 - CREATED

204 - NO CONTENT

200 - OK

200 or 204

Status code cheat sheet

 

The 2xx range is reserved for successful messages where all goes as planned.

The 3xx range is reserved for traffic redirection (common for SEO purpose such as temporary and permanent redirect).

The 4xx range is reserved for responding to errors made by the Consumer, e.g. they’re providing bad data or asking for things which don’t exist. These requests should be idempotent, and not change the state of the server.

The 5xx range is reserved as a response when the Server makes a mistake. Often times, these errors are thrown by low-level functions even outside of the developers hands, to ensure a Consumer gets some sort of response. The Consumer can’t possibly know the state of the server when a 5xx response is received, and so these should be avoidable.

 

The 2xx range is reserved for 

The 3xx range is reserved for 

The 4xx range is reserved for 

The 5xx range is reserved for

Authorization

vs

Authentication

Reference

  1. Install the bundle
     
  2. Enable the bundle
$ composer require friendsofsymfony/rest-bundle
$ composer require jms/serializer-bundle
// app/AppKernel.php
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new FOS\RestBundle\FOSRestBundle(),
            new JMS\SerializerBundle\JMSSerializerBundle(),
        );

        // ...
    }
}

Automatic routing 

<?php

namespace ProductBundle\Controller;

use FOS\RestBundle\Controller\Annotations\RouteResource;

/**
 *
 * @RouteResource("Product")
 */
class ProductsController
{

    /**
     * c stands for Collection
     * /products
     */
    public function cgetAction()
    {
        
    }

    /**
     * /products/{$sku}
     * @param $sku
     */
    public function getAction($sku)
    {

    }

}

Automatic routing 

  php bin/console debug:router --env=prod
       
  get_products               GET      ANY      ANY    /products.{_format}                
  get_product                GET      ANY      ANY    /products/{slug}.{_format}         

Tools

Postman

# app/config.php
fos_rest:
    versioning: true

# app/routing.php
products:
    type:     rest
    prefix: /api/{version}
    resource: ProductBundle\Controller\ProductsController


# ProductBundle/Controller/ProductsController
use FOS\RestBundle\Controller\Annotations\Version;

/**
 *
 * @RouteResource("Product")
 * @Version({"v1", "v2"})
 */
class ProductsController extends FOSRestController

* Only available since FOSRESTBundle 2.x>

Serialization

# app/config.php
jms_serializer:
    metadata:
        auto_detection: true
        directories:
            UserBundle:
                path: "@UserBundle/Resources/config/serializer"

# UserBundle\Resources\config\serializer\Entity.User.yml
UserBundle\Entity\User:
    exclusion_policy: ALL
    properties:
        username:
            expose: true

Some more examples


    public function getUserAction($slug)
    {} // "get_user"             [GET] /users/{slug}

    public function editUserAction($slug)
    {} // "edit_user"            [GET] /users/{slug}/edit

    public function putUserAction($slug)
    {} // "put_user"             [PUT] /users/{slug}

    public function patchUserAction($slug)
    {} // "patch_user"           [PATCH] /users/{slug}

    public function getUserCommentsAction($slug)
    {} // "get_user_comments"    [GET] /users/{slug}/comments

    public function newUserCommentsAction($slug)
    {} // "new_user_comments"    [GET] /users/{slug}/comments/new

    public function postUserCommentsAction($slug)
    {} // "post_user_comments"   [POST] /users/{slug}/comments

Documentation

Documentation

The good

 

The bad

  • Postes Canada : Not documenting required variables
  • Monetico (Desjardins) : print_f to valide an api call?!!!

Security

Testing

  • Behat
  • PHPSpec
  • FactoryMuffin

Here's a simple comprehensive tutorial

Merci!

Create an API with FOSRESTBundle 10/05/2016

By michael-villeneuve

Create an API with FOSRESTBundle 10/05/2016

Créer un API avec FOSRestBundle http://www.meetup.com/Symfony-Montreal/events/230078580/

  • 2,007