How to extend¶
The xAPI offers default GraphQL schemas for objetcs, queries, and mutations. However, as every project has unique requirements, it's impossible to anticipate and include them all in the base schema. Fortunately, the xAPI provides extension points that allow you to modify the baseline behavior and data structures. In this article, we explore the key extension points and techniques available for customizing your projects.
In addition to the extension points that platform provides, the xAPI project has the following main extensions points. However, there is no opportunity to extend AutoMapper's profiles using AbstractTypeFactory<>
type.
Extend root GraphQL schema¶
Within the xAPI, we have the flexibility to choose between the:
- Schema-first approach.
- Graphtype-first approach.
We've opted to focus exclusively on the graphtype-first approach, for it:
- Aligns more naturally with .NET development practices.
- Provides access to all the properties of your GraphTypes and Schema.
- Is better suited for extending the model through type overrides.
To register a new query or mutation:
-
Derive a custom schema type from
ISchemaBuilder
. TheISchemaBuilder
interface is designed to dynamically add new queries to the root GraphQL schema. MultipleISchemaBuilder
instances are used to add the queries to the root schema when the application starts. The example below shows how to define schema types for existing domain types. Here, we create a new GraphQL schema object type,InventoryType
, for the underlying domain type,Inventory
, from the inventory module. -
Define a new type that implements this interface
-
Register your custom schema type in the Dependency Injection (DI) container. In your
module.cs
file, initialize and register the custom schema using theservices.AddSchemaBuilder<CustomSchema>()
method:
Extend existing schema type with new properties¶
To extend the existing GraphQL type:
-
Create a new schema type that derives from the existing type you want to extend. This allows you to build upon the existing structure:
-
Register your type override using the appropriate syntax in the
module.cs
file. This step is crucial to ensure that your extension is recognized and integrated into the GraphQL schema:
Extend validation logic/ replace validators¶
In the system, the Platform's abstract type factory is employed to create instances of validators. Consequently, the approach for extending validation logic is similar to other cases, such as extending domain models:
-
Create your custom validator by deriving it from the original one. This allows you to build upon the existing validation logic:
-
Override the original validator type with your custom validator. This step is crucial to inform the factory that CartValidator2 should replace the original validator, ensuring that your custom logic is used:
Extend generic behavior pipelines¶
xAPI extension points extend beyond data structure modifications. You can also modify behavior and business logic without altering the original source code.
Generic behavior pipelines are primarily designed to break down complex logic into loosely coupled stages (middleware). These middleware stages can be defined in various locations and combined into a single logical pipeline that can be executed in response to specific system events or requests.
You can extend the existing generic pipelines with you custom middlewares or even replace the existing middleware with your custom version.
In the example below, you will replace the existing generic pipeline responsible for enriching the ProductSearchResult
with pricing and availability data from different sources.
-
Define the new middleware:
-
Register it for the generic behavior pipeline:
-
To replace the existing middleware with the new one, use the following syntax:
Replace command/ query handlers¶
xAPI is built using the clean architecture based on CQRS and DDD principles, where each command and query has its own handler responsible for processing incoming actions. You can easily override and substitute any existing handler with your implementation, thereby changing the default behavior.
It is just enough to replace the required handler in the DI container with your own implementation:
To replace an existing command with your implementation:
-
Register and override your input type:
module.cs -
Register your implementations of the command and handler:
module.cs