# HTTP Requests

# Architecture

In a Single Page Application (SPA), communication with the server is done via asynchronous HTTP requests (AJAX) or more specialized protocols such as WebSocket. We will see how to make these network requests from a Vue application.

Vue.js is a framework that focuses on the user interface and offers nothing special to exchanges with a server. This choice of implementation is left to the developer. The easiest way to make an asynchronous HTTP request in JavaScript is to use the fetch method. It is not supported on Internet Explorer but there are polyfills to complete the support. As an alternative, you can use other more complete third-party libraries such as Axios, which is recommended by the Vue team.

TIP

Network calls should not be done directly from the code of a component. This prevents reuse of the code easily if another component has to make the same call. Instead, opt to systematically separate the application logic from the logic of your views.

By convention, we code the application logic in JS files called services, distributed according to the large functional parts of your application and placed in a folder src/services.

# Practical Work: Communicate with a back-end

We will use a server-provided API (the back-end) to authenticate users and search for films. This back-end has already been developed and deployed on Heroku.

TIP

The back-end interface contract is available here: api-docs

  1. Create a generic service (services/api.js) to call the backend, with this content:
import store from "@/store.js";

export const BASE_URL = "https://vue-js-backend.herokuapp.com";

export async function api(url, params = {}) {
  params = Object.assign(
    {
      mode: "cors",
      cache: "no-cache"
    },
    params
  );

  params.headers = Object.assign(
    {
      Authorization: `Bearer ${store.state.token}`,
      "Content-Type": "application/json"
    },
    params.headers
  );

  let response = await fetch(BASE_URL + url, params);
  let json = (await response.json()) || {};
  if (!response.ok) {
    let errorMessage = json.error
      ? json.error.error || json.error
      : response.status;
    throw new Error(errorMessage);
  }
  return json;
}

There is no code specific to Vue here, it is a utility function around the fetch method to communicate with our backend. The Authorization header is used to send the authentication token to the backend. Other options are used to configure HTTP caching and the cross-origin permissions to apply.

  1. Create a services/UserService.js service that will use API endpoints for user registration and login:
import { api } from "@/services/api.js";

export default {
  register(credentials) {
    return api("/user/register", {
      method: "POST",
      body: JSON.stringify(credentials)
    });
  },
  login(credentials) {
    return api("/user/login", {
      method: "POST",
      body: JSON.stringify(credentials)
    });
  },
  user() {
    return api("/user");
  }
};
  1. In LoginForm component, add a second button to register next to the login button, then modify the LoginForm methods to call the functions located in UserService:
import UserService from "@/services/UserService.js";

export default {
  methods: {
    async register() {
      try {
        const response = await UserService.register({
          email: this.email,
          password: this.password,
          firstname: "John",
          lastname: "Smith"
        });
        this.$store.dispatch("login", {
          user: response.user,
          token: response.token
        });
        this.$router.push("/search");
      } catch (error) {
        this.error = error.toString();
      }
    },
    async login() {
      try {
        const response = await UserService.login({
          email: this.email,
          password: this.password
        });
        this.$store.dispatch("login", {
          user: response.user,
          token: response.token
        });
        this.$router.push("/search");
      } catch (error) {
        this.error = error.toString();
      }
    }
  }
};
  1. Note that in the event of an error, the error message is stored in an error variable. Declare this variable in the data of the component and use it in the template to display the error message in case of authentication failure.

  2. Note also that the response of the back-end after login contains a token to authenticate the user, which is passed to the store in the parameters of the login action. Modify store.js to store this token, adding asetToken mutation called by the login action.

  3. The api service is already configured to add this token to the request authorization header. Check that the token is sent as a HTTP header via the developer tools of your browser.

  4. Bonus: Modify the store's logout action to remove the token and user info from the store upon logout, and redirect to the login form.

  5. Create a FilmService service with a method to search for films, following the API documentation (GET /movies/search).

  6. Change the film search page to call the back-end and allow the user to enter the name of a film, and get all the details for this film.

TIP

If you discovered that there was a film called Undefined, then you were wrong somewhere 😃

Undefined, the movie