Best practices of designing APIs in Laravel

Stolen from here.

This post has an audio version thanks to Miguel Piedrafita’s Blogcast app.

I am not only a DevOps Engineer, but I have also started from ground zero with PHP since I was a little kid, back in the days when I was in the 5th grade. Since then, I have continously improved my code and I found out that people adopted some kind of standards for cleaner code, better visualization and since they are standards, everyone could understand better what every other developer wrote in the same codebase.

I have always loved to code more in the backend rather than in the frontend. I tried to be a Full Stack, but it didn’t suit me. So i have fallen back to writing backend, especially APIs. This time, I won’t speak about coding standards in general, but I’ll speak about what APIs are actually working and how to develop them easier, from security to tips on how to do it better.


Basically, an API is an interface that returns data in a special format that any kind of application, either it’s an Android app or a web app, can understand.

JSON is widely used, since it’s almost everywhere. The other option is to use XML, but I had trouble with some third party APIs (especially payment providers down in my country) that used XML over JSON and the development was a total crap. I recommend developing JSON APIs, all the time, unless somebody requests an XML API.

When developing an API, you need to take into considerations some things, in this particular order:

  • security — securing it using OAuth, an API key or CORS is a must. Optionally, you should be using a throttler to limit the requests to your app.
  • headers — make sure that your apps send the right content type. A content type header is a bit of information that tells the client that receives the data: “this thing i send you is JSON” or “that over here is XML. parse it properly”, so the browser or your client knows how to properly decode it.
  • coding standards and naming — this is purely backend. Make sure you’re consistent in your responses. Stick to only one kind of naming and proper formatting.

I love to code APIs in Laravel since you want to scale further with Event Broadcasting or the other missing Lumen features that makes it blazing-fast, you can do it without re-writing the whole project from scratch. In case you will stay on bare minimum, feel free to use Lumen.

Security

The biggest problem you should be take care of is security. It’s easy to secure it, but if not doing it properly, you might get unwanted access. In Laravel, you might want to use Laravel Passport — it belongs to the Laravel ecosystem, supports authentication through that App ID — App Secret thingy in order to get an access token, either to impersonate somebody or a server, either it’s backend or frontend. Basically, you will make a request with your App ID and App Secret to an OAuth endpoint to receive a token, that can either be generated by a server (i.e. accessing an API from a command which runs in a cronjob every day) or by an user that has logged in into your app.

Alternatively, you might want to use a JWT Token authentication, which runs almost the same, but maybe it’s easier to understand. Pick your choice, and see which one suits better for you, both in implementation and your needs.

Headers

Web requests are just normal conversations between a client and a server, or between two servers. They rely on a request and a response, unless it’s a websocket kind of request, but that’s just another story. When requesting or sending back things, there are some bit of information called Headers that need to be taken care of — some of them tells the server how to process the information that has received or how the client would like to receive the answer.

It’s like your mom telling you: “go buy some milk from the store but buy only from the X branded milk”. You know what to do, but you have to pick it only one type of milk. It’s the same as the requests: “i want to get the list of users, but give me them in JSON” or “i want to get all the users, but send me in chunks of 20” (in case you specify a GET parameter). For this, there’s a header called Accepts, which can be application/json in case you want the response as JSON. It’s not a must, but for some apps like AJAX, if it detects that header, it will automatically decode the response for the client, without having to do something like this:

var data = JSON.parse(response.data);

Another header you must be aware of is the Content-Type, that is a bit confusing, but it’s opposite to Accepts: it tells the server how to treat the content he gets. For example, if you want to send RAW data, like a JSON string, you can set the Content-Type to application/json, but if you want to receive the content through the $_POST variable, you should set it to x-www-form-urlencoded . This will help you not only with parsing the content directly through $_POST, but it should be used in HTML forms because it’s easily accessible. If you send data like binary, through file inputs, for example, make sure you sent the content multipart/form-data.

In Laravel, this won’t be a problem since you can access data directly.

For application/json:

function index(Request $request)
{
$var = $request->variable;
}

However, in application/json you cannot see using the ->all() method the JSON content:

function index(Request $request)
{
$data = $request->all(); // this is empty array, even we have data.
}

If set to x-www-form-urlencoded or multipart/form-data, you can see all variables using ->all().

When replying back, in Laravel, you can use the built-in JSON response OR you can work it out better and use a package like Laravel Fractal or Laravel Responder. From my own experience, Laravel Responder did the job better, in a more semantic way. Let me show you:

function getUsers(Request $request)
{
return responder()->success(User::all())->respond();
}

This will return all users with status OK 200 and formatted as JSON. Cool, right? For errors, you have to do something like that, which allows you to send a code and a message:

function getUsers(Request $request)
{
$users = User::all();

if ($users->count() === 0) {
return responder()->error('no_users', 'There are no users.')->respond();
}
   return responder()->success($users)->respond();
}

This package supports much more, so head over to the docs because it can be easily integrated with transformers and custom data sent.

Coding standards

What i love to see is people sticking with some standards that suit them, or are clean. Here are some tips that might help you build cleaner code and structure your API routes better.

Use routes/api.php file for API routes

Laravel comes with a separate routes/api.phpfile that defers from the usual routes/web.php file which is used for web routing. api.php file is created to store your API routes. It has an on-board applied middleware (which can be seen in app/Http/Kernel.php, in the $middlewareGroups variable, under api) and a prefix of /api, so all routes defined are already available to /api

Make use of the route names

What i like to do, is to set an as setting to the whole API, so i can access the routes by their name, with api. prefix.

Route::get('/users', 'API\UserController@getUsers')->name('get.users');

This route’s URL can be get using route('get.users'), but it might conflict with web.php.

Route::group(['as' => 'api.'], function () {
Route::get('/users', 'API\UserController@getUsers')->name('get.users');
});

This way, you will have it on route('api.get.users').

If you use the route namings, if your write tests, you won’t need to replace the URL everywhere if you plan to change the URL location and keep the route name.

Descriptive, yet simple routes

A good practice is to group the routes into sections, like users, posts, etc. and use the simplest things you can think of:

Route::group(['as' => 'api.'], function () {
Route::group(['as' => 'account.', 'prefix' => '/account'], function () {
Route::get('/users', 'API\UserController@getUsers')->name('get.users');
Route::get('/user/{id}', 'API\UserController@getUser')->name('get.user');
Route::post('/user', 'API\UserController@createUser')->name('create.user');
// etc.
});
});

Use ::get() for getting data, ::post() for creating, ::patch() or ::put() for editing and ::delete() for deleting data. Like this, you end up using the same endpoint, but with the different kind of requests, you will be able to trigger other actions.

Inside your controller, you should be using simple coding, and make use of the Request classes, as i have explained in Pushing Laravel further — best tips & good practices for Laravel 5.7

function getUsers(Request $request)
{
$users = User::all();
   return responder()->success($users)->respond();
}
function getUser($id, Request $request)
{
$user = User::findOrFail($id);
   return responder()->success($user)->respond();
}
function createUser(CreateUserRequest $request)
{
$user = User::create($request->all());
    return responder()->success($user)->respond();
}
// etc.

Also, make sure you stick to the same namings for all actions, so you’ll find them faster and anyone can come into the codebase and make it easy to maintain or create content. Don’t commit API keys or sensitive data to the repo, write clean code and be eager to learn from the others.

Too hard to understand? Reach me!

If you have more questions about Laravel, if you need help with any information related to DevOps or simply want to say a Thank you!, you can find me on Twitter @rennokki!