VOOZH about

URL: https://deepwiki.com/mathsgod/light/4-graphql-api

⇱ GraphQL API | mathsgod/light | DeepWiki


Loading...
Last indexed: 31 January 2026 (cf9511)
Menu

GraphQL API

Purpose and Scope

This document describes the GraphQL API layer in the Light framework, which provides the primary programmatic interface for client applications. The GraphQL API uses a code-first approach powered by the GraphQLite library, generating the schema automatically from PHP annotations and attributes.

This page covers:

  • Schema generation and the type system
  • Available queries and mutations through root entry points
  • Authorization enforcement via annotations
  • Request processing pipeline

For details on specific controllers and their operations, see Controllers. For authentication flow details, see Authentication Architecture. For the overall request lifecycle including middleware, see Request Lifecycle.


Architecture Overview

The Light framework implements a code-first GraphQL API where the schema is automatically generated from PHP class annotations rather than being defined in GraphQL SDL files. This approach provides type safety, IDE support, and keeps API definitions co-located with implementation code.

Core GraphQL Components


Sources: src/App.php121-130 src/App.php529-530

The SchemaFactory is initialized during application bootstrap and configured with:

  • Namespace scanning for type discovery
  • Custom type mappers for database models
  • Authentication and authorization services
  • Caching configuration based on dev/prod mode

Schema Generation Process

Initialization and Configuration

The GraphQL schema is generated during application initialization through a multi-step process:


Sources: src/App.php121-127 src/App.php529-530

The schema factory setup occurs in the Light\App constructor:

Configuration StepCode ReferencePurpose
Create GraphQL Serversrc/App.php121Initializes with cache lifetime (15s dev, 0s prod)
Get Cache Instancesrc/App.php123PSR-16 cache for schema storage
Get Schema Factorysrc/App.php124GraphQLite factory instance
Add Namespacesrc/App.php125Scan "Light" namespace for types
Add Type Mappersrc/App.php126Custom mapper for Light\Db models
Set Auth Servicessrc/App.php529-530Inject authentication context

Annotation-Based Type System

The GraphQL schema is defined using PHP attributes (annotations) on classes and methods:


Sources: src/Type/App.php28 src/Type/App.php32-36 src/Type/App.php186-194

Example from Light\Type\App showing annotation usage:

AnnotationUsageLocation
#[Type]Declares the class as a GraphQL typesrc/Type/App.php28
#[Field]Exposes a method as a GraphQL fieldsrc/Type/App.php32
#[Logged]Requires user to be authenticatedsrc/Type/App.php33
#[Right("permission")]Requires specific permissionsrc/Type/App.php343
#[Autowire]Injects dependency from containersrc/Type/App.php191
#[InjectUser]Injects authenticated usersrc/Type/App.php461

Schema Caching Strategy

The framework employs different caching strategies based on environment mode:

EnvironmentCache LifetimeSchema RegenerationCode Reference
Development (mode != "prod")15 secondsEvery 15s or on cache misssrc/App.php113
Production (mode == "prod")0 seconds (indefinite)Only on cache clearsrc/App.php111

The cache lifetime is determined during initialization:

if ($this->mode === "prod") {
 $defaultLifetime = 0; // Cache indefinitely
} else {
 $defaultLifetime = 15; // Re-scan every 15 seconds
}

Sources: src/App.php104-119


Root Query Type: Light\Type\App

The primary GraphQL entry point is the Light\Type\App class, which serves as the root query type exposing system-level queries and configuration access.

System Query Fields


Sources: src/Type/App.php28-544

Authentication and User Context Queries

FieldReturn TypeAuthenticationAuthorizationPurpose
getAuthAuthNoNoneReturns authentication status and OAuth configuration
isLoggedBooleanNoNoneChecks if current user is authenticated and has base roles
getMenus[Menu]Yes (@Logged)NoneReturns menu structure filtered by user permissions
getPermissions[String]Yes (@Logged)NoneLists all permissions discovered in the system

Sources: src/Type/App.php93-96 src/Type/App.php331-337 src/Type/App.php197-202 src/Type/App.php186-194

The getMenus field implements hierarchical permission filtering:

Filter Logic:
1. Iterate through menu tree
2. For each menu item with "permission" property:
 - If permission starts with "#", check role membership
 - Otherwise, check if any user role has the permission via RBAC
3. Recursively filter children
4. Remove parent items with no accessible children

Sources: src/Type/App.php204-261


Configuration and Feature Queries

FieldReturn TypeAuthenticationAuthorizationPurpose
getVersionStringNoNoneReturns Light framework version from composer
isDevModeBooleanNoNoneChecks if Config.mode != "prod"
isTwoFactorAuthenticationBooleanNoNoneChecks if 2FA is enabled in config
isWebAuthnEnabledBooleanNoNoneChecks if WebAuthn/FIDO2 is enabled
hasBioAuthBooleanNoNoneChecks if webauthn-lib is installed
getCompanyStringNoNoneReturns company name from config (default: "HostLink")
getCompanyLogoString?NoNoneReturns company logo URL from config

Sources: src/Type/App.php109-112 src/Type/App.php181-184 src/Type/App.php175-178 src/Type/App.php288-291 src/Type/App.php168-172 src/Type/App.php301-304 src/Type/App.php313-316

These queries are typically used during application bootstrap on the frontend to configure the UI.


Data Listing Queries

The Light\Type\App class provides several list queries that support filtering and sorting:

FieldReturn TypeRequired PermissionFilter SupportSort Support
listUserLight\Db\Queryuser.listYesYes
listConfigLight\Db\Queryconfig.listYesYes
listEventLogLight\Db\Queryeventlog.listYesYes
listMailLogLight\Db\Querymaillog.listYesYes
listUserLogLight\Db\Queryuserlog.listYesYes
listPermissionLight\Db\Querypermission.listYesYes
listSystemValueLight\Db\Querysystemvalue.listYesYes

Sources: src/Type/App.php460-473 src/Type/App.php442-451 src/Type/App.php422-439 src/Type/App.php412-420 src/Type/App.php522-532 src/Type/App.php534-543 src/Type/App.php510-520

All list queries return Light\Db\Query objects which support:

  • Filter expressions: $filters = ["username" => ["contains" => "john"]]
  • Sort specifications: $sort = "created_time DESC"
  • Lazy evaluation and pagination

The listUser query includes special logic to prevent non-administrators from listing administrators:

if (!$user->is("Administrators")) {
 $q->where("user_id NOT IN (SELECT user_id FROM UserRole WHERE role = 'Administrators')");
}

Sources: src/Type/App.php466-470


File System Access Queries

FieldReturn TypeAuthenticationParametersPurpose
fsFilesystemYes (@Logged)NoneReturns filesystem operations interface
getDriveDriveYes (@Logged)index: Int = 0Returns specific drive by index
getDrives[Drive]Yes (@Logged)NoneReturns all configured drives
getDriveTypes[String]Yes (@Logged)NoneLists available storage backend types
listFileSystemmixedNoNoneLists filesystem configurations (requires filesystem.list permission)

Sources: src/Type/App.php32-37 src/Type/App.php386-393 src/Type/App.php395-409 src/Type/App.php362-384 src/Type/App.php500-508

The getDriveTypes field dynamically discovers available storage backends by checking installed composer packages:

Storage TypeRequired PackageCheck Location
localAlways availableN/A
s3league/flysystem-aws-s3-v3src/Type/App.php372-374
hostlinkhostlink/hostlink-storage-adaptersrc/Type/App.php376-378
aliyun-ossalphasnow/aliyun-oss-flysystemsrc/Type/App.php380-382

Controller-Based Mutations

While Light\Type\App provides the root query type, mutations are primarily organized in controller classes within the Light\Controller namespace. Each controller focuses on a specific domain.

Controller Registration

Controllers are registered in the dependency injection container during Light\App initialization:


Sources: src/App.php75-97

The registration code shows all controllers added to the container:

Controller ClassDomainRegistration Line
AuthControllerAuthenticationsrc/App.php77
AppControllerConfigurationsrc/App.php75
FileSystemControllerFile operationssrc/App.php93
RevisionControllerAudit trailsrc/App.php94
UserControllerUser managementsrc/App.php78
RoleControllerRole managementsrc/App.php79
ConfigControllerConfig updatessrc/App.php83
PermissionControllerPermissionssrc/App.php82
WebAuthnControllerBiometric authsrc/App.php90
TranslateControllerInternationalizationsrc/App.php89
CustomFieldControllerDynamic schemasrc/App.php97

For detailed documentation of each controller's operations, see:


Authorization Enforcement

The GraphQL API integrates authorization at multiple levels using annotations and the RBAC system.

Authorization Annotation Hierarchy


Sources: src/App.php529-530

Authentication Check: @Logged

The @Logged annotation requires a user to be authenticated before accessing a field:

#[Field]
#[Logged]
public function getMenus(#[InjectUser()] User $user, #[Autowire] LightApp $app)
{
 return $this->filterMenus($app->getMenus(), $app->getRbac(), $user->getRoles());
}

Sources: src/Type/App.php197-202

When @Logged is present:

  1. GraphQLite checks the AuthenticationService (which is Auth\Service)
  2. Calls isLogged() method
  3. If false, throws authentication error before method execution
  4. If true, proceeds to execute method (and checks @Right if present)

Permission Check: @Right

The @Right annotation requires specific permissions checked through the RBAC system:

#[Field]
#[Logged]
#[Right("user.list")]
public function listUser(#[InjectUser] \Light\Model\User $user, $filters = [], ?string $sort = "")
{
 // Implementation
}

Sources: src/Type/App.php460-473

When @Right("permission") is present:

  1. GraphQLite checks the AuthorizationService (which is Auth\Service)
  2. Calls isAllowed("permission") method
  3. Auth\Service delegates to Rbac system to check user's roles
  4. Permission is checked against role hierarchy and user-specific permissions
  5. If denied, throws authorization error
  6. If allowed, method executes

Permission Discovery

The framework discovers all permissions at runtime by scanning for @Right annotations:


Sources: src/App.php392-472

The permission discovery process:

StepCode ReferencePurpose
Scan Controllerssrc/App.php395-403Find @Right in Light\Controller\*
Scan Modelssrc/App.php405-413Find @Right in Light\Model\*
Scan Typessrc/App.php425-433Find @Right in Light\Type\*
Scan Databasesrc/App.php415-423Find @Right in Light\Database\*
Scan Custom Namespacessrc/App.php435-451Find @Right in custom Controller/Model namespaces
Extract Menu Permissionssrc/App.php453-455Get permissions from menu definitions
Add RBAC Permissionssrc/App.php458-460Include permissions from RBAC system
Filter Role Markerssrc/App.php463-465Remove permissions starting with "#"

This comprehensive discovery ensures the system has a complete inventory of all permissions for management and UI display purposes.


Request Processing Flow

End-to-End GraphQL Request


Sources: src/App.php500-537 src/App.php154-170 src/App.php567-579

Query Execution Steps

PhaseMethodResponsibilitiesCode Reference
1. Request PreprocessingApp::process()Parse body, handle uploads, initialize authsrc/App.php500-537
2. Auth Context Setupnew Auth\Service()Extract JWT, validate, load usersrc/App.php526
3. Factory ConfigurationsetAuthenticationService()Inject auth into GraphQLitesrc/App.php529-530
4. Schema CreationFactory::createSchema()Load from cache or generatesrc/App.php577
5. Query ExecutionGraphQL::executeQuery()Parse and execute GraphQLsrc/App.php578
6. Authorization ChecksBefore resolverCheck @Logged and @RightN/A (handled by GraphQLite)
7. Resolver InvocationController methodExecute business logicVaries by resolver
8. Response FormattingApp::handle()Format result, add debug info if devsrc/App.php154-170

Request Body Parsing

The framework handles two types of GraphQL request bodies:

// Standard GraphQL POST
{
 "query": "query { app { isLogged } }",
 "variables": { ... }
}

// Multipart upload (for file uploads)
Content-Type: multipart/form-data
operations: {"query": "mutation($file: Upload!) { ... }", "variables": {...}}
map: {"0": ["variables.file"]}
0: [file data]

The body parsing logic:

StepCodePurpose
Check parsed bodysrc/App.php508-511If not parsed by middleware, decode JSON manually
Process uploadssrc/App.php513-514Use UploadMiddleware to handle multipart uploads
Extract query & variablessrc/App.php573-574Parse from request body

Sources: src/App.php508-514 src/App.php567-574


Response Format

Standard Response Structure

GraphQL responses follow the standard format:


Debug Mode Enhancement

When in development mode (Config.mode != "prod"), responses include additional debug information:

Debug FlagIncluded InformationCode Reference
INCLUDE_DEBUG_MESSAGEDetailed error messagessrc/App.php161
INCLUDE_TRACEStack traces for errorssrc/App.php161
JSON_UNESCAPED_UNICODEPreserve Unicode characterssrc/App.php161-163

The debug flag logic:

if ($this->isDevMode()) {
 return new JsonResponse(
 $result->toArray(DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE),
 200,
 [],
 JsonResponse::DEFAULT_JSON_FLAGS | JSON_UNESCAPED_UNICODE
 );
}

Sources: src/App.php160-164

This ensures sensitive error details (like stack traces and internal paths) are not exposed in production environments while providing helpful debugging information during development.


Type Mapping and Custom Scalars

The GraphQL schema generation includes custom type mappers to bridge between PHP types and GraphQL types, particularly for database models.

Type Mapper Factory Registration


Sources: src/App.php126

The type mapper factory is registered during app initialization:

$this->factory->addTypeMapperFactory(new \Light\Db\GraphQLite\Mappers\TypeMapperFactory);

This allows database models extending Light\Db\Model to be automatically exposed as GraphQL types without manual schema definition, maintaining synchronization between database schema and GraphQL schema.

Common Output Types

Several fields use special output type declarations:

Output TypeUsagePurposeExample Field
mixedDynamic/untypedFor configuration objects with varying structuresrc/Type/App.php39 generate2FASecret
[mixed]Array of dynamicFor flexible arrayssrc/Type/App.php58 getCustomFieldSchema
[String]String arrayFor list of stringssrc/Type/App.php82 getCustomFieldModels

These type declarations provide flexibility for fields that don't conform to strict GraphQL object types while maintaining type safety where possible.


Summary

The Light framework's GraphQL API provides a comprehensive, type-safe interface through:

  1. Code-first schema generation using GraphQLite annotations, eliminating schema/code drift
  2. Automatic type discovery from the Light namespace and custom namespaces
  3. Integrated authorization via @Logged and @Right annotations enforced before resolver execution
  4. Flexible querying through Light\Type\App providing system queries and configuration
  5. Domain-organized mutations through specialized controller classes
  6. Environment-aware behavior with debug information in development and optimized caching in production
  7. Permission discovery enabling dynamic UI generation based on user capabilities

The architecture ensures security is enforced at the GraphQL field level, provides comprehensive audit capabilities, and maintains a clear separation between API surface and implementation while keeping them synchronized through annotations.

Refresh this wiki

On this page