Developer preview 0.3MIT licensedPHP 8.3+

The typed, long-running PHP framework for SaaS backends.

Compiled before deploy. Secure by default. Built to run for years.

composer require el-hendawy/keel

01

Types are the contract.

One input class is the validation, the OpenAPI documentation and the client SDK. Send the wrong shape and you get every error at once, with its path.

src/Orders/CreateOrder.phpendpoint
#[Post('/orders')]
#[Status(201)]
#[Authorize(scopes: ['orders:write'])]
final class CreateOrder
{
public function __invoke(CreateOrderInput $in, Orders $orders): OrderResource
{
return OrderResource::from($orders->create($in));
}
}
src/Orders/CreateOrderInput.phpinput
final readonly class CreateOrderInput
{
public function __construct(
#[Uuid] public string $merchantId,
/** @var list<LineItem> */
#[Count(min: 1)] public array $items,
) {}
}
POST /ordersrequest
{"merchantID": "x", "items": []}
422 Unprocessable Contentautomatic
{
"status": 422,
"errors": [
{"path": "merchantID", "code": "unknown_field"},
{"path": "merchantId", "code": "required"},
{"path": "items", "code": "count"}
]
}
unknown
Fields the class does not declare are refused, not ignored.
required
A non-nullable property with no default must be sent.
count
Constraints are attributes on the same property.

02

What is in the box.

03

Performance.

A 10 ms PostgreSQL query per request, 512 connections, 8 processes.
Serverreq/s
keel run, non-blocking driver17,027
FrankenPHP, 64 workers5,734

3×

the throughput of 64 FrankenPHP workers when requests wait on the database, in about the same memory (244 MB).

Method and caveats
One worker, in-process, PHP 8.3, Apple silicon.
Requestreq/s
Health check51,600
Authenticated read23,400
Validation error29,700
Create order, with transaction13,350

2×–6.3×

more requests per second than Laravel Octane, end to end on PostgreSQL (v0.1).

Method and caveats

04

A stolen token is worth nothing.

Tokens are bound to the client that holds them. Copy one to another machine and Keel refuses it, then revokes it.

  1. 1

    Issued

    Client A signs in and receives a token.

    token · active
  2. 2

    Bound to client

    The token only works from client A.

    token · client A
  3. 3

    Replayed

    Someone copies it and calls the API from client B.

    token · from client B
  4. 4

    Refused and revoked

    The request is refused. The token is revoked for everyone.

    token · revoked

05

Five-minute start.

Install the keel command, create an app, serve it. Then follow the tutorial from your first endpoint to production.

Start the tutorial
terminal
$ composer global require el-hendawy/keel
$ keel new acme && cd acme && composer install
$ keel serve