Simplify your GraphQL API with SDL

October 8, 2024

Exposing JCR definitions via custom GraphQL entrypoints

In Jahia, there are two ways to access content through the GraphQL API:

  • A generic JCR node-based API that is very powerful but requires understanding the underlying JCR tree structure to access content
  • An SDL GraphQL API that makes it possible to map GraphQL fields to content definitions so that the GraphQL API will be very close to the definition structure. No understanding of the JCR node structure is needed.

This section uses the SDL GraphQL API approach, making it a lot more natural to front-end developers as well as any developers who work with GraphQL. You will learn how to map existing JCR types to custom GraphQL API entry points using SDL Generator Tools and use these entrypoints to get data for your application. You will learn how to:

  • Use the tool
  • Get Jahia to recognize the generated SDL schema
  • Run queries against custom entry points

This tutorial builds on the work done in the previous tutorial, the same module and components will be used/reused, so make sure to look at that.

We created a demo web application project with ReactJS and Apollo for reference. You can use this repository https://github.com/Jahia/companies-reactjs-app on the branch graphql-sdl

Mapping a type

Let's see how to map a type.

Add a type

In Jahia, navigate to Developer Tools>SDL Generator Tool.

sdl-generator.png

Click on Add new type and select the type you want.

Note: by default you will see only the types that are editorial content, but you can use the search bar to search for any type on the system.

advanced-headless-tutorial-03.png

If you flip the switch you will be able to access nodes of the given type by path or id if you need to do so.

Note: metadata field is generated by default for all new types. You can remove it if you don't need it.

advanced-headless-tutorial-04.png

Add a property

To select a property click on Add property. You will have two options: Select and map property to type and Select property.

advanced-headless-tutorial-05.png

If the property is not a complex object type, you should choose Select property. Example below shows selection of title property

advanced-headless-tutorial-06.png

You should choose Select and map property to type if the property you want is a weakreference or a child. 

advanced-headless-tutorial-14.png

By default there are four predefined types:

  • Asset
  • ImageAsset
  • Category
  • Metadata

Now map industryCat property to Category type.

advanced-headless-tutorial-15.png

As list switch allows you indicate if you want the property to be fetched as list or not. It is automatically determined in most cases and should only be manually handled if Select a property field is empty in which the system cannot determine which type you want.

You can add more properties to achieve the following result.

Screen Shot 2019-04-11 at 10.01.46 AM.png

Define finders

Once you have your type and properties setup you can transition to the next step to define a finder. A finder is nothing more than an entry point name.

On Define finder page select the type you want and click on Add a finder and add Company  finder to have allCompany entry point.

advanced-headless-tutorial-19.png

In the dialog you will have all available finders based on property selections for a given type.

all...
all...Connection
...byTitle
...byTitleConnection
...byDescription
...byDescriptionConnection
...byEmail
...byEmailConnection

About finder types

There are three classes of finders: all finders, connection finders and by property finders.

All finders return you everything that's found for that particular type.

Connection finders allow you have pagination and by property finders allow you to select nodes by property, even if the property in question is a weakreference.

Once you have selected the type of finder you want and named it, you are ready to transition to schema export.

Export the schema

On the last step of SDL generation process you have two options: to copy resulting schema to clipboard or to download it as a file. It is up to you which option you decide to choose.

type Company @mapping(node: "jdnt:company") {
    title: String @mapping(property: "jcr:title")
    description: String @mapping(property: "overview")
    image: ImageAsset @mapping(property: "thumbnail")
    industry: Category @mapping(property: "industryCat")
}

extend type Query {
    allCompany: [Company]
}

Deploy your schema

Schema deployment is very straight forward. All you need to do is create a module, add graphql-extension.sdl file to META-INF folder and deploy that module. Once the module is deployed any modifications to the schema file will be handled without the need to deploy the module again.

You can check the status of your schema by navigating to Developer Tools>SDL Report Tool.

Run queries with React/Apollo

To use new entry point in the demo web application, there are a few changes that you need to make to ComponentList.container.jsx.

Modify the query as follows.


const COMPANIES_QUERY = gql`
    query CompaniesListQuery($language: String) {
        allCompany(language: $language) {
            uuid
            title
            description
            industry {
                title
            }
            image {
                url
            }
        }
    }
`;

And change the rest of the component as follows

const CompanyListContainer = () => {
    const [selectedIndustry, updateSelectedIndustry] = useState('All');
    const variables = {
        language: "en"
    };
    //By default, there are no companies associated with `Technology` and `Goods` in the demo
    const industries = [
        'All',
        'Media',
        // 'Technology',
        'Retail',
        // 'Goods',
        'Healthcare'
    ];
    const generateURL = path => {
        return `http://localhost:8080/${path}?t=thumbnail2`;
    };
    return (
        <Query query={COMPANIES_QUERY} variables={variables} fetchPolicy="network-only">
            {({loading, data}) => {
                let companies = [];
                if (data && data.allCompanies) {
                    //Build the company data as expected by the Company component
                    data.allCompanies.forEach(company => {
                        companies.push({
                            id: company.uuid,
                            title: company.title,
                            description: company.description,
                            image: generateURL(company.image.url),
                            industry: company.industry.title
                        })
                    });
                }
                return(
                    <Fragment>
                        <Grid container justify="center" spacing={24}>
                            {industries.map((industry) => {
                               return (
                                   <Grid key={industry} item>
                                       <Typography style={{cursor: "pointer"}}
                                                   color={industry === selectedIndustry ? "textPrimary" : "textSecondary"}
                                                   variant={"h5"}
                                                   onClick={() => updateSelectedIndustry(industry)}>
                                           {industry}
                                       </Typography>
                                   </Grid>
                               )
                            })}
                        </Grid>
                        <Grid>
                            <CompanyList loading={loading}
                                         companies={companies.filter(company => selectedIndustry === 'All' || company.industry === selectedIndustry)}
                                         industries={industries}/>
                        </Grid>
                    </Fragment>
                )
            }}
        </Query>
    );
};

That's it, you are now ready to use your new GraphQL entry point.

In this tutorial, you:

  • Used SDL Generator Tools to create a custom type mapping
  • Created a finder for custom type
  • Exported and deployed SDL schema
  • Modified the React app to use new entry point