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`](#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`](#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`](#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`](#validation) | when `beforeValidation` returns `true` | validating any _input_ and _attribute_ value in regards to the configuration | ... only when you need another implementation (unusual) | | [`beforeSave`](#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`](#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`](#aftersave) | when `save` returns `true` | returns `true` | ... any business logic after data was saved | | [`buildResult`](#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`](#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 |