Skip to main content

Custom Queries

Although the Organic CMS app provides all the necessary data to render each page type, there may be situations where you need to retrieve custom blocks or custom fields data from the CMS. In such cases, you will need custom queries that are available either to specific templates or to the entire application globally. We offer two approaches to accomplish this:

  • Template queries: These queries are specific to a template and are executed on the server each time the template type is requested.
  • Global queries: These queries are executed once on the server and made available to the entire application globally.

Template Queries

As stated in the documentation for registering templates, you have the ability to register multiple templates for each page type. In addition, you can register multiple queries per template. This approach allows you to avoid requesting unnecessary data unless it is required by the template.

Registering Template Queries

We introduced a new type called TemplateQuery, which offers straightforward approach to generate new queries. Furthermore, It introduces a new hook, useTemplateQuery, which can be utilized to retrieve the outcomes of custom queries within a component.

The TemplateQuery type has four properties:

  • query: A required property of type string, representing the GraphQL query body.
  • name: A required property of type string, representing the unique name of the query, which is used to generate a unique key.
  • queryArguments: An optional property that takes a function that returns query arguments based on the main query data and generic settings. The function takes two arguments: contentData of type ContentData and settings of type Settings.
  • cacheKeys: An optional property that takes a function that returns cache keys related to posts and taxonomies displayed on the template by this custom query. The function takes two arguments: queryData of type QueryData and contentData of type ContentData, and returns an array of strings.

Example

Let's say we want to fetch an author's biographical information from the WordPress API. Here's an example of how we can generate a custom query for this:

  1. Let's give the query a name: biographicalDetailsQuery
  2. Let's write the query:
import { gql } from 'graphql-request';

const biographicalDetailsQuery = gql`
query BiographicalDetails($slug: ID!) {
authorTag(id: $slug, idType: SLUG) {
biographicalDetails {
joinDate
highlights {
bulletPoint
}
}
}
}
`;

export default biographicalDetailsQuery;
caution

To ensure that code generation includes custom queries, it is crucial to store all queries in the folder designated in the graphqlCodegenPackageConfig documents property configuration. By default, this folder is set to src/lib/queries/. Hence, it is important to make sure that all custom queries are located in this folder to enable successful code generation.

info

Note that the argument requires a slug to specify the requested author, and the query name "BiographicalDetails" will be included as part of the type names.

  1. To generate types for argument and result data, you can use the yarn graphql:codegen command. This command will generate types based on the queries defined in the designated folder (as specified in the graphqlCodegenPackageConfig documents property configuration).
yarn graphql:codegen
  1. Declare the query inside the desired page type template in the theme file:
// 1. Import custom query types
import {
IBiographicalDetailsQuery,
IBiographicalDetailsQueryVariables,
} from '@orgnc/core/generated/graphql-operations';

// 2. Import query
import biographicalDetailsQuery from 'path/to/biographicalDetailsQuery.ts';

// 3. Declare query template object
const biographicalDetailsQuery: AuthorTemplateQuery<
IBiographicalDetailsQuery,
IBiographicalDetailsQueryVariables,
> = {
name: 'biographicalDetailsQuery',
query: biographicalDetailsQuery,
queryArguments: (data) => {
// Function to get author's slug from default provided template data
// (in this case the author page data since is an author page type template)
return { slug: data.slug || '' };
},
};

// 4. Add query to page type template
const theme = createTheme({
pageTypes: {
// ...
author: {
templates: [
{
name: 'default',
component: AuthorTemplate,
queries: [biographicalDetailsQuery], // <- Custom template queries go here
},
],
},
//...
},
});

export default theme;
  1. Retrieve data within the template that was requested by a custom query using useTemplateQuery.
export default function AuthorTemplate({ authorData }: IAuthorTemplateProps) {
// By calling the useTemplateQuery function, you can obtain the data from the custom query.
// Since we named the query "biographicalQuery," it is now included in the hook's data.
const { data: biographicalQuery } = useTemplateQuery(
biographicalDetailsQuery, // template query
authorData, // template content data
);

return <pre>{JSON.stringify(biographicalQuery, null, 2)}</pre>;
}

Using Template Queries useTemplateQuery

The useTemplateQuery hook is a custom React hook that uses the useQuery hook from the @tanstack/react-query library to fetch data for a given template. It takes in three generic types: ContentData, QueryData, and QueryArguments.

The hook expects two arguments: a templateQuery and contentData. The templateQuery is an object that contains the necessary information to make a query, including a name and a function that returns query arguments. The contentData is the data needed to make the query.

This hook is useful for fetching data needed for a specific template and can be used in conjunction with other React hooks to create more complex functionality.

Using Cache Keys

cacheKeys is an optional property that takes a function that returns a cache keys array related to posts and taxonomies requested by a custom query. To achieve this, we utilize a helper function called getPostCacheKey(id).

When do we need to use cache keys?

Every time you request posts or taxonomies that are not part of the default template query.

Example with Cache Queries

Let's consider the previous example, but this time, let's request some featured posts within this template. To do this, we need to update the query.

import { gql } from 'graphql-request';
import postFragment from '@orgnc/core/lib/wordpress/api/fragments/postFragment';

const query = gql`
query BiographicalDetails($slug: ID!) {
authorTag(id: $slug, idType: SLUG) {
biographicalDetails {
joinDate
highlights {
bulletPoint
}
# Featured Posts
featuredPosts {
... on Post {
...PostFragment
}
}
}
}
}
${postFragment}
`;

export default biographicalDetailsQuery;

Please note that we requested featuredPosts in the query, and because we want to cache these posts, we need to add cacheKeys to the template query.

Lets update the custom template query:

// 1. Import custom query types
import {
IBiographicalDetailsQuery,
IBiographicalDetailsQueryVariables,
} from '@orgnc/core/generated/graphql-operations';

// 2. Import query
import biographicalDetailsQuery from 'path/to/biographicalDetailsQuery.ts';

// 3. Declare query template object
const biographicalDetailsQuery: AuthorTemplateQuery<
IBiographicalDetailsQuery,
IBiographicalDetailsQueryVariables
> = {
name: 'biographicalDetailsQuery',
query: biographicalDetailsQuery,
queryArguments: (data) => {
// Function to get author's slug from default provided template data
// (in this case the author page data since is an author page type template)
return { slug: data.slug || '' };
},
cacheKeys: (queryData: IBiographicalDetailsQuery) => {
// Iterate over all the requested posts and use their database Ids to generate cache keys using the `getPostCacheKey` function.
return queryData.authorTag?.biographicalDetails?.featuredPosts
?.filter((post): post is IPostFragment => post?.__typename === 'Post')
// Generate cache keys using the `getPostCacheKey` function.
?.map((post) => getPostCacheKey(post.databaseId))
|| [];
}
};

// 4. Add query to page type template
const theme = createTheme({
pageTypes: {
// ...
author: {
templates: [
{
name: 'default',
component: AuthorTemplate,
queries: [biographicalDetailsQuery],
},
],
},
//...
},
});

export default theme;

Global Queries

Registering Global Queries

Organic CMS provides data that is globally available, such as settings, tokens, and more. To add data to this global query and make it accessible globally, you can extend the global query. To extend the global query.

A global query object has the following attributes:

  • query: A required property of type string, representing the GraphQL query body.
  • name: A required property of type string, representing the unique name of the query, which is used to generate a unique key.
  • cacheKeys: An optional property that takes a function that returns cache keys related to posts and taxonomies requested by this custom query. The function takes one argument: queryData of type QueryData, and returns an array of strings.
import someQuery from 'path/to/someQuery';

const theme = createTheme({
//...
extend: {
queries: {
global: [someQuery]; // Queries here
}
}
});

export default theme;

Using Global Queries useGlobalQuery

To utilize this extended data, you can call the useGlobalQuery hook to retrieve the data, passing the extended query as a parameter.

import someQuery from 'path/to/someQuery';
import useGlobalQuery from '@orgnc/core/hooks/useGlobalQuery';

export default function SomeComponent() {
const { data } = useGlobalQuery(someQuery);

return <pre>{JSON.stringify(data, null, 2)}</pre>;
}