close

Filter

loading table of contents...

Headless Server Developer Manual / Version 2310

Table Of Contents

4.3 The @fetch Directive

The standard GraphQL Java @fetch directive has been extended by CoreMedia to support the Spring Expression Language. This gains a lot of flexibility for implementing data fetching logic, often avoiding the need to extend a Java class with corresponding properties.

As a simple example, assume you want to make the name field of some object to be available as is and, additionally, with all characters converted to upper case:

type SomeObjectType {
      name
      uppercaseName: @fetch(from: "name.toUpperCase()")
}

The special SpringEL variables #this and #root are initially bound to the target object of the field. Note that, according to SpringEL semantics, the #root variable remains to be bound to this object during expression evaluation, while the #this variable may change, depending on expression context.

The following fields all fetch the same value:

      name
      name2: @fetch(from: "name")
      name3: @fetch(from: "#root.name")
      name4: @fetch(from: "#this.name")

With the original @fetch directive from GraphQL Java, only the first, simple form is allowed, the "expression language" is restricted to simple identifiers. The CoreMedia Headless Server @fetch directive implements a strict superset.

In GraphQL, fields may take arguments. Inside the fetch expression, these are available as SpringEL variables of the same name:

type Query {
  add(x: Int!, y: Int): Int! @fetch(from: "#x + #y")
}

Other SpringEL variables may be defined by adding Spring beans with the qualifier globalSpelVariables. Moreover, SpringEL variables may also be bound to functions. Such functions might help to keep the SpringEL expressions short and concise. For example, in CaasConfig.java, a SpringEL function #first is defined with a static method from class SpelFunctions. It retrieves the first element of a list, or null if the list is itself null or empty:

  @Bean
  @Qualifier("globalSpelVariables")
  public Method first() throws NoSuchMethodException {
    return SpelFunctions.class.getDeclaredMethod("first", List.class);
  }

A @fetch directive utilizing this function may look like this:

      authors: [CMTeasable]
      author: CMTeasable @fetch(from: "#first(authors)")

The same functionality might be expressed with a rather lengthy expression using the ternary (conditional) operator:

      authors: [CMTeasable]
      author: CMTeasable
        @fetch(from: "authors?authors.length>0?authors[0]:null:null")

Note that SpringEL variables all share the same name space, so be aware of possible name clashes.

The GraphQL schema content-schema.graphql contains many more examples for Spring EL expressions.

When accessing settings or nested properties there are two ways to do so. Firstly, it is possible to access the properties via the Spring expressions:

      @fetch(from: "structName?.pathSegmentA?.pathSegmentB?.propertyName")
    

Using this will however result in an error if one of the path segments or the property itself does not exist on the object. A more reliable way of accessing settings and properties would be to use the SettingsAdapter and the StructAdapter (see Section 4.8, “Adapter”) for access to these kinds of properties. They take care of existing properties, it is possible to query multiple properties at once and to pass them default values. Additionally, they provide the option to wrap a value in its path, which means that the adapter does not return the value directly, but instead wrapped in a hierarchical structure, representing the path.

      @fetch(from: "@structAdapter.to(#root).getWrappedInStruct('structName', {'pathSegmentA','pathSegmentB','propertyName'}, 'defaultValue')"
    

The result would be something like: {structName:{pathSegmentA:{pathSegmentB:{propertyName:propertyValue}}}}

Search Results

Table Of Contents
warning

Your Internet Explorer is no longer supported.

Please use Mozilla Firefox, Google Chrome, or Microsoft Edge.