Making a JS request with the Fetch API in Symfony 3

Making a JS request with the Fetch API in Symfony 3

18/11/17

Previous Article:

SteamWorld: Heist, a Steampunk game on Linux

Hey! It has been a while, isn’t it? ;)

After this long pause of two months without a bit of article, et much longer without a technical one, I am back to have a talk with you about a project I work on. In this project I had to use the Fetch API from JavaScript to do some AJAX request, inside a file served from a Symfony 3 applicaion back-end. It wasn’t easy but I won!

Photo by Ilya Pavlov on Unsplash

The project

First, a little bit of context. During my education, I have to create a project from scratch in 2 years, with a group of 7 students (including me). We are free to choose the subject, but it has to be validated by a range of presentations in front of selected jurys in order to check if the project has potential to lead to the creation of a startup.

Our project is called Playficient. We found out thanks to statistics that 40% of work time (average) is lost. One of the main reason for this is by switching from one tool to another. A lot of softwares and tools have been made in order to reduce the number of tools used in work (Synchronize with an API, use “LogIn with Facebook”, using IFTTT, or creating WebHoks on Slack or Github).

We want to create a similar tool, for small and very small companies, which have strong contact with computers. But we want to go further, with the principles of Gamification in order to motivate workers, but also with clever advices with an Articial Intelligence which allows users to get better in their everyday working processes. To make this a little more tricky, we want this project to be customizable, with a modular system that can add or remove features if the user wants to, just like the add-ons in Wordpress or in Firefox (Subliminal text: Go to Firefox 57!)

(Link to a showcase website to come if you wish to take a look, don’t hesitate to hive us some feedback it would be awesome and sooo kind! =D)

Hey, didn’t you talk about a technical article?

Indeed indeed, it’s time for me to talk about it. To manage with ease the modular system, we use a server-side framework in PHP called Symfony 3 which works with Bundles, and 2 people are working on this back-end for now. The Front-End part is managed by Symfony through Templates and Assets:

  • A Template is an HTML page with Twig syntax which is reusable, and customisable by variable substitution and some other features like loops and conditions.
  • An Asset is a file which gives stuff to a template, like JavaScript code, an image, CSS, etc.

So we use a Template to create reusable page, or to create a specific page to a Route. But we won’t find in a Template anything but HTML code and Twig tags (and no other code if you write ugly code). So we don’t really care about Templates if we want to write JavaScript code.

On the other hand, Assets are much more interesting, because it is thanks to them that we will be able to serve to client a JavaScript file. But we have a problem here: We do not have Twig syntax from Templates to use server-side variables in JavaScript.

And this is problematic when we need to get a Route’s URL.

FOSJSBundle comes to rescue us

Like I said, Symfony works with Bundles, just like npm packages in JavaScript or Gems in Ruby. These bundles are used as dependencies to a project, and we can find hundreds of them already existing thanks to the huge Symfony community.

FOSJSBundle is a bundle that gives us a JavaScript library, called Routing to get a Route’s URL.

Here is how to use it:

const url = Routing.generate('route_name', {your parameters}, absoluteUrlBool);

This function returns the URL corresponding to the Route given in first parameter, exactly what we needed!

Now, we need to create the so called AJAX request. Lets take a use case (Randomly, what I was working on today). We are user X, connected to Playficient and in possession of the Task Manager module. In this we have a list of boxes, each one is a task and can contain any number of sub tasks. We’d like to do some “drag’n drop” of a sub tasks to change the box where it belongs. We we do this, we need to tell the server that we want to remove the task from its previous position, and add it to its new one.

For this, we go use som JavaScript and create an AJAX request. There is multiple way to handle this, the most newest way to do this is by using the Fetch API. The Fetch API works with two Objects and a method:

  • Request: It is the Object that defines the request (easy, isn’t it?). We define in it the URL, parameters, method, etc.
  • Response: This Object defines the server’s response. We get in this object when a fetch Promise is resolved.
  • fetch(): This method has the Request as first parameter. This method returns a Promise that resolves a Response object.

Now you know more about it, lets take a look at code corresponding to a request to remove a sub task from the box it originally was:

const url = Routing.generate('rask_remove', {
  taskId: 1,
  parentId: 2,`
});
const request = new Request(url, { method: 'GET' });

fetch(request)
  .then(reponse => {
    console.log('We removed this task!');
  })
  .catch(error => {
    console.error(`Error when removing: ${error}`);
  });

Ok, everything’s here? Lets run the server and do the manipulation… Here is what Firefox’s console gives us:

GET   http://localhost/task/2/1/remove        302 Found
GET   http://localhost/login                  200 Ok
'We removed this taks!'

And when we reload the page… the task is still here!

Quickly, we can see that there is two requests done: One to remove the task, aand another to the login page… that’s weird. I’m not gonna let the suspense last any longer and explain why this happened.

We are still user X (not professor), and connected as it is, the server needs to know in the name of who we are doing this request. This is why cookies are used in Symfony. From one page to another, the user is walking around with this cookies that allows him/her to stay connected until he/she click on “Log Out”.

So, what we need to do in our request is to get this cookie and send it, but how? This is, in fact, really simple with the Fetch API and Symfony, as this is a simple parameter to put to the fetch method, to send ‘credentials’:

fetch(request, { credentials: 'same-origin' })

Lets run again our code:

GET   http://localhost/task/2/1/remove      200 OK
'We removed this task!'

And here comes our request!

Using Fetch API from JavaScript and Symfony, it is pretty easy to do all these things, but at the condition that we know how. (I spent 3 days to look for how to get routes in JavaScript from Symfony, and understanding this credentials error even though it was simple in fact!)

This article is also available in french if you’d like to. I’m going to take advantage of my stay at Quebec and of my end-school project to write more articles about technical studd. By the way, I am working on a plugin on Talk from the CoralProject, when this will be done I might write something about it ;)

Until then, have a nice day everyone!

Comment