What you'll learn
  • how to extend the Headless CMS-related GraphQL types and operations
Use the webiny watch command to continuously deploy application code changes into the cloud and instantly see them in action. For quick (manual) testing, you can use the built-in API Playground.

Custom Queries
anchor

Let’s say we wanted to extend our GraphQL schema with a custom listMyPosts query, which, as the name suggests, would enable us to quickly retrieve all posts created via the Headless CMS application, for the currently logged in user.

In other words, we want to return all content entries of the Post content model, where the createdBy points to the currently logged in user. For demonstration purposes, our Post content model will be very simple:

The Post Content ModelThe Post Content Model
(click to enlarge)

The createdBy field is automatically assigned to every content entry and it represents the currently logged in user.

Creating the new listMyPosts query can be achieved via a single GraphQLSchemaPlugin external link plugin.

apps/api/headlessCMS/src/plugins/posts.ts
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins";
import { ListResponse } from "@webiny/handler-graphql/responses";
import { CmsEntry, CmsEntryMeta } from "@webiny/api-headless-cms/types";

// Make sure to import the `Context` interface and pass it to the `GraphQLSchemaPlugin`
// plugin. Apart from making your application code type-safe, it will also make the
// interaction with the `context` object significantly easier.
import { Context } from "~/types";

export default [
  new GraphQLSchemaPlugin<Context>({
    // Extend the `Query` type with the `listMyPosts` query. Note the `PostListResponse` type.
    // It exists because we've previously created the `Post` content model via Admin Area.
    typeDefs: /* GraphQL */ `
      extend type Query {
        # List posts that were created by the currently logged in user.
        listMyPosts: PostListResponse
      }
    `,
    // In order for the `listMyPosts` to work, we also need to create a resolver function.
    resolvers: {
      Query: {
        listMyPosts: async (_, args: { id: string }, context) => {
          const { security, cms } = context;

          // Retrieve the `post` model.
          const model = await cms.getModel("post");

          // Use the `cms.listLatestEntries` method to fetch latest entries for the currently
          // logged in user. Note that you could also use the `listPublished` method here instead
          // of `cms.listLatestEntries`, if a list of published posts is what you need.
          const response: [CmsEntry[], CmsEntryMeta] = await cms.listLatestEntries(model, {
            where: {
              // Retrieving the currently logged in user is as easy
              // as calling the `security.getIdentity` method.
              createdBy: security.getIdentity().id
            }
          });

          return new ListResponse(...response);
        }
      }
    }
  })
];

The code above can be placed in the api/headlessCMS/src/plugins/posts.ts external link file, which doesn’t exist by default, so you will have to create it manually. Furthermore, once the file is created, make sure that it’s actually imported and registered in the api/headlessCMS/src/index.ts external link entrypoint file.

With all the changes in place, we should be able to run the following GraphQL mutation:

{
  listPosts {
    data {
      title
      text
    }
  }
  listMyPosts {
    data {
      title
      text
    }
  }
}

For example:

Using the listMyPosts QueryUsing the listMyPosts Query
(click to enlarge)

As we can see, the listPosts query returned a total of three posts. On the other hand, the listMyPosts only returned posts for the currently logged in user, which is the expected result.

FAQ
anchor

What Is the context Object and Where Are All of Its Properties Coming From?
anchor

In the shown examples, you may have noticed we were using the context object in GraphQL resolver functions. This object contains multiple different properties, mainly being defined from different Webiny applications that were imported in the Headless CMS GraphQL API’s api/headlessCMS/src/index.ts external link entrypoint file.

That’s why, for example, we were able to utilize the cms.models.get external link and cms.entries.listLatest external link methods.

For easier discovery and type safety, we suggest a type is always assigned to the context object in your GraphQL resolver functions.