OperationContext
Any lifecycle method has access to the operation's context that holds all data for the operation's mutation resolve. You can read or write its content in your lifecycle implementations.
| Property | Type | Description |
|---|---|---|
runtime | Runtime | the Runtime instance, you can use the Runtime to access any part of the DomainGraph |
input | any | the operation input as from the mutation's request |
data | any | initially a clone of the input but here values are set and manipulated while leaving the input untouched |
validationViolations | ValidationViolation[] | the array of validation violations; when this array is non-empty the operation will not execute its business logic and sends it to the client |
savedRootEntityIds | {[name:string]:number[]} | any entity item that is saved; used for building the result by the default implementation |
result | any | the result of this operation as it is sent to the client |
validationRun | boolean | whether the validation was already performed (will replaced by a "state" ) |
inputDispositions | {[name:string]:InputDisposition} | the evaluated input dispositions |
principal | PrincipalType (read-only) | the principal of this mutation execution |
log | ErrorLog (read-only) | the error log |
root | any | the root value of the mutation resolver |
args | any | the args value of the mutation resolver |
info | any | the info value of the mutation resolver |
context | ResolverContext | the context value of the mutation resolver |
Operation Lifecycle Methods
| Lifecycle method | is called ... | Default implementation | Custom implementation |
|---|---|---|---|
onContextCreate | at the creation of the context for this operation execution | ... when you want to add something to the operation context, e.g. for evaluating FEEL expressions | |
dispositions | anytime the operation's mutation is resolved | evaluate the default input dispositions for the operation input and attributes | ... only when you need another implementation (unusual) |
beforeValidation | after the input disposition are evaluated. Only when the mutation should return either the validation violations or the result type | returns true | ... when you want to sanitize some values, determine additional information or stop following execution (by returning false) |
validation | when beforeValidation returns true | validating any input and attribute value in regards to the configuration | ... only when you need another implementation (unusual) |
beforeSave | when the result field of the operation's mutation was requested and validate returns true | returns true | ... any business logic like checking / manipulating validated data from the operation before they are saved or stop following execution (by returning false) |
save | when beforeSave returns true | saves all input types referring to an entity and returning true when successful | ... any business logic (whether saving data or not) instead of automagic saving. This is most likely the lifecycle method that you want to implement |
afterSave | when save returns true | returns true | ... any business logic after data was saved |
buildResult | when afterSave returns true | loads all entity items of root input types that are part of the result type tries to find any matching output type field in the input sets it in the operation's result and returns true | ... when you want another implementation to build the operation's result |
afterBuildResult | when buildResult returns true | ... when you want to check / manipulate the data from the operation's result before it is sent to the client |
onContextCreate
{ onContextCreate: undefined | (ctx:OperationContext) => void|Promise<void> }
| Config | Description |
|---|---|
undefined | default implementation does nothing |
(ctx:OperationContext) => void|Promise<void> | custom implementation |
In this example we use the onContextCreate callback to set a value in the data object of the OperationContext. This data can be evaluated e.g. in FEEL-expressions.
operation:
RentCar:
input:
Car:
attributes:
brand:
type: String!
validation:
expression: notIncludes( @brand, @blacklist ) operation: {
RentCar: {
onContextCreate: ctx => {
_.set( ctx.data, 'blacklist', ['Tesla', 'BYD'] )
}
}
}
}mutation { RentCar( Car: { brand: "Tesla" } ) { validationViolations } }[{
path: 'Car.brand',
message: `did not satisfy expression: notIncludes( @brand, @blacklist )`
}]dispositions
{ dispositions: undefined | (ctx:OperationContext) => void|Promise<void> }
| Config | Description |
|---|---|
undefined | default implementation evaluates all input dispositions and puts them into ctx.inputDispositions |
(ctx:OperationContext) => void|Promise<void> | custom implementation |
There is usually no need to change the default implementation. When you provide a custom implementation, you have to create all input dispositions in the OperationContext for this operation in regards to its input.
beforeValidation
{ beforeValidation: undefined | OperationCallback<boolean> }
| Config | Description |
|---|---|
undefined | default implementation does nothing except returns true |
(ctx:OperationContext) => boolean|Promise<boolean> | custom implementation |
In the beforeValidation callback you would typically complete all data necessary to validate the operation's input and execute any business logic.
If this method returns anything else but true the following lifecycle methods are not executed. The values in the operation context (data, input, dispositions, validationViolations) are returned to the client.
You can either provide an implementation for this callback or use one of the following shortcuts for typical use cases.
| Shortcut | Description |
|---|---|
OperationCallbackEntity | load one or many entity items into the data |
OperationCallbackRestCall | execute a RestCall and evaluate its result |
OperationCallbackEntity
{ entity:string, id:string, path?:string }
| Config | Default | Description |
|---|---|---|
entity | the entity that should be loaded into data | |
id | the path where the id or ids could be find the data | |
path | where to put the loaded entity item(s) into the data |
In this example the operation input has a carId but we want the Car entity item with this id in the data e.g. for further validation.
entity:
Car:
attributes:
brand: String!
seeds: 10
operation:
RentCar:
input:
Rental:
attributes:
carId: String!
beforeValidation:
- entity: Car
id: Rental.carIdAssuming there is a car entity with the id "0102030405" we could call the mutation like so:
mutation { RentCar( Rental: { carId: "0102030405" } ) { result { data } } } When we see the data after this we see the car item in there, we could use in further business logic.
{
Rental: { carId: '0102030405' },
car: {
brand: 'Porsche',
__typename: 'Car',
indication: 'Porsche',
id: '0102030405'
}
}OperationCallbackRestCall
{ restCall: string }
| Config | Type | Default | Description |
|---|---|---|---|
endpoint | string | FeelExpressionConfig | the URL for the rest call | |
method | GET | POST | PUT | POST when body is defined; else GET | the http method |
headers | {[key:string]: string | FeelExpressionConfig } | any header value | |
params | {[key:string]: string | FeelExpressionConfig } | any request parameter / value | |
body | object | the body of the http request | |
result | ApiCallSetResultFn | ApiCallCopyPath | ApiCallCopyPath[] | transformation of rest call response body to data |
You can execute rest calls to load additional data that you can use in further business logic.
In this example we want to determine the vehicle brand or manufacturer for a given VIN. For that we use a public API https://api-ninjas.com (opens in a new tab) We can use this API call https://api.api-ninjas.com/v1/vinlookup?vin=XXXXXXX
restCall:
CarAPI:
endpoint: https://api.api-ninjas.com/v1/vinlookup
params:
vin: "@vin!"
headers:
X-Api-Key: YOUR_API_KEY
result:
from: manufacturer
to: Rental.brand
operation:
RentCar:
input:
Rental:
attributes:
vin: String
beforeValidation:
- restCall: CarAPImutation { RentCar( Rental: { vin: "5UXCR6C01N9K17074" } ) { result { data } } }{
Rental: { vin: '5UXCR6C01N9K17074', brand: 'BMW' }
}validation
{ validation: undefined | (ctx:OperationContext) => void|Promise<void> }
There is usually no need to change the default implementation. When you provide a custom implementation, you have to validate all operation inputs in the OperationContext for this operation in regards to the input dispositions.
| Config | Description |
|---|---|
undefined | default implementation validates the mutation input and adds any validationViolation to the context |
(ctx:OperationContext) => boolean|Promise<boolean> | custom implementation |
If the context validationViolations are not empty after this method, the following lifecycle methods are not executed. The values in the operation context (data, input, dispositions, validationViolations) are returned to the client.
beforeSave
{ beforeSave: OperationCallback<boolean> }
| Config | Description |
|---|---|
undefined | default implementation does nothing except returns true |
(ctx:OperationContext) => boolean|Promise<boolean> | custom implementation |
In the beforeSave callback you would typically check / manipulate validated data from the operation before they are saved or stop the following execution (by returning false).
If this method returns anything else but true the following lifecycle methods are not executed. The values in the operation context (data, input, dispositions, validationViolations) are returned to the client.
You can either provide an implementation for this callback or use one of the following shortcuts for typical use cases (for details see beforeValidation).
| Shortcut | Description |
|---|---|
OperationCallbackEntity | load one or many entity items into the data |
OperationCallbackRestCall | execute a RestCall and evaluate its result |
save
{ save: undefined | true | false | (ctx:OperationContext) => boolean|Promise<boolean> }
| Config | Description |
|---|---|
undefined | same as true |
true | saves all input types referring to an entity; returning true when successful |
false | skip any saving and returns false |
(ctx:OperationContext) => boolean|Promise<boolean> | custom implementation |
You can either rely on the saving of entity items from the input or implement any business logic in this method, whether it actually includes saving of entities or calling 3rd party APIs or anytime else.
If this method returns anything else but true the following lifecycle methods are not executed. The values in the operation context (data, input, dispositions, validationViolations) are returned to the client.
afterSave
{ afterSave: undefined | (ctx:OperationContext) => boolean|Promise<boolean> }
| Config | Description |
|---|---|
undefined | default implementation does nothing except returns true |
(ctx:OperationContext) => boolean|Promise<boolean> | custom implementation |
If this method returns anything else but true the following lifecycle methods are not executed. The values in the operation context (data, input, dispositions, validationViolations) are returned to the client.
You can either provide an implementation for this callback or use one of the following shortcuts for typical use cases.
| Shortcut | Description |
|---|---|
OperationCallbackEntity | load one or many entity items into the data |
OperationCallbackRestCall | execute a RestCall and evaluate its result |
buildResult
buildResult: undefined | (ctx:OperationContext) => boolean|Promise<boolean>
| Config | Description |
|---|---|
undefined | default implementation sets any saved entity item that is defined in the result type |
(ctx:OperationContext) => boolean | you can set your custom implementation to set the result in the context. If the method returns false the following method will be skipped |
afterBuildResult
{ afterBuildResult: undefined | (ctx:OperationContext) => void|Promise<void> }
| Config | Description |
|---|---|
undefined | default implementation does nothing |
(ctx:OperationContext) => void|Promise<void> | custom implementation |
You can either provide an implementation for this callback or use one of the following shortcuts for typical use cases.
| Shortcut | Description |
|---|---|
OperationCallbackEntity | load one or many entity items into the data |
OperationCallbackRestCall | execute a RestCall and evaluate its result |