Expert Topics

How to create a basic REST API in Symfony?

9 Comments // Reading Time: 17 min.

Note: This article is part of a series. The first article discussed the creation of an initial Symfony 2 project, and the second is about Symfony 2 forms.

In this article I will describe a nice and easy way to implement a REST API with the Symfony framework.

What is a REST API?

REST API is a web service which uses REST architecture.

REST = "REpresentational State Transfer"
API = "Application Programming Interface"

REST architecture is resource based which means that it is focused on resources instead on actions meaning that it calls (in URI) a resource (in this tutorial the resource is 'type') and uses http verb (method) to say what operation it wishes to do on the resource.

Rest typically runs over http and has several architectural constraints:

  1. Decouples consumers from producers
  2. Stateless existence
  3. Able to leverage a cache
  4. Leverages a layered system
  5. Leverages a uniform interface

1. Install dependencies

A good practice in programing world is not to reinvent the wheel in each project. - Reuse the code. If there is a good code (package) that meets our needs, we should use it. In this project I've used a cool bundle: FOSRest bundle (and 2 more) to help me build REST API.

So, let's include the bundles via composer. Add these lines too "require" section of your composer.json file:

Or, you can add those bundles via composer require command.

Important notice:

Maybe you noticed that I've used '@dev' versions. Use dev versions only if you don't have choice, or you have a good reason for it. You should use the newest stable version instead.

Don't forget to add the bundles to the AppKernel class:

2. Create database

You need to set the database parameters in app/config/parameters.yaml, or you can run composer install command to generate the file automatically. In any case, you must provide necessary database parameters.

Then run this command:

php app/console doctrine:database:create
 


Does your company need a web specialist to talk to on an equal footing?

 


3. Let's create our entity

For the purpose of this article, I will present the Type entity and make REST API for it. It is linked with other entities, but they won't be displayed here because complete process of making the Type entity an API can be applied on any other entity.

Type entity class can be created via this interactive command: 

php app/console doctrine:generate:entities TypoScriptBackendBundle:Type

Or you can make the php file in Entity directory. 

Run this command to see generated SQL:

php app/console doctrine:schema:update --dump-sql

If you are happy with produced SQL, run this command to generate the table in database:

php app/console doctrine:schema:update --force

 We will also need the TypeRepository class. It is created automatically when you executed the command:

php app/console doctrine:generate:entities TypoScriptBackendBundle:Type

If it is not created, make it 'by hand'.

4. The routes and the FOSRest bundle configuration

Create routes.yaml file in your bundle and include it in the main routing file:

5. Symfony form

We will need a way to validate the data submitted by the API. Easy way to do it is to use Symfony forms. So, let's create a basic Symfony form for the Type entity:

6. REST controller

 We came to the most important part - implementation of the REST methods.

To seize the power of the FOSRest bundle, our TypeController should extend the class FOS\RestBundle\Controller\FOSRestController:

Lets add REST methods to the controller.

6.1 GET method

getTypeAction will produce HTTP GET method on the type resource. It can be tested from console (Linux) with curl command:

curl -G 127.0.0.1/app.php/api/typoscript/types/1

By default it returns the Type in JSON format, but XML is also supported, and the format can be explicitly specified:

curl -G 127.0.0.1/app.php/api/typoscript/types/1.xml

So what does this method do? It fetches the entity by ID from the repository (database). If the entity is not found, NotFoundHttpException is thrown. (We will talk about exceptions later in this text) And if the entity is found, it is returned, and FOSRest bundle will convert it automatically in requested format.

6.2 POST method

We use the postTypeAction to create a new Type and persist it to database. It is done within createNewType method:

As you can see, in the method processForm, we are using previously created Symfony form to validate the request data. If the data is valid, we are persisting it to the database.

6.3 PUT method

If HTTP PUT method is called, we should search the database for the entity by the given ID.

If we find the entity, we apply the request data to it, validate it and persist it if valid. If the data is invalid, an exception is thrown. All that, except the search, is done within processForm method, which is presented in the text above.

If the entity doesn't exist in the database, it's being created, validated and persisted just like the POST method is called.

6.4 DELETE method

This method deletes a Type by ID. First, we are trying to fetch the Type from the database. If we find it, we delete it, otherwise an exception is thrown.

6.5 PATCH method

As you can see, first we get the entity and then we 'patch' it - update some of it's fields. There lies the main difference between PUT and PATCH HTTP methods - PATCH updates only the fields which were submitted in the request data, and other fields stay unchanged. While PUT updates all the fields of the entity. If some fields are omitted in the request data, those fields will be set to NULL in the entity. Also, PUT method can create new entity while PATCH can't.

7. Exceptions

Good handling of exceptions is important because they help you in development by giving you precious feedback, and they show an error to a user if it occurs.  FOSRest bundle catches the exceptions thrown from the controller actions and makes http error messages which are returned to the user.

Important!

There is difference in FOSRest bundle behaviour in development and production environments. In development environment, complete error messages from all uncaught exceptions are sent to the user (developer), while in production, only http error codes with their message are sent to the user. For example if there is some database error caused by invalid user data, FOSRest bundle will return http code 500 with error message: 'Internal server error', which is good. In that way sensitive internal information about the system (which might be contained within  e.g. database error message) is being protected. But, if the developer wishes to give more descriptive message to the users, then he must register an exception class and associate it with a http error code. Then he will be able to set a custom error message with more details about the error, and yet he will have control over system data which is being presented to the user.

Here is an example of a custom exception class:

All custom exceptions must be registered in config.yaml file, what you can see in routes and configuration section of this article.

All exceptions thrown by the system (e.g. doctrine) must be catched, and the useful information must be extracted and presented to the client in a suitable message. That is the job of the throwFosrestSupportedException function which is called from catch blocks of my code. In this example, the function doesn't do anything smart, it just transmits the error message to the BadRequestDataException which is registered in FOSRest bundle configuration and has assigned http error code. So this function lacks the code which should parse the exception and adapt the message for the client.

On our git repository you can see complete code used in this tutorial. Cheers! :)

Contact us!

We are a digital agency, which is specialized in the development of digital products. Our core topics are websites and portals with TYPO3, eCommerce with Shopware and Android and iOS-Apps. In addition, we deal with many other topics in the field of web development. Feel free to contact us with your concerns!

Comments

  • Stefan Galinski

    Stefan Galinski

    at 26.10.2015

    Thanks Damjan for this extensive tutorial! Thanks Damjan for this extensive tutorial!

  • Maximilian Mayer

    Maximilian Mayer

    at 26.10.2015

    Nice Symfony tutorials, Damjan! :-) Nice Symfony tutorials, Damjan! :-)

  • olivedev

    olivedev

    at 05.10.2016

    This is quite a long and tiring process. It would take a really long time for you to create a simple rest api. You can make it much quicker like shown in this guide on how to create rest api in [...] This is quite a long and tiring process. It would take a really long time for you to create a simple rest api. You can make it much quicker like shown in this guide on how to create rest api in Symfony 3.1: https://www.cloudways.com/blog/rest-api-in-symfony-3-1/

  • pHzao

    pHzao

    at 04.11.2017

    The best tutorials i have read until now! Good Work! The best tutorials i have read until now! Good Work!

  • PARDEEP

    PARDEEP

    at 09.03.2018

    Nice Article ! i am also working restful api with symfony and publish article on that
    Iink: https://www.cloudways.com/blog/rest-api-in-symfony-3-1/ Nice Article ! i am also working restful api with symfony and publish article on that
    Iink: https://www.cloudways.com/blog/rest-api-in-symfony-3-1/

  • Lukasz

    Lukasz

    at 24.11.2018

    Something less simple - hope documentation is ok :) https://github.com/tulik/symfony-4-rest-api Something less simple - hope documentation is ok :) https://github.com/tulik/symfony-4-rest-api

  • Stas

    Stas

    at 03.09.2019

    Just a short note. API - application programming interface Just a short note. API - application programming interface

  • johan

    johan

    at 27.06.2020

    Just to mention that API means "Application Programming Interface" and NOT "APplication Interface" Just to mention that API means "Application Programming Interface" and NOT "APplication Interface"

    • Stefan Galinski

      Stefan Galinski

      at 27.06.2020

      Thanks. I changed that. Thanks. I changed that.