REST Methods

Introduction

The API response format must stay consistent along the application. Ideally it would be good to follow a standard as the JSON:API so your frontend app could align with the API.

Restify provides several different approaches to respond consistent to the application's incoming request. By default, Restify's base rest controller class uses a RestResponse structure which provides a convenient method to respond to the HTTP request with a variety of handy magic methods.

Restify Response Quickstart

To learn about Restify's handy response, let's look at a complete example of responding a request and returning the data back to the client.

Defining The Route

First, let's assume we have the following routes defined in our routes/api.php file:

Route::post('users', 'UserController@store');

Route::get('users/{id}', 'UserController@show');

The GET route will return back a user for the given id.

Creating The Controller

Next, let's take a look at a simple API controller that handles this route. We'll leave the show and store methods empty for now:

<?php

namespace App\Http\Controllers;

use Binaryk\LaravelRestify\Controllers\RestController;

class UserController extends RestController
{
    /**
     * Store a newly created user in storage.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // The only way to do great work is to love what you do.
    }

    /**
     * Display the user entity
     *
     * @param int $id
     * @return Response
     */
    public function show($id)
    {
        // Very little is needed to make a happy life.
    }
}

Writing The API Response Logic

Now we are ready to fill in our show method with the logic to respond with the user resource. To do this, we will use the respond method provided by the parent Binaryk\LaravelRestify\Controllers\RestController class. A JSON response will be sent for API request containing data and errors properties.

To get a better understanding of the respond method, let's jump back into the show method:

/**
 * Display the user entity
 *
 * @param int $id
 * @return Response
 */
public function show($id)
{
    return $this->response(User::find($id));
}

As you can see, we pass the desired data into the respond method. This method will wrap the passed data into a JSON object and attach it to the data response property.

Receiving API Response

Once the respond method wrapping the data, the HTTP request will receive back a response having always the structure:

{
  "data": {
    "id": 1,
    "name": "User name",
    "email": "kshlerin.hertha@example.com",
    "email_verified_at": "2019-12-20 09:48:54",
    "created_at": "2019-12-20 09:48:54",
    "updated_at": "2020-01-10 12:01:17"
  }
}

or:

{
  "errors": []
}

Response factory

In addition the parent RestController provides a powerful response factory method. To understand this let's return back to our store method from the UserController:

/**
 * Store a newly created resource in storage.
 *
 * @param Request $request
 * @return Response
 */
public function store(Request $request)
{
    return $this->response();
}

The response() method will be an instance of Binaryk\LaravelRestify\Controllers\RestResponse. For more information on working with this object instance, check out its documentation.

$this->response()
->data($user)
->message('This is the first user');

The response will look like:

{
  "data": {
    "id": 1,
    "name": "User name",
    "email": "kshlerin.hertha@example.com",
    "email_verified_at": "2019-12-20 09:48:54",
    "created_at": "2019-12-20 09:48:54",
    "updated_at": "2020-01-10 12:01:17"
  },
  "meta": {
    "message": "This is the first user"
  }
}

Displaying Response Errors

As we saw above, the response always contains an errors property. This can be either an empty array, or a list with errors. For example, what if the incoming request parameters do not pass the given validation rules? This can be handled by the errors proxy method:

/**
 * Store a newly created resource in storage.
 *
 * @param Request $request
 * @return Response
 */
public function store(Request $request)
{
    try {
        $this->validate($request, [
            'title' => 'required|unique:users|max:255',
        ]);

        // The user is valid
    } catch (ValidationException $exception) {
        // The user is not valid
        return $this->errors($exception->errors());
    }
}

And returned API response will have the 400 HTTP code and the following format:

{
  "errors": {
    "title": [
      "The title field is required."
    ]
  }
}

Custom Header

Sometimes you may need to respond with a custom header, for example according with JSON:API after storing an entity, we should respond with a Location header which has the value endpoint to the resource:

return $this->response()
    ->header('Location', 'api/users/1')
    ->data($user);

Optional Attributes

By default Restify returns data and errors attributes in the API response. It also wrap the message into a meta object. But what if we have to send some custom attributes. In addition to generating default fields, you may add extra fields to the response by using setMeta method from the RestResponse object:

return $this->response()
    ->data($user)
    ->setMeta('related', [ 'William Shakespeare', 'Agatha Christie', 'Leo Tolstoy' ]);

Hiding Default Attribute

Restify has a list of predefined attributes: 'line', 'file', 'stack', 'data', 'errors', 'meta'.

Some of those are hidden in production: 'line', 'file', 'stack', since they are only used for tracking exceptions.

If you would like the API response to not contain any of these fields (or hiding a specific one, errors for example), this can be done by setting in the application provider the:

RestResponse::$RESPONSE_DEFAULT_ATTRIBUTES = ['data', 'meta'];

Rest Response Methods

The $this->response() returns an instance of Binaryk\LaravelRestify\Controllers\RestResponse. This expose multiple magic methods for your consistent API response.

Attach data

As we already have seen, attaching data to the response can be done by using:

->data($info)

Headers setup

Header could be set by using header method, it accept two arguments, the header name and header value:

->header('Location', 'api/users/1')

Meta information

In addition with data you may want to send some extra attributes to the client, a message for example, or anything else:

->setMeta('name', 'Eduard Lupacescu')
->message(__('Silence is golden.'))

Response code modifiers

Very often we have to send an informative response code. The follow methods are used for setting the response code:

Refresh 103

->refresh()

Success 200

->success()

Created 201

->created()

Deleted (No Content) 204

->deleted()
->blank()

Invalid 400

->invalid()

Unauthorized 401

->unauthorized()

Forbidden 403

->forbidden()

Missing 404

->missing()

Throttle 429

->throttle()

Unavailable 503

->unavailable()

Debugging

The follow methods could be used to debug some information in the dev mode:

Line debugging

$lineNumber = 201;
$this->line($lineNumber)

Debug to file

This could be used for debugging the file name

$this->file($exception->getFile())

Stack traces

With this you may log the exception stach trace

$this->stack($exception->getTraceAsString())

Errors methods

The follow methods could be used for adding errors to the response:

Adding multiple errors

Adding a set of errors at once:

$this->errors([ 'Something went wrong' ])

addError function

Adding error by error in a response instance:

$this->addError('Something went wrong')

Custom Paginator

Sometimes you have a custom paginator collection, and you want to keep the same response format as the Repositorydoes.

You can use this static call:

        $paginator = User::query()->paginate(5);

        $response = Binaryk\LaravelRestify\Controllers\RestResponse::index(
            $paginator
        );

The $paginator argument should be an instance of: Illuminate\Pagination\AbstractPaginator.

The expected response will contain:

{
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "path": "http://localhost",
    "per_page": 5,
    "to": 1,
    "total": 1
  },
  "links": {
    "first": "http://localhost?page=1",
    "last": "http://localhost?page=1",
    "prev": null,
    "next": null
  },
  "data": []
}

Edit this page on GitHub Updated at Tue, Nov 16, 2021