Minimal Symfony Rest API Tutorial

In this tutorial I’m gonna show you some basic tricks which will allow you to write a simple RESTful API, or at least to begin to.

If you read this previuos tutorial you’ll be aquainted with Symfony‘s CRUD operations and it’s gonna be easy for me to tell you how to proceed.

Symfony the web framework

The first thing you’re gonna have to do is to setup your environment, to do that I advice you to install Xammp in order to have PHP and MySQL working on your machine.

Another useful installation is Git for Windows if you’re on Windows like me, just to have cURL installed. Otherwise if you’re on Linux or Mac, just install it with your package manager.

The second thing to do is to install Symfony and all the necessary dependencies:

$ composer create-project symfony/skeleton symfony-rest-api 4.4.*
$ cd symfony-rest-api
$ composer require symfony/serializer
$ composer require doctrine/annotations
$ composer require friendsofsymfony/rest-bundle
$ composer require symfony/orm-pack
$ composer require symfony/property-access
$ composer require symfony/web-server-bundle --dev
$ composer require symfony/maker-bundle --dev

Next you’ll have to configure your database settings inside Symfony’s installation, so edit the .env file on the root folder like so:

DATABASE_URL=mysql://yourusername:yourpassword@127.0.0.1:3306/blog?serverVersion=5.7

And now we have to create our database and a table in it, with an Article entity which will be our table. Follow these steps:

$ php bin/console doctrine:database:create
$ php bin/console make:entity

If you don’t remember how to crete this entity look again at the tutorial linked above. Then follow these steps to create a migration:

$ php bin/console make:migration
$ php bin/console doctrine:migrations:migrate

Let’s configure our rest routes and annotations inside the Symfony installation:

#config/packages/fost_rest.yaml

fos_rest:
   view:
     view_response_listener:  true
   format_listener:
     rules:
       - { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json ] }
#config/routes/annotations.yaml

rest_controller:
    resource: ../../src/Controller/
    type: annotation
    prefix: /api

Ok then, it’s time to get our hands dirty with our Symfony code. Let’s first create some private helpers methods for our application. This is for serializing our output:

private function returnNormalized($param)
{
    $encoders = [new JsonEncoder()];
    $normalizers = [new ObjectNormalizer()];
    $serializer = new Serializer($normalizers, $encoders);

    return $serializer->serialize($param, 'json');
}

To discover the other little magic under the hood, with a little bit of abstraction and for the full code of the tutorial, go to this GitHub. Now it’s time to put in place our REST methods, let’s begin with POST to create a new Article:

/**
 * @Rest\Post("/articles")
 * @param Request $request
 * @return View
 */
public function postArticle(Request $request): View
{
    $article = new Article();
    $this->setArticleFields($article, $request);

    $em = $this->getDoctrine()->getManager();
    $em->persist($article);
    $em->flush();

    return View::create($this->returnNormalized($article), Response::HTTP_OK);
}

Then we have our GET methods, to retrive one Article in particular, or everyone:

/**
 * @Rest\Get("/articles/{id}")
 * @param Request $request
 * @return View
 */
public function getArticle(Request $request): View
{
    $article = $this->getArticleById($request);
    return View::create($this->returnNormalized($article), Response::HTTP_OK);
}

/**
 * @Rest\Get("/articles")
 * @return View
 */
public function getArticles(): View
{
    $articles = $this->getDoctrine()
        ->getRepository('App\Entity\Article')
        ->findAll();

    return View::create($this->returnNormalized($articles), Response::HTTP_OK);
}

It follows the PUT method, to update our Articles:

/**
 * @Rest\Put("/articles/{id}")
 * @param Request $request
 * @return View
 */
public function putArticle(Request $request): View
{
    $article = $this->getArticleById($request);

    if ($article) {
        $this->setArticleFields($article, $request);

        $em = $this->getDoctrine()->getManager();
        $em->persist($article);
        $em->flush();
    }

    return View::create($this->returnNormalized($article), Response::HTTP_OK);
}

The last thing to remember is the DELETE method:

/**
 * @Rest\Delete("/articles/{id}")
 * @param Request $request
 * @return View
 */
public function deleteArticle(Request $request)
{
    $article = $this->getArticleById($request);

    if ($article) {
        $em = $this->getDoctrine()->getManager();
        $em->remove($article);
        $em->flush();
    }

    return View::create($this->returnNormalized($article), Response::HTTP_OK);
}

Alright, let’s run our application:

$ php bin/console server:run

And test it, in another terminal with cURL. Make like this: create an Article, see that it is created. Create another one, see that both are present now. Update it, see that it is updated. Delete the first, see that there’s only one remaining. That’s all.

$ curl -H "Content-Type: application/json" -d "{\"title\":\"Article Title\", \"author\":\"Mirko Benedetti\", \"body\":\"Article Body\", \"url\":\"Article Url\"}" http://127.0.0.1:8000/api/articles
$ curl -v http://127.0.0.1:8000/api/articles/1

$ curl -H "Content-Type: application/json" -d "{\"title\":\"Second Title\", \"author\":\"Mirko Benedetti\", \"body\":\"Second Body\", \"url\":\"Second Url\"}" http://127.0.0.1:8000/api/articles
$ curl -v http://127.0.0.1:8000/api/articles

$ curl -X PUT -H "Content-Type: application/json" -d "{\"title\":\"Updated Title\", \"author\":\"Francesco Angeli\", \"body\":\"Updated Body\", \"url\":\"Updated Url\"}" http://127.0.0.1:8000/api/articles/1
$ curl -v http://127.0.0.1:8000/api/articles/1

$ curl -X DELETE http://127.0.0.1:8000/api/articles/1
$ curl -v http://127.0.0.1:8000/api/articles

In this brief tutorial you learned how to build a basilar RESTful API with the help of Symfony.

Did you like this post? Please comment here below and share it on your preferred social networks, thank you!

Leave a Reply

Give me your opinion, I will be grateful.