Schema tutorial
Schema tutorialLesson 5: Customizing content for different users

Lesson 5: Customizing content for different users

We can retrieve a different response in a field depending on some piece of queried data, such as the roles of the logged-in user.

GraphQL query to customize content for different users

This GraphQL query retrieves the post content, appending an "Edit this post" link at the bottom of the content for the admin user only:

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
      value: "administrator",
      array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}

For admin users, the response will be:

{
  "data": {
    "user": {
      "isAdminUser": true
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n<p><a href=\"https:\/\/mysite.com\/wp-admin\/post.php?post=1&amp;action=edit\">(Admin only) Edit post<\/a><\/p>"
    }
  }
}

For non-admin users, the response will be:

{
  "data": {
    "user": {
      "isAdminUser": false
    },
    "post": {
      "content": "\n<p>Welcome to WordPress. This is your first post. Edit or delete it, then start writing!<\/p>\n"
    }
  }
}

Having the GraphQL server (given all the possible multiple conditions) dynamically compute the required value for a field:

  • Simplifies the logic of the application, as there is a single source of truth, the code becomes DRY, and clients don't need to implement the corresponding logic anymore
  • Makes the application more reliable, specially when multiple clients access data from the server, as different implementations of the same logic can be non-identical, potentially leading to bugs (more so when clients are based on different technologies, such as JavaScript for a website, Java for an Android app, Swift for an iPhone app, and others)

Step by step: creating the GraphQL query

Below is the detailed analysis of how the query works.

Finding out if the user is an admin

This query checks if the logged-in user has the "administrator" role, and exports this condition under dynamic variable $isAdminUser:

query
{
  me {
    roleNames
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}

Conditional execution of operations

When Multiple Query Execution is enabled, directives @include and @skip can also be applied to operations. This way, we can execute an operation or not depending on the value of some dynamic variable.

In the query below, only one of the two operations will be executed:

  • RetrieveContentForAdminUser is executed only when $isAdminUser is true
  • RetrieveContentForNonAdminUser is executed only when $isAdminUser is false
query RetrieveContentForAdminUser
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  # ...
}
 
query RetrieveContentForNonAdminUser
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  # ...
}

Let's provide two different responses for the post's content field depending on the user being an admin or not:

  • The first operation uses content as an alias, and computes the field's value dynamically, appending fields originalContent and wpAdminEditURL together via _sprintf
  • The second operation retrieves the content field directly
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content
    wpAdminEditURL
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}

Adding the operation to be executed

Now we have two operations that may be executed, however we can provide only one ?operationName=... when executing the query.

Then, we add operation ExecuteAll that depends on both RetrieveContentForAdminUser and RetrieveContentForNonAdminUser, containing the simple field id (because we must query something in the operation):

query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id
}

Invoking the endpoint with ?operationName=ExecuteAll will now load both operations, however only one of them will be actually executed.

Removing unneeded data

The final step is to remove all fields that are auxiliary (and as such we don't need to print their output in the response) via @remove.

The consolidated GraphQL query is:

query InitializeDynamicVariables
  @configureWarningsOnExportingDuplicateVariable(enabled: false)
{
  isAdminUser: _echo(value: false)
    @export(as: "isAdminUser")
    @remove
}
 
query ExportConditionalVariables
  @depends(on: "InitializeDynamicVariables")
{
  me {
    roleNames @remove
    isAdminUser: _inArray(
        value: "administrator",
        array: $__roleNames
    )
      @export(as: "isAdminUser")
  }
}
 
query RetrieveContentForAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @include(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    originalContent: content @remove
    wpAdminEditURL @remove
    content: _sprintf(
      string: "%s<p><a href=\"%s\">%s</a></p>",
      values: [
        $__originalContent,
        $__wpAdminEditURL,
        "(Admin only) Edit post"
      ]
    )
  }
}
 
query RetrieveContentForNonAdminUser($postId: ID!)
  @depends(on: "ExportConditionalVariables")
  @skip(if: $isAdminUser)
{
  post(by: { id : $postId }) {
    content
  }
}
 
query ExecuteAll
  @depends(on: [
    "RetrieveContentForAdminUser",
    "RetrieveContentForNonAdminUser"
  ])
{
  id @remove
}