Custom GraphQL schema

October 8, 2024

Headless development through GraphQL dedicated APIs

The Jaha GraphQL API allows you to define your own types and queries with the GraphQL Schema Definition Language (SDL). You can easily deploy it along with your module. To learn how to create a Jahia module, see Creating a new module which shows how to create a module in Studio or from scratch.

After you deploy your SDL schema, you can check the deployment status in Developer Tools>SDL Report Tool in Jahia. Jahia also has an integrated GraphQL console to let you view and test your custom schema definitions. You open the console from Developer Tools>GraphQL.

Note: Studio and Developer Tools both require the appropriate permissions. Note that Studio only available when Jahia is started in development mode. For more information on required permissions, see Managing roles and permissions.

Creating an SDL file

First, create and place your SDL file in your module under the following directory.

/<module_home>/src/main/resources/META-INF/graphql-extension.sdl

Since Jahia GraphQL API uses a fixed SDL file name to look up custom schema, you must use the graphql-extension.sdl file name to maintain all GraphQL types.

SDL example

This example SDL defines two custom object types and queries that you could place into your module. If you are not familiar with the syntax of SDL don’t worry, more details are provided later.

The SDL file is plain text and you can use any text editor to create it.


extend type Query {
  newsByTitle: NewsConnection
  allNews: [News]
}

type hotel @mapping(node: "jnt:hotel"){
  name: String! @mapping(property: "jcr:title")
  city: String @mapping(property: "city")
  address: String @mapping(property: "address")
  country: String @mapping(property: "country")
}

type booking @mapping(node: "jnt:booking"){
  title: String @mapping(property: "jcr:title")
room: Int @mapping(property: "roomNo")
  created: Date @mapping(property: "date")
}

Deploying the module using Jahia

Once you create your SDL file and place it into your module, you are ready to deploy your own schema along with your module. Now your own SDL schema is deployed. You can use Jahia’s SDL Report Tool to check the deployment status.

Once module is deployed:

  1. In Jahia, navigate to Developer Tools>SDL Report Tool, as shown in the following example.
    custom-graphql-schema-03.png
  2. Then you should see the my-module-with-SDL deployment status and your custom types in the SDL Report Tool. The SDL Report Tool has two sections:
    • SDL Definitions
      Displays individual type definitions in all schemas which are deployed. If there is an error for an individual type, an error message displays.
    • SDL Schema
      Displays deployment status of each schema from each module in general. This helps you to address problems with modules quickly.
  3. If the indicators are all green, your schema is ready to use. Otherwise, go back to your SDL schema definition to fix it. You don’t have to redeploy your module as all graphql-extension.sdl files are monitored for changes.

Testing custom types and queries

Jahia provides a tool for viewing all GraphQL types and running queries.

To test custom types and queries:

  1.  Open Jahia Support Tools in your web browser using a URL similar to http://localhost:8080/tools.
  2. Click Jahia GraphQL Core Provider :  graphql-playground.
    custom-graphql-schema-05.png
  3. To view your custom types, click the Docs button at the top-right corner. Enter a type name to lookup the type and query.
    custom-graphql-schema-06.png

    Screenshot of GRaphiQL, taken using a version prior to Jahia 8.0.2. GraphQL Playground, introduced with Jahia 8.0.2 offers similar features.

You should find the type and query definition documents in the current schema. You can also use this GraphQL console to run a query statement to test your types and queries.

Note: You can also navigate to Developer Tools>GraphQL Report Tool in Jahia to view and run your GraphQL query.

SDL syntax

SDL syntax is similar to JSON syntax. Jahia GraphQL API supports the standard syntax and provides some special directives for Jahia content type. The example that follows the data type descriptions shows how to create your own schema using Jahia GraphQL directives.

Basic data types

To start learning SDL syntax, first get to know the most basic data type. Then the most complex data types supported in Jahia GraphQL API. The following two tables provide a summary of these data types.

Type Format Example
ID

^([a-zA-Z0-9]+[-])+

126e20ba-2599-49af-a6cd-b575b5417482

String

 

A brown fox jumps over the lazy dog

Int

-2³¹ ~ 2³¹-1

1, 2, 342, -3, ...

Float

3.4E +/- 38

123.45

Double

1.7E +/- 308

123.087223423554

Short

An integer in between -32,768 ~ 32,767

123

Long

-2⁶³ ~ 2⁶³-1

8746655929836

*BigInteger

Any integer larger than Long type

1234567890987654321

*BigDecimal

 

0.33333333333

Boolean

 

true | false

Date yyyy-MM-dd'T'hh:mm:ss.SSSXXX 2016-01-06T18:49:45.308+01:00
Note: For BigInteger and BigDecimal types in Jahia JCR, when the data is stored as a larger number than Long or a longer decimal than Double, the actual value will be Long or Double.

Jahia GraphQL API provides a set of composite types which contains multiple basic data types.

Although you could define your own composite type, it is easier to reuse built-in composite types to make your definitions simpler. In addition, you could also define your type by extending one of these.

Jahia data types

Type

Format

Metadata
 

{
  created: Date,
  createdBy: String,
  lastModified: Date,
  lastModifiedBy: String,
  lastPublished: Date,
  lastPublishedBy:String
}

Asset

{
  type: String,
  size: Long,
  metadata: Metadata
}

ImageAsset

{
  type: String,
  size: Long,
  height: Long,
  width: Long,
  metadata: Metadata
}

Category

{
  title: String
  metadata: Metadata
  description: String
}

Defining a Jahia content model

To define your own type, you must define the content model in a CND file first. Usually you can create the definition in the definitions.cnd file under the same directory as the SDL file, for example:

/<module_home>/src/main/resources/META-INF/definitions.cnd

You can deploy the CND file along with the module.

Here is a sample CND for a content model. For example, you want to publish hotel information on your website. In the CND, you define different fields for the content, such as address, city and country.

<jmix = 'http://www.jahia.org/jahia/mix/1.0'>
<jnt = 'http://www.jahia.org/jahia/nt/1.0'>

[jnt:hotel] > jnt:content, jmix:basicContent, mix:title
 - city (string) internationalized
 - country (string) internationalized
 - address (string) internationalized

You can learn how to manage your content model in more detail here Content Structures.

GraphQL type without directive example

This is an example of a GraphQL SDL without any directives. It just defines one type, hotel, with its own properties. However, if you upload this SDL along with your module, it won’t work because the Jahia GraphQL API doesn’t know how to map the type to its content node and node properties.

type hotel {
  name: String!
  city: String
  address: String
  country: String
}

Therefore, after creating a new type, you need to define the mapping directives.

Use mapping definitions with directives

Add the mapping directive to your types in graphql-extension.sdl file.

Note that @mapping directive supports: node and property arguments for a type and its fields.

type hotel @mapping(node: "jnt:hotel"){
  name: String! @mapping(property: "jcr:title")
  city: String @mapping(property: "city")
  address: String @mapping(property: "address")
  country: String @mapping(property: "country")
  metadata: Metadata
  rooms: [Room]
}

type Room @mapping(node: "jnt:room"){
  number: Int! @mapping(property: "roomNo")
  specification: String @mapping(property: "description")
  available: Boolean @mapping(property: "available")
}

Node mapping is used on a type to specify which JCR node type will be mapped.

type hotel @mapping(node: "jnt:hotel"){
     ...
}

Here jnt:hotel is a specific JCR node type. By specifying the node type you enable Jahia to access the right content node.

Property mapping is used in the fields of a type.

type hotel @mapping(node: "jnt:hotel"){
  name: String! @mapping(property: "jcr:title")
  city: String @mapping(property: "city")
  address: String @mapping(property: "address")
  country: String @mapping(property: "country")

  metadata: Metadata
  rooms: [Room]
}

In this type definition, you could define its fields, such as name, city, address and country. Use the property argument to map the field to the property of a content node. This allows Jahia's GraphQL API to resolve the field correctly

For the custom type Metadata, it maps to the fields in the type which are predefined in the system schema, you don’t need to specify the properties to be mapped here.

For the children of custom type, like Room, it maps to multiple references of the other type. You should get the references along with the main type, when you do the query.

Defining a custom query by extending Query type

Once you define your own types, you may want to query data for the new types. Jahia GraphQL API provides the Query extension for you to define your own query.

Note: All query types in your SDL must extend the Query type.

Defining an all query

If you want to fetch all data set in a list, you could define an all query.

extend type Query {
  allHotel: [hotel]
}

Defining a single property query

Jahia GraphQL API provides queries for a single property.

extend type Query {
  hotelByCity: [hotel]
  hotelByCityConnection: hotelConnection
  hotelByCountry: [hotel]
  hotelByCountryConnection: hotelConnection
}

The Jahia GraphQL API will generate the default ByID and ByPath queries for your type, which returns a single unique record. You do not need to define the queries explicitly in your SDL.

You can define any By query for any single property in a type, which returns a list of records. Also make sure you use the exact type name as the query prefix, and the exact property name as suffix, for example, hotelByAddress, hotelByName, or hotelByCity.

Inline descriptions

Descriptions can be added to describe types and their associated fields. Descriptions are useful for communicating information with other developers i.e. consumers of the API.

In your SDL, you can use quotation marks to add descriptions as shown in the following example:

"""
hotel content with contact
"""
type hotel {
     """unique name of hotel"""
     name: String!
     address: String
     city: String
     country: String
}

Troubleshooting SDL Issues

Whenever SDL markup is changed it is validated to make sure that it is syntactically correct, that JCR and GraphQL types are available etc. The SDL Report Tool displays a red indicator to indicate errors discovered during validation.

SDL syntax error

An SDL syntax error displays under SDL schema to help you find the line containing the error.

#missing end brace
type hotel {
     uuid: ID!
     name: String!
     address: String
     city: String
     country: String

custom-graphql-schema-07.png

If you encounter typo error in your SDL, you will see error message similar to this.

#typo error
typo hotel {
     uuid: ID!
     name: String!
     address: String
     city: String
     country: String
}

custom-graphql-schema-08.png

Type and extension error

If your SDL has an extension with a nonexisting type, you will see an error message similar to this.

#type ParagraphSDL does not exist
extend type ParagraphSDL {
   content: @mapping(property: "jnt:content")
}

custom-graphql-schema-09.png

If a type contains an invalid field type in your SDL, you will see an error message similar to this.

#invalid field type
type hotel @mapping(node: "jnt:hotel"){
   uuid: ID!
   name: String @mapping(property: "jcr:title")
   address: String
   city: String

   country: String
   #field type Metadata does not exist
   metadata: Metadata
}

custom-graphql-schema-13.png

Mapping directive errors

If a type maps to an invalid node type, you will see an error message similar to this.

#invalid node type mapping
type hotel @mapping(node: "jnt:someInvalidNodeType"){
   uuid: ID!
   name: String @mapping(property: "jcr:title")
   address: String
   city: String

   country: String
}

custom-graphql-schema-10.png

If a type contains a field which maps to invalid property, you will see an error message similar to this.

type hotel @mapping(node: "jnt:hotel"){
   uuid: ID!
   name: String @mapping(property: "jcr:title")
   address: String
   city: String

   country: String
   #invalid node property mapping
   startDate: String @mapping(property: "jnt:checkinDate")
}

custom-graphql-schema-11.png

Connection error

If you try to add a connection to an entry point which does not return a list you will see an error in the status tool.

extends type Query {
   #invalid connection
   hotelsByPropertyConnection: Hotel
}

custom-graphql-schema-12.png