Why Should Your Online Business Offer API
There are several ways to extend a business model but API is a hot topic right now as the online world is expanding very fast. If you’re a developer or at least interacted with APIs before, you probably know why public APIs are so important, but there’s a big chance you didn’t hear or care about them before and now you’re wondering why everyone talks about them.
What is an API
In computer programming, an application programming interface (API) is a set of subroutine definitions, protocols, and tools for building application software. In general terms, it's a set of clearly defined methods of communication between various software components. (Wikipedia)
There is a simple way of saying this: an API is like a contract between two computer software agreeing to share information in a more or less standardised way.
Now it’s pretty clear what are we talking about, but why are them so important? How can APIs help us? In the following rows I will try to argument some good reasons.
Getting Started with Building APIs in Symfony2
Grow your business
You can grow your online business by integrating with additional tools or apps and engaging with others. This can be done using public APIs.
Let’s take Uber and Google Maps: everytime you search directions in Google Maps (from home to work, i.e.) you can automatically request an Uber, see your driver on the map or even contact him, all without having to leave Maps app thanks to Uber’s API.
Or if you have an online store, you might wanna offer public APIs so other apps can request price offers and display your products on their platforms.
Get ready for scaling
It’s all sweet and fun to start a new business and you probably want to do it faster and cost effective. Usually this means a monolithic application.
Success means scaling and this can be done by breaking the app into microservices. This will enable multiple options for you.
Let’s say you have a microservice that is being used very often and affects your server. That microservice can be moved on a new server with dedicated resources only for it and it will be accessible for the rest of the app via an API.
Or there is the case when you want to rewrite a single microservice (some languages are more efficient than others). This is the beauty of standardised API - you only have to make sure you accept the same API call as before and return the answer in the same format, so other dependent services won’t be affected.
Time saving
UX/UI is very important and we strongly advise you to continue to invest in that area, but there are cases when having to explore an UI for some actions is time consuming for some (more technical) users.
Let’s take SendGrid and their Marketing Campaigns app. You can create a new campaign by going through the UI process or you can simply make a call to their API. Second option is usually faster (but for more technical people or at least you need to develop an integration first) and the flow will always be the same, while UI can suffer some modifications over the time.
Mobile app
At some point you will probably want to add a dedicated mobile app to your business. Having APIs makes it possible. You are free to develop a new design or a new template without any changes on the API side.
Providing APIs must be a concern for every company, whether they focus on internal operations, partner integrations or public access. Join the revolution! Add API support to your product! Get a free quote now.
Getting Started with Building APIs in Symfony2
Hello all you Interwebs friends! While we're passing through the shallow mists of time, REST is becoming more and more of a universal standard when building web applications. That said, here's a very brief tutorial on how to get started with building APIs in Symfony2.
Spoiler alert: the bits of code written below use FosUserBundle + Doctrine.
1. Generate a New Bundle
It's nice to keep your code neat and tidy, so ideally, you should create a separate bundle that will only store your API code, we can generically name ApiBundle.
$ php app/console generate:bundle --bundle-name=ApiBundle --format=annotation
2. Versioning
This isn't by any means mandatory, but if you believe that your API endpoints will suffer major changes along the way, that cannot be predicted from the get go, it would be nice to version your code. This means that you would, initially, have a prefix in your endpoints, like: `/v1`/endpoint.json, and you'd increase that value each time a new version comes along. I'll describe how to actually create the first version (`v1`) of your API a little further down the line.
3. Install a Few Useful Bundles
- FosRestBundle - this bundle will make our REST implementation a lot easier.
$ composer require friendsofsymfony/rest-bundle
and then include FOSRestBundle in your `AppKernel.php` file:
$bundles = array( // ... new FOS\RestBundle\FOSRestBundle(), );
- JmsSerializerBundle - this will take care of the representation of our resources, converting objects into JSON.
composer require jms/serializer-bundle
and then include JMSSerializerBundle in your `AppKernel.php`:
$bundles = array( // ... new JMS\SerializerBundle\JMSSerializerBundle(), // ... );
4. Configurations
Configure the Response object to return JSON, as well as set a default format for our API calls. This can be achieved by adding the following code in `app/config/config.yml`:
fos_rest: format_listener: rules: - { path: ^/api/, priorities: [ html, json, xml ], fallback_format: ~, prefer_extension: true } routing_loader: default_format: json param_fetcher_listener: true view: view_response_listener: 'force' formats: xml: true json: true templating_formats: html: true
5. Routing
I prefer using annotations as far as routes go, so all we need to do in this scenario would be to modify our API Controllers registration in `/app/config/routing.yml`. This registration should have already been created when you ran the generate bundle command. Now we'll only need to add our version to that registration. As far as the actual routes of each endpoint go, we'll be manually defining them later on, in each action's annotation.
api: resource: "@ApiBundle/Controller/V1/" type: annotation prefix: /api/v1
At this point we're all set to start writing our first bit of code. First off, in our Controller namespace, we would want to create a new directory, called `V1`. This will hold all of our v1 API Controllers. Whenever we want to create a new version, we'll start from scratch by creating a `V2` namespace and so on.
After that's done let's create an action that will GET a user (assuming we've previously created a User entity and populated it with users). This would look something like:
<?php namespace ApiBundle\Controller\V1; use FOS\RestBundle\Controller\FOSRestController; use FOS\RestBundle\Controller\Annotations as Rest; class UserController extends FOSRestController { /** * @return array * @Rest\Get("/users/{id}") * @Rest\View */ public function getUserAction($id) { $em = $this->getDoctrine()->getManager(); $user = $em->getRepository('AppBundle:User')->find($id); return array('user' => $user); } }
If we want to GET a list of all users, that's pretty straightforward as well:
/** * GET Route annotation. * @return array * @Rest\Get("/users/get.{_format}") * @Rest\View */ public function getUsersAction() { $em = $this->getDoctrine()->getManager(); $users = $em->getRepository('AppBundle:User')->findAll(); return array('users' => $users); }
With that done, when running a GET request on `https://yourapp.com/api/v1/users/1.json`, you should get a `json` response with that specific user object.
What about a POST request? Glad you asked! There are actually quite a few options to do that. One would be to get the request data yourself, validate it and create the new resource yourself. Another (simpler) option would be to use Symfony Forms which would handle all this for us.
The scenario here would be for us to add a new user resource into our database.
If you're also using FosUserBundle to manage your users, you can just use a similar RegistrationFormType:
<?php namespace ApiBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class RegistrationFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('email', 'email') ->add('username') ->add('plainPassword', 'password') ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\User', 'csrf_protection' => false )); } public function getName() { return 'my_awesome_form'; } }
Next, we'll want to actually create our addUserAction():
/** * POST Route annotation. * @Rest\Post("/users/new.{_format}") * @Rest\View * @return array */ public function addUserAction(Request $request) { $userManager = $this->container->get('fos_user.user_manager'); $user = $userManager->createUser(); $form = $this->createForm(new \ApiBundle\Form\Type\RegistrationFormType(), $user); $form->handleRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($user); $em->flush(); return array('user' => $user); } return View::create($form, 400); }
To make a request, you'll need to send the data as raw JSON, to our `https://yourapp.com/api/v1/users/new.json` POST endpoint:
{ "my_awesome_form":{ "email":"andrei@ibw.com", "username":"sdsa", "plainPassword":"asd" } }
And that's all there is to it.
We haven't covered the cases where you'd want Delete or Update a user yet. Updating resources through the REST standards can be done using either PUT or PATCH. The difference between them is that PUT will completely replace your resource, while PATCH will only, well, patch it... meaning that it will partially update your resource with the input it got from the API request.
Let's get to it then. We'll use the same form as before, and we'll try to only change (we'll use the PATCH verb for that) the email address, username and password of our previously created user:
/** * PATCH Route annotation. * @Rest\Patch("/users/edit/{id}.{_format}") * @Rest\View * @return array */ public function editAction(Request $request, $id) { $userManager = $this->container->get('fos_user.user_manager'); $user = $userManager->findUserBy(array('id'=>$id)); $form = $this->createForm(new \ApiBundle\Form\Type\RegistrationFormType(), $user, array('method' => 'PATCH')); $form->handleRequest($request); if ($user) { if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($user); $em->flush(); return array('user' => $user); } else { return View::create($form, 400); } } else { throw $this->createNotFoundException('User not found!'); } }
The request body is the same as the one shown for the POST method, above. There are a few small differences in our edit action though. First off - we're telling our form to use the PATCH method. Second - we are handling the case where the user ID provided isn't found.
The Delete method is the easiest one yet. All we need to do is to find the user and remove it from our database. If no user is found, we'll throw a "user not found" error:
/** * DELETE Route annotation. * @Rest\Delete("/users/delete/{id}.{_format}") * @Rest\View(statusCode=204) * @return array */ public function deleteAction($id) { $em = $this->getDoctrine()->getManager(); $user = $em->getRepository('AppBundle:User')->find($id); $em->remove($user); $em->flush(); }
Related: Routing in Symfony2
Conclusions
Aaand we're done. We now have a fully working CRUD for our User Entity. Thanks for reading and please do share your opinions/suggestions in the comment section below.