How a GraphQL request is resolved - serlo/documentation GitHub Wiki

After you have read the introduction to api.serlo.org and an overview to it's architecture we have everything we need to understand how a GraphQL request is resolved. Let's take the following request:

query {
  uuid(
    alias: {
      instance: en
      path: "/community/41297/multiplying-decimal-fractions"
    }
  ) {
    ... on Article {
      currentRevision {
        title
        content
      }
    }
  }
}

Resolving the request step by step

1. Resolve uuid

Since we have a query request we start at the resolver function uuid() of Query. So the following function is called:

const resolvers = {
  Query: {
    async uuid(_parent, args, { dataSources }) {

    }
  }
}

Since we have just started to resolve the GraphQL request the passed parent object for _parent is {} (the underscore in the variable name says that we do not want to use this variable). args hold the arguments given to uuid in the query. In our example args will be { alias: { instance: "en", path: "/community/41297/multiplying-decimal-fractions" } }. dataSources gives access to our data sources.

You can find the actual code for uuid() at https://github.com/serlo/api.serlo.org/blob/main/packages/server/src/schema/uuid/abstract-uuid/resolvers.ts In our case it will look into the path /community/41297/multiplying-decimal-fractions and sees that it already contains the uuid 41297 of the requested article. This id is used to request the article model object with dataSources.serlo.getUuid({ id: 41297 }).

Now the serlo data source will make the following request to the database layer:

curl -X POST \
     -H "Content-Type: application/json"  \
     --data '{ \
         "type": "UuidQuery", \
         "payload": { "id": 41297 } \
     }' https://<IP of database layer>/

The database layer will respond with the model object of the article which is returned as a result of uuid():

{
  "__typename": "Article",
  "id": 41297,
  "currentRevisionId": 44855,
  ...
}

2. Resolve type via __resolveType()

In the GraphQL schema we have defined that the uuid endpoint has the type AbstractUuid:

type Query {
  uuid(alias: ..., id: ...): AbstractUuid
}

AbstractUuid is an interface and thus Apollo Server does not know the concrete GraphQL type of the returned object. To determine the concrete type Apollo Server passes the returned object { __typename: "Article", id: 41297, ... } of uuid() to the function AbstractUuid.__resolveType():

export const resolvers = {
  AbstractUuid: {
    __resolveType(uuid) {
      return uuid.__typename
    },
  }
}

As you can see __resolveType() returns the property __typename of the passed object. In this case we have the object { __typename: "Article", id: 41297, ... } from the last step as the argument uuid and thus the string "Article" is returned. Now Apollo Server knows that { __typename: "Article", id: 41297, ... } shall represent an Article an can proceed with the GraphQL request.

3. Resolve currentRevision

Apollo Server now knows that { __typename: "Article", id: 41297, currentRevisionId: 44855, ... } represent an article. Since we have defined a resolver function Article.currentRevision() Apollo Server knows that this property shall be calculated dynamically. Thus it passes the object to this resolver function as parent:

const resolvers = {
  Article: {
    async currentRevision(parent, _args, { dataSources }) {
      if (parent.currentRevisionId === null) return null
      return await dataSources.serlo.getUuid({ id: parent.currentRevisionId })
    }
  }
}

Since parent.currentRevisionId === 44855 another request is made to the database layer to resolve the article revision 44855:

curl -X POST \
     -H "Content-Type: application/json"  \
     --data '{ \
         "type": "UuidQuery", \
         "payload": { "id": 44855 } \
     }' https://<IP of database layer>/

The response will be something like:

{
  "__typename": "ArticleRevision",
  "id": 44855,
  "title": "Multiplying decimal fractions",
  "content": ""[[{\"col\":24,\"content\":\"## Procedure\"}],[{\"col\":16,\"content\":\"1. Ignore ..."
  ...
}

This is returned as the result of currentRevision().

4. Resolve title and content

Since we have already stated in GraphQL that the property currentRevision of Article is of type ArticleRevision Apollo server already knows the returned object has the concrete type ArticleRevision. Thus the properties title and content can be resolved.

Since there are no resolver functions ArticleRevision.title() nor ArticleRevision.content() and the model object { __typename: "ArticleRevision", id: 44855, title: "Multiplying decimal fractions", ... } already have the properties title and content these values are returned.

Now Apollo Server has collected all necessary data and can return as a result the JSON data:

{
  "data": {
    "uuid": {
      "currentRevision": {
        "title": "Multiplying decimal fractions",
        "content": "[[{\"col\":24,\"content\":\"## Procedure\"}],[{\"col\":16,\"content\":\"1. Ignore ..."
      }
    }
  }
}

Summary

While resolving the query the following happens:

position in query        | state of Apollo Server     | resolver function call      | result of resolver function
------------------------------------------------------------------------------------------------------------------
query {                  | model object: {}           | Query.uuid(                 | {
→ uuid(alias: ...) {     | type: "Query"              |  parent = {},               |   __typename: "Article",
    ... on Article {     |                            |  args = {                   |   id: 41297,
      currentRevision {  |                            |   alias: {                  |   currentRevisionId: 44855,
        title            |                            |    instance: "en",          |   ...
        content          |                            |    path: "/communit...",    | }
      }                  |                            |   }                         |
    }                    |                            |  },                         |
  }                      |                            |  context                    |
}                        |                            | )                           |
------------------------------------------------------------------------------------------------------------------
query {                  | model object: {            | AbstractUuid.__resolveType( | "Article"
  uuid(alias: ...) {     |  __typename: "Article",    |  parent = {                 |
  → ... on Article {     |  id: 41297,                |   __typename: "Article",    |
      currentRevision {  |  currentRevisionId: 44855, |   id: 41297,                |
        title            | }                          |   currentRevisionId: 44855, |
        content          | type: "AbstractUuid"       |  }                          |
      }                  |                            | )                           |
    }                    |                            |                             |
  }                      |                            |                             |
}                        |                            |                             |
------------------------------------------------------------------------------------------------------------------
query {                  | model object: {            | Article.currentRevision(    | {
  uuid(alias: ...) {     |  __typename: "Article",    |  parent = {                 |  __typename: "ArticleRevision",
    ... on Article {     |  id: 41297,                |   __typename: "Article",    |  id: 44855,
    → currentRevision {  |  currentRevisionId: 44855, |   id: 41297,                |  title: "Multipl...",
        title            | }                          |   currentRevisionId: 44855, |  content: "..."
        content          | type: "Article"            |  }                          |  ...
      }                  |                            |  args = {},                 | }
    }                    |                            |  context                    |
  }                      |                            | )                           |
}                        |                            |                             |
------------------------------------------------------------------------------------------------------------------
query {                  | model object: {            | no resolver function call   | "...",
  uuid(alias: ...) {     |  __typename:               | necessary since property    |
    ... on Article {     |    "ArticleRevision",      | be retrieved from model     |
      currentRevision {  |  title: "Multipl...",      | object                      |
        title            |  content: "..."            |                             |
      → content          |  ...                       |                             |
      }                  | }                          |                             |
    }                    |                            |                             |
  }                      |                            |                             |
}                        |                            |                             |

------------------------------------------------------------------------------------------------------------------