In this section, you are going to implement the first API operation that provides the functionality of a Hacker News clone: querying a feed of links that were posted by other users.
Let’s start by implementing a feed
query which allows you to retrieve a list of Link
elements. In general, when
adding a new feature to the API, the process will look pretty similar every time:
This process is also referred to as schema-driven or schema-first development.
So, let’s go ahead and tackle the first step, extending the GraphQL schema definition.
Pretty straightforward, right? You’re defining a new Link
type that represents the links that can be posted to Hacker
News. Each Link
has an id
, a description
, and a url
. You’re then adding another root field to the Query
type
that allows you to retrieve a list of Link
elements. This list is guaranteed to never be null
(if anything, it will
be empty) and never contain any elements that are null
- that’s what the two exclamation marks are for.
If you wish to read more about GraphQL and nullability, go ahead to GraphQL official documentation.
The next step is to implement the resolver function for the feed
query. In fact, one thing we haven’t mentioned yet is
that not only root fields, but virtually all fields on the types in a GraphQL schema have resolver functions. So,
you’ll add resolvers for the id
, description
, and url
fields of the Link
type as well.
Let’s walk through the numbered comments again:
Link
type defines the TypeScript object structure that we wish to use in our code. links
variable is used to store the links at runtime. For now, everything is stored only in-memory rather
than being persisted in a database.feed
root field. Notice that a resolver always has to be named exactly after
the corresponding field from the schema definition.Link
type from the schema definition. We’ll
discuss what the parent
argument that’s passed into the resolver here is in a bit.Go ahead and test the implementation by running the server and navigate to http://localhost:3000/graphql
in your browser. If you expand
the documentation of the GraphiQL, you’ll notice that another query called feed
is now available:
Try it out by sending the following query:
query {
feed {
id
url
description
}
}
Awesome, the server responds with the data you defined in links
:
{
"data": {
"feed": [
{
"id": "link-0",
"url": "www.howtographql.com",
"description": "Fullstack tutorial for GraphQL"
}
]
}
}
Feel free to play around with the query by removing any fields from the selection set and observe the responses sent by the server.
Let’s now quickly talk about how a GraphQL server actually resolves incoming queries. As you already saw, a GraphQL query consists of a number of fields that have their source in the type definitions of the GraphQL schema.
Let’s consider the query from above again:
query {
feed {
id
url
description
}
}
All four fields specified in the query (feed
, id
, url
, and description
) can also be found inside the schema
definition. Now, you also learned that every field inside the schema definition is backed by one resolver function
whose responsibility it is to return the data for precisely that field.
Can you imagine what the query resolution process looks like now? Effectively, all the GraphQL server has to do is invoke all resolver functions for the fields that are contained in the query and then package up the response according to the query’s shape. Query resolution thus merely becomes a process of orchestrating the invocation of resolver functions!
One thing that’s still a bit weird in the implementation right now are the resolvers for the Link
type that all seem
to follow a very simple and trivial pattern:
Link: {
id: (parent: Link) => parent.id,
description: (parent: Link) => parent.description,
url: (parent: Link) => parent.url,
}
First, it’s important to note that every GraphQL resolver function actually receives four input arguments. As the remaining three are not needed in our scenario right now, we’re simply omitting them. Don’t worry, you’ll get to know them soon.
The first argument, commonly called parent
(or sometimes root
) is the result of the previous resolver execution
level. Hang on, what does that mean? 🤔
Well, as you already saw, GraphQL queries can be nested. Each level of nesting (i.e. nested curly braces) corresponds to one resolver execution level. The above query therefore has two of these execution levels.
On the first level, it invokes the feed
resolver and returns the entire data stored in links
. For the second
execution level, the GraphQL server is smart enough to invoke the resolvers of the Link
type (because thanks to the
schema, it knows that feed
returns a list of Link
elements) for each element inside the list that was returned on
the previous resolver level. Therefore, in all of the three Link
resolvers, the incoming parent
object is the
element inside the links
list.
Note: To learn more about this, check out this article.
The implementation of the
Link
resolvers is trivial, you can actually omit them and the server will work in the same way as it did before 👌 We just wanted you to understand what’s happening under the hood 🚗