How to expose a RESTful API from a Symfony app with minimal effort. At Forky we launched our MVP in one month with only 3 developers -it was imperative we waste no time with simple problems so we could have time over to deal with the difficult tasks. This is how we did it.
5. How Symfony helped us
• Great variety of bundles
e.g. FOSUserBundle, FOSRestBundle, FOSOAuthServerBundle,
NelmioApiDocBundle and many more
• Sound architecture
• Predictable release schedule
• Excellent documentation
6. With the right combination of
bundles, the Forky MVP was built in
just 1 month.
7. Our goal
Expose a Symfony app RESTfully with minimal effort:
• /orders endpoint that accepts GET, POST, PUT &
DELETE HTTP methods
• Users authenticated using OAuth
• Document API endpoint, parameters & response codes
9. Step 1: Building the /orders endpoint
FOSRestBundle provides tools to rapidly
develop RESTful APIs:
• Automatic RESTful route generation
• Format agnostic output (e.g. xml, json, or html)
• Decoding of request body and headers
• Exception controller with appropriate HTTP codes
10. # app/config/routing.yml
orders:
type: rest
resource: AppBundleControllerApiOrderController
prefix: /api
Step 1: Building the /orders endpoint
We need 2 things:
• ApiOrderController to handle API requests
to /orders
• A routing definition that defines
ApiOrderController as a REST resource
11. Step 1: Building the /orders endpoint
To POST an order, create a “postOrdersAction”:
use FOSRestBundleViewView;
/* ... */
class ApiOrderController extends Controller {
public function postOrdersAction() {
$user = $this->container->get('security.context')->getToken()->getUser();
$order = new Order($user);
$form = $this->createForm(new OrderType(), $order);
$form->submit($this->getRequest()->get($form->getName()));
if(!$this->getRequest()->request->has($form->getName())) {
throw new HttpException(400, "No parameters passed");
} else if(!$form->isValid()) {
throw new HttpException(400, $form->getErrorsAsString());
}
$this->container->get('acme.order.manager')->sendOrder($form->getData());
$view = View::create()->setStatusCode(201)->setData($form->getData());
return $this->container->get('fos_rest.view_handler')->handle($view);
}
}
12. Step 1: Building the /orders endpoint
• To GET an order:
/* ... */
class ApiOrderController extends Controller {
/* … */
public function getOrdersAction(Order $order) {
$view = View::create()->setStatusCode(200)->setData($order);
return $this->container->get('fos_rest.view_handler')->handle($view);
}
}
• Similarly, define putOrderAction and
deleteOrderAction for PUT and DELETE
13. Step 2 – Authentication
• REST is stateless – no cookies or sessions
• Implement OAuth2 authentication flow with
FOSOAuthServerBundle
• This hooks into Symfony’s security and firewall
components as well as FOSUserBundle
14. Step 3 - Documentation
NelmioApiDocBundle auto-generates up-to-
date documentation:
• Needs only a few annotations per endpoint
• Auto-detects input & output parameters
15. Step 3 - Documentation
Add this annotation block above the relevant
controller method:
use NelmioApiDocBundleAnnotationApiDoc;
/* ... */
class ApiOrderController extends Controller {
/**
* @ApiDoc(
* description="Place a new Order",
* input="AppBundleFormTypeOrderType",
* output="AppBundleEntityOrder"
* )
*/
public function postOrdersAction() {
/* ... */
}
}