PHPackages                             routiller/routiller - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. routiller/routiller

ActiveLibrary[API Development](/categories/api)

routiller/routiller
===================

Laravel-like REST API routing for WordPress plugins. Conflict-free, multi-plugin safe.

v1.0.0(2mo ago)22MITPHPPHP &gt;=7.4

Since Apr 9Pushed 2mo agoCompare

[ Source](https://github.com/iftakharul-islam/Routiller)[ Packagist](https://packagist.org/packages/routiller/routiller)[ RSS](/packages/routiller-routiller/feed)WikiDiscussions master Synced 2w ago

READMEChangelogDependenciesVersions (2)Used By (0)

Routiller
=========

[](#routiller)

Laravel-like REST API routing for WordPress plugins. Zero conflicts when multiple plugins use the same package.

Install
-------

[](#install)

```
composer require routiller/routiller
```

Quick Start
-----------

[](#quick-start)

```
use Routiller\Routiller;
use App\Controllers\ProjectController;
use App\Permissions\Authentic;

// Create an isolated router instance for your plugin
$routiller = Routiller::create( 'my-plugin/v1' );
$router    = $routiller->router();

// Define routes
$router->get( 'projects', [ProjectController::class, 'index'] )
    ->permission( [Authentic::class] );

$router->post( 'projects', [ProjectController::class, 'store'] )
    ->permission( [Authentic::class] )
    ->validator( CreateProjectValidator::class )
    ->sanitizer( ProjectSanitizer::class );

$router->get( 'projects/{id}', [ProjectController::class, 'show'] );
$router->put( 'projects/{id}', [ProjectController::class, 'update'] );
$router->delete( 'projects/{id}', [ProjectController::class, 'destroy'] );

// Register with WordPress
$routiller->register();
```

Your routes are now available at `/wp-json/my-plugin/v1/projects`.

Route Groups
------------

[](#route-groups)

```
$router->group( ['prefix' => 'projects/{project_id}'], function ( $router ) {
    $router->get( 'tasks', [TaskController::class, 'index'] );
    $router->post( 'tasks', [TaskController::class, 'store'] );
    $router->get( 'tasks/{id}', [TaskController::class, 'show'] );
});
// Produces: projects/(?P\d+)/tasks, etc.
```

Groups support `prefix`, `middleware`, and `permission` attributes. They nest properly.

Loading Route Files
-------------------

[](#loading-route-files)

Instead of defining all routes inline, load them from a directory:

```
$routiller = Routiller::create( 'my-plugin/v1' );
$routiller->loadRoutes( __DIR__ . '/routes' );
$routiller->register();
```

Each file in `routes/` receives `$router`:

```
// routes/projects.php
$router->get( 'projects', [ProjectController::class, 'index'] );
$router->post( 'projects', [ProjectController::class, 'store'] );
```

URI Parameters
--------------

[](#uri-parameters)

SyntaxRegexMatches`{id}``(?P\d+)`Numeric (default)`{slug:string}``(?P[a-zA-Z0-9_-]+)`Alphanumeric string`{path:any}``(?P.+)`AnythingPermissions
-----------

[](#permissions)

Implement `Routiller\Contracts\PermissionInterface`:

```
use Routiller\Contracts\PermissionInterface;
use WP_REST_Request;

class Authentic implements PermissionInterface {
    public function check( WP_REST_Request $request ) {
        return is_user_logged_in();
    }
}

class AdminOnly implements PermissionInterface {
    public function check( WP_REST_Request $request ) {
        if ( ! current_user_can( 'manage_options' ) ) {
            return new \WP_Error( 'forbidden', 'Admin access required.', ['status' => 403] );
        }
        return true;
    }
}
```

Multiple permissions use OR logic — if any returns `true`, access is granted.

Validators
----------

[](#validators)

Implement `Routiller\Contracts\ValidatorInterface`:

```
use Routiller\Contracts\ValidatorInterface;
use WP_REST_Request;

class CreateProjectValidator implements ValidatorInterface {
    public function rules() {
        return [
            'title' => 'required|string',
            'description' => 'string',
        ];
    }

    public function validate( WP_REST_Request $request, $key ) {
        // Your validation logic
    }

    public function get_errors( $key = null ) {
        // Return error messages
    }
}
```

Schema
------

[](#schema)

Make your endpoints self-documenting by attaching a JSON Schema. WordPress serves this via `OPTIONS` requests and the `/wp-json` discovery index — clients can introspect your API without any external docs.

Implement `Routiller\Contracts\SchemaInterface`:

```
use Routiller\Contracts\SchemaInterface;

class ProjectSchema implements SchemaInterface {
    public function schema() {
        return [
            '$schema'    => 'http://json-schema.org/draft-04/schema#',
            'title'      => 'project',
            'type'       => 'object',
            'properties' => [
                'id' => [
                    'description' => 'Unique identifier for the project.',
                    'type'        => 'integer',
                    'context'     => ['view', 'edit'],
                    'readonly'    => true,
                ],
                'title' => [
                    'description' => 'The title of the project.',
                    'type'        => 'string',
                    'context'     => ['view', 'edit'],
                    'required'    => true,
                ],
                'status' => [
                    'description' => 'Project status.',
                    'type'        => 'string',
                    'enum'        => ['active', 'archived', 'completed'],
                    'context'     => ['view', 'edit'],
                ],
                'created_at' => [
                    'description' => 'The date the project was created.',
                    'type'        => 'string',
                    'format'      => 'date-time',
                    'context'     => ['view'],
                    'readonly'    => true,
                ],
            ],
        ];
    }
}
```

Chain it on any route:

```
$router->get( 'projects', [ProjectController::class, 'index'] )
    ->permission( [Authentic::class] )
    ->schema( ProjectSchema::class );

$router->get( 'projects/{id}', [ProjectController::class, 'show'] )
    ->schema( ProjectSchema::class );
```

Now `OPTIONS /wp-json/my-plugin/v1/projects` returns the full schema, and clients like JavaScript frontends or mobile apps can auto-generate types from it.

### Schema `context`

[](#schema-context)

The `context` array controls which fields appear in different views:

- `'view'` — fields returned on normal GET requests
- `'edit'` — fields returned when editing (typically includes more detail)
- `'embed'` — minimal fields for `_embed` responses

### Schema with groups

[](#schema-with-groups)

```
$router->group( ['prefix' => 'projects'], function ( $router ) {
    $router->get( '', [ProjectController::class, 'index'] )
        ->schema( ProjectCollectionSchema::class );

    $router->get( '{id}', [ProjectController::class, 'show'] )
        ->schema( ProjectSchema::class );

    $router->post( '', [ProjectController::class, 'store'] )
        ->schema( ProjectSchema::class )
        ->validator( CreateProjectValidator::class );
});
```

Sanitizers
----------

[](#sanitizers)

Implement `Routiller\Contracts\SanitizerInterface`:

```
use Routiller\Contracts\SanitizerInterface;
use WP_REST_Request;

class ProjectSanitizer implements SanitizerInterface {
    public function filters() {
        return [
            'title'       => 'sanitize_text_field',
            'description' => 'wp_kses_post',
        ];
    }

    public function sanitize( WP_REST_Request $request, $key ) {
        // Your sanitization logic
    }
}
```

Multi-Plugin Safety
-------------------

[](#multi-plugin-safety)

The core design principle: **each plugin gets its own isolated instance**.

```
// Plugin A
$a = Routiller::create( 'plugin-a/v1' );
$a->router()->get( 'items', [AController::class, 'index'] );
$a->register();

// Plugin B (same package, zero conflicts)
$b = Routiller::create( 'plugin-b/v2' );
$b->router()->get( 'items', [BController::class, 'index'] );
$b->register();
```

- Routes are stored per-instance, not in global statics
- Different namespaces = different WP REST endpoints
- `Routiller::create()` returns the same instance for the same namespace (safe to call multiple times)

Requirements
------------

[](#requirements)

- PHP &gt;= 7.4
- WordPress (REST API)

License
-------

[](#license)

MIT

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance85

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

79d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1481182?v=4)[ifat](/maintainers/ifat)[@ifat](https://github.com/ifat)

---

Top Contributors

[![iftakharul-islam](https://avatars.githubusercontent.com/u/88052038?v=4)](https://github.com/iftakharul-islam "iftakharul-islam (2 commits)")

---

Tags

wordpressroutingREST APIlaravel-like

### Embed Badge

![Health badge](/badges/routiller-routiller/health.svg)

```
[![Health](https://phpackages.com/badges/routiller-routiller/health.svg)](https://phpackages.com/packages/routiller-routiller)
```

###  Alternatives

[airesvsg/acf-to-rest-api

Exposes Advanced Custom Fields Endpoints in the WordPress REST API

1.4k77.2k](/packages/airesvsg-acf-to-rest-api)[sybrew/the-seo-framework

An automated, advanced, accessible, unbranded and extremely fast SEO solution for any WordPress website.

47582.8k](/packages/sybrew-the-seo-framework)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
