Restful APIs

An Application Programming Interface (API) is a tool that allows other programmers to build their own software off of your data or system. APIs are designed to separate the front end implementation from the back end, and can often support multiple different backend systems.

Creating an API

Laravel makes it easy to create an API, through the creatation of routes, controllers and models. In fact, creating an API is very similar to create a Laravel web application with two exceptions. First, all routes will be place inside the the routes/api.php file, and second, no views will be used.

For example, lets say, we want to create an API to retrieve all the movies from a movies table. We would would create a new model and controller, which can be done using the following artisan command.

php artisan make:model Movie -c

The command will create a app/Models/Movie.php file and a app/Http/Controllers/MovieController.php file just as we might expection, and we could add a controller model to the MovieController.php file just as we would in a oridinary Laravel application.

// app/Http/Controllers/MovieController.php

public function index () {
  $movies = Movie::all();

}

Now, where thing will differ from a oridinary Laravel application, is that instead of returning a view, we will be returning JSON, using a JSON Responseopen in new window.

// app/Http/Controllers/MovieController.php

public function index () {
  $movies = Movie::all();
  return response()->json($movies, 200);
}

The method return a response that would look something like this:

[{"movie_id":1,"movie_title":"Labyrinth","director":"Jim Henson","year":1986,
"genre_id":1},{"movie_id":2,"movie_title":"Highlander","director":"Russell
 Mulcahy","year":1986,"genre_id":1},{"movie_id":3,"movie_title":"Alien",
 "director":"Ridley Scott","year":1979,"genre_id":2},{"movie_id":4,
 "movie_title":"Conan the Barbarian","director":"John Milius","year":1982,
 "genre_id":1},{"movie_id":5,"movie_title":"The Hobbit: An Unexpected Journey",
 "director":"Peter Jackson","year":2012,"genre_id":1}]

Of course, before we will be able to see the response, we must add a route so we will have a address to place in our browser. By default, Laravel includes the api.php routes file, which where we will place our routes for our API. All routes placed in the api.php routes file, will be prefixed with a /api/.

NOTE

For this demonstration, we will NOT be adding any security or authentication.

// routes/api.php

use App\Http\Controllers\MovieController;

Route::get('/movies', [MovieController::class, 'index']);

With this route in place, we will be able to go to /api/movies to get the JSON string containing the movies.

Consuming an API with JavaScript

The purpose of an API is to allow other developers to create their own front-end. However, there is no reason we cannot also take advantage of our own API. Typically, an API is consumed by JavaScript and the use of a asynchorous called to the API. For our purpose, we will use the Fetch APIopen in new window to make the request to our Laravel API.

Make a GET request with the Fetch API is quite straight forward. We provide the URL await the response, which come back in the form of a promise. From then, we will request the response data and finally the JSON data.

fetch('/api/movies')
  .then(response => response.json())
  .then(movies => console.log(movies))

The above example simply output the return JSON to the console. However, if we wanted to display the movie data to the page. We will require a little bit more JavaScript. The following example will the Vue Framework to display the movies data to the page.

const app = Vue.createApp({
  data: function () {
    return {
      movies: []
    }
  },
  created: function () {
    fetch('/api/movies')
      .then(response => response.json())
      .then(movies => { this.movies => movies })
  }
})

const vm = app.mount('#app')
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Movies</title>
  <script src="https://unpkg.com/vue@3"></script>
</head>
<body>
  <div id="app" class="container">
    <ul>
      <li v-for="movie in movies">{{ movie.movie_title }}</li>
    </ul>
  </div>

  <script src="app.js"></script>
</body>
</html>

Non-GET API Requests

In the above examples, we only focused on GET requests. But an API and most applications will require the user to make non-GET requests for adding, editing, deleting data.

For example, if we wanted to give our API the ability to add a new movie, we would create a new movie object and set it properties received as a JSON string. Accessing this JSON string can be done by using the request method and the json method. Once we have saved the new movie to database, we would return a response much in the same way we did in the previous example. The end result is we would have a method that looks something like this:

// app/Http/Controllers/MovieController.php
function store () {
  $movie = new Movie;
  $movie->movie_title = request()->json('movie_title');
  $movie->director = request()->json('director');
  $movie->year = request()->json('year');
  $movie->genre_id = request()->json('genre_id');
  $movie->save();

  return response()->json($movie, 201);
}

With our controller method created, we need to create a route to point to the method. This route will share the same URI as our previous route, but will use a different HTTP verb.

// routes/api.php

use App\Http\Controllers\MovieController;

Route::get('/movies', [MovieController::class, 'index']);
Route::post('/movies', [MovieController::class, 'store']);

Finally, with our route in place will we use our new API using JavaScript and Fetch API. Because, we will now making a POST Fetch request, will need to add an options object. This object will contain a method, a body and headers object. The method would be set to the HTTP request method, in this case POST. The body will be a JSON string of the movie data.

const app = Vue.createApp({
  data: function () {
    return {
      movies: [],
      movie: {}
    }
  },
  created: function () {
    this.getMovies()
  },
  methods: {
    getMovies: function () {
       fetch('/movies')
        .then(response => response.json())
        .then(movies => { this.movies => movies })
    },
    addMovie: function () {
      fetch('/api/movies', {
        method: 'POST',
        body: JSON.stringify(this.movie),
        headers: {
          'Content-Type': 'application/json'
        }
      })
        .then(response => response.json())
        .then(movie => { this.getMovies() })
    }
  }
})

const vm = app.mount('#app')
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Movies</title>
  <script src="https://unpkg.com/vue@3"></script>
</head>
<body>
  <div id="app" class="container">
    <ul>
      <li v-for="movie in movies">{{ movie.movie_title }}</li>
    </ul>
    <form @submit.prevent="addContact">
      <label>Movie Title</label>
      <input type="text" v-model="movie.movie_title">

      <label>Director</label>
      <input type="text" v-model="movie.director">

      <label>Year</label>
      <input type="number" v-model="movie.year">

      <label>Genre</label>
      <select v-model="movie.genre_id">
        <option value="1">Fantasy</option>
        <option value="2">Sci-Fi</option>
        <option value="3">Action</option>
        <option value="4">Comedy</option>
        <option value="5">Drama</option>
        <option value="6">Horror</option>
        <option value="7">Romance</option>
        <option value="8">Family</option>
      </select>
      <button type="submit">Add Movie</button>
    </form>
  </div>

  <script src="app.js"></script>
</body>
</html>