wp-graphql
Client-side GraphQL convenience wrapper for the WordPress REST API
Why?
- Declarative data fetching.
- Human-readable queries/mutations.
- Zero nested callbacks or long promise chains.
Install
$ npm install --save wp-graphql
GraphiQL Demo
Note: The demo above queries against the public WordPress REST API demo provided by WordPress. Because of this, some queries and all mutations will not work.
Usage
Load and localize script
When loading the script, localize it with a rest_url
and generated nonce
(only required if you need to make authenticated requests).
Create and use instance
Once an instance is created, you are able to query and mutate data using standard GraphQL syntax.
// myscript.js ; const gql = AUTHroot auth: AUTHnonce ; gql;
Result
{ user: { id: 1, name: 'root', email: 'admin@wordpress.com', }, firstThreeUsers: [ { name: 'Bob' }, { name: 'Sue' }, { name: 'Jan' }, ], settings: { title: 'My WordPress Site', }, }
API
Configuration
The WPGraphQL
class accepts an object with the Config
interface as the second argument to configure the instance.
/** Authenticate with Basic Auth (Requires Basic Auth plugin) */ /** Either a BasicAuth object or a WordPress nonce string */;
Methods
send
Execute a single GraphQL query or mutation.
batch
Execute a chain of GraphQL queries and/or mutations.
If at any point a response returns with a field labelled extract
, all first-level fields are available to be used in the immediately subsequent query or mutation. Variables may also still be passed to batch
, but keep in mind that the variables passed to the batch
signature will always win if a name conflict occurs in an extracted variable.
Keep in mind also that if queries are batched with the same field names, each subsequent query will overwrite the last one. To avoid this, just tag the fields with a unique name.
Example:
; ; gql.batch` query first { extract: me { id name } } query second($id: Int!) { user(id: $id) { name } } mutation third($anotherId: Int!, $name: String) { updateUser(id: $anotherId, name: $name) { id name } }`, , .thenconsole.logdata;
Result
{ extract: { id: 1, name: 'Bob Smith', }, user: { id: 1, name: 'Bob Smith', }, updateUser: { id: 2, name: 'Sally Jones', }, }
Resolvers
The preloaded queries
and mutations
are listed below with their required parameters where applicable.
Note: Some of the parameters are enum
type (e.g. context
). Do not surround these in quotes when performing your queries/mutations. Additionally, parameters of type String
must be surrounded in double quotes ("
).
Queries
Query | Description |
---|---|
categories |
Fetch a list of categories. |
category(id: Int!) |
Fetch a single category by ID. |
comments |
Fetch a list of comments. |
comment(id: Int!) |
Fetch a single comment by ID. |
mediaList |
Fetch a list of media items. |
media(id: Int!) |
Fetch a single media item by ID. |
pages |
Fetch a list of pages. |
page(id: Int!) |
Fetch a single page by ID. |
postStatuses |
Fetch a list of post statuses. |
postStatus(id: PostStatus!) |
Fetch a single post status by name. |
postTypes |
Fetch a list of post types. |
postType(slug: String!) |
Fetch a post type by slug. |
posts |
Fetch a list of posts. |
post(id: Int!) |
Fetch a single post by ID. |
revisions(id: Int!) |
Fetch a list of revisions by post ID. |
revision(id: Int!, parentId: Int!) |
Fetch a single revision by revision ID and parent post ID. |
settings |
Fetch site settings. |
tags |
Fetch a list of tags. |
tag(id: Int!) |
Fetch a single tag by ID. |
taxonomies |
Fetch a list of taxonomies. |
taxonomy(slug: String!) |
Fetch a single taxonomy by slug. |
users |
Fetch a list of users. |
user(id: Int!) |
Fetch a single user by ID. |
me |
Fetch the currently logged in user. |
Mutations
Mutation | Description |
---|---|
addCategory(name: String!) |
Create a new category. |
updateCategory(id: Int!) |
Update a category by ID. |
deleteCategory(id: Int!) |
Delete a category by ID. |
addComment(content: String!) |
Create a new comment. |
updateComment(id: Int!) |
Update a comment by ID. |
deleteComment(id: Int!) |
Delete a comment by ID. |
addMedia(file: String!, filename: String!) |
Create a new media item. Note: file must be of type Blob , File , or ArrayBuffer . |
updateMedia(id: Int!) |
Update media by ID. |
deleteMedia(id: Int!) |
Delete media by ID. |
addPage() |
Create a new page. |
updatePage(id: Int!) |
Update a page by ID. |
deletePage(id: Int!) |
Delete a page by ID. |
addPost(title: String!, content: String!, excerpt: String!) |
Create a new post. Note: Only one of title , content , or excerpt is required for a sucessful request. |
updatePost(id: Int!) |
Update a post by ID. |
deletePost(id: Int!) |
Delete a post by ID. |
deleteRevision(id: Int!, parentId: Int!) |
Delete a single revision by revision ID and parent post ID. |
updateSettings() |
Update site settings. |
addTag(name: String!) |
Create a new tag. |
updateTag(id: Int!) |
Update a tag by ID. |
deleteTag(id: Int!) |
Delete a tag by ID. |
addUser(email: String!, password: String!, username: String!) |
Create a new user. |
updateUser(id: Int!) |
Update a user by ID. |
deleteUser(id: Int!) |
Delete a user by ID. |
Advanced Usage
Static type checking with TypeScript
Note: Requires TypeScript
^2.3.0
. (At time of writing, that's currentlytypescript@next
).
All types in this library are available for consumption. Types that contain a meta
property can optionally be passed the shape of the expected metadata as a Generic to expand your type checking into the meta fields. The API response returned from the send()
method can also optionally be typed by passing the expected shape of the response as a Generic.
If desired, the fields of each type interface can be narrowed by using TypeScript's built-in Pick
method.
; ; ; gql.send` query { user(id: 1, context: edit) { id name meta email } firstThreeUsers: users(per_page: 3, orderby: id) { name } settings { title } }`.then;
Custom queries and mutations
// customQuery.js;const NAMESPACE = 'myRoute/v1'; const getStringData = description: 'Get a string from a custom endpoint.' type: GraphQLString args: id: description: 'The ID of the string I need.' type: GraphQLInt someOtherArg: description: 'Some other argument to pass as a parameter' type: GraphQLString root ; getStringData
// customMutation.js;const NAMESPACE = 'myRoute/v1'; const postStringData = description: 'Post a string to a custom endpoint.' type: GraphQLString args: myString: description: 'Some other argument to pass as a parameter' type: GraphQLString root ; const deleteStringData = description: 'Delete a string from a custom endpoint.' type: GraphQLString args: id: description: 'The ID of the string to delete.' type: GraphQLInt root ; postStringData deleteStringData
// Using the custom queries and mutations.;;; const gql = 'http://localhost:8080/wp-json' queries mutations ;
Generating queries and mutations for custom post types
Let's assume that your site has a custom post type called books
. If you'd like to query and mutate this custom post type similarly to how you would for regular posts, all you need to do is register the custom type with wp-graphql
on instantiation by setting the postTypes
config parameter. This will set up resolvers using the same conventions as regular posts.
; const gql = 'http://localhost:8080/wp-json' postTypes: name: 'book' namePlural: 'books' restBase: 'books' ; gql;
Using default queries, mutations, and schema in your own server side JS codebase
;;; const app = ; const gql = 'https://my-wordpess-site.com/wp-json'; app; app;
Limitations
The primary limitation of this library is that it only provides a convenient way to fetch and mutate data over top of the existing REST API on the client side. Because of this, the primary benefit of GraphQL, querying and mutating data in a single round trip, is lost.
The "Meta" Limitiation
In order to be declarative, GraphQL requires users to be explicit about the shape of their API responses. This creates a unique problem with Post
, User
, and Comment
meta, since all three objects can have essentially an unlimited number of shapes.
Because there is no reasonable way to know up front what shape the Meta fields are going to be, the meta
must be JSON.stringified
prior to being transacted by GraphQL. This process is done automatically for queries, but must be done manually for mutations.
With that in mind, it's important to remember that GraphQL only sees the meta fields as a String
. When the meta field is returned to you, it will be converted to back to an object by wp-graphql
. Although it is an object when you receive it, you will not be able to query into the meta fields like you would with a typical GraphQL object. In other words, the meta
field is a leaf type.