PHPackages                             ginger-tek/routy - 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. [HTTP &amp; Networking](/categories/http)
4. /
5. ginger-tek/routy

ActiveLibrary[HTTP &amp; Networking](/categories/http)

ginger-tek/routy
================

A simple, robust PHP router for fast app and API development

4.4.1(2mo ago)1333↓86.4%1MITPHPPHP &gt;=8.1.0

Since Dec 23Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/ginger-tek/routy)[ Packagist](https://packagist.org/packages/ginger-tek/routy)[ Docs](https://github.com/ginger-tek/routy)[ RSS](/packages/ginger-tek-routy/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (10)DependenciesVersions (37)Used By (1)

Routy
=====

[](#routy)

A simple, robust PHP router for fast app and API development

Getting Started
===============

[](#getting-started)

Composer
--------

[](#composer)

```
composer require ginger-tek/routy

```

```
require 'vendor/autoload.php';

use GingerTek\Routy;

$app = new Routy;
```

Starter Example
---------------

[](#starter-example)

Handlers for each route can be any kind of callable, such as regular functions, arrow functions, closure variables, or static class methods.

```
$app = new Routy;

// Standard Function
$app->get('/', function (Routy $app) {
  $app->sendData('Hello!');
});

// Arrow Function
$app->get('/', fn () => $app->sendData('Hello!'););

// Closure
$handler = function (Routy $app) {
  $app->sendData('Hello!');
};
$app->get('/closure', $handler);

// Static Class Method
class ProductsController {
  public static function list(Routy $app) {
    ...
    $app->sendJson($products);
  }
}

$app->get('/products', \ProductsController::list(...));
```

Configurations
==============

[](#configurations)

You can pass an associative array of optional configurations to the constructor.

- `root` to set the root app directory when running from a sub-directory, i.e. public/. Defaults to current directory.
- `base` to set a global base URI when running from a sub-directory
- `render` to set a default template rendering strategy to use in the [`render()`](#render) response method. Defaults to false.

```
$app = new Routy([
  'root' => '../',
  // i.e. app instance is created in public/index.php and your app root is one directory above

  'render' => function () {},
  // this will be called by the render method and the returned string value will be sent as the response

  'base' => '/api',
  // i.e. your app files are in /wwwroot/my-api-app and is accessed via https://domain.com/api
])
```

If you need to use any of these configuration values later on, you can access them using `getConfig()`; however, you can not update configurations after instantiation.

```
new \PDO('sqlite:' . $app->getConfig('root') . '/app.db');
```

Features
========

[](#features)

Method Wrappers
---------------

[](#method-wrappers)

Use the method wrappers for routing GET, POST, PUT, PATCH, or DELETE method requests. There is also a catch-all wrapper for matching on all standard HTTP methods, including HEAD and OPTIONS.

```
$app->get('/products', ...); // HTTP GET
$app->post('/products/:id', ...); // HTTP POST
$app->put('/products', ...); // HTTP PUT
$app->patch('/products/:id', ...); // HTTP PATCH
$app->delete('/products/:id', ...); // HTTP DELETE
$app->any('/products/:id', ...); // HTTP GET, POST, PUT, PATCH, DELETE, HEAD, or OPTIONS
```

Use `*` for the route argument to match on any route.

```
$app->get('*', ...); // HTTP GET for all routes
$app->any('*', ...); // Any standard HTTP method for all routes
```

Custom Routing
--------------

[](#custom-routing)

You can also use the `route()` method directly, which is what the common method wrappers use underneath, to craft more specific method conditions on which to match.

```
$app->route('GET|POST', '/form', ...); // HTTP GET and POST for the /form route
$app->route('GET|POST|PUT', '/products', ...); // HTTP GET, POST and PUT for the /products route
```

Dynamic Routes
--------------

[](#dynamic-routes)

To define dynamic route parameters, use the `:param` syntax and access them via the `getParam()` method on the `$app` context. The values are URL-decoded automatically.

```
$app->get('/products/:id', function(Routy $app) {
  $id = $app->getParam('id');
  // ...
});
```

Middleware
----------

[](#middleware)

### Global Middleware

[](#global-middleware)

If you want to define global middleware, you can use the `use()` method. Any middleware or route handler callable must have one argument to accept the current Routy instance.

(See [Context Sharing](#context-sharing) about sharing data between middleware/handlers)

```
function authenticate(Routy $app) {
  if(!($token = @$app->getHeaders()['authorization']))
    $app->end(401);
  $app->setCtx('user', parseToken($token));
}

$app->use(authenticate(...));
```

### Route Middleware

[](#route-middleware)

All arguments set after the URI string argument are considered middleware functions, including the route handler, so you can define as many as needed.

```
$app->get('/products', authenticate(...), function (Routy $app) {
  $userId = $app->getCtx('user')->id;
  $items = getProductsByUser($userId);
  $app->sendJson($items);
});
```

Context Sharing
---------------

[](#context-sharing)

To share data between handlers/middleware or provide a global resource to the instance, use the `setCtx()` and `getCtx()`. Any data type can be passed in for the value.

```
$app->setCtx('db', new PDO('sqlite:myData.db'));
...
$app->get('/products', function (Routy $app) {
  $db = $app->getCtx('db');
  $stmt = $db->prepare('select * from products');
  $stmt->execute();
  $result = $stmt->fetch();
  ...
})
```

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

[](#route-groups)

You can define route groups using the `group()` method.

```
$app = new Routy;

$app->group('/products', function (Routy $app) {
  $app->post('/', ...);
  $app->get('/', ...);
  $app->get('/:id', ...);
  $app->put('/:id', ...);
  $app->delete('/:id', ...);
});
```

You can also add middleware to your nested routes, which will apply it to all the routes nested within.

```
$app->group('/products', authenticate(...), function (Routy $app) {
  $app->get('/', ...);
});
```

Fallback Routes
---------------

[](#fallback-routes)

Fallbacks are used for returning custom 404 responses, or to perform other logic before returning.

To set a fallback route, use the `fallback()` method to set a handler function, which will automatically have the HTTP 404 response header set.

Fallback routes are scoped to wherever they are defined, and will only be reached if they match the incoming URI's parent path.

```
$app = new Routy;

$app->group('/products', function (Routy $app) {
  $app->get('/', fn (Routy $app) => $app->sendJson([]));

  // GET /products/asdf will end up here
  $app->fallback(fn () => $app->render('product-not-found'));
});

// GET /asdf will end up here
$app->fallback(fn () => $app->render('not-found'));
```

Serve Static Files (SPA)
------------------------

[](#serve-static-files-spa)

To serve static files from a specified directory via a proxy route, use the `serveStatic()` method ***after*** all other normal route definitions. You can use this to serve asset files or a whole SPA from the same app. If the requested URI is a directory, an `index.html` file will be served, if one exists, and client-side routing will take over. Otherwise, if any requested file is not found, a generic 404 response with be sent back.

**NOTE: Serving static files is typically best performed by a web server (Apache/nginx/Caddy) via rewrite rules, so this is a convienence for less demanding applications. Consider your performance requirements in production scenarios when using this feature.**

```
$app = new Routy;

$app->group('/api', ApiController::index(...));
$app->serveStatic('/nm', 'node_modules');
$app->serveStatic('/', 'public');
```

Request Properties
------------------

[](#request-properties)

You can access the incoming request via the `uri` and `method` properties on the `$app` instance.

```
$app->uri;     // /some/route
$app->method;  // GET, POST, PUT, etc.
```

Request Helper Methods
----------------------

[](#request-helper-methods)

There are a few helper methods for handling incoming requests.

### `getQuery()`

[](#getquery)

Use to retrieve an incoming URL query parameter. Key lookup is case-sensitive. Returns false if not found.

```
$name = $app->getQuery('name'); // getParam('param'); // get('/products', function (Routy $app) {
  $body = $app->getBody();
  $body->someProperty;  // JSON: { "someProperty": "..." }
                        // or
  $body->username;      // multipart/form-data:
});
```

### `getHeader()`

[](#getheader)

Use to retrieve an incoming HTTP header by name. Lookup is case-insensitive, i.e. both `Content-Type` and `content-type` will work.

```
$app->get('/products', function (Routy $app) {
  $authToken = $app->getHeader('authorization'); // 'Bearer eyhdgs9d8fg9s7d87f...'
});
```

### `getFiles()`

[](#getfiles)

Use to retrieve uploaded files from multipart/form-data requests. Returns an object array of all files.

```

  Submit

```

```
$app->post('/upload', function (Routy $app) {
  $files = $app->getFiles('field-name');
  // Destructure assignment for a single file upload
  [$file] = $app->getFiles('field-name');
});
```

Response Helper Methods
-----------------------

[](#response-helper-methods)

There are plenty of helper methods for handling responses.

### `sendData()`

[](#senddata)

Use to return string data or a file's raw contents.

```
$app->sendData('Raw HTML');
```

If the data is a file path, the Content-Type will be automatically detected, if it has a known MIME type.

```
$app->sendData('path/to/file.html');
```

Otherwise, the Content-Type can be specified explicitly.

```
$app->sendData($base64EncodedImage, 'image/png');
$app->sendData($pathToFileWithNoExtension, 'text/csv');
```

### `sendJson()`

[](#sendjson)

Use to return data as a JSON string

```
$app->sendJson(['prop' => 'value']); // { "prop": "value" }
$app->sendJson([1, 2, ['three' => 4], 5]); // [1, 2, { "three: 4 }, 5]
```

### `render()`

[](#render)

Use to render a view file, calling the configured render strategy callback. Template rendering strategy is left up to the developer, allowing for the use any kind of templating engine.

To configure a template rendering strategy, set the `render` option on your Routy instance to a callback. This callback must follow this argument signature:

```
function myRenderCallback(string $view, array $context, Routy $app): string
```

The callback must return a string value of your rendered content, which will be returned as the response. The app instance can be useful for acceessing the root path when dealing with nested directory structures.

Below is an example using [Twig](https://twig.symfony.com/):

```
$app = new Routy([
  'render' => function (string $view, array $context, Routy $app): string {
    $loader = new \Twig\Loader\FilesystemLoader($app->getConfig('root') . 'views/');
    $twig = new \Twig\Environment($loader);
    $model['app'] = $app;
    return $twig->render("$view.html.twig", $model);
  }
]);
...
$app->render('home'); // views/home.html.twig
$app->render('about'); // views/about.html.twig
```

Another example using plain PHP templating:

```
$app = new Routy([
  'render' => function (string $view, array $context, Routy $app): string {
    ob_start();
    $context['view'] = $app->getConfig('root') . "views/$view.php";
    extract($context, EXTR_OVERWRITE);
    include $app->getConfig('root') . 'views/_layout.php';
    return ob_get_clean();
  }
]);
...
$app->render('home'); // views/home.php
$app->render('about'); // views/about.php
```

### `status()`

[](#status)

Use to set the HTTP status code. This method can chained to other response methods.

```
$app->post('/products', function (Routy $app) {
  $app->status(400)->sendJson(['error' => 'Bad payload']);
  // or
  $app->status(201)->sendData('Successfully created!');
});
```

### `redirect()`

[](#redirect)

Use to send a temporary or permanent redirect to a new URL.

```
$app->redirect('/go/here'); // HTTP 302
$app->redirect('/new/permanent/location', true); // HTTP 301
```

### `end()`

[](#end)

Use to return immediately with an optional HTTP status code.

```
$app->end(); // Defaults to 200 = Success/OK
$app->end(401); // Unauthorized
$app->end(403); // Forbidden
$app->end(404); // Not Found
```

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance86

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity63

Established project with proven stability

 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

Every ~24 days

Recently: every ~40 days

Total

36

Last Release

73d ago

Major Versions

3.8.4 → 4.0.02025-08-19

PHP version history (2 changes)3.0.1PHP &gt;=8.3.0

3.1.1PHP &gt;=8.1.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/8702d713294f280b05c35791ea2604a52b0052f2cd2c9ecce2d6f85cf4fe5557?d=identicon)[ginger-tek](/maintainers/ginger-tek)

---

Top Contributors

[![ginger-tek](https://avatars.githubusercontent.com/u/38133930?v=4)](https://github.com/ginger-tek "ginger-tek (83 commits)")

---

Tags

routerroutingtemplatingapirestrouterroutingtemplatingmpa

### Embed Badge

![Health badge](/badges/ginger-tek-routy/health.svg)

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

###  Alternatives

[contributte/api-router

RESTful Router for your Apis in Nette Framework - created either directly or via attributes

20809.0k3](/packages/contributte-api-router)[aphiria/aphiria

The Aphiria framework

1428.0k2](/packages/aphiria-aphiria)[lukasdev/drouter

Um sistema simplista de roteamento, com o intuito de ser utilizado em aplicacoes web pequenas e webservices REST

172.6k1](/packages/lukasdev-drouter)

PHPackages © 2026

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