User-defined functions

User-defined functions (UDF) are Fauna Query Language Lambda functions, and they can be exposed through the GraphQL API by using the @resolver directive at fields in the Query and Mutation types. This directive has no effect if placed elsewhere.

The UDF must accept an array of arguments, the same number and order as the associated field in the GraphQL schema. There is no association between the arguments' names in the GraphQL schema and the arguments' names in the UDF definition. When a UDF field is queried, the GraphQL API simply calls the underlying UDF with an array of the given arguments.

The UDF result type must be a GraphQL-compatible type. If an object is returned, an equivalent type must exist in the GraphQL schema to allow users to select which fields to return. Embedded types can be used to map return types that are not associated with any existing type.

UDFs with pagination support must return a database page. In addition to the function arguments, the API calls the UDF with three additional arguments appended the query’s arguments:

  • size: the requested page size

  • after: the database forward cursor

  • before: the database backwards cursor

A UDF’s implementation must account for these additional arguments and call the appropriate form of the FQL Paginate function to return a database page to the GraphQL API.

When a schema is imported that involves the @resolver directive, the import logic checks for an existing UDF having the specified name. If no UDF exists with the specified name, a "template" UDF is created for you. The template UDF aborts the query with an error, since the actual logic to handle the field has not been implemented.

Once a template UDF has been created, you can update the UDF with the actual functionality. For example:

Update(
  Function("my_function"),
  {
    "body": Query(
      Lambda(["param1", "param2"],
       // your logic here
     )
  }
)

If you attempt to run CreateFunction using the template UDF’s name, you receive an error because the UDF already exists.

Examples

A UDF that returns a scalar type:

The following is a UDF, using FaunaDB Shell syntax, that returns a scalar type:

CreateFunction({
  name: "say_hello",
  body: Query(Lambda(["name"],
    Concat(["Hello ", Var("name")])
  ))
})

A GraphQL schema that uses the UDF:

type Query {
  sayHello(name: String!): String! @resolver(name: "say_hello")
}

With these in place, when you run the following query:

{
  sayHello(name: "Jane")
}

The result should be:

{
  "data": {
    "sayHello": "Hello Jane"
  }
}

A UDF that returns an embedded object

The following is a UDF, in FaunaDB Shell syntax, that returns an embedded object:

CreateFunction({
  name: "sample_obj",
  body: Query(Lambda([], {
    time: Time("now"),
    sample: true
  }))
})

A GraphQL schema that uses the UDF:

type SampleObj @embedded {
  time: Time!
  sample: Boolean!
}

type Query {
  sampleObj: SampleObj! @resolver(name: "sample_obj")
}

With these in place, when you run the following query:

{
  sampleObj {
    time
    sample
  }
}

The result should be:

{
  "data": {
    "sampleObj": {
      "time": "2019-06-14T17:42:54.001987Z",
      "sample": true
    }
  }
}

A UDF that returns a database page

The following is a UDF (along with a Collection and Index), in FaunaDB Shell syntax, that returns a database page, so that repeated querying can retrieve all of the paginated results.

CreateCollection({
  name: "users"
})
CreateIndex({
  name: "vip_users",
  source: Collection("users"),
  terms: [{ field: [ "data", "vip" ] }]
})
CreateFunction({
  name: "vip_users",
  body: Query(Lambda(["size", "after", "before"],
    Let(
      {
        match: Match(Index("vip_users"), true),
        page: If(
          Equals(Var("before"), null),
          If(
            Equals(Var("after"), null),
              Paginate(Var("match"), { size: Var("size") }),
              Paginate(Var("match"), { size: Var("size"), after: Var("after") })
          ),
          Paginate(Var("match"), { size: Var("size"), before: Var("before") }),
        )
      },
      Map(Var("page"), Lambda("ref", Get(Var("ref"))))
    )
  ))
})

A GraphQL schema that uses the UDF:

type User @collection(name: "users") {
  username: String!
  vip: Boolean!
}

type Query {
  vips: [User!] @resolver(name: "vip_users", paginated: true)
}

With these in place, when you run the following query:

{
  vips(
    _size: 2
    _cursor: "2DOB2DRyMjM1MTcwOTY5MDA0NTQwNDIzgWV1c2Vyc4FnY2xhc3Nlc4CAgIA="
  ) {
    data {
      username
    }
    after
    before
  }
}

The result should be:

{
  "data": {
    "vips": {
      "data": [
        { "username": "Mary" },
        { "username": "Ted" }
      ],
      "after": null,
      "before": "2DKB2DRyMjM1MTcwOTY5MDA0NTQwNDIzgWV1c2Vyc4FnY2xhc3Nlc4CAgIA="
    }
  }
}

Was this article helpful?

We're sorry to hear that.
Tell us how we can improve! documentation@fauna.com

Thank you for your feedback!