Search by

mathsgod / light

mathsgod

A lightweight PHP GraphQL framework

Package info

github.com/mathsgod/light

pkg:composer/mathsgod/light

Statistics

Installs: 353

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 1

v1.44.1 2026-09-18 07:10 UTC

README

Ask DeepWiki

light

A lightweight PHP 8.3+ GraphQL backend framework for building admin/CMS applications. Exposes a GraphQL API (plus a few REST endpoints for file serving) backed by a custom ORM (mathsgod/light-db), RBAC, and a PSR-15 middleware pipeline.

The companion frontend module is nuxt-light — a Nuxt 4 module using Quasar UI.

Requirements

  • PHP >= 8.3
  • Composer
  • MySQL / MariaDB (or any Laminas DB-supported database)

Installation

composer install

Configuration

All configuration is read from a .env file in the project root.

Database

DATABASE_HOSTNAME=
DATABASE_DATABASE=
DATABASE_USERNAME=
DATABASE_PASSWORD=
DATABASE_PORT=
DATABASE_CHARSET=

JWT signing

HS256 remains the default for backwards compatibility:

JWT_ALGORITHM=HS256
JWT_SECRET=replace-with-a-random-secret

For an Auth API that issues tokens to other services, use RS256. Only the Auth API receives the private key:

JWT_ALGORITHM=RS256
JWT_PRIVATE_KEY_PATH=/run/secrets/light-jwt-private.pem
JWT_PUBLIC_KEY_PATH=/etc/light/light-jwt-public.pem
JWT_KEY_ID=auth-2026-09
JWT_ISSUER=https://auth.example.com
JWT_AUDIENCE=auth-api
JWT_AUDIENCES_PATH=/path/to/project/audiences.yml
JWT_RESET_SECRET=replace-with-a-separate-random-secret

JWT_PUBLIC_KEY_PATH is optional because Light can derive the public key from the private key. JWT_AUDIENCE identifies tokens accepted by this Auth API and is required before issuing tokens for other audiences. JWT_RESET_SECRET protects password-reset verification codes and is required in RS256 deployments that do not retain the legacy JWT_SECRET.

Generate an RSA key pair, for example:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

When RS256 is enabled, Light publishes the public key at:

GET /.well-known/jwks.json

JWTs include the configured kid header, allowing consumers to select the matching public key. The private key is never included in the JWKS response.

Audience-scoped access tokens

Define which permissions may be issued to each API in audiences.yml. JWT_AUDIENCES_PATH may point to an explicit configuration file outside the package directory. If unset, Light checks audiences.yml in the current project directory before falling back to the bundled file:

auth-api:
  permissions:
    - '*'

business-api:
  permissions:
    - order.*
    - customer.*

infra-api:
  permissions:
    - server.*
    - deployment.*

An authenticated user can request a short-lived token for a registered audience with the createAudienceAccessToken GraphQL mutation:

mutation {
  createAudienceAccessToken(audience: "business-api")
}

The token contains only the intersection of the user's permissions and the patterns allowed for that audience. Unknown audiences are rejected, internal permissions beginning with # are omitted, and unclassified permissions are not issued. An administrator's global * is reduced to the requested audience's configured patterns.

Timezone

TZ=Asia/Hong_Kong

Google Sign-In (optional)

Install the Google API client:

composer require google/apiclient

Then set:

GOOGLE_CLIENT_ID=

Other optional settings

API_PREFIX=       # URL prefix for the GraphQL endpoint
CORS=             # Allowed CORS origin domain

Development Server

# Linux / macOS
sh run.sh

# Windows
run.bat

Both start php -S 0.0.0.0:8888 router.php.

Database Schema

Initialize the database schema (defined in db.json):

php bin/light db:install

CLI Scaffolding

php bin/light make:controller Name   # Generate a GraphQL controller
php bin/light make:model Name        # Generate an ORM model
php bin/light make:input Name        # Generate a GraphQL input type
php bin/light make:ts                # Generate TypeScript definitions from the schema

Architecture

index.php → Light\App::run()
  → Middleware pipeline (CORS, JWT auth, file upload)
  → Router:
      GET  /fs/{protocol}/{path}   — Flysystem file serving
      GET  /drive/{index}/{path}   — Drive/storage access
      POST /refresh_token          — JWT token refresh
      *                            — GraphQL execution

Schema generation is annotation-driven via TheCodingMachine/GraphQLite. Controllers in src/Controller/ declare queries and mutations using PHP 8 attributes (#[Query], #[Mutation], #[Type], etc.).

ORM — models live in src/Model/ and extend Light\Model. The schema is defined in db.json. save() and delete() auto-populate audit fields (created_time, updated_time, created_by, updated_by) and write to EventLog.

RBAC — role → permission mappings bootstrap from permissions.yml; menus bootstrap from menus.yml. The Administrators role always has * (wildcard) permission.

File storage — file operations go through Light\Drive (Flysystem MountManager). Supported adapters: Local, AWS S3, Aliyun OSS, Hostlink.

Directory Layout

Path Purpose
src/Controller/ GraphQL controllers (queries & mutations)
src/Model/ ORM models, extend Light\Model
src/Input/ GraphQL input types for mutations
src/Type/ GraphQL output types
src/Command/ Symfony Console CLI commands
src/Auth/ JWT auth & authorization logic
src/Drive/ Flysystem drive abstraction
function/ Global helper functions (auto-loaded)
pages/ Optional plain-PHP pages (return JSON)
db.json Database schema definition
menus.yml Hierarchical menu definitions
permissions.yml Role → permission bootstrap mappings

Authentication Flow

  1. Login via GraphQL mutation → returns access_token (short-lived JWT) + refresh_token
  2. Subsequent requests send Authorization: Bearer <access_token>
  3. Token refresh: POST /refresh_token
  4. Optional 2FA (TOTP) and WebAuthn supported via src/Security/

Testing

./vendor/bin/phpunit --no-coverage                          # Run all tests
./vendor/bin/phpunit --no-coverage tests/SomeTest.php       # Single test file
./vendor/bin/phpunit --no-coverage --filter testMethodName  # Single test method
./vendor/bin/phpstan analyse src/                           # Static analysis

Tests require a real DB connection (integration tests). Each test wraps in a DB transaction and rolls back in tearDown().

License

MIT