Define a Custom Resolver
On this page
Overview
You can define custom resolvers that extend the GraphQL API for your app's use cases. Custom resolvers allow you to define new root-level operations that are more complex or specific than the generated query and mutation resolvers. You can also add new computed fields to generated document types that dynamically evaluate a result whenever an operation reads a document of the extended type.
Procedure
Define the Resolver Field Name
Specify App Servicese name for the resolver in the GraphQL Field Name input. App Services exposes the custom resolver in its parent type using this name, so the name should describe what the resolver does in a way that is useful to developers who work with the GraphQL API.
Define the Parent Type
App Services exposes every custom resolver as a field on a parent type. The parent type can be a root-level query, mutation, or a generated document type.
In the Parent Type dropdown, select one of the following options:
Option | Description | ||||
---|---|---|---|---|---|
Query | The resolver is a root-level ExampleA custom resolver for a query named
| ||||
Mutation | The resolver is a root-level ExampleA custom resolver for a mutation named
| ||||
Document Type | The resolver is a computed property on the specified document type. Any query or mutation that returns the document type can also ask for computed properties defined by custom resolvers on the type. ExampleA custom resolver that defines a computed property on the
|
Define the Input Type
A custom resolver can accept input parameters from the incoming query or mutation. You can use an existing generated input type or define a new custom input type specifically for the resolver.
If you specify an input type, App Services exposes the input
parameter in the
custom resolver's generated GraphQL schema definition as an optional parameter
that accepts the specified input type. If you don't specify an input type, the
custom resolver does not accept any arguments.
In the Input Type dropdown, select one of the following options:
Option | Description | |||||||
---|---|---|---|---|---|---|---|---|
None | The resolver does not accept any input. ExampleA custom resolver named
| |||||||
Scalar | The resolver uses an existing scalar type from the generated GraphQL schema. In the second dropdown input, select either a single scalar or an array of multiple scalars of the same type. ExampleA custom resolver named
| |||||||
Existing Type | The resolver uses an existing input type from the generated GraphQL schema. In the second dropdown input, select either a single input object or an array of multiple input objects of the same type. ExampleA custom resolver named
| |||||||
Custom Type | App Services generates a new input type specifically for the resolver based on
a schema that you define. The schema must be an
ExampleA custom resolver named
|
Define the Payload Type
All GraphQL resolvers must return a payload that conforms to a specific type in the schema. For a custom resolver, you can use an existing generated document type, define a new custom payload type specifically for the resolver, or use a default payload. App Services includes the specified payload type in the custom resolver's generated GraphQL schema definition.
In the Payload Type dropdown, select one of the following options:
Option | Description | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
DefaultPayload | The resolver returns the automatically generated
The
ExampleA custom resolver named
| ||||||||||
Scalar | The resolver uses an existing scalar type from the generated GraphQL schema. In the second dropdown input, select either a single scalar or an array of multiple scalars of the same type. ExampleA custom resolver named
| ||||||||||
Existing Type | The resolver returns an existing document type from the generated GraphQL schema. In the second dropdown input, select either a single document type or an array of multiple documents of the same type. ExampleA custom resolver named A custom resolver named
| ||||||||||
Custom Type | App Services generates a new payload type specifically for the resolver based
on a schema that you define. The schema must be an
ExampleA custom resolver named
|
Define the Resolver Function
When a user calls a custom resolver App Services executes the resolver function and returns the result, which must conform to the resolver's Payload Type.
App Services passes the function any input data from the operation, if applicable. If the resolver is a computed property on a document type, App Services passes the function the specific document that the resolver was called on.
A custom resolver function has one of two possible signatures, depending on whether or not it accepts an input:
exports = function myCustomResolver(input, source) { // The `input` parameter that contains any input data provided to the resolver. // The type and shape of this object matches the resolver's input type. const { someArgument } = input; // If the resolver is a computed property, `source` is the parent document. // Otherwise `source` is undefined. const { _id, name } = source; // The return value must conform to the resolver's configured payload type return { "someValue": "abc123", }; }
exports = function myCustomResolver(source) { // If the resolver is a computed property, `source` is the parent document. // Otherwise `source` is undefined. const { _id, name } = parent; // The return value must conform to the resolver's configured payload type return { "someValue": "abc123", }; }
To define the resolver function, click the Function dropdown and either select an existing function or create a new one.
Custom Resolver Examples
Scenario & Schemas
Consider a hypothetical dashboard that a sales team uses to show various statistics and other performance metrics for a given time period. The dashboard uses the custom resolvers in this section to handle some of its specific use cases.
The resolvers all reference Sale
documents, which have the following schema:
type Sale { _id: ObjectId! customer_id: String! year: String! month: String! saleTotal: Float! notes: [String] }
{ "title": "Sale", "bsonType": "object", "required": ["_id", "customer_id", "year", "month", "saleTotal"], "properties": { "_id": { "bsonType": "objectId" }, "customer_id": { "bsonType": "string" }, "year": { "bsonType": "string" }, "month": { "bsonType": "string" }, "saleTotal": { "bsonType": "decimal" }, "notes": { "bsonType": "array", "items": { "bsonType": "string" } } } }
Custom Query Resolver
The sales team's hypothetical dashboard uses a custom query resolver that returns aggregated sales data for a specific month.
App Services generates schema definitions for the resolver's custom input and payload
types and adds the resolver to its parent type, the root-level Query
:
type Query { averageSaleForMonth(input: AverageSaleForMonthInput): AverageSaleForMonthPayload } input AverageSalesForMonthInput { month: String!; year: String!; } type AverageSaleForMonthPayload { month: String!; year: String!; averageSale: Float!; }
Configuration
The resolver uses the following configuration:
Option | Description | |||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Parent Type | Query | |||||||||||||||||||||||
GraphQL Field Name | averageSaleForMonth | |||||||||||||||||||||||
Input Type | Custom Type:
| |||||||||||||||||||||||
Payload Type | Custom Type:
| |||||||||||||||||||||||
Function |
|
Example Usage
To call this custom query, you could use the following operation and variables:
query GetAverageSaleForMonth($averageSaleInput: AverageSaleForMonthInput!) { averageSaleForMonth(input: $averageSaleInput) { month year averageSale } }
{ "variables": { "averageSaleInput": { month: "March", year: "2020" } } }
Custom Mutation
The sales team's hypothetical dashboard uses a custom mutation resolver that
adds a string note to a specific Sale
document, identified by its _id
.
App Services generates schema definitions for the resolver's custom input type and adds
the resolver to its parent type, the root-level Mutation
:
type Mutation { addNoteToSale(input: AddNoteToSaleInput): Sale } input AddNoteToSaleInput { sale_id: ObjectId!; note: String!; }
Configuration
The resolver uses the following configuration:
Option | Description | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Parent Type | Mutation | ||||||||||||||||||||||||
GraphQL Field Name | addNoteToSale | ||||||||||||||||||||||||
Input Type | Custom Type:
| ||||||||||||||||||||||||
Payload Type | Existing Type:
| ||||||||||||||||||||||||
Function |
|
Example Usage
To call this custom query, you could use the following operation and variables:
mutation AddNoteToSale($addNoteToSaleInput: AddNoteToSaleInput) { addNoteToSale(input: $addNoteToSaleInput) { _id customer_id month year saleTotal notes } }
{ "variables": { "addNoteToSaleInput": { "sale_id": "5f3c2779796615b661fcdc25", "note": "This was such a great sale!" } } }
Computed Properties
The sales team's hypothetical dashboard uses a custom resolver that adds a new
computed property to each Sale
document. When an operation requests the
computed field for a given Sale
, the resolver queries an external system and
returns support cases filed by the associated customer.
App Services generates schema definitions for the resolver's custom payload type and
adds the resolver to its parent type, Sale
:
type Sale { _id: ObjectId! customer_id: String! year: String! month: String! saleTotal: Float! notes: [String] customerSupportCases: [CustomerSupportCase] } type CustomerSupportCase { caseId: String! description: String! }
Configuration
The resolver uses the following configuration:
Option | Description | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Parent Type | Sale | ||||||||||||||||||||||||||
GraphQL Field Name | customerSupportCases | ||||||||||||||||||||||||||
Input Type | None | ||||||||||||||||||||||||||
Payload Type | Custom Type:
| ||||||||||||||||||||||||||
Function |
|
Example Usage
To use this custom computed property, you could run the following operation:
query GetSalesWithSupportCases { sales { _id customer_id year month saleTotal notes customerSupportCases { caseId description } } }