Skip to content

Commit cd0048f

Browse files
committed
add solution fundamentals
1 parent 66b7899 commit cd0048f

File tree

5 files changed

+603
-435
lines changed

5 files changed

+603
-435
lines changed

README.md

Lines changed: 221 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,263 @@
1-
# Thinking in GraphQL exercise
1+
# GraphQL API Fundamentals
22

33
This exercise is part of the [React GraphQL Academy](http://reactgraphql.academy) learning material. The goal of the exercise is to help you get started transitioning from REST to GraphQL.
44

55
## Learning objectives
66

7-
- Thinking in Graphs
8-
- Learn how to connect resolvers to a REST API
9-
- Understand Schema Design principles
7+
- Understand the main functionalities and responsibilities of a GraphQL server
8+
- Learn how to migrate an existing REST API to GraphQL and start “thinking in graphs”
9+
- Start identifying potential problems when running real-world GraphQL APIs
1010

1111
## Exercise part 1
1212

13-
[https://rickandmortyapi.com/graphql/](https://rickandmortyapi.com/graphql/)
13+
[](TODO DEPLOY URL)
1414

15-
- Query a list with all the character names
16-
- Query how many characters are in the system
17-
- Query a single characther by id (try id equals 1) and get its name
15+
- Query a list with all the training titles
16+
- Query how many training are in the system?
17+
- Query a single training by id (try id equals TODO ADD ONE????????) and get its name
1818
- How many types do we have in the system?
1919

2020
## Exercise part 2
2121

2222
### To get started
2323

24-
We are going to create our own GraphQL API on top of this [Rick and Morty API](https://rickandmortyapi.com/documentation/#rest)
24+
We are going to create our own GraphQL API on top of this [REST API](https://mockedrestapi.reactgraphql.academy/)
2525

26-
- `git clone git@github.com:reactgraphqlacademy/rest-to-graphql-workshop.git`
27-
- `cd rest-to-graphql-workshop`
26+
- `git clone https://github.com/reactgraphqlacademy/graphql-api-training.git`
27+
- `cd graphql-api-training`
2828
- `yarn install` or `npm install`
2929
- `yarn start` or `npm start`
3030

31+
### Before we start
32+
33+
- Clone the repo, git checkout the `fundamentals` branch, install the dependencies and let me walk you through the code meanwhile.
34+
- We use nodemon in the `start` script, so every time you save the server will restart automatically.
35+
- The `src/index.js` is the [getting started tutorial](https://www.apollographql.com/docs/apollo-server/getting-started/) from Apollo.
36+
- Let's replace the schema:
37+
38+
```graphql
39+
type Query {
40+
books: [Book]
41+
}
42+
```
43+
44+
with
45+
46+
```graphql
47+
type Query {
48+
avocados: [Book]
49+
}
50+
```
51+
52+
What do we need to change so the field avocados returns the array of books when we run the query? I'll give you 2 minutes to fix it :)
53+
3154
### Tasks
3255

33-
- [ ] 1. Create a `Character` type in your schema. Use the [documentation of the character endpoint](https://rickandmortyapi.com/documentation/#character-schema) to define the shape of the `Character` type.
56+
⚠️ Some info before you start the tasks:
57+
1- You can define an array using square brackets and the type, example `[Book]`
58+
2- You can use the type `ID` for ids.
59+
3- In GraphQL types are nullable by default. If you want to make a type non-nullable use `!` (excalmation mark). Example:
3460

35-
- [ ] 1.1. Add a `characters` field to the `Query` type. You can replace the `books` field from Query type on line 32 with `characters` since we won't use books. The `characters` field in the `Query` type should return an array of [Character].
36-
- [ ] 1.2. Add a `characters` resolver to the Query's resolvers. You can replace the `books` field from Query type on line 40 with `characters` since we won't use books. You can return the mock characters array (which is in the scope and defined at the bottom of the file index.js) in the resolver function.
37-
- [ ] 1.3 You should be able to manually test the `characters` query in Playground at [http://localhost:4000/](http://localhost:4000/)
61+
```graphql
62+
type Book {
63+
id: ID!
64+
}
65+
```
3866

39-
- [ ] 2. Create an `Episode` type in your schema. Use the [documentation of the episode endpoint](https://rickandmortyapi.com/documentation/#episode-schema) to define the shape of the `Episode` type. Here you are practicing what you've learned on the previous task (1).
67+
To complete the tasks you'll use the mock data and helper functions that are at the bottom of the file `src/index.js`.
4068

41-
- [ ] 2.1. Add an `episodes` field to the `Query` type. The `episodes` field should return an array of [Episode]
42-
- [ ] 2.2. Add an `episodes` resolver to the Query's resolvers. You can return the mock episodes array (which is in the scope and defined at the bottom of the file index.js) in the resolver function.
43-
- [ ] 2.3 You should be able to manually test the `episodes` query in Playground at [http://localhost:4000/](http://localhost:4000/)
69+
- [ ] 1. Create a `Training` type in your schema. Define the following fields `title`, `id`, `objectives`, `curriculum`. Have a look at the mock training data in `src/index.js` to identify the types of each field.
4470

45-
- [ ] 3. Replace the mock data using https://rickandmortyapi.com/documentation/#rest.
71+
- [ ] 1.1. Add a `trainings` field to the `Query` type. You can replace the `books` field from Query type with `trainings` since we won't use books. The `trainings` field in the `Query` type should return an array of training. .
72+
- [ ] 1.2. Add a `trainings` resolver to the Query's resolvers. You can replace the `books` field from Query type with `trainings` since we won't use books. You can return the trainingMockData array (which is in the scope and defined at the bottom of the file index.js) in the resolver function.
73+
- [ ] 1.3 You should be able to manually test the `trainings` query in Playground at [http://localhost:4000/](http://localhost:4000/)
4674

47-
- You can use the `fetchEpisodes` and `fetchCharacters` defined at the bottom of this file `src/index.js`
48-
- You'll need to replace mock data in 2 different places:
49-
- Query characters
50-
- Query episodes
75+
- [ ] 2. Create a `Discount` type in your schema. Define only the fields `code`, `id`, and `discountPercentage`. Have a look at the discount mock data in `src/index.js` to identify the types of each field.
5176

52-
- [ ] 4. Create a relationship between Episode type and Character type in your schema. Please have a look at the [documentation of the episode endpoint](https://rickandmortyapi.com/documentation/#episode-schema) to see how to get the episodes of a given character (heads up! we are calling the field in our Characters `episodes` but the REST API is calling the field that returns an array of episodes as `episode` - singular!). Hints:
77+
- [ ] 2.1. Add an `discounts` field to the `Query` type. The `discounts` field should return an array of discounts.
78+
- [ ] 2.2. Add an `discounts` resolver to the Query's resolvers. You can return the mock discounts array (which is in the scope and defined at the bottom of the file index.js) in the resolver function.
79+
- [ ] 2.3 You should be able to manually test the `discounts` query in Playground at [http://localhost:4000/](http://localhost:4000/)
5380

54-
- You need to add a `Character` key in the resolvers object and an object with an `episodes` key in `Character`. Similar to the Author type and books field in the [Apollo documentation](https://www.apollographql.com/docs/apollo-server/essentials/data#resolver-map). Hint: The first argument of the resolver is the 'parent' type, in this case, the parent of the `episodes` field is the `Character`. parent.episode gives you the array of episodes returned from the REST API.
55-
- You can use the helper fetch functions defined at the bottom of this file `src/index.js`.
81+
- [ ] 3. Replace the mock data with real data using the following endpoints:
82+
- [https://mockedrestapi.reactgraphql.academy/v1/trainings](https://mockedrestapi.reactgraphql.academy/v1/trainings)
83+
- [https://mockedrestapi.reactgraphql.academy/v1/discounts](https://mockedrestapi.reactgraphql.academy/v1/discounts)
5684

57-
- [ ] 5. Create a query that returns a single Character given an id. You need to fetch the character using `https://rickandmortyapi.com/documentation/#get-a-single-character`. Hint, you need to use [arguments](https://graphql.org/graphql-js/passing-arguments/)
85+
Hint. You can use the `fetchTrainings` and `fetchDiscounts` defined at the bottom of this file `src/index.js`
5886

59-
### Bonus
87+
- You'll need to replace mock data in 2 different places:
88+
- Query discounts
89+
- Query trainings
6090

61-
- Create the types and resolvers so the following query works:
91+
Note on mocking. In the next session we'll use the automocking feature of Apollo Server. The only thing you need to do is `mocks:true` in your Apollo Server configuration. More info [here](https://www.apollographql.com/docs/apollo-server/testing/mocking/).
6292

93+
```js
94+
const server = new ApolloServer({
95+
typeDefs,
96+
mocks: true // ⬅️⬅️⬅️⬅️
97+
});
6398
```
64-
query episode {
65-
episode(id: 1) {
99+
100+
#### 🏋️‍♀️ Bonus exercise part 2
101+
102+
Congratulations, you've completed part 2! You've learned how to create object types in GraphQL and add fields to them using scalar types (`String`,`Int`, `ID`) or your own object types (`Training`, `Discount`). You've also learned how to create a relationship between two object types.
103+
104+
In GraphQL you can also create your custom scalars types, like [Date](https://graphql.org/learn/schema/#scalar-types).
105+
106+
Bonus task, add a field called `startDate` to the `Training` object type using a `DateTime` scalar type. GraphQL doesn't provide a DateTime type. Instead of creating a custom `DateTime` scalar type you are going to use [https://github.com/excitement-engineer/graphql-iso-date](https://github.com/excitement-engineer/graphql-iso-date). Note, the package is already installed in package.json.
107+
108+
## Exercise part 3
109+
110+
### Before we start
111+
112+
Resolvers are functions that have 4 arguments `(parent, args, context, info)`. In this exercise, we are only going to use the first 2 arguments: `parent` and `args`.
113+
114+
#### The first argument of the resolver
115+
116+
The first argument, often called `parent`, points to the parent object. For instance, we could override the default resolver of the title field in the Training and return an upper case version of the title.
117+
118+
⚠️ Trainer implements:
119+
120+
```js
121+
const resolvers = {
122+
Query: {
123+
//...
124+
},
125+
Training: {
126+
title: (parent) {
127+
return parent.title.toUpperCase()
128+
}
129+
}
130+
};
131+
```
132+
133+
We could also create a new field that returns the upper case version of the title without changing the title field. Example:
134+
135+
⚠️ Learners implement (only 5 minutes to implement and write a query to test it!):
136+
137+
```graphql
138+
type Training {
139+
title: String!
140+
upperCaseTitle: String!
141+
# the rest remains the same
142+
}
143+
```
144+
145+
```js
146+
const resolvers = {
147+
Query: {
148+
//...
149+
},
150+
Training: {
151+
upperCaseTitle: (parent) {
152+
return parent.title.toUpperCase()
153+
}
154+
}
155+
};
156+
```
157+
158+
🏋️‍♀️Bonus exercise, return all the URLs of the discounts field in upper case.
159+
160+
#### The second argument of the resolver
161+
162+
The second argument of the resolver (we are calling it `args`) points to the arguments passed to the field. In the following example `args` contains `id`:
163+
164+
```js
165+
const schema = gql`
166+
type Query {
167+
author(id: ID!): Author
168+
}
169+
`;
170+
const resolvers = {
171+
Query: {
172+
author(parent, args) {
173+
console.log(args); // { id: 3 } based on the query below
174+
}
175+
}
176+
};
177+
```
178+
179+
```graphql
180+
query authorName {
181+
author(id: 3) {
66182
name
67-
characters {
68-
name
183+
}
184+
}
185+
```
186+
187+
### Tasks
188+
189+
To complete the tasks you'll use the helper functions that are at the bottom of the file `src/index.js`
190+
191+
- [ ] 4. Implement a new field in the `Query` type that returns a single training given an id. You need to fetch the training from this endpoint `https://restapi.reactgraphql.academy/v1/trainings/` + `id`. Hint, you need to pass [arguments](https://graphql.org/graphql-js/passing-arguments/) to the field, and then use the second argument in the resolver. There is a helper function at the bottom of `src/index.js`.
192+
193+
Once implemented you should be able to run the following query:
194+
195+
```graphql
196+
query getTraining {
197+
training(id: "tra:22") {
198+
title
199+
}
200+
}
201+
```
202+
203+
- [ ] 5. Create the following relationship between the Training type and the Discount type in your schema.
204+
205+
```graphql
206+
type Training {
207+
discounts: [Discount]
208+
# the rest of the fields remain the same
209+
}
210+
```
211+
212+
- You need to add a `Training` key in the resolvers object and an object with an `discounts` key in `Training`. Similar to the Author type and books field in the [Apollo documentation](https://www.apollographql.com/docs/apollo-server/essentials/data#resolver-map)
213+
- You need to use the **first argument of the resolver**: the 'parent'. In this case, the parent of the `discounts` field is the `Training`. `parent.discounts` gives you the array of URLs that you can use to fetch each discount from the REST API.
214+
- You can use the helper function `fetchDiscountByUrl` defined at the bottom of this file `src/index.js`.
215+
- Heads up! We want our Training type to have a field called `discounts` that returns an array of `Discount` types not an array of `String`
216+
- Once implemented, you should be able to run the following query:
217+
218+
```graphql
219+
query getTraining {
220+
training(id: "tra:22") {
221+
title
222+
discounts {
223+
code
69224
}
70225
}
71226
}
72227
```
73228

74-
- Once implemented, do you see any vulnerability issues on that query?
229+
#### 🏋️‍♀️ Bonus exercise part 3
230+
231+
- Create the types and resolvers so the following query works:
232+
233+
```graphql
234+
query getDangerousDiscount {
235+
discount(id: "dis:421") {
236+
code
237+
training {
238+
title
239+
discounts {
240+
code
241+
# why this query could be dangerous?
242+
}
243+
}
244+
}
245+
}
246+
```
247+
248+
Once implemented, do you see any problems/ vulnerability issues on that query?
249+
250+
## Homework
251+
252+
You are going to build a GraphQL API on top of an existing REST API. Steps:
253+
254+
1- Choose a public API. You have a list of public APIs [here](https://github.com/public-apis/public-apis). Suggestion, choose an API that doesn't require authentication and has decent documentation.
255+
256+
2- Create a GraphQL server to validate and execute the GraphQL queries. You can get started using the [getting started tutorial](https://www.apollographql.com/docs/apollo-server/getting-started/) from Apollo Server.
257+
258+
3- Create the GraphQL schema using the [Schema Definition Language (SDL)](https://www.prisma.io/blog/graphql-sdl-schema-definition-language-6755bcb9ce51) . You'll define types and relationships between those types.
259+
260+
4- Add the resolvers to your schema. We are following a SDL-first approach to build our schema. It's the most popular approach in the GraphQL JavaScript community, but be aware that it's not the only one. You can read more about it and other alternatives in this [article](https://www.prisma.io/blog/the-problems-of-schema-first-graphql-development-x1mn4cb0tyl3).
75261

76262
## Articles and links
77263

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
},
1919
"homepage": "https://github.com/reactgraphqlacademy/rest-to-graphql-workshop#readme",
2020
"dependencies": {
21-
"apollo-server": "^2.5.0",
22-
"graphql": "^14.3.1",
21+
"apollo-server": "^2.11.0",
22+
"graphql": "^14.6.0",
23+
"graphql-iso-date": "^3.6.1",
2324
"graphql-relay": "^0.6.0",
2425
"node-fetch": "^2.6.0"
2526
},

0 commit comments

Comments
 (0)