Skip to content

Repository files navigation

petstore

CI Coverage Status Mutation testing badge

bugs code_smells coverage duplicated_lines_density ncloc sqale_rating alert_status reliability_rating security_rating sqale_index vulnerabilities

Description

A simple skeleton to build api's based on the chubbyphp-framework.

Requirements

Environment

Add the following environment variable to your system, for example within ~/.bashrc or ~/.zshrc:

export USER_ID=$(id -u)
export GROUP_ID=$(id -g)

Mount points

Creates every file which gets mounted into the php container (shell rc/history, git, ssh, npm and the coding agent auth/settings files) without overwriting existing ones. Adjust the seeded settings files afterwards to your liking, they stay on the host and get mounted.

./setup-mount-points.sh

Coding agents

The following coding agents (harnesses) are preinstalled within the php container, their auth and settings files get mounted from the host (see docker-compose.yml):

  • Claude Code: ~/.claude.json, ~/.claude/.credentials.json, ~/.claude/settings.json
  • Codex: ~/.codex/auth.json, ~/.codex/config.toml
  • Opencode: ~/.config/opencode/opencode.jsonc, ~/.config/opencode/tui.json, ~/.local/share/opencode/auth.json
  • PI incl. pi-llama: ~/.pi/agent/auth.json

llama.cpp

PI can run against a local model via pi-llama, start a llama.cpp server on the host, for example:

llama-server \
    -hf lmstudio-community/Qwen3.6-35B-A3B-GGUF:Q4_K_M \
    -c 32768 \
    -ngl 999 \
    --flash-attn on \
    --host 0.0.0.0 \
    --port 9931

Docker

docker compose up -d
docker compose exec php bash

Setup

composer install
composer setup:dev

Urls

Pet (oidc protected)

Database

psql "postgresql://petstore:4aAUfBjDACcdZxNwJgJ6@localhost:5432/petstore"

Oidc (keycloak)

All routes below /api are protected by chubbyphp/chubbyphp-oidc, only /ping and /openapi are public. The keycloak container acts as the identity provider, the realm petstore gets imported from docker/development/keycloak/import/petstore-realm.json on startup (delete and recreate the keycloak container to reimport after changes) and contains two users:

  • john.doe (password: johndoe1234): a regular end user, meant to log in via the browser based frontend (petstore-frontend client, see below).
  • petstore (password: GBanBPatEBRZ7hf7cAxKn8Ptt): a technical user for requesting tokens via password grant while testing (see the curl example below).

and two clients:

  • petstore-frontend: public client for a separate (browser based) frontend codebase, which authenticates against keycloak via authorization code flow + PKCE (S256) and sends the resulting access token as Authorization: Bearer <token> header to this api. The cors setup allows the Authorization header for localhost origins in development.
  • petstore (secret: 5FbFAgTAWyVAWSQtDPqCLZzY): confidential client for backend integrations and for requesting tokens via password grant while testing.

Both clients use an audience mapper, so that the access token contains aud: petstore, which this api requires.

Admin console: http://keycloak:8080 (admin / TCUJyCbLtLbBc4eXYYzD9ecm). Keycloak is configured with the fixed hostname keycloak, so that the issuer claim is always http://keycloak:8080/realms/petstore; requests via http://localhost:8080 get redirected to that hostname. Add 127.0.0.1 keycloak to /etc/hosts on the host to use the admin console or to request tokens from the host:

ACCESS_TOKEN=$(curl -s http://keycloak:8080/realms/petstore/protocol/openid-connect/token \
  -d 'grant_type=password' \
  -d 'client_id=petstore' \
  -d 'client_secret=5FbFAgTAWyVAWSQtDPqCLZzY' \
  -d 'username=petstore' \
  -d 'password=GBanBPatEBRZ7hf7cAxKn8Ptt' | sed -E 's/.*"access_token":"([^"]+)".*/\1/')

curl --insecure -H "Authorization: Bearer ${ACCESS_TOKEN}" -H 'Accept: application/json' https://localhost/api/pets

The integration tests run against keycloak as well (no auth mocking): tests/Helper/AuthHelper.php waits for the discovery endpoint of OIDC_ISSUER to be reachable and requests tokens via password grant with the petstore client and user. Within the php container keycloak is reachable as keycloak, in ci a keycloak container gets started and published on the docker bridge gateway (see .github/workflows/ci.yml).

Structure

Collection

Collections are sortable, filterable paginated lists of models.

Dto

A DTO, or Data Transfer Object, is a simple object used to transport data between software application components.

Model

Models, entities, documents what ever fits your purpose the best.

ORM

ORM Mapping definitions.

Parsing

Parses and validates data against predefined schemas, ensuring that incoming data conforms to expected structures and criteria.

Repository

Repositories get data from storages like databases, elasticsearch, redis or whereever your models are stored or cached.

RequestHandler

RequestHandler alias Controller, or Controller actions to be more precise. There is a directory with generic crud controllers. If you like the idea adapt them for your generic use case, if not drop them. I highly recommend to not extend them.

ServiceFactory

Service factories are the glue code of the dependeny injection container.

Opensearch

Policy to delete logstash formatted indicies after 14 days.

curl -XPUT 'https://localhost:9200/_plugins/_ism/policies/logstash-policy' \
    -u 'admin:98T722Eqw99oqFCSJCnB' \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -d '{
      "policy": {
        "description": "Logstash",
        "default_state": "hot",
        "states": [
          {
            "name": "hot",
            "actions": [],
            "transitions": [
              {
                "state_name": "delete",
                "conditions": {
                  "min_index_age": "14d"
                }
              }
            ]
          },
          {
            "name": "delete",
            "actions": [
              {
                "delete": {}
              }
            ]
          }
        ],
        "ism_template": {
          "index_patterns" : ["logstash-*"],
          "priority": 100
        }
      }
    }' \
    --insecure

Dashboard

Before you start, produce at least one error, produce a 404.

Create Index Pattern

  • Username: admin
  • Password: 98T722Eqw99oqFCSJCnB
  • Index pattern name: logstash-*
  • Time field: @timestamp

Discover

Copyright

2026 Dominik Zogg

About

A simple skeleton to build api's based on the chubbyphp-framework, mezzio (former zend-expressive) or slim.

Topics

Resources

Stars

38 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages