Introduction

Kipchak CLI

The Kipchak command-line tool creates a project, adds any driver or middleware in one command, generates code in the starter's conventions and validates the OpenAPI document against the routes.

The Kipchak CLI creates a project, adds drivers and middleware to it, writes resources, endpoints and middleware in the starter's conventions, and checks the OpenAPI document against the routes. It writes plain code into files your project owns, so the API never depends on it at run time: remove it and nothing changes.

Installation

Projects created from starter 4.2 or later already have it, as a development dependency:

vendor/bin/kipchak list

In an existing project, add it with Composer:

composer require --dev kipchak/cli

To create projects with it, install it globally:

composer global require kipchak/cli

The CLI needs PHP 8.4 or later and works with projects on kipchak/core 3.

kipchak new

kipchak new my-api

Creates a project from the starter, as composer create-project kipchak/starter my-api does, and prints the next steps. --starter-version chooses a starter version. A project created this way is identical to one created with Composer.

kipchak add

kipchak add doctrine

Adds a Kipchak driver or middleware to the project:

  1. installs the package with composer require;
  2. copies its sample configuration into config/, never over a file that already exists;
  3. initialises it in drivers/drivers.php or middlewares/middlewares.php, in the position its documentation requires;
  4. lists any settings you must supply, such as API keys.

A driver often needs other Kipchak drivers. The Anthropic driver, for example, uses the HTTP driver. add initialises those too, before the package that needs them, and copies their configuration, so the API works on its first request:

$ kipchak add anthropic
Created config/kipchak.http.php
Created config/kipchak.anthropic.php
Modified drivers/drivers.php
kipchak/driver-http: https://kipchak.dev/docs/driver-http
kipchak/driver-anthropic: https://kipchak.dev/docs/driver-anthropic
Set these before using it:
  config/kipchak.anthropic.php: ANTHROPIC_API_KEY

A driver that is already initialised is left as it is.

Names

doctrine, driver-doctrine and kipchak/driver-doctrine all name the Doctrine driver. A few names are both a driver and a middleware, such as feature-flags and opentelemetry; give those in full, as driver-feature-flags or middleware-feature-flags. Every package is listed on the ecosystem page.

Enterprise packages install the same way, with your project's own Composer credentials for the Enterprise repository.

Where middleware goes

Middleware is placed between the two Error::initialise($app) lines of middlewares/middlewares.php (see Middleware), in the order each package documents. Metering and Quotas, for example, go before the authentication middleware, so that they run after it and see who the caller is. The firewall goes after it, so that it runs first. If the order you have already written cannot satisfy a package, add changes nothing and tells you the line to add by hand.

Each package declares its configuration file, its initialise() line and its order in its own composer.json, under extra.kipchak, so the same information is available to any tool.

Previewing changes

kipchak add doctrine --dry-run

shows every change as a diff and writes nothing. For a package that is not installed yet, a dry run says so and runs no Composer command: its configuration and position are known only once the package is installed.

Generating code

The make: commands write code in the starter's conventions, into your project's own namespaces: DTOs that carry their OpenAPI schema, thin controllers whose attributes describe each operation, and route files with one group per API version. What they write is ordinary code for you to change; nothing is generated again later. Each accepts --dry-run, and --api-version (v1 by default) chooses the version folder.

make:resource

kipchak make:resource Orders --crud --fields=id:int,reference:string,total:float,placedAt:datetime,note:?string

Writes a resource the way the starter's Sultans is written:

Created api/DataTransferObjects/v1/Order.php
Created api/Models/Orders.php
Created api/Controllers/v1/Orders.php
Created routes/v1/orders.php
Created tests/OrdersTest.php

The controller has list and get, and with --crud also create, update and delete, which read the request body once and validate it by mapping it to the DTO. The model holds two example rows built from the fields, so the endpoints answer, and the tests pass, before any storage exists. Replace them with a driver, such as Doctrine, when you choose where the data lives.

The DTO's name is the singular of the resource (Order for Orders); --dto and --singular choose others. A resource needs id:int among its fields.

Fields

--fields takes name:type pairs, separated by commas:

TypePHPOpenAPI
intintinteger
floatfloatnumber
stringstringstring
boolboolboolean
datetimestringstring, format: date-time, an ISO 8601 example
AddressAddressa reference to the Address schema
LineItem[]array of LineIteman array of references

?type makes a field nullable. Each property gets a description and, unless it refers to another DTO, an example, for you to replace with your own.

make:dto and make:controller

kipchak make:dto Address --fields=street:string,city:string,postcode:?string
kipchak make:controller Reports

make:dto writes a DTO alone; make:controller writes a controller with its OpenAPI tag and no operations, ready for make:endpoint.

make:endpoint

kipchak make:endpoint POST '/v1/orders/{id:[0-9]+}/cancel' Orders::cancel

Adds an operation to an existing controller: the method, with its OpenAPI attribute, and its route. The path starts with the API version. The route goes into the version's group in the route file that already routes the controller, or into routes/v1/orders.php, which is created if it does not exist. A parameter constrained to digits, as {id:[0-9]+}, is documented as an integer; any other as a string. --dto=Order refers the body and the response to the Order schema.

The new method answers 501 Not Implemented until you write it, so an unfinished endpoint never returns data that does not match its schema.

make:middleware

kipchak make:middleware RequireRole --global

Writes api/Middlewares/RequireRole.php, a PSR-15 middleware. With --global, it is also added for every request in middlewares/middlewares.php, between the two Error::initialise($app) lines, as kipchak add places a package. Without it, add the middleware to a route or a group yourself.

Changing existing files

make:endpoint and make:middleware --global change files you already have, and only by adding lines: the method above the controller's closing brace, the imports it needs in alphabetical place, the route as the last line of its group. Every other line stays as it was, including its line endings. If a file does not have the shape the CLI expects (a route file with no group for the version, or more than one), it is left alone and the line to add is printed instead.

kipchak routes

kipchak routes

Lists the routes the application registers, as Slim sees them, with the controller method each calls:

GET     /v1/orders                     Api\Controllers\v1\Orders::list
GET     /v1/orders/{id:[0-9]+}         Api\Controllers\v1\Orders::get
POST    /v1/orders/{id:[0-9]+}/cancel  Api\Controllers\v1\Orders::cancel
GET     /status                        Api\Controllers\Status::get
GET     /openapi.json                  Api\Controllers\OpenApi::get

kipchak openapi:validate

kipchak openapi:validate

Checks the OpenAPI document your API generates, against the OpenAPI specification and against the routes the application registers:

CheckSeverityWhat it checks
OAS-001ErrorThe document is valid against the OpenAPI JSON Schema for its version, 3.0 or 3.1.
OAS-002Errorswagger-php reported no warning while generating it.
OAS-003ErrorEvery $ref resolves.
KIP-001ErrorEvery route has an operation, and every operation has a route.
KIP-002ErrorEvery path parameter of a route is declared on its operation.
KIP-003ErrorEvery operation has a unique operationId, a summary and a tag.
KIP-004WarningRequest and response bodies refer to component schemas instead of defining objects inline, apart from the response envelope (code, status, data).
KIP-005WarningEvery property of a component schema has a description and an example.

Each finding names its check and the path, operation or schema concerned:

KIP-002 ERROR GET /v1/orders/{id}: The path parameter id is not declared as a parameter of the operation.
KIP-005 WARNING schema Order, property note: The property has no example.
1 error, 1 warning.

It exits with 1 when there is an error, and --strict makes a warning fail too, so the command can gate a release in CI. --no-routes checks the document without booting the application. The route that serves the document, /openapi.json, is not expected in it; --ignore-route="GET /internal/health" (repeatable) leaves out others.

The CLI generates the document and boots the application in a separate PHP process, with your project's own autoloader, so the check uses exactly the swagger-php, Slim and driver versions your API runs. It calls Api\Utils\OpenApi::generate(), which the starter has, or scans api/ when a project has no such class. Reading the routes boots the application as a request would, so its drivers and their configuration must load.

kipchak project

kipchak project

Shows the project the CLI has found (the nearest directory above the current one whose composer.json requires kipchak/core), its kipchak/core version and its namespaces.

Options and exit codes

Every command accepts --format=json, which writes one JSON document to standard output, and --project-dir, to work on a project other than the current directory's. Commands that write files accept --dry-run. Composer's own output goes to standard error.

Exit codeMeaning
0The command did what was asked.
1It found a problem, such as a file that already exists, an unknown package or an error in the OpenAPI document. Nothing was written.
2It could not run: no Kipchak project was found, dependencies are not installed, or the core version is not supported.

The commands that write files return the changes, messages and settings. For kipchak add --format=json:

{
    "changes": [
        {"path": "config/kipchak.anthropic.php", "action": "create", "content": "...", "diff": "..."}
    ],
    "messages": ["kipchak/driver-anthropic: https://kipchak.dev/docs/driver-anthropic"],
    "settings": [{"file": "config/kipchak.anthropic.php", "key": "ANTHROPIC_API_KEY"}],
    "written": true
}

Fields are added in later versions but never removed or renamed within a major version.

Source

The CLI is MIT-licensed. Its source is at https://1x.ax/mamluk/kipchak/cli, mirrored to https://github.com/kipchak-dev/cli.

Previous
Hardened Runtime Image