docs/apis/dual-api/reference/infrastructure-classes.md
These classes live in Automattic\WooCommerce\Api\Infrastructure (and Api\Utils). Some are convention classes that ApiBuilder detects per plugin; the rest are runtime helpers. Plugin override rules apply equally when adjusting core's own behavior.
ApiBuilder looks for these at <api-namespace>\Infrastructure\* and wires whatever it finds into the generated controller. Ship one only to diverge from the default; otherwise the default applies. The signature must match exactly.
ClassResolverpublic static function resolve_class( string $class_name ): object
Instantiates command and infrastructure classes. Default: wc_get_container()->get( $class_name ). Ship your own to route through a different DI container. When no resolver is present at all, generated resolvers fall back to new $class_name().
PrincipalResolverpublic function resolve_principal(): Principal
// or
public function resolve_principal( \WP_REST_Request $request ): Principal
Resolves the per-request principal once. Default: returns new Principal( wp_get_current_user() ) (no $request parameter). The return type declares the plugin's principal type, which the builder uses to type-check authorize()/$_principal against. Throw UnauthorizedException/InvalidTokenException to reject credentials. Anonymous requests are a resolved principal (not null).
PrincipalThe default principal wraps a WP_User:
public function __construct( public readonly \WP_User $user )
public function is_authenticated(): bool // user->ID > 0
public function can_introspect(): bool // user_can( $user, 'manage_woocommerce' )
public function can_use_debug_mode(): bool // user_can( $user, 'manage_options' )
A custom principal can be any class. Recognized (all optional, duck-typed) methods:
| Method | If declared | If absent |
|---|---|---|
is_authenticated(): bool | distinguishes 401 vs 403 on denial; used by your own code | denials default to 403 (FORBIDDEN) |
can_introspect(): bool | gates native introspection (and _apiMetadata, as fallback) | introspection denied |
can_use_debug_mode(): bool | gates debug mode (with _debug=1) | debug mode denied |
can_query_metadata(): bool | gates _apiMetadata specifically | falls back to can_introspect(), else deny |
Core's Principal deliberately omits can_query_metadata(), so _apiMetadata follows can_introspect().
HttpStatusResolverpublic function resolve_status( int $default_status, array $output, \WP_REST_Request $request ): int
Optional. Override the framework-computed HTTP status for any response (e.g. always 200), or return $default_status to defer. Called for both success and error responses. Must not throw: any throw is converted to a fixed 500 INTERNAL_ERROR. Default: core ships none, so its per-error-code mapping applies. See Settings and caching.
You generally don't call these directly (generated code does), but they're public for advanced use.
GraphQLControllerBaseAbstract base for the generated controller; owns the request lifecycle. Notable public members:
get_schema(): SchemaHandle: schema handle for metadata inspection.build_schema(): Schema\Schema: returns the engine-decoupled wrapper, never the engine type.get_endpoint_url(), get_max_query_depth(), get_max_query_complexity().ResolverHelpersStatic helpers used by generated resolvers: exception translation, pagination construction, authorization checks, and the public compute_preauthorized( string $command_fqcn, object $principal ): bool.
MainBootstrap and registration:
is_enabled(): bool: checks PHP 8.1+ and the dual_code_graphql_api flag.register_graphql_endpoint( string $plugin_dir_or_controller_class, string $route_namespace, string $route, array $methods = ['GET','POST'] ): void: register a plugin endpoint. No-op when the feature is off.instantiate_graphql_controller( string $controller_class_name ): ?GraphQLControllerBase.MetadataController, QueryInfoExtractorHand-written runtime pieces: the _apiMetadata field/types, and the ResolveInfo → _query_info extraction. See Extending the infrastructure.
SchemaHandle (Api\Utils)Opaque, engine-independent handle returned by get_schema():
public function get_all_metadata(): array
public function find_metadata( ?string $name = null, ?string $type = null, ?string $field = null ): array
Plain helpers (mappers, repositories) live under Api/Utils/ and are not exposed in the schema, e.g. Utils\Products\ProductRepository (find( int $id ): ?\WC_Product, save( \WC_Product $product ): void). Inject them into commands via the ClassResolver/DI container. Plugins place their own helpers under their Api/Utils/.