2022-11-16 17:57:44 +00:00
|
|
|
---
|
2022-11-19 17:00:05 +00:00
|
|
|
tags: [graphql]
|
2022-11-16 17:57:44 +00:00
|
|
|
---
|
|
|
|
|
|
|
|
# Using arguments with Apollo Client
|
|
|
|
|
2022-11-18 19:39:00 +00:00
|
|
|
## What are arguments?
|
|
|
|
|
|
|
|
- An argument is a value you provide for a particular field in a query.
|
|
|
|
- The schema must define the arguments that a field accepts (if any)
|
2024-02-02 15:58:13 +00:00
|
|
|
- The resolver invokes a field's provided arguments to determine how to return
|
|
|
|
specific data
|
2022-11-18 19:39:00 +00:00
|
|
|
- Some example use cases of arguments:
|
|
|
|
- retrieve specific objects
|
|
|
|
- filter through a set of objects
|
|
|
|
- transform a field's returned value
|
|
|
|
|
|
|
|
## Updated schema
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
In order to demonstrate arguments we need to expand the
|
2024-02-17 11:57:44 +00:00
|
|
|
[original schema](Apollo_Server.md#example-schema).
|
2022-11-16 17:57:44 +00:00
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Remember a Track is a group of modules that teaches about a specific topic. We
|
|
|
|
are going to add:
|
2022-11-16 17:57:44 +00:00
|
|
|
|
|
|
|
- `description` and `numberOfViews` fields to the original `Track` type
|
|
|
|
- A new `Module` type
|
|
|
|
- A field `modules` to the `Track` type that will be an array of type `Module`
|
|
|
|
|
|
|
|
```js
|
2022-11-18 19:39:00 +00:00
|
|
|
type Query {
|
|
|
|
tracksForHome: [Track!]!
|
|
|
|
}
|
|
|
|
|
2022-11-16 17:57:44 +00:00
|
|
|
type Track {
|
|
|
|
id: ID!
|
2024-02-17 13:27:49 +00:00
|
|
|
authorId: String! // They added this on the sly
|
2022-11-16 17:57:44 +00:00
|
|
|
thumbnail: String
|
|
|
|
length: Int
|
|
|
|
modulesCount: Int
|
|
|
|
description: String
|
|
|
|
numberOfViews: Int
|
|
|
|
modules: [Module!]!
|
|
|
|
}
|
|
|
|
|
|
|
|
type Module {
|
|
|
|
id: ID!
|
2024-02-17 13:27:49 +00:00
|
|
|
length: Int
|
2022-11-16 17:57:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Author {
|
|
|
|
id: ID!
|
|
|
|
name: String!
|
|
|
|
photo: String
|
2022-11-18 19:39:00 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Adding additional query
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
> Currently we only have one query in the schema (`tracksForHome`) and this
|
|
|
|
> returns an array of all the tracks. To demonstrate arguments we want to return
|
|
|
|
> a specific track by its `id`. We are going to add a query that enables this.
|
2022-11-18 19:39:00 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
type Query {
|
|
|
|
track(id: ID!): Track
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
This query will take an `id` as an argument and the resolver will match the `id`
|
|
|
|
to a given track and return that `Track`.
|
2022-11-18 19:39:00 +00:00
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
To define an argument for a field in the schema we add parentheses after the
|
|
|
|
field name. In the parentheses we write the name of the argument and its type.
|
|
|
|
If we have more than one argument we can separate them with commas.
|
2022-11-18 19:39:00 +00:00
|
|
|
|
|
|
|
## Create resolver for new query
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Now we have to create a resolver for our new `track` query. We will quickly run
|
2024-02-17 11:57:44 +00:00
|
|
|
through the [server-side process](Apollo_Server.md).
|
2022-11-18 19:39:00 +00:00
|
|
|
|
|
|
|
### Update our `RESTDataSource`
|
|
|
|
|
|
|
|
```js
|
|
|
|
class TrackAPI extends RESTDataSource {
|
|
|
|
constructor() {...}
|
|
|
|
getTracksForHome() {...}
|
|
|
|
getAuthor(authorId) {...}
|
|
|
|
|
|
|
|
getTrack(trackId){
|
|
|
|
return this.get(`track/${trackId}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
### Add resolver to `resolvers.js`
|
|
|
|
|
|
|
|
```js
|
|
|
|
const resolvers = {
|
|
|
|
Query: {
|
|
|
|
tracksForHome: (_, __, { dataSources }) => {
|
|
|
|
return dataSources.trackApi.getTracksForHome();
|
|
|
|
},
|
|
|
|
track: (_, { id }, { dataSources }) => {
|
|
|
|
return dataSources.trackAPI.getTrack(id);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Track: {
|
|
|
|
author: ({ authorId }, _, { dataSources }) => {
|
|
|
|
return dataSources.trackApi.getAuthor(authorId);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
### Making a query
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
With the server changes complete, we can now issue a query with an argument from
|
|
|
|
the client:
|
2022-11-18 19:39:00 +00:00
|
|
|
|
2022-11-19 17:00:05 +00:00
|
|
|
```js
|
2022-11-18 19:39:00 +00:00
|
|
|
query track(id: 'xyz'){
|
|
|
|
title
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
This will return the `title` field from the track with the specific id. This
|
|
|
|
query complies with the schema since the `Track` type has the field `title`
|
2022-11-18 19:39:00 +00:00
|
|
|
|
|
|
|
### Resolver chains
|
|
|
|
|
|
|
|
What about the following query:
|
|
|
|
|
2022-11-19 17:00:05 +00:00
|
|
|
```js
|
2022-11-18 19:39:00 +00:00
|
|
|
query track(id: 'xyz'){
|
|
|
|
title
|
|
|
|
author {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
2022-11-16 17:57:44 +00:00
|
|
|
```
|
2022-11-19 15:30:04 +00:00
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
It's not obvious how the resolver should respond to the nested query here since
|
|
|
|
author name is not a field on `Track`, it is a field on `Author`:
|
2022-11-19 15:30:04 +00:00
|
|
|
|
2022-11-19 17:00:05 +00:00
|
|
|
```js
|
2022-11-19 15:30:04 +00:00
|
|
|
type Track {
|
|
|
|
id: ID!
|
2024-02-17 13:27:49 +00:00
|
|
|
authorId: String!
|
2022-11-19 15:30:04 +00:00
|
|
|
thumbnail: String
|
|
|
|
length: Int
|
|
|
|
modulesCount: Int
|
|
|
|
description: String
|
|
|
|
numberOfViews: Int
|
|
|
|
modules: [Module!]!
|
|
|
|
}
|
|
|
|
|
|
|
|
type Author {
|
|
|
|
id: ID!
|
|
|
|
name: String!
|
|
|
|
photo: String
|
|
|
|
}
|
|
|
|
```
|
2022-11-19 16:00:05 +00:00
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
This is where we use a **resolver chain**. Because `authorId` exists on `Track`
|
|
|
|
we can use this to get the `name` part of the query. We already have a resolver
|
|
|
|
for `author` under the `Track` resolver:
|
2022-11-19 16:00:05 +00:00
|
|
|
|
2022-11-19 17:00:05 +00:00
|
|
|
```js
|
2022-11-19 16:00:05 +00:00
|
|
|
Track: {
|
|
|
|
author: ({ authorId }, _, { dataSources }) => {
|
|
|
|
return dataSources.trackApi.getAuthor(authorId);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Notice that `authorId` is used in the place of the `parent` parameter. It
|
|
|
|
already exists on the `Track` type that wraps the resolver. So this can be
|
|
|
|
invoked to fulfill `author` and thereby access the author name from the graph.
|
2022-11-19 17:00:05 +00:00
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
This process is also required for our extended schema. The `Track` type now has
|
|
|
|
a `modules` field that comprises an array of the `Module` type.
|
2022-11-19 17:00:05 +00:00
|
|
|
|
|
|
|
Here's a reminder of the `Module` type:
|
|
|
|
|
|
|
|
```js
|
|
|
|
type Module {
|
|
|
|
id: ID!
|
2024-02-17 13:27:49 +00:00
|
|
|
length: Int
|
2022-11-19 17:00:05 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
The modules for a `Track` can be sourced from the track `id`. So we can again
|
|
|
|
add a resolver chain function to the resolvers file:
|
2022-11-19 17:00:05 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
const resolvers = {
|
|
|
|
Query: {
|
|
|
|
tracksForHome: (_, __, { dataSources }) => {
|
|
|
|
return dataSources.trackApi.getTracksForHome();
|
|
|
|
},
|
|
|
|
track: (_, { id }, { dataSources }) => {
|
|
|
|
return dataSources.trackAPI.getTrack(id);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Track: {
|
|
|
|
author: ({ authorId }, _, { dataSources }) => {
|
|
|
|
return dataSources.trackApi.getAuthor(authorId);
|
|
|
|
},
|
|
|
|
modules: ({ id }, _, { dataSources }) => {
|
|
|
|
return dataSources.trackAPI.getTrackModules(id);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Once again we are using a property on the parent `Track` type to use as a
|
|
|
|
parameter in the resolver function.
|
2022-11-19 17:00:05 +00:00
|
|
|
|
|
|
|
This setup would enables like the following to be run:
|
|
|
|
|
|
|
|
```js
|
|
|
|
query track(id: 'xyz'){
|
|
|
|
title
|
|
|
|
modules
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
The `id` parameter would be used by the `modules` resolver to return the array
|
|
|
|
of the `Modules` type.
|
2022-11-19 17:30:05 +00:00
|
|
|
|
|
|
|
## Variables in arguments
|
|
|
|
|
|
|
|
Instead of writing the following within our query constants on the client-side:
|
|
|
|
|
|
|
|
```js
|
|
|
|
query GetTrack {
|
|
|
|
track(id: 'xyz'){
|
|
|
|
title
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
We can make the code more reusable by using variables instead of the hardcoded
|
|
|
|
`id` argument:
|
2022-11-19 17:30:05 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
query GetTrack($trackId: ID!) {
|
|
|
|
track(id: $trackId){
|
|
|
|
title
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
This way we do not need to write a new query constant every time we want to
|
|
|
|
return a specific track.
|
2022-11-19 17:30:05 +00:00
|
|
|
|
|
|
|
## Send query with arguments using Apollo `useQuery`
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
We can now write a proper query using the
|
2024-02-17 11:57:44 +00:00
|
|
|
[useQuery hook](Apollo_Client.md#usequery-hook) from
|
2024-02-02 15:58:13 +00:00
|
|
|
Apollo Client, with variables.
|
2022-11-19 17:30:05 +00:00
|
|
|
|
|
|
|
First define our query constant:
|
|
|
|
|
|
|
|
```js
|
|
|
|
export const GET_TRACK = gql`
|
|
|
|
query GetTrack($trackId: ID!) {
|
|
|
|
track(id: $trackId) {
|
|
|
|
id
|
|
|
|
title
|
|
|
|
author {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
description
|
|
|
|
modules {
|
|
|
|
id
|
|
|
|
title
|
|
|
|
length
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
```
|
|
|
|
|
|
|
|
Then to employ in React:
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
const trackId = "xyz";
|
|
|
|
|
|
|
|
const { loading, error, data } = useQuery(GET_TRACK, {
|
2022-11-28 13:12:07 +00:00
|
|
|
variables: {
|
|
|
|
id: trackId,
|
|
|
|
},
|
2022-11-19 17:30:05 +00:00
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Note that in contrast to the
|
2024-02-17 11:57:44 +00:00
|
|
|
[simple example](Apollo_Client.md#query-constants)
|
2024-02-02 15:58:13 +00:00
|
|
|
because we are using variables, we have to pass-in an additional options object
|
|
|
|
with the query constant that specifies our variables.
|