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&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
istrue
RetrieveContentForNonAdminUser
is executed only when$isAdminUser
isfalse
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 fieldsoriginalContent
andwpAdminEditURL
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
}