From 11ae8d945397a402f3ce69c8aaa0f150e03e44cb Mon Sep 17 00:00:00 2001 From: AshMusick Date: Tue, 28 May 2024 15:55:12 -0300 Subject: [PATCH 1/2] English style corrections for introduction and day 1 --- introduction/introduction.md | 14 ++++++------- lessons/day_01/day_01.md | 40 ++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/introduction/introduction.md b/introduction/introduction.md index e21f19f..bf2c85e 100644 --- a/introduction/introduction.md +++ b/introduction/introduction.md @@ -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! @@ -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: @@ -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. diff --git a/lessons/day_01/day_01.md b/lessons/day_01/day_01.md index 538b20e..ae60175 100644 --- a/lessons/day_01/day_01.md +++ b/lessons/day_01/day_01.md @@ -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. > @@ -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?!!! 🤔 @@ -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 @@ -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 @@ -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. @@ -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: @@ -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 @@ -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: @@ -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? @@ -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. @@ -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 = { @@ -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"** @@ -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. @@ -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 @@ -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**... > From 9f24fea9079443c0258bc2dbed0f68ca53027667 Mon Sep 17 00:00:00 2001 From: AshMusick Date: Fri, 31 May 2024 16:17:04 -0300 Subject: [PATCH 2/2] English style corrections for day 2 through 7 --- lessons/day_02/day_02.md | 14 +++++++------- lessons/day_03/day_03.md | 4 ++-- lessons/day_04/day_04.md | 10 +++++----- lessons/day_05/day_05.md | 14 +++++++------- lessons/day_06/day_06.md | 6 +++--- lessons/day_07/day_07.md | 2 +- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lessons/day_02/day_02.md b/lessons/day_02/day_02.md index fcd4bb6..6584710 100644 --- a/lessons/day_02/day_02.md +++ b/lessons/day_02/day_02.md @@ -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. @@ -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 @@ -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!]`) @@ -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) diff --git a/lessons/day_03/day_03.md b/lessons/day_03/day_03.md index cc57dd5..0ea49ad 100644 --- a/lessons/day_03/day_03.md +++ b/lessons/day_03/day_03.md @@ -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) @@ -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 { diff --git a/lessons/day_04/day_04.md b/lessons/day_04/day_04.md index 312ae21..a4ff9ac 100644 --- a/lessons/day_04/day_04.md +++ b/lessons/day_04/day_04.md @@ -18,8 +18,8 @@ Let's compare them. | **Mandatory** Root Operation Type | **Optional** Root Operation Type | | Object Type | Object Type | | Accept arguments | Accept arguments | -| **Arguments** values be valid **input types** | **Arguments** values be valid **input types** | -| **Output** values be **valid output types** | **Output** values be **valid output types** | +| **Argument** values are valid **input types** | **Argument** values are valid **input types** | +| **Output** values are **valid output types** | **Output** values are **valid output types** | | resolver's signature arity of 4 | resolver's signature arity of 4 | | sample resolver signature
```(root, args, context, info)``` | sample resolver signature
`(root, args, context, info)` | | expected to be **side-effect free and idempotent** | well ... •͡˘㇁•͡˘ ... it's expected to **mutate** something | @@ -183,9 +183,9 @@ A correct executor must generate the following result for that selection set: } ``` -Obviously the execution order is critical even in such a silly example. All **ShireLanders** where moved to **Buckland** first, then all **BuckLanders** were moved to **The Shire** and they had one beer each at the Green Dragon back and forth. If the execution order wasn't predictable and respected, the owner wouldn't know beforehand if the minimum available stock should be 11 or 10 for this operation, but that's another story ... is it? ... nope, that's exactly the point. +Obviously the execution order is critical even in such a silly example. All **ShireLanders** where moved to **Buckland** first, then all **BuckLanders** were moved to **The Shire** and they had one beer each at the Green Dragon back and forth. If the execution order wasn't predictable and respected, the owner wouldn't know beforehand if the minimum available stock should be 11 or 10 for this operation, but that's another story ... or is it? ... nope, that's exactly the point. -So far so good? Now, if you were attentive you might have noticed the following: +So far, so good? Now, if you were paying attention, you might have noticed the following: GraphQL spec determines **only top‐level mutation fields to be executed serially**; that means *every nested field level will be executed normally!!!!* @@ -219,7 +219,7 @@ For a given datasource ([abstracted as json here](datasource/data.json)) contain - an entity model - a db abstraction -The code contains the solution for previous exercises so you can have a starting point example. +The code contains the solution for previous exercises; so you can have a starting point example. ### Exercise requirements diff --git a/lessons/day_05/day_05.md b/lessons/day_05/day_05.md index f91d892..03d36a2 100644 --- a/lessons/day_05/day_05.md +++ b/lessons/day_05/day_05.md @@ -28,7 +28,7 @@ Let's start with the spec and gradually break it down and understand how the "In > Source: [GraphQL spec - June 2018 - Interface](http://spec.graphql.org/June2018/#sec-Interface) So far is pretty much the same concept you'll see in OOP: -> you'll might be tempted to replace the `Type` word with `Class` for a mental map but I'd discourage that, it might mislead you +> you might be tempted to replace the `Type` word with `Class` for a mental map but I'd discourage that. It might mislead you - A common shape abstracted in an `Interface` without the concrete implementation - Many different `Types`, having a **shape intersection** (common fields) and individual **specific characteristics** (distinctive fields) which **can be identified as individuals AND as part of a wider group**, can `implement` the common `Interface` (this is a design decision, having common fields doesn't mean they're necessarily part of a group and sharing and interface) @@ -76,7 +76,7 @@ type Query { ``` But what if later on, we have the need to distinguish the characters? -Can we do that in a backwards compatible way? (remember, a "breaking change" is like Chucky, Freddy Krueger, CandyMan and COVID-19 all knocking at your door whilst the Bogyman is coming down the chimney in a Santa's suit ... you ain't do it). +Can we do that in a backwards compatible way? (remember, a "breaking change" is like Chucky, Freddy Krueger, CandyMan and COVID-19 all knocking at your door whilst the Bogyman is coming down the chimney in a Santa's suit ... you better not do it). So? @@ -135,7 +135,7 @@ At this point you might start making questions like: 3. Oh, so where the heck the "cannot be directly used" thing fits here? - (⌐■_■) Exactly !!!!! remember every field in a type will eventually execute a resolver function either explicit or implicit (default)? There you go, the disambiguation happens at resolver level and there's where you end up not-using the Interface directly. -I know, the documentation is not really enlighten on this topic, furthermore, there's not a definition on how to resolve an abstract type on the spec, concretely because is a concern of the server implementation to deal with that. +I know, the documentation is not really enlightening on this topic. Furthermore, there's not a definition on how to resolve an abstract type on the spec, concretely because dealing with that is a concern of the server implementation. On chapter [6.4.3 Value Completion](http://spec.graphql.org/June2018/#sec-Value-Completion) of the June2018 spec we can read: @@ -159,7 +159,7 @@ and ... but that's still delegating the concrete resolution to the server implementation. -We'll see how to do it with Apollo due to it's simplicity, taking in consideration other server implementations might defer but they're very similar. +We'll see how to do it with Apollo due to it's simplicity, taking in consideration other server implementations might differ, but they're very similar. Originally, before adding the interface, we had this: @@ -187,7 +187,7 @@ const resolvers = { } ``` -But now we can't use the `Character` fields resolvers, it's an `Interface`, hence we need first to identify the concrete Type and then let GraphQL know that because it doesn't know it at this point. I know it's not really beautiful (it might feel like going back to the dark ages) but the common way is to rely on what makes each type different from the others based on the shape, it'll vary depending on the server implementation, the language and in the end, the team writing the app. +But now we can't use the `Character` field resolvers, it's an `Interface`, hence we need first to identify the concrete Type and then let GraphQL know that because it doesn't know it at this point. I know it's not really beautiful (it might feel like going back to the dark ages) but the common way is to rely on what makes each type different from the others based on the shape, it'll vary depending on the server implementation, the language and in the end, the team writing the app. ```javascript const resolvers = { @@ -240,7 +240,7 @@ const resolvers = { } ``` -I'm oversimplifying this, but if you see how it's described on [Apollo's documentation](https://www.apollographql.com/docs/apollo-server/schema/unions-interfaces/#interface-type) is pretty much that. +I'm oversimplifying this, but if you see how it's described in [Apollo's documentation](https://www.apollographql.com/docs/apollo-server/schema/unions-interfaces/#interface-type) it is pretty much that. Now in order to make a query operation and get the specific values for each type we have to use the same old props for the common fields and [inline fragments](https://graphql.org/learn/queries/#inline-fragments) for the type specific ones and you may also add a specific [meta field](https://graphql.org/learn/queries/#meta-fields) `__typename` to know what's the type of each record: @@ -693,7 +693,7 @@ This is a long one, keep it simple and put all your attention on the GraphQL asp - All previous operations (from previous days) MUST still work - All queries related to the `Person` interface can now support inline fragments to request fields for specific types, try some options and see what happens (e.g. on the `globalSearch` operation). -- Don't bother about validations except for those provided by GraphQL and only GraphQL. +- Don't bother with validations except for those provided by GraphQL and only GraphQL. #### Operations list diff --git a/lessons/day_06/day_06.md b/lessons/day_06/day_06.md index 3f74bd8..399d835 100644 --- a/lessons/day_06/day_06.md +++ b/lessons/day_06/day_06.md @@ -297,9 +297,9 @@ There's so much to say about this argument, and the considerations will vary dep One of the first things that will hit you is the "[One Graph](https://principledgraphql.com/integrity#1-one-graph)" principle which by any means is referred to have one single file; it means that you have to have a single source of truth (one unified graph) that represents the interactions between the actors and the data through [Aggregates](https://khalilstemmler.com/articles/typescript-domain-driven-design/aggregate-design-persistence/#Aggregates) (Objects & Relationships), Views (Queries) and Commands (Mutations); here is where we have 2 primary options, the **Monolithic architecture** (e.g. **Schema Stitching**), and the **Federated architecture**, (e.g. **Apollo Federation**). Other options like [graphql-modules](https://github.com/Urigo/graphql-modules) combine the declaration and execution code all together but we won't describe it here. While the former is considered deprecated or not recommended for many reasons we'll see later, we still encourage you to know it and practice it because: - 1. It's still applies to small project + 1. It still applies to small project 2. You'll still find it alive and kicking in many projects - 3. The migration from schema stitching to federation is [thoroughly documented](https://www.apollographql.com/docs/apollo-server/federation/migrating-from-stitching/), and you may consider this process the natural evolution of small or legacy projects when the need for scale arise. + 3. The migration from schema stitching to federation is [thoroughly documented](https://www.apollographql.com/docs/apollo-server/federation/migrating-from-stitching/), and you may consider this process the natural evolution of small or legacy projects when the need for scale arises. Clearly this discussion is around architecture and not about GraphQL explicitly, therefore the actual implementation will depend on the technology. We'll see the concept as detached from the technologies as possible but when a particular example will require it, we'll use Apollo's related technologies as it's leading the trend for now. @@ -321,7 +321,7 @@ All above is only achievable thanks to `extend`, a simple keyword on the spec is ### SDL -So how our example would look like if we use the stitching technique? +So what would our example look like like if we use the stitching technique? **schema.gql** diff --git a/lessons/day_07/day_07.md b/lessons/day_07/day_07.md index ecc9d6c..2920135 100644 --- a/lessons/day_07/day_07.md +++ b/lessons/day_07/day_07.md @@ -245,7 +245,7 @@ So: ... and many other questions can derive from this behavior! So, what do we do about this? -There's no "the right answer" for that. +There's no "right answer" for that. In terms of community trends related to best practices there are different opinions: