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.
Yii2 Advanced Project Structure
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
Creating the API Application
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.
Creating api/web/index.php
Create the entry file:
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require __DIR__ . '/../../vendor/autoload.php';
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
require __DIR__ . '/../../common/config/bootstrap.php';
$config = yii\helpers\ArrayHelper::merge(
require __DIR__ . '/../../common/config/main.php',
require __DIR__ . '/../../common/config/main-local.php',
require __DIR__ . '/../config/main.php',
require __DIR__ . '/../config/main-local.php'
);
(new yii\web\Application($config))->run();
This bootstraps Yii and loads the shared common config plus API-specific configuration.
Creating api/config/main.php
Create the main API config file:
<?php
return [
'id' => '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.
Creating api/config/main-local.php
Create a local config file:
<?php
return [
'components' => [
'request' => [
'cookieValidationKey' => 'generate-a-random-secret-key-here',
],
],
];
Use a real random string for cookieValidationKey.
Creating api/config/params.php
Create an empty params file:
<?php
return [];
Adding Autoloading
If your project does not already autoload the api namespace, add it to composer.json:
"autoload": {
"psr-4": {
"api\\": "api/"
}
}
Then run:
composer dump-autoload
Creating a Test Controller
Create a simple controller to confirm the API app works.
<?php
namespace api\controllers;
use yii\rest\Controller;
class SiteController extends Controller
{
public function actionIndex()
{
return [
'status' => '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"
}
Where Do URL Rules Go?
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.
Do You Need to Add Every API Endpoint Manually?
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.
Adding Custom API Actions
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.
Complete URLs
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
Adding API Versioning
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
Creating the V1 Module
Create api/modules/v1/Module.php:
<?php
namespace api\modules\v1;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'api\modules\v1\controllers';
}
Creating the V2 Module
Create api/modules/v2/Module.php:
<?php
namespace api\modules\v2;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'api\modules\v2\controllers';
}
Registering API Modules
Register the modules in api/config/main.php:
'modules' => [
'v1' => [
'class' => api\modules\v1\Module::class,
],
'v2' => [
'class' => api\modules\v2\Module::class,
],
],
Your config now includes the versioned modules.
Adding Versioned URL Rules
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
Creating a Versioned Controller
Create api/modules/v1/controllers/UserController.php:
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'common\models\User';
}
Now the following endpoints are available:
GET /v1/users
GET /v1/users/123
POST /v1/users
PUT /v1/users/123
PATCH /v1/users/123
DELETE /v1/users/123
For v2, create:
<?php
namespace api\modules\v2\controllers;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'common\models\User';
}
Now v2 endpoints are available:
GET /v2/users
GET /v2/users/123
POST /v2/users
PUT /v2/users/123
PATCH /v2/users/123
DELETE /v2/users/123
Adding Versioned Auth Endpoints
For an auth controller inside v1:
[
'class' => 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
Adding Bearer Token Authentication
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:
<?php
namespace api\modules\v1\controllers;
use yii\rest\ActiveController;
use yii\filters\auth\HttpBearerAuth;
class UserController extends ActiveController
{
public $modelClass = 'common\models\User';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::class,
];
return $behaviors;
}
}
Requests must include an Authorization header:
Authorization: Bearer your-token-here
Finding Users by Access Token
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.
Recommended Final Structure
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/
Final Notes
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.
Top comments (0)