Building a Versioned REST API with Authentication in Yii2 Advanced
Source: Dev.to
Yii2 Advanced comes with separate applications for frontend, backend, console, and shared code in common. It does not include an api application by default, but Yii2 Advanced is designed in a way that makes adding one straightforward. In this post, we’ll create a separate API application, configure REST routes, add versioned URLs like /v1/users and /v2/users, and prepare the structure for authentication. A default Yii2 Advanced project usually looks like this: backend/ common/ console/ frontend/ vendor/ composer.json
To build an API, create a new application folder at the same level as frontend and backend: api/
After adding it, your structure becomes: api/ backend/ common/ console/ frontend/ vendor/ composer.json
Inside the api folder, create this structure: api/ config/ main.php main-local.php params.php controllers/ modules/ runtime/ web/ index.php
The api/web folder is the public document root for the API application. api/web/index.php
Create the entry file: run();
This bootstraps Yii and loads the shared common config plus API-specific configuration. api/config/main.php
Create the main API config file: ‘app-api’, ‘basePath’ => dirname(DIR), ‘controllerNamespace’ => ‘api\controllers’,
'components' => [
'request' => [
'csrfParam' => '_csrf-api',
'parsers' => [
'application/json' => yii\web\JsonParser::class,
],
],
'response' => [
'format' => yii\web\Response::FORMAT_JSON,
],
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'GET /' => 'site/index',
],
],
],
];
The important parts are: ‘parsers’ => [ ‘application/json’ => yii\web\JsonParser::class, ],
This allows Yii2 to read JSON request bodies. And: ‘response’ => [ ‘format’ => yii\web\Response::FORMAT_JSON, ],
This makes API responses return JSON by default. api/config/main-local.php
Create a local config file: [ ‘request’ => [ ‘cookieValidationKey’ => ‘generate-a-random-secret-key-here’, ], ], ];
Use a real random string for cookieValidationKey. api/config/params.php
Create an empty params file: ‘ok’, ‘app’ => ‘api’, ]; } }
With this URL rule: ‘rules’ => [ ‘GET /’ => ‘site/index’, ],
You should be able to open: https://api.yourdomain.com/
or locally: http://localhost:8080/
and see: { “status”: “ok”, “app”: “api” }
URL rules are added inside the urlManager component in api/config/main.php: ‘components’ => [ ‘urlManager’ => [ ‘enablePrettyUrl’ => true, ‘showScriptName’ => false, ‘rules’ => [ ‘GET /’ => ‘site/index’, ], ], ],
The rules array belongs inside: components.urlManager
not at the top level of the config. No. Yii2 REST URL rules can generate standard REST endpoints automatically. For example: [ ‘class’ => yii\rest\UrlRule::class, ‘controller’ => ‘user’, ]
generates standard REST routes like: GET /users GET /users/123 POST /users PUT /users/123 PATCH /users/123 DELETE /users/123
Yii2 pluralizes REST controller names by default: user -> /users product -> /products order -> /orders
For multiple controllers, you can group them: [ ‘class’ => yii\rest\UrlRule::class, ‘controller’ => [ ‘user’, ‘product’, ‘order’, ‘payment’, ‘notification’, ], ],
This gives each controller its standard REST URLs. For non-standard endpoints, use extraPatterns. Example: [ ‘class’ => yii\rest\UrlRule::class, ‘controller’ => ‘auth’, ‘extraPatterns’ => [ ‘POST login’ => ‘login’, ‘POST logout’ => ‘logout’, ‘POST refresh-token’ => ‘refresh-token’, ], ],
This creates: POST /auth/login POST /auth/logout POST /auth/refresh-token
So even with many endpoints, you usually do not list all of them manually. You define REST controllers and add only custom routes where needed. The complete URL depends on your API base URL. If your API is hosted at: https://api.yourdomain.com
then the routes become: GET https://api.yourdomain.com/users GET https://api.yourdomain.com/users/123 POST https://api.yourdomain.com/users PUT https://api.yourdomain.com/users/123 PATCH https://api.yourdomain.com/users/123 DELETE https://api.yourdomain.com/users/123
For custom auth routes: POST https://api.yourdomain.com/auth/login POST https://api.yourdomain.com/auth/logout POST https://api.yourdomain.com/auth/refresh-token
If running locally: http://localhost:8080/users http://localhost:8080/auth/login
If pretty URLs are not configured correctly, URLs may include index.php: http://localhost:8080/index.php/users http://localhost:8080/index.php/auth/login
For versioned API URLs like: https://api.yourdomain.com/v1/users https://api.yourdomain.com/v2/users
the recommended Yii2 approach is to use modules. Create this structure: api/ modules/ v1/ Module.php controllers/ UserController.php v2/ Module.php controllers/ UserController.php
Create api/modules/v1/Module.php: [ ‘v1’ => [ ‘class’ => api\modules\v1\Module::class, ], ‘v2’ => [ ‘class’ => api\modules\v2\Module::class, ], ],
Your config now includes the versioned modules. Inside components.urlManager.rules, add versioned REST rules: ‘rules’ => [ [ ‘class’ => yii\rest\UrlRule::class, ‘controller’ => [ ‘v1/user’, ‘v1/product’, ‘v1/order’, ], ], [ ‘class’ => yii\rest\UrlRule::class, ‘controller’ => [ ‘v2/user’, ‘v2/product’, ‘v2/order’, ], ], ],
Now Yii2 maps these controllers: api/modules/v1/controllers/UserController.php api/modules/v2/controllers/UserController.php
to these URLs: /v1/users /v2/users
Create api/modules/v1/controllers/UserController.php: yii\rest\UrlRule::class, ‘controller’ => ‘v1/auth’, ‘extraPatterns’ => [ ‘POST login’ => ‘login’, ‘POST logout’ => ‘logout’, ‘POST refresh-token’ => ‘refresh-token’, ], ],
This creates: POST /v1/auth/login POST /v1/auth/logout POST /v1/auth/refresh-token
Your controller would live at: api/modules/v1/controllers/AuthController.php
Yii2 supports bearer token authentication with yii\filters\auth\HttpBearerAuth. In a REST controller, add: use yii\filters\auth\HttpBearerAuth;
public function behaviors() { $behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::class,
];
return $behaviors;
}
Example: HttpBearerAuth::class, ];
return $behaviors;
}
}
Requests must include an Authorization header: Authorization: Bearer your-token-here
Your common\models\User model should implement: public static function findIdentityByAccessToken($token, $type = null) { return static::findOne([‘access_token’ => $token]); }
For this to work, your user table needs an access_token column, or you need a separate token table. For production APIs, avoid storing permanent plain tokens. A better approach is usually: short-lived access tokens refresh tokens hashed token storage JWT OAuth2
The simple access_token example is useful for learning, but production authentication should be designed more carefully. A versioned API app may look like this: api/ config/ main.php main-local.php params.php modules/ v1/ Module.php controllers/ AuthController.php UserController.php ProductController.php v2/ Module.php controllers/ AuthController.php UserController.php ProductController.php runtime/ web/ index.php backend/ common/ console/ frontend/
In Yii2 Advanced, an API is simply another application beside frontend and backend. You create the api folder manually, give it its own config and web entry point, then use REST URL rules and modules to keep the code organized. The most common pattern is: api/modules/v1/controllers/UserController.php -> /v1/users api/modules/v2/controllers/UserController.php -> /v2/users
This keeps your API versions cleanly separated in both the URL and the codebase.