Skip to main content

Extend GraphQL API

What you'll learn
  • how to extend the Headless CMS-related GraphQL types and operations
info

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#

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 Model
(click to enlarge)
tip

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 plugin.

api/code/headlessCMS/src/plugins/posts.ts
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins";
import { ListResponse } from "@webiny/handler-graphql/responses";
import { CmsContentEntry, CmsContentEntryMeta } 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.models.get("post");
// Use the `cms.entries.listLatest` method to fetch latest entries for the currently
// logged in user. Note that you could also use the `listPublished` method here instead
// of `cms.entries.listLatest`, if a list of published posts is what you need.
const response: [
CmsContentEntry[],
CmsContentEntryMeta
] = await cms.entries.listLatest(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);
}
}
}
})
];
info

The code above can be placed in the api/code/headlessCMS/src/plugins/posts.ts 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/code/headlessCMS/src/index.ts 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` 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#

What is the context object and where are all of its properties coming from?#

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/code/headlessCMS/src/index.ts entrypoint file.

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

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

Last updated on by Adrian Smijulj