Implementing RESTful Web Service APIs
=====================================

Yii provides a whole set of tools to greatly simplify the task of implementing RESTful Web Service APIs.
In particular, Yii provides support for the following aspects regarding RESTful APIs:

* Quick prototyping with support for common APIs for ActiveRecord;
* Response format (supporting JSON and XML by default) and API version negotiation;
* Customizable object serialization with support for selectable output fields;
* Proper formatting of collection data and validation errors;
* Efficient routing with proper HTTP verb check;
* Support `OPTIONS` and `HEAD` verbs;
* Authentication;
* Authorization;
* Support for HATEOAS;
* Caching via `yii\web\HttpCache`;
* Rate limiting;
* Searching and filtering: TBD
* Testing: TBD
* Automatic generation of API documentation: TBD


A Quick Example
---------------

Let's use a quick example to show how to build a set of RESTful APIs using Yii.
Assume you want to expose the user data via RESTful APIs. The user data are stored in the user DB table,
and you have already created the ActiveRecord class `app\models\User` to access the user data.

First, create a controller class `app\controllers\UserController` as follows,

```php
namespace app\controllers;

use yii\rest\ActiveController;

class UserController extends ActiveController
{
	public $modelClass = 'app\models\User';
}
```

Then, modify the configuration about the `urlManager` component in your application configuration:

```php
'urlManager' => [
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
		['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
    ],
]
```

With the above minimal amount of effort, you have already finished your task of creating the RESTful APIs
for accessing the user data. The APIs you have created include:

* `GET /users`: list all users page by page;
* `HEAD /users`: show the overview information of user listing;
* `POST /users`: create a new user;
* `GET /users/123`: return the details of the user 123;
* `HEAD /users/123`: show the overview information of user 123;
* `PATCH /users/123` and `PUT /users/123`: update the user 123;
* `DELETE /users/123`: delete the user 123;
* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;
* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.

You may access your APIs with the `curl` command like the following,

```
curl -i -H "Accept:application/json" "http://localhost/users"
```

which may give the following output:

```
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self, <http://localhost/users?page=2>; rel=next, <http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

[{"id":1,..},{"id":2,...}...]
```

Try changing the acceptable content type to be `application/xml`, and you will see the result
is returned in XML format:

```
curl -i -H "Accept:application/xml" "http://localhost/users"
```

```
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self, <http://localhost/users?page=2>; rel=next, <http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8"?>
<response><item><id>1</id>...</item><item><id>2</id>...</item>...</response>
```

> Tip: You may also access your APIs via Web browser by entering the URL `http://localhost/users`.

As you can see, in the response headers, there are information about the total count, page count, etc.
There are also links that allow you to navigate to other pages of data. For example, `http://localhost/users?page=2`
would give you the next page of the user data.

Using the `fields` and `expand` parameters, you may also request to return a subset of the fields in the result.
For example, the URL `http://localhost/users?fields=id,email` will only return the `id` and `email` fields in the result:


> Info: You may have noticed that the result of `http://localhost/users` includes some sensitive fields,
> such as `password_hash`, `auth_key`. You certainly do not want these to appear in your API result.
> You can/should filter out these fields as described in the following sections.


In the following sections, we will explain in more details about implementing RESTful APIs.


General Architecture
--------------------

Using the Yii RESTful API framework, you implement an API endpoint in terms of a controller action, and you use
a controller to organize the actions that implement the endpoints for a single type of resource.

Resources are represented as data models which extend from the [[yii\base\Model]] class.
If you are working with databases (relational or NoSQL), it is recommended you use ActiveRecord to represent resources.

You may use [[yii\rest\UrlRule]] to simplify the routing to your API endpoints.

While not required, it is recommended that you develop your RESTful APIs as an application, separated from
your Web front end and back end.


Adding or Removing Endpoints
----------------------------

As explained above, controllers and actions are used to implement API endpoints.

To add an API endpoint servicing a new kind of model (resource), create a new controller class by extending
[[yii\rest\ActiveController]] or [[yii\rest\Controller]]. The difference between these two base controller
classes is that the former is a subclass of the latter and implements a commonly needed actions to deal
with ActiveRecord. The controller class should be named after the model class with the `Controller` suffix.
For example, for the `Post` model, you would create a `PostController` class.

If your new controller class extends from [[yii\rest\ActiveController]], you already have a whole set of
endpoints available out of box, as shown in the quick example. You may want to disable some of the actions
or customize them. This can be easily done by overriding the `actions()` method, like the following,

```php
public function actions()
{
	$actions = parent::actions();

	// disable the "delete" and "create" actions
	unset($actions['delete'], $actions['create']);

	// customize the data provider preparation with the "prepareDataProvider()" method
	$actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];

	return $actions;
}

public function prepareDataProvider()
{
	// prepare and return a data provider for the "index" action
}
```

You can certainly create new actions like you do with regular controllers. The only difference is that
instead of calling [[yii\base\Controller::render()]] to render views, you directly return the data
in your action. For example,

```php
public function actionSearch($keyword)
{
	$result = SolrService::search($keyword);
	return $result;
}
```

The data will be automatically formatted and sent to the client, as we will explain in the next section.


Formatting Response Data
------------------------

By default, Yii supports two response formats for RESTful APIs: JSON and XML. If you want to support
other formats, you should configure [[yii\rest\Controller::supportedFormats]] and also [[yii\web\Response::formatters]].

The data formatting is in general a two-step process:

1. The objects (including embedded objects) in the response data are converted into arrays by [[yii\rest\Serializer]];
2. The array data are converted into different formats (e.g. JSON, XML) by [[yii\web\ResponseFormatterInterface|response formatters]].

Step 2 is usually a very mechanical data conversion process and can be well handled by the built-in response formatters.
Step 1 involves some major development effort as explained below.

When the [[yii\rest\Serializer|serializer]] converts an object into an array, it will call the `toArray()` method
of the object if it implements [[yii\base\ArrayableInterface]]. If an object does not implement this interface,
an array consisting of all its public properties will be returned.

For classes extending from [[yii\base\Model]] or [[yii\db\ActiveRecord]], besides directly overriding `toArray()`,
you may also override the `fields()` method and/or the `extraFields()` method to customize the data to be returned.

The method [[yii\base\Model::fields()]] declares a set of fields of an object that should be included in the result.
The default implementation returns all attributes of a model as the output fields. You can customize it to add,
remove, rename or reformat the fields. For example,

```php
class User extends \yii\db\ActiveRecord
{
	public function fields()
	{
		$fields = parent::fields();

		// remove fields that contain sensitive information
		unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);

		// add a new field "full_name" defined as the concatenation of "first_name" and "last_name"
		$fields['full_name'] = function () {
			return $this->first_name . ' ' . $this->last_name;
		};

		return $fields;
	}
}
```

The return value of `fields()` should be an array. The array keys are the field names, and the array values
are the corresponding field definitions which can be either property/attribute names or anonymous functions
returning the corresponding field values.

> Warning: Because by default all attributes of a model will be included in the API result, you should
> examine your data to make sure they do not contain sensitive information. If there is such information,
> you should override `fields()` or `toArray()` to filter them out. In the above example, we choose
> to filter out `auth_key`, `password_hash` and `password_reset_token`.

You may use the `fields` query parameter to specify which fields in `fields()` should be included in the result.
If this parameter is not specified, all fields returned by `fields()` will be returned.

The method [[yii\base\Model::extraFields()]] is very similar to [[yii\base\Model::fields()]].
The difference between these methods is that the latter declares the fields that should be returned by default,
while the former declares the fields that should only be returned when the user specifies them in the `expand` query parameter.

For example, `http://localhost/users?fields=id,email&expand=profile` may return the following JSON data:

```php
[
	{
		"id": 100,
		"email": "100@example.com",
		"profile": {
			"id": 100,
			"age": 30,
		}
	},
	...
]
```


Routing
-------


Authentication
--------------


Authorization
-------------


Versioning
----------


Caching
-------


Rate Limiting
-------------


Error Handling
--------------

* `200`: OK. Everything worked as expected.
* `201`: A resource was successfully created in response to a `POST` request. The `Location` header
   contains the URL pointing to the newly created resource.
* `204`: The request is handled successfully and the response contains no body content (like a `DELETE` request).
* `304`: Resource was not modified. You can use the cached version.
* `400`: Bad request. This could be caused by various reasons from the user side, such as invalid JSON
   data in the request body, invalid action parameters, etc.
* `401`: No valid API access token is provided.
* `403`: The authenticated user is not allowed to access the specified API endpoint.
* `404`: The requested resource does not exist.
* `405`: Method not allowed. Please check the `Allow` header for allowed HTTP methods.
* `415`: Unsupported media type. The requested content type or version number is invalid.
* `422`: Data validation failed (in response to a `POST` request, for example). Please check the response body for detailed error messages.
* `429`: Too many requests. The request is rejected due to rate limiting.
* `500`: Internal server error. This could be caused by internal program errors.


Documentation
-------------

Testing
-------

