@resolver directive

Specifies the name of a Fauna user-defined function to use instead of a default field value resolver.

Schema location

This directive is valid for field declarations in Query or Mutation types within a GraphQL schema.

Arguments

Argument Type Required Default Description

name

String

No

The field’s name.

The name for the resolver function.

paginated

Boolean

No

false

When true, the resolver accepts three arguments, size, afterCursor, and beforeCursor, and then returns a Page of results. See the Examples below, and the UDF that returns a database page.

Description

The @resolver directive marks a Query or Mutation that has an associated user-defined function in the database. Queries to fields annotated with the @resolver directive are resolved by calling the underlying user-defined function, which is a Fauna Query Language Lambda function.

The name of the function is controlled by the name argument, which defaults to the name of the field. The paginated argument controls pagination support for the values returned by the user-defined function.

When a schema is imported into the GraphQL API, resolvers for your schemas fields are automatically generated. When you apply the @resolver directive to a query field, the named resolver is used instead of the automatically-generated resolver for the field.

The GraphQL API always passes arguments to resolver’s user-defined function as an array. Even if your schema only ever passes one argument to your resolver, you must implement the resolver’s user-defined function to accept the arguments as an array. See the function_names function in the Examples section.

User-defined functions cannot be created directly with GraphQL queries. When you import a schema that refers to a resolver that does not exist, the GraphQL API creates template functions that throw resolver-specific errors reminding you to implement them. For example, if your GraphQL schema referred to an unimplemented resolver called prune_order, the GraphQL API would create the following function:

Get(Function("prune_order"))
{
  ref: Ref(Ref("functions"), "prune_order"),
  ts: 1634594202150000,
  name: "prune_order",
  data: {
    gql: {
      ts: Time("2021-10-18T21:56:42.121219Z"),
      meta: {
        location: "Query",
        field: {
          name: "prune_order",
          directives: [
            {
              name: "resolver",
              args: {
                name: "prune_order",
                paginated: false
              }
            }
          ],
          type: {
            Named: "String"
          }
        }
      }
    }
  },
  body: Query(
    Lambda(
      "_",
      Abort(
        "Function prune_order was not implemented yet. Please access your database and provide an implementation for the prune_order function."
      )
    )
  )
}

To implement the resolver, you should use the Update function to replace the function’s body field definition.

Any functions created by the GraphQL API include a data field that contains metadata about the resolver: if you delete the function and recreate it with the desired body function, you would lose the GraphQL metadata, which would prevent the resolver from functioning.

One common trick is to use the @resolver directive to block document updates. Because the template function generated by the @resolver directive calls the FQL Abort function, all mutations involving the type would fail.

This is useful when GraphQL queries are implemented in browsers, and you do not want the browser user to be able to make changes to your documents simply because they can modify the queries that the browser would send.

With the partial update feature introduced in mid-February 2022, you would need to add the @resolver directive to any partial update mutations in order to prevent updates to single document fields. See GraphQL/schemas.adoc#partial-updates for more information.

For more information, see User-defined functions.

Examples

The following two FQL queries each create a function:

CreateFunction({
  name: "say_hello",
  body: Query(Lambda([], "hello"))
})
CreateFunction({
  name: "function_names",
  body: Query(Lambda(["size", "afterCursor", "beforeCursor"],
    Map(
      Paginate(Functions()),
      Lambda("ref",
        Select("name", Get(Var("ref")))
      )
    )
  ))
})

The first function, called say_hello, only returns the string hello. The second function, called function_names, returns the list of functions defined in the database. For a more in-depth example, see A UDF that returns a database page.

We can use these functions in GraphQL queries by using the following schema:

type Query {
  sayHello: String! @resolver(name: "say_hello")
  functionNames: [String!] @resolver(name: "function_names", paginated: true)
}

With the functions and the schema in place, we can call the first function with this GraphQL query:

{
  sayHello
}

which should produce the following response:

{
  "data": {
    "sayHello": "hello"
  }
}

Calling the second function would look like this:

{
  functionNames {
    data
  }
}

And the response would look like:

{
  "data": {
    "functionNames": {
      "data": [
        "function_names",
        "say_hello"
      ]
    }
  }
}

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!