Add Laravel API End Points
Part 7 of 48 in API Driven Development With Laravel and VueJSUPDATE 10/12/2017
I had the wrong slashes in the routes/api.php file for the namespace of the CafesController.php. These have been changed to be properly namespaced in the tutorial. Sorry for the inconvenience!
In the last article Configuring Vue Router for a Single Page App we added the routes for viewing pages on Roast. Now we need to make sure the pages have data. Remember, being a single page app that’s API driven, we need to AJAX load the data from the API on a page load if we don’t already have it. I like to build the API end points first in Laravel first so we can kind of mess around with the data and know what to expect when we call an endpoint in VueJS.
Tangent: Begin with the end in mind
This is an approach that I take to all development, ESPECIALLY when trying to learn development. You could run through as many Hello World examples in every language on the internet, but that really doesn’t spark motivation. They are great to get an environment up and running and provide that excitement when it works, but if you really want to dive into development, I’d say begin with the end in mind. Start first with asking yourself what you want to build. Even if it’s a moon shot, then work towards that goal. It will keep you motivated throughout the process. That’s what we will continue to do with Roast And Brew. I have a vision (obviously, more input is better since it’s open source) but we will keep adding steps and tutorials until we get an app and keep releasing updates in the future. I will eventually write a blog post about this as well. I think it is an essential thought process for learning anything.
Step 1: Design Your Routes
The first thing we need to think about is what routes we need. In the last tutorial, we added front facing routes of:
- /cafes
- /cafes/new
- /cafes/:id
These are all separate API calls from a back end perspective. In the /cafes route, we will want a list of all of the cafes in the system. Remember the tangent? This will eventually be filterable as more cafes get loaded, but for now it will return all of the cafes.
The /cafes/new route on the front end is a GET request (or just a page with a form) for now, but on the API side this will be a POST request for adding an individual cafe. A great source for designing APIs can be found here: RESTful API Designing guidelines — The best practices We will be following a lot of the terminology discussed in that article as well as other RESTful API design styles.
The /cafes/:id route will load an individual cafe. Usually this loads all of the fine details as well. For now we won’t be doing too much different with data, but as we shell out our cafe further, we probably won’t need as much data when we load all of the cafes as we will when we load an individual one. Remember the tangent? 😉
Step 2: Add the routes to your routes/api.php file
So first we will open routes/api.php
. This file will contain all of the app’s API routes. This will be where most of route adding will take place. When we configured our API with Laravel Passport (https://serversideup.net/installing-configuring-laravel-passport/) we added a v1
prefix to all API routes. This is good practice for scaling. Not all apps will be successful but a simple 3 second addition will have your API set up for versioning.
First we will add the cafes route, so in your route group add:
Route::get(‘/cafes’, ‘API\CafesController@getCafes’);
We haven’t built the cafes controller yet, but trust me we will, we are just shelling out routes. If you follow Adam Wathan’s test driven Laravel: Test-Driven Laravel – Build robust applications with TDD. he usually builds this, writes a test and then solves for the test. Now I’m following along with the series, I’m not very good at TDD yet, but as I learn, I will be writing SPA tutorials as well.
Second, we will add our new cafes route. This is a POST route since POST is used for adding an object.
I’d add the route right after our last route:
Route::post(‘/cafes’, ‘API\CafesController@postNewCafe’);
One thing to notice about this route. We didn’t call it /cafes/new
. That is usually reserved for the form used to add the cafe. We just make a POST request to a /cafes route.
Finally, we will add our route to get a cafe. We can add this right after our get cafes route. I like keeping the methods organized by request:
Route::get(‘/cafes/{id}’, ‘API\CafesController@getCafe’);
Another thing I like to do is prefix the method on the controller with the type of request. You can see I named each method with a prefix of get or post. Now there’s a whole bunch of different things you can do with Laravel such as resource controllers Controllers – Laravel – The PHP Framework For Web Artisans and other route naming conventions. This is just the way I deal with things, since I don’t want to accidentally leave a worthless route out there by accident. Feel free to follow your own flow!
Our routes/api.php
file should look like:
<?php
use Illuminate\Http\Request;
Route::group(['prefix' => 'v1', 'middleware' => 'auth:api'], function(){
Route::get('/user', function( Request $request ){
return $request->user();
});
/*
|-------------------------------------------------------------------------------
| Get All Cafes
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes
| Controller: API\CafesController@getCafes
| Method: GET
| Description: Gets all of the cafes in the application
*/
Route::get('/cafes', 'API\CafesController@getCafes');
/*
|-------------------------------------------------------------------------------
| Get An Individual Cafe
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes/{id}
| Controller: API\CafesController@getCafe
| Method: GET
| Description: Gets an individual cafe
*/
Route::get('/cafes/{id}', 'API\CafesController@getCafe');
/*
|-------------------------------------------------------------------------------
| Adds a New Cafe
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes
| Controller: API\CafesController@postNewCafe
| Method: POST
| Description: Adds a new cafe to the application
*/
Route::post('/cafes', 'API\CafesController@postNewCafe');
});
The last thing to notice, is I added comments. This is always important in any coding so you don’t lose track and waste time trying to figure out what you wrote. I also namespaced the CafesController as API/CafesController because that will help organize our app. We will build this out in the next step!
Step 3: Build your API/CafesController.php
UPDATE 10/16/2017
We will be using the Request facade provided by Laravel to access the Request data. I’ll be writing a validation tutorial that will inject a Request later!
Time to handle these requests! We first need to add the /app/Http/Controllers/API/CafesController.php
. Initially it should look like this:
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Request;
class CafesController extends Controller
{
}
Next we will fill in the methods that handle each endpoint. First, add the getCafes
method to the file:
public function getCafes(){
}
After that method, add the method to get an individual cafe:
public function getCafe( $id ){
}
Notice how this has a parameter of $id
? That gets passed through the route we defined so we know which cafe to grab. There is also dependency injection and other fun things you can do with Laravel: Controllers – Laravel – The PHP Framework For Web Artisans.
Finally, we will shell out our POST method. To do that, just add the following to your CafesController.php file:
public function postNewCafe( ){
}
We have this method using an implicit binding to the Request object. This will allow us to grab the form data sent from the user to add a new cafe.
So now we have our basic routes laid out. Our CafesController.php file should look like:
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Request;
class CafesController extends Controller
{
/*
|-------------------------------------------------------------------------------
| Get All Cafes
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes
| Method: GET
| Description: Gets all of the cafes in the application
*/
public function getCafes(){
}
/*
|-------------------------------------------------------------------------------
| Get An Individual Cafe
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes/{id}
| Method: GET
| Description: Gets an individual cafe
| Parameters:
| $id -> ID of the cafe we are retrieving
*/
public function getCafe( $id ){
}
/*
|-------------------------------------------------------------------------------
| Adds a New Cafe
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes
| Method: POST
| Description: Adds a new cafe to the application
*/
public function postNewCafe(){
}
}
More comments too 😉 . There are definitely some features we will be adding such as requests in Laravel. But for now we will leave things as they are. Next we will begin to shell out our Cafe model and build the individual methods.
Step 4: Build your Cafes.php model
Your models can go anywhere as long as you include them correctly when needed. I like to make a directory for them. So first, create the directory app/Models
.
Next, you have a default User.php model. You want to move that into your new app/Models
directory. Then at the top, adjust the namespace to be:
namespace App\Models;
Your User.php model should look like:
<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
Now you have everything nice and organized. A few more buckets for things!
Now in the app/Models
directory create a file called Cafe.php
. I use the singular for cafe because each model should be thought of as a row in the database. Since we are using Eloquent, which is an Object Relation Map (ORM) tool, this is the kind of idea you will have to get used to. In the file you will want to add the namespace:
namespace App\Models;
and then instruct the object to use and extend the Eloquent Model. Your file will look like:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Cafe extends Model
{
protected $table = 'cafes';
}
Notice that we are using the Eloquent Model provided by Laravel. For more information, and there’s a lot of information, check out Laravel’s Eloquent docs: Eloquent: Getting Started – Laravel – The PHP Framework For Web Artisans.
We also added an attribute of protected $table = ‘cafes’
. You can have eloquent assume which database table it should map to, but I like to explicitly state it.
Now that we have our model, we have to add a database to store some information in!
Step 5: Building the Cafes Migration
A migration in Laravel helps keep track of the different versions of the database. A migration will alter your database by adding columns, tables, dropping columns, tables, etc. For more information on migrations, see the Laravel documentation: Database: Migrations – Laravel – The PHP Framework For Web Artisans. I’ll explain what we are doing for our purposes along the way, but Laravel docs are awesome and provide a much more in-depth approach.
To create a migration, I use the artisan command line. It’s super efficient. You can actually use it for building models and controllers as well if you want. First you need to ssh into your server and navigate to the directory housing the code (top level) and run:
php artisan make:migration added_cafes_table
It will create a migration file in your /database/migrations
directory. This is where all of your migrations will be. You can already see the users migration that got ran when we installed the packages. Find your {timestamp}_added_cafes_table.php file and open it.
When thinking about the database in terms of migrations, you think of it very modular. Each migration has a method called up and down. Up is when you run the migration where down should be the exact opposite to reverse the migration. This makes it super easy to manage the state of your database especially across multiple developers.
Step 6: What Initial Cafe Data Do We Want?
This should be basic right off of the bat. Due to the glory of migrations, we can always add more data. I’m thinking, just a name, address, coordinates for now.
So in the up()
method of the migration you created add:
Schema::create('cafes', function( Blueprint $table ){
$table->increments('id');
$table->string('name');
$table->text('address');
$table->string('city');
$table->string('state');
$table->string('zip');
$table->decimal('latitude', 11, 8);
$table->decimal('longitude', 11, 8);
$table->timestamps();
});
What this will do is create a cafes table and add the fields we need for a basic cafe. The increments method adds an auto incrementing column named ID which is the primary key of our cafe. The timestamps column auto adds created_at and updated_at fields that get adjusted automatically by eloquent which is sweet! I also added two decimal fields not discussed for latitude and longitude. Maybe we should map these cafes? I think so, (hype for a Google Maps tutorial).
In the down()
method of the migration simply add:
Schema::drop(‘cafes’);
When you run a php artisan migrate:rollback
command it will reverse the table creation by removing the table.
Your migration should look like:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddedCafesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cafes', function( Blueprint $table ){
$table->increments('id');
$table->string('name');
$table->text('address');
$table->string('city');
$table->string('state');
$table->string('zip');
$table->decimal('latitude', 11, 8)->nullable();
$table->decimal('longitude', 11, 8)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('cafes');
}
}
Now run php artisan migrate
and check your database and you have a cafes table!
Step 7: Complete the /api/v1/cafes
route
Now that we have our routes stubbed out, our Cafe.php model created and our migration run, we can finally finish our routes!
Now go back to your app/Http/Controllers/API/CafesController.php
file. On the top of the file add use App\Models\Cafe;
. This will instruct the controller to use the Cafe.php model we just added so it saves us typing and a cleaner code as we write our methods.
Let’s begin by focusing on the getCafes()
method. This should simply return all of the cafes. Eloquent makes this super easy! All we need to do is add this to the method:
$cafes = Cafe::all();
return response()->json( $cafes );
The first line grabs all of the cafes and the second line Laravel returns them all as JSON. Done! Now when we call this method from VueJS we will get all the cafes in the app as JSON.
Step 8: Complete the /api/v1/cafes/:id
route
In the CafesController.php file we will focus on the getCafe( $id )
method. This will get an individual cafe. Once again Eloquent has some sweet helpers to grab this. All you have to do is add the following to your method:
$cafe = Cafe::where('id', '=', $id)->first();
return response()->json( $cafe );
What this does is get the first cafe that has the ID of the cafe we are looking for. Then return that cafe as a JSON object. Pretty slick right? In the future we will be extending these methods to grab relations and such but for now this is all we need!
Step 9: Complete the POST /app/v1/cafes
route
Of the 3 routes we are adding in this tutorial, this one takes the most time to add a cafe. But still though, Laravel helps out. Right now we are building it functionally with little checks for valid data. This route will take input from a user and create a new cafe.
To do this, we will be grabbing the request data from the user. The user will be sending along the following fields:
- name
- address
- city
- state
- zip
The rest of the fields will be added dynamically. I’ll write a tutorial on how to grab latitude and longitude from Google Maps later, it’s pretty cool!
Since we are expecting these fields, we can fill out our postNewCafe method! To do that add the following code:
$cafe = new Cafe();
$cafe->name = Request::get('name');
$cafe->address = Request::get('address');
$cafe->city = Request::get('city');
$cafe->state = Request::get('state');
$cafe->zip = Request::get('zip');
$cafe->save();
return response()->json($cafe, 201);
Initially what we do is create a new Cafe model from Eloquent. We then assign the request data to the attributes on the model and save it. Then following RESTful principals we return the cafe as JSON that we created with a status code of 201 meaning that an entity was created.
Our CafesController.php file should look like this after this tutorial:
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\Cafe;
use Request;
class CafesController extends Controller
{
/*
|-------------------------------------------------------------------------------
| Get All Cafes
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes
| Method: GET
| Description: Gets all of the cafes in the application
*/
public function getCafes(){
$cafes = Cafe::all();
return response()->json( $cafes );
}
/*
|-------------------------------------------------------------------------------
| Get An Individual Cafe
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes/{id}
| Method: GET
| Description: Gets an individual cafe
| Parameters:
| $id -> ID of the cafe we are retrieving
*/
public function getCafe( $id ){
$cafe = Cafe::where('id', '=', $id)->first();
return response()->json( $cafe );
}
/*
|-------------------------------------------------------------------------------
| Adds a New Cafe
|-------------------------------------------------------------------------------
| URL: /api/v1/cafes
| Method: POST
| Description: Adds a new cafe to the application
*/
public function postNewCafe(){
$cafe = new Cafe();
$cafe->name = Request::get('name');
$cafe->address = Request::get('address');
$cafe->city = Request::get('city');
$cafe->state = Request::get('state');
$cafe->zip = Request::get('zip');
$cafe->save();
return response()->json($cafe, 201);
}
}
Wrapping up
That was a long one! But now we have a model for cafes, functioning API endpoints, a table to house the data and proper responses set up! We have a little bit of functionality! The next tutorial will go through how to access this with VueJS.