Are you building an API? If you are, chances are you’ll want it to have:
Laravel offers all of this out of the box. It also has excellent documentation and an active community offering tips and writing articles like this one. This is why I keep coming back to Laravel time and again.
In this article I’m going to walk you through the steps required to build a REST API with Laravel. I’m going to start right at the beginning — creating a Laravel project — and will cover everything needed to build a functioning API. I’ll also cover related issues such as how to structure your endpoints and what form your responses should take. I’m assuming you already have a webserver, PHP and a database up and running.
Let’s dive in.
Composer is a tool for dependency management in PHP. Don’t worry about exactly what that means. The bottom line is that it’s the simplest way to create a Laravel project.
There are instructions for installing Composer here. You can read the instructions but you don’t really need to. Just punch this address into a browser:
https://getcomposer.org/installer
and save the file installer to the parent directory of which your Laravel project directory will be a child. For example, if you have a directory Sites which will contain your Laravel project directory, you should save installer in the Sites directory.
Then, on the command line, navigate to the Sites directory and run the installer like this:
php installer
You’ll now see another file in the directory: composer.phar. What you’ve done is install Composer locally, meaning that to use it you need to navigate to the directory in which it is located before issuing commands. You can move or copy composer.phar to other directories — it’s not tied to the directory in which it was originally installed.
Alternatively you can install Composer globally, which will allow you to issue commands from anywhere on your system. The installation instructions linked to above tell you how to do that.
Now that Composer is installed, you can delete the installer if you like:
MacOS or Unix:
rm installer
Windows:
del installer
As recommended on the Laravel installation page, you can create a new Laravel project with this command:
composer create-project — prefer-dist laravel/laravel restapi
There are a few things to note about this:
This will likely take a couple of minutes and may at first appear not to be doing anything. Be patient.
This command assumes you have Composer installed globally. If not, instead of ‘composer’ you’ll need to type ‘php composer.phar’. So the full command will be:
php composer.phar create-project --prefer-dist laravel/laravel restapi
This will create a new directory restapi into which Laravel will be installed. If you installed Composer locally, copy and paste composer.phar into restapi. We’ll need to run Composer commands from within the directory a bit later on.
Laravel should now be installed and working. You can test it by entering the file path for restapi/public/ into a browser address bar. The full path will be something like this:
http://localhost/... /restapi/public/
You should now see the Laravel welcome page:
Screenshot — Laravel welcome page
If instead you get a permission denied error, there’s a fix for that here. Once you’ve run the fix, try the page again. It should now be working.
Find the .env file within the root of your project directory. It may be hidden by default. If it is, do one of the following:
Now open .env in a code editor and find this section of the file:
DB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=homesteadDB_USERNAME=homesteadDB_PASSWORD=secret
Replace the default values with those applicable to your set up. I’m using MySQL with a standard set up, so for me the only values that require changing are the last three:
DB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=name_of_dbDB_USERNAME=name_of_userDB_PASSWORD=password
There are instructions for doing this here, and it’s all pretty easy. On the command line, make sure you’re in your project directory and run these two commands:
php artisan make:authphp artisan migrate
The first command adds a few views to your project that enable your users to register, login and reset their password. You can take a look at these yourself by visiting:
The second command adds two tables to your database:
As of version 5.8, Laravel ships with a default API authentication scheme that involves generating and inspecting a random token assigned to your application. This is not a robust solution and Laravel recommends instead using Laravel Passport to handle API authentication for production applications. That’s exactly what we’re going to do.
Setting up Laravel passport is a little more involved than setting up user authentication, but it’s not too hard. You can take a look at the official instructions here.
From your project directory run these three commands:
php composer.phar require laravel/passportphp artisan migratephp artisan passport:install
The first command installs Laravel Passport (which may take a few minutes), the second creates some required database tables and the third creates the encryption keys needed to generate secure access tokens.
Next you need to alter four files that ship with Laravel. The first is restapi/app/User.php. This is your User model. Models are Laravel’s way of mapping classes to underlying database tables. The parts of User.php that you need to add or alter are in bold.
<?phpnamespace App;use Laravel\Passport\HasApiTokens;use Illuminate\Notifications\Notifiable;use Illuminate\Foundation\Auth\User as Authenticatable;class User extends Authenticatable{use HasApiTokens, Notifiable;}
The second is restapi/app/Providers/AppServiceProvider.php. This is where you register and run services for your application, such as Laravel Passport.
<?phpnamespace App\Providers;use Laravel\Passport\Passport;use Illuminate\Support\Facades\Gate;use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;class AuthServiceProvider extends ServiceProvider{/** * The policy mappings for the application. * * @var array */ protected $policies=[ 'App\Model'=> 'App\Policies\ModelPolicy',]; /** * Register any authentication / authorization services. * * @return void */ public function boot() {$this->registerPolicies(); Passport::routes();}}
The third is restapi/config/auth.php, which contains authentication configuration options for your Laravel instance.
'guards'=> [ 'web'=> [ 'driver'=> 'session', 'provider'=> 'users',], 'api'=> [ 'driver'=> 'passport', 'provider'=> 'users',],],
The last is restapi/app/Http/Kernal.php, which contains various middleware settings for your Laravel instance. Middleware is discussed in a little more detail below.
'web'=> [ // Other middleware... \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,],
Laravel Passport is now set up. It’s time to build our API.
An API endpoint is a URL that, when called, performs some action against a resource, such as a database. To be RESTful, an API endpoint must adopt a certain design pattern. For our purposes this means it should:
With this in mind, let’s create our first API endpoint.
The first thing we need to do is create a route for our endpoint. Think of a route as a URL. For example, the route ‘/example’ would be accessible by visiting http://localhost/... / restapi/public/example in a browser.
Laravel ships with a restapi/routes folder which contains files for different types of routes. For example, web.php contains web routes (ie. routes you can reach via a web browser) and api.php contains API routes (ie. routes you can reach via API calls). Since we’re creating an API route, open api.php and add the following after the existing content:
Route::get('/v1/cars', function (Request $request) {return 'From the server: details of two cars';});
And… that’s it, we have our first API endpoint!
To test our new API endpoint we’re going to need to set up our application to make and receive an API call. In a web app this is typically done via Javascript, which is how we’ll do it here.
Open restpi/resources/views/layouts/app.blade.php. This is a template used by a number of pages throughout the application, including the login, registration and home pages. Add the following immediately after the opening BODY tag:
<div id="response"> API response here</div><script> fetch('api/v1/cars') .then(function(response) {response.text().then(function(data) {document.getElementById('response').innerHTML=data;});}) .catch(function(err) {console.log('Error: ' + err);});</script>
We’ve done two things here:
Now navigate your browser to http://localhost/... / restapi/public/login. You should see the words ‘From the server: details of two cars’ in the top left corner of the page. Our API endpoint is working!
At the moment our API endpoint is returning plain text. Ideally it should return JSON, which we can do by amending restapi/routes/api.php as follows:
Route::get('/v1/cars', function (Request $request) {return response()->json([ 'cars'=> [ 'registration'=> 'ABC001', 'dateRegistered'=> '2019-01-01', 'color'=> 'black', 'make'=> 'tesla', 'model'=> 's']], 200);});
Our API is now returning JSON. Notice that we are also setting the HTTP status code to 200, indicating that the request was successful. When returning JSON, especially when handling errors, you should take care to use the correct HTTP status code. Common ones are 400, indicating a bad request (eg. a parameter was missing or was incorrectly formatted), and 404, indicating that the requested resource was not found. A full list of HTTP status codes is here. I recommend taking a look at 418.
To test our new endpoint we need to update the script that we added to restpi/resources/views/layouts/app.blade.php to handle JSON responses:
<script> fetch('api/v1/cars') .then(function(response) {response.json().then(function(data) {var str=JSON.stringify(data.cars); document.getElementById('response').innerHTML=str;});}) .catch(function(err) {console.log('Error: ' + err);});</script>
Refresh http://localhost/... / restapi/public/login and you should now see the details of the car in top left corner of the page.
Our new endpoint works just fine but it has a couple of limitations. Firstly, it does not require authentication, meaning it is accessible to the whole world. Secondly, any programming logic required to return results must go in the routing file api.php, which is not the best place for it. Let’s rectify these issues now.
Update the code in api.php as follows:
Route::middleware('auth:api')->get('/v1/cars', function (Request $request) {return response()->json([ 'cars'=> [ 'registration'=> 'ABC001', 'dateRegistered'=> '2019-01-01', 'color'=> 'black', 'make'=> 'tesla', 'model'=> 's']], 200);});
We’ve added the API authentication middleware to our route. You can read about middleware here. Essentially it is code that runs “in the middle” — between receipt of an incoming request and your own code attached to the route. The API authentication middleware checks that an incoming API request is authenticated, which requires:
To include a CSRF token in the page, go back in to restpi/resources/views/layouts/app.blade.php and add the following anywhere inside the HEAD section:
<meta name="csrf-token" content="{{csrf_token()}}">
csrf_token() is a Laravel function that, you guessed it… generates a CSRF token. We can now update our Javascript to ensure that this token is included in all API requests coming from the page:
<script> fetch('api/v1/cars',{headers:{'x-csrf-token' : document.querySelector("meta[name='csrf-token']").getAttribute("content")}}) .then(function(response) {response.json().then(function(data) {var str=JSON.stringify(data.cars); document.getElementById('response').innerHTML=str;});}) .catch(function(err) {console.log('Error: ' + err);});</script>
Now all we need to do is create a user account and log in. Go to http://localhost/... / restapi/public/register. You should see ‘API response here’ in the top left corner of the page, indicating that the API request has failed. The page is calling your API endpoint but no response is received because you are not authenticated.
Create an account using the form on the registration page. Use any credentials you like — they will be stored in your local database. Once the account is created Laravel will log you in and redirect you to http://localhost/... / restapi/public/home. You should now see the car details in the top left corner of the page, received via a successful API request.
Recall that the route associated with our API endpoint looks like this:
Route::middleware('auth:api')->get('/v1/cars', function (Request $request) {return response()->json([ 'cars'=> [ 'registration'=> 'ABC001', 'dateRegistered'=> '2019-01-01', 'color'=> 'black', 'make'=> 'tesla', 'model'=> 's']], 200);});
This is returning static data. In a real-world application we would likely want to retrieve details of one or more cars from the database. We would probably also want to allow the user to specify which cars they want to retrieve via parameters attached to the endpoint. This adds complexity and requires programming logic, which should be handled in a Controller rather than in the routing file. Let’s fix this now.
On the command line enter the following to create a Controller:
php artisan make:controller Controller_Cars
Laravel will create the Controller for you in this file restapi/app/Http/Controllers/Controller_Cars.php. Open it and add a new public method get() to the Controller_Cars class. The class should look like this:
class Controller_Cars extends Controller{public function get(Request $request) {}}
Now we can copy the programming logic from our route into this method, and call the get() method from the route, like this:
restapi/app/Http/Controllers/Controller_Cars.php:
public function get(Request $request) {return response()->json([ 'cars'=> [ 'registration'=> 'ABC001', 'dateRegistered'=> '2019-01-01', 'color'=> 'black', 'make'=> 'tesla', 'model'=> 's']], 200);}
restapi/routes/api.php:
Route::middleware(['auth:api'])->get('/v1/cars', 'Controller_Cars@get');
Refresh http://localhost/... / restapi/public/home and you should once again see the details of the car, only this time it’s coming from Controller_Cars. Any programming logic associated with the API endpoint can now go in the Controller, where it belongs.
Suppose you are building an API that deals with resources relating to both cars and motorbikes. Chances are you need endpoints to handle creating, retrieving, updating and deleting records. A RESTful implementation of this API might have these endpoints and methods:
yoursite.com/api/v1/cars:
yoursite.com/api/v1/motorbikes:
And you could implement this as follows:
yoursite/routes/api.php:
Route::middleware(['auth:api'])->group(function () {Route::post('/v1/cars', 'Controller_Cars@post'); Route::get('/v1/cars', 'Controller_Cars@get'); Route::put('/v1/cars', 'Controller_Cars@put'); Route::delete('/v1/cars', 'Controller_Cars@delete'); Route::post('/v1/motorbikes', 'Controller_Motorbikes@post'); Route::get('/v1/motorbikes ', 'Controller_ Motorbikes @get'); Route::put('/v1/motorbikes ', 'Controller_ Motorbikes @put'); Route::delete('/v1/motorbikes ', 'Controller_ Motorbikes @delete');}
Notice that we are using a route group to attach the API authentication middleware to all routes included in the group.
yoursite/app/Http/Controllers/Controller_Cars.php:
public function post(Request $request) {// code to create records of cars}public function get(Request $request) {// code to retrieve records of cars}public function put(Request $request) {// code to update records of cars}public function delete(Request $request) {// code to delete records of cars}
yoursite/app/Http/Controllers/Controller_Motorbikes.php:
public function post(Request $request) {// code to create records of motorbikes}public function get(Request $request) {// code to retrieve records of motorbikes}public function put(Request $request) {// code to update records of motorbikes}public function delete(Request $request) {// code to delete records of motorbikes}
In this article we’ve covered everything you need to build a RESTful API with Laravel. Now it’s up to you to flesh it out with actual code. That’s the fun part — happy coding!
If you liked this article, please leave a clap and consider following me. I’d also love to hear your thoughts in the comments below.
Check out my latest project: Lithium List.
Follow me on Twitter.