Skip to content

English style corrections #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions introduction/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Without going in details with things like partial matches, etc., the above state

### The relationships (`graph`)

Here it comes the `graph` thing.
Now as for the `graph` thing:

If you go to the [Thinking in Graphs](https://graphql.org/learn/thinking-in-graphs/) section at [graphql.org](https://graphql.org/) you'll see the phrase **"It's Graphs All the Way Down"** with a citation to the ["Turtles all the way down"](https://en.wikipedia.org/wiki/Turtles_all_the_way_down) expression referring to the infinite regress problem. Despite the philosophical implications that you can explore if you want, what we can derive from that phrase is that it's all about graphs!

Expand All @@ -74,7 +74,7 @@ type Person {

`Person` is clearly a `vertex` or `node`, which has many properties related to it, hence we can describe the properties as `nodes` and the relationships as `edges`!!! All the way down! until no edges are found and all the data structure is returned.

Also did you noticed that `friend` property ( only one possible friend? that's cruel ) can hold a `Person`?
Also did you notice that `friend` property ( only one possible friend? that's cruel ) can hold a `Person`?

That means:

Expand Down Expand Up @@ -135,14 +135,14 @@ Let's take a look at the best explanation about this topic we've found: [GraphQ

## GraphQL vs RESTful

There are a [lot of documents on the web](https://www.google.com/search?q=restful+vs+graphql) describing the "differences" between them, some are very precise, detailed and descriptive, but a lot are misleading or inaccurate (that's why the quotes). It's extremely important, before moving forward, to make some of those statements clear.
There are a [lot of documents on the web](https://www.google.com/search?q=restful+vs+graphql) describing the "differences" between them, some are very precise, detailed and descriptive, but a lot are misleading or inaccurate (that's why "differences" is in quotes). It's extremely important, before moving forward, to make some of those statements clear.

It:

- WON'T lower down your database load, it doesn't care how/where you persist your data
- WON'T lower down the network traffic between your graphQL server and your persistence infrastructure per-se, that's business logic and not graphql ... but
- WILL tend to lower down the network traffic between the clients and the graphQL server
- WON'T solve your messy/inconsistent data structure or your legacy mixed persistence infrastructure ... but
- WON'T reduce your database load; it doesn't care how/where you persist your data
- WON'T reduce the network traffic between your graphQL server and your persistence infrastructure per-se, that's business logic and not graphql ... but
- WILL tend to reduce the network traffic between the clients and the graphQL server
- WON'T solve your messy/inconsistent data structures or your legacy mixed persistence infrastructure ... but
- WILL help you provide a consistent, unified and scalable contract for your data structure from graphql to the clients.

That said, we'd like to share with you some enlightening insights from a Facebook Engineering platform's post, see an extract below.
Expand Down
40 changes: 20 additions & 20 deletions lessons/day_01/day_01.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Try it yourself here: [AST Explorer](https://astexplorer.net/#/gist/bc30ff1ae53a

#### 2. Validation

Now is time to validate the produced AST:
Now it's time to validate the produced AST:

> GraphQL does not just verify if a request is syntactically correct, but also ensures that it is unambiguous and mistake‐free in the context of a given GraphQL schema.
>
Expand All @@ -87,7 +87,7 @@ Now is time to validate the produced AST:
>
> Source: [GraphQL spec (June 2018) - Validation](http://spec.graphql.org/June2018/#sec-Validation)

The brilliant thing of the validation phase is that, as a developer, you have to do nothing about it!! The runtime will do that for you and in case of error it'll provide you a verbose error message so you can fix it.
The brilliant thing of the validation phase is that, as a developer, you don't have to do anything about it!! The runtime will do that for you and in case of error it'll provide you a verbose error message so you can fix it.

What?!!! 🤔

Expand All @@ -97,7 +97,7 @@ What?!!! 🤔
– yes
– will it check if the type of the argument is defined on the Query Type?
– yes
– will it recursively do those verification down to the last leaf?
– will it recursively do those verifications down to the last leaf?
– yes
– will it ...
– enough
Expand All @@ -119,13 +119,13 @@ Let's see how that might look like:

### A Query-driven schema approach

Of course you can design your schema mirroring your data storage structure, nothing stops you from doing that ... but ... what's the advantage on that?
Of course you can design your schema mirroring your data storage structure, nothing stops you from doing that ... but ... what's the advantage of that?

The real power of your schema design relies on abstraction! You can create your Object Types based on the operations you need to execute, AND you can populate those object from any number of sources! You may have one field coming from a specific table of a MySQL database and another field coming from MongoDB and another coming from an XML-RPC or a third-party API!
The real power of your schema design relies on abstraction! You can create your Object Types based on the operations you need to execute, AND you can populate those objects from any number of sources! You may have one field coming from a specific table of a MySQL database and another field coming from MongoDB and another coming from an XML-RPC or a third-party API!

Since the query hierarchical structure is self-descriptive:

1. Think about which data you're gonna need and how the relationships should look like.
1. Think about which data you're gonna need and what the relationships should look like.
2. Write a query
3. Define your Type system
4. Define your resolvers
Expand All @@ -136,7 +136,7 @@ That's a game changer, now we have:

1. An agnostic data storage layer that doesn't need to know or consider the client needs
2. An abstraction layer able to easily define a data shape contract independently from how and where the data is stored, and adaptable to the client's needs
3. A declarative client layer able to lead the way the data and operation's shapes should look like
3. A declarative client layer able to drive the way the data and operation's shapes should be

Note the scale now is tilted to the client's need and not the other way around.

Expand All @@ -155,11 +155,11 @@ Your **schema** acts as an explicit contract which **determines**

### Breaking Changes

The schema **"contract"** is one of the most important, correction, we'd dare to say **IS THE MOST IMPORTANT** part of your GraphQL API, and keeping it consistent over time is critical.
The schema **"contract"** is one of the most important, corrections, we'd dare to say **IS THE MOST IMPORTANT** part of your GraphQL API, and keeping it consistent over time is critical.

> You can change the way you solve a problem, or break down a solution into many pieces, or optimize things, granted you won't introduce a breaking change on your schema.

If you do so, you're gonna be in big trouble (people's gonna hurt you) since everyone is expecting additive changes without mayor versioning changes of the API and ideally an ad-vitam backwards compatible API.
If you do so, you're gonna be in big trouble (people are gonna hurt you) since everyone is expecting additive changes without mayor versioning changes of the API and ideally an ad-vitam backwards compatible API.

Here's a list of things on the DO-NOT-EVER-EVER-BY-ANY-MEANS-DO list:

Expand Down Expand Up @@ -216,7 +216,7 @@ We won't go again through the "execution phase" we already saw on the Query sect
Why not just "containing arbitrary code"?
What's that "body" word telling us?

Depending on the language it might be explicit, or implicit, but **every resolver function in GraphQL will accept 4 positional arguments** (that's not on the body and it's not arbitrary)
Depending on the language it might be explicit, or implicit, but **every resolver function in GraphQL will accept 4 positional arguments** (that are not on the body...and this is not arbitrary)

```javascript

Expand All @@ -238,8 +238,8 @@ function myResolver (obj, args, context, info){

In case you didn't specify a resolver for a type, GraphQL will fallback to a **Default Resolver** which will:

1. Returns a property from `obj` with the same field name, or
2. Calls a function on `obj` with the same field name and passes the query arguments along to that function.
1. Return a property from `obj` with the same field name, or
2. Call a function on `obj` with the same field name and passes the query arguments along to that function.

For more detailed descriptions:

Expand Down Expand Up @@ -291,7 +291,7 @@ At this point you might have noticed some weird things.

1. `getPerson` is returning a partial shape
2. There's no resolver for `Person` or for its fields.
3. Some information (`age`) I asked for is not present on the resolver's returned data, but is present on the output
3. Some information (`age`) I asked for is not present in the data returned by the resolver, but it is present on the output
4. All above just happened without errors

What happened here?
Expand Down Expand Up @@ -328,7 +328,7 @@ and here a human readable explanation from [Apollo team's Default resolvers docu

If you were really attentive there's another interesting thing happening there:

`age` is defined as a `nullable` property in our schema, so defaulting to `null` when the property is missing makes total sense, on the other hand, `fullName` is defined as `non-nullable`! why it didn't throw an error?
`age` is defined as a `nullable` property in our schema, so defaulting to `null` when the property is missing makes total sense. On the other hand, `fullName` is defined as `non-nullable`! Why didn't it throw an error?

Because a resolver will ONLY be invoked when the information is required for the output, since it's not directly or indirectly (relationship) involved on the requested query, it won't be executed, therefore it won't fail.

Expand Down Expand Up @@ -416,7 +416,7 @@ WOW! The top-level resolver `getPerson` passed along the `Person` object contain

In a real world application you'll deal with one or more different sources for your data which will require asynchronous actions to return the data. One of the possible values a resolver can return is a `Promise`.

Here a trivial example in JavaScript
Here is a trivial example in JavaScript

```javascript
const resolverMap = {
Expand Down Expand Up @@ -445,7 +445,7 @@ The top-level resolver will return a `Promise`, after 5 seconds it'll resolve to

### Multiple Queries

Let's put it this way: Is just like the **"Fight Club"** rules
Let's put it this way: It's just like the **"Fight Club"** rules

1. What's the first rule of GraphQL? **"It's Graphs All the Way Down"**
2. What's the second rule of GraphQL? **"It's Graphs All the Way Down"**
Expand All @@ -464,7 +464,7 @@ query {
}
```

If we already said **all siblings are executed in parallel** like `name` and `age` of `getPerson`, what does it make the graph relationship different from `getPerson` and `persons` of `query`?
If like we already said, **all siblings are executed in parallel** like `name` and `age` of `getPerson`, what difference is there between graph relationships `getPerson` and `persons` of `query`?

Nothing.

Expand Down Expand Up @@ -531,7 +531,7 @@ If we have Person `A`, `B`, `C`, `D` and all persons (but `A`) are friends of `A

It means that we didn't over fetch data from the client to GraphQL server, but we did from GraphQL to the persistence system because we had to ask for the `A` Person's data 4 times!! (`n` rountrips for the friends + `1` for the `A` Person)

This problem is usually tackled with **batched requests** or **caching** and even though there's nothing out of the box that automatically do that for you there's no need to re-invent the wheel, there are several options already available for you to include in your application, you might do it client-side, server-side, in-memory, HTTP caching and it'll depend on the tools available for your architecture.
This problem is usually tackled with **batched requests** or **caching** and even though there's nothing out of the box that automatically do that for you, there's no need to re-invent the wheel. There are several options already available for you to include in your application. You might do it client-side, server-side, in-memory, HTTP caching and it'll depend on the tools available for your architecture.

See the [Learning Resources](#learning-resources) for more info

Expand All @@ -541,10 +541,10 @@ See the [Learning Resources](#learning-resources) for more info

The execution flow is [non-deterministic](https://en.wikipedia.org/wiki/Nondeterministic_algorithm) because

- Even though **BFS** is a well known algorithm, the **execution order** for each sibling node is **NOT GUARANTEED**, it' will depend on the runtime implementation.
- Even though **BFS** is a well known algorithm, the **execution order** for each sibling node is **NOT GUARANTEED**, it will depend on the runtime implementation.
- Since resolvers can be asynchronous, the **resolution order** for each sibling node or an entire branch is **NOT GUARANTEED**

So, **defining** your **resolvers** as **atomic** and **pure functions** is critical. Meaning **DON'T mutate the context object** on your resolvers, ever, or you'll get badly hurt rather sooner than later.
So, **defining** your **resolvers** as **atomic** and **pure functions** is critical. Meaning **DON'T mutate the context object** on your resolvers, ever, or you'll get badly hurt sooner rather than later.

> ...the resolution of fields other than top‐level mutation fields must always be **side effect‐free** and **idempotent**, **the execution order must not affect the result**...
>
Expand Down
14 changes: 7 additions & 7 deletions lessons/day_02/day_02.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Arguments

On [Day 01](../day_01/day_01.md) we learned [queries](../day_01/day_01.md#query) and [resolvers](../day_01/day_01.md#resolver)., and we also learned how to ask for a subset of scalars from a given object (or each one of a list of objects). But what if we want to request a specific subset of records based on its values (filtering) or tell the server to perform a specific data transformation on a specific field? Here's where `arguments` comes into play.
On [Day 01](../day_01/day_01.md) we learned [queries](../day_01/day_01.md#query) and [resolvers](../day_01/day_01.md#resolver)., and we also learned how to ask for a subset of scalars from a given object (or each one of a list of objects). But what if we want to request a specific subset of records based on its values (filtering) or tell the server to perform a specific data transformation on a specific field? Here's where `arguments` come into play.

Imagine a scenario where the underlying persistence system returns a collection of rows, we usually filter with a URL query string param on a REST request.

Expand Down Expand Up @@ -425,11 +425,11 @@ Sending an operation is not only a communication overhead but is also a potentia

How to solve that?

The name is **Persisted queries** and they're way beyond the scope of this training but they're absolutely worthy to mention.
The name is **Persisted queries** and they're way beyond the scope of this training but they're absolutely worth mentioning.

#### TL;DR

Imagine a mechanism to define at development time a map to connect your operations to an identifier, this map is a contract between the client and the server so that you can perform a query defining the identifier and the variables, if the identifier is invalid so the operation will be; this way you perform only the allowed ops and also save a lot of traffic. There are several implementations of this concept, a simple [Google search](https://www.google.com/search?q=graphql+persisted+queries) will give you more info.
Imagine a mechanism that, at development time, defines a map for connecting your operations to an identifier. This map is a contract between the client and the server so that you can perform a query defining the identifier and the variables, if the identifier is invalid, the operation will be too; this way you perform only the allowed ops and also save a lot of traffic. There are several implementations of this concept, a simple [Google search](https://www.google.com/search?q=graphql+persisted+queries) will give you more info.

## Exercise

Expand Down Expand Up @@ -474,13 +474,13 @@ query {
### Exercise requirements

- Update the type definition and the resolvers to be be able to perform the query operations listed below (can you provide other sample queries when your code is completed?).
- Discuss with someone else which would be the best way to use variables and try some. (there's always a tricky question)
- Discuss with someone else which would be the best way to use variables and try some. (this is always a tricky question)

#### Operations list

Provide the necessary code so that a user can perform the following `query` operations (the argument's values are arbitrary, your code should be able to respond for any valid value consistently):
Provide the necessary code so that a user can perform the following `query` operations (the argument values are arbitrary; your code should be able to respond for any valid value consistently):

The typedef should declare the following behavior and the resolvers should behave consistently for the arguments' logic:
The typedef should declare the following behavior and the resolvers should behave consistently for the argument logic:

- All arguments for all queries are optional
- if a `selectionSet` name is plural, a list should be returned, otherwise a `scalar` or `Object type` ( e.g `person` -> `Person` / `persons` -> `[Person!]`)
Expand Down Expand Up @@ -566,7 +566,7 @@ Select the exercise on your preferred technology:
- [Python](python/README.md)
- [NetCore](netcore/README.md)

This exercise, hopefully, will generate more questions than answers depending on how deep you want to dig into reusability, scalability, performance and other topics. Here you have some extra considerations to investigate; they're beyond the scope of the exercise, but really worthy to be mentioned, of course they're NOT the absolute truth but they'll provide you some starting point to investigate further if you want.
This exercise, hopefully, will generate more questions than answers depending on how deep you want to dig into reusability, scalability, performance and other topics. Here you have some extra considerations to investigate; they're beyond the scope of the exercise, but really worth mentioning. Of course they're NOT the absolute truth, but they'll provide you with a starting point to investigate further if you want.

- [GraphQL Resolvers: Best Practices
](https://medium.com/paypal-engineering/graphql-resolvers-best-practices-cd36fdbcef55) by [Mark Stuart](https://medium.com/@mark_stuart)
Expand Down
4 changes: 2 additions & 2 deletions lessons/day_03/day_03.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ and here the response
}
```

### Fields validation
### Field validations

If you try to use an unknown field for the Input Object you'll have an error from the server (and a warning from your preferred dev tool if you have one)

Expand Down Expand Up @@ -242,7 +242,7 @@ enum Kind {

```

On the [sample query defined here](#sample-query-with-input-object) we could pass any arbitrary value to the `kind` field (e.g `input: { kind: "whatever", homeland: "The Shire", skill: "the ringy thing" }`) without problems, the query can be performed and no errors will be thrown, probably returning no records. But, in the following example, we'll add an enum definition forcing the value to be one of the set (or null in this case) and throw an error if it's not.
On the [sample query defined here](#sample-query-with-input-object) we could pass any arbitrary value to the `kind` field (e.g `input: { kind: "whatever", homeland: "The Shire", skill: "the ringy thing" }`) without any problem, the query can be performed and no errors will be thrown, probably returning no records. But, in the following example, we'll add an enum definition forcing the value to be one of the set (or null in this case) and throw an error if it's not.

```graphql
input InputCharacter {
Expand Down
Loading