Skip to content

Commit c7ea54c

Browse files
committed
Merge branch '201705-docs' of github.com:apollographql/apollo into 201705-docs
2 parents 327af9d + adc13dd commit c7ea54c

3 files changed

Lines changed: 114 additions & 50 deletions

File tree

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"hexo-renderer-less": "0.2.0",
1515
"hexo-renderer-marked": "0.3.2",
1616
"hexo-server": "0.3.2",
17-
"meteor-theme-hexo": "1.0.11-beta.0"
17+
"meteor-theme-hexo": "1.0.11-beta.1"
1818
},
1919
"scripts": {
2020
"start": "npm run build && chexo apollo-hexo-config -- server",

docs/source/fundamentals/tips.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ description: Your starting point for learning GraphQL and Apollo
55

66
TK WIP TODO finish this intro. Depending on the size of an organization, there are usually existing practices surrounding the management of the data
77

8-
## Adopt incrementally
8+
<h2 id="incremental-adoption">Incremental adoption</h2>
99

1010
While it makes an excellent longer-term goal, GraphQL doesn’t need to stand above the entire data tree in an organization in order to provide value and expending large amounts of engineering effort to switch an entire stack should generally be avoided.
1111

1212
Unlike some technologies, which are difficult to embrace without getting buy-in from all the teams in an organization, GraphQL can be utilized by smaller units (e.g. component by component, team by team) and later integrated into the bigger picture.
1313

1414
Thanks to GraphQL’s ability to fetch data from any data source, it makes sense to keep existing APIs as they are and use GraphQL to “wrap” the data. This can be appreciated by organizations with existing collections of REST (or similar) APIs which power their user experiences since they can lean heavily on their existing investments. This allows small teams to immediately realize many benefits of GraphQL without waiting for deeper adoption and allows existing versions of the application, which have already been deployed and rely on the existing API, to keep functioning uninterrupted.
1515

16-
## Schema scope and ownership
16+
<h2 id="schema">Schema scope and ownership</h2>
1717

1818
APIs change as the products which use them evolve and their ideal structure is usually defined by the front-end applications which consume them. While it’s certainly possible, and sometimes desirable, to have a single GraphQL schema which blankets an entire organization’s operational concerns, trying to manage a large schema presents challenges which are avoided by separating the schema into more manageable pieces that align with individual products.
1919

@@ -23,7 +23,7 @@ Having product teams own the GraphQL schema for their products allows the schema
2323

2424
Organizations looking to offer a single API endpoint can assemble the individual product schemas, managed by individual product teams into a monolithic API by “stitching” the various schemas together.
2525

26-
## Monitor GraphQL performance
26+
<h2 id="performance">Monitor GraphQL performance</h2>
2727

2828
An API implemented using GraphQL allows developers to query the exact information they desire from an API and nothing more. With proper visibility into how the API performs, developers can understand the implications of adding or removing fields, especially those which might be slow (notoriously or otherwise!).
2929

@@ -35,7 +35,7 @@ In addition to empowering front-end developers to make quick, educated decisions
3535

3636
This clarity allows pushing schema changes to production with the confidence of knowing that the API is performing as well as it was before, if not better!
3737

38-
## Write the server in JavaScript
38+
<h2 id="javascript">Write the server in JavaScript</h2>
3939

4040
Facebook's reference implementation has been written in JavaScript since its original release and fresh developments in the GraphQL ecosystem have frequently appeared first in JavaScript or languages which transpile to JavaScript, like TypeScript.
4141

docs/source/guides/schema-design.md

Lines changed: 109 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ title: Schema design
33
description: Our recommendations for architecting your dream GraphQL API
44
---
55

6-
GraphQL schemas are at their best when they are designed around the needs of client applications. When a team is building their first GraphQL schema, they might be tempted to create literal mappings on top of existing database collections or tables using CRUD-like root fields but it’s important to consider how this could be disadvantageous.
6+
Proper schema design is important during GraphQL adoption. While GraphQL makes it easier to evolve an API, large applications will find it more difficult to allocate time for refactoring. Therefore, schema design decisions should be made carefully to avoid accumulating technical debt.
77

8-
While this literal database-to-schema mapping may be a fast way to get up and running, we strongly suggest avoiding it and instead building the schema around based on how the GraphQL API will be used by the front-end.
8+
This article details some practices around schema design which should help avoid expensive refactoring in the future.
99

1010
<h2 id="style">Style conventions</h2>
1111

@@ -17,17 +17,38 @@ The GraphQL specification is flexible in the style that it dictates and doesn't
1717

1818
<h2 id="gql">Wrapping documents with gql</h2>
1919

20-
There are two common ways to write GraphQL schemas and queries. The first is to write queries into a `.graphql` file and import them into your other files for usage. The second is to write them wrapped them with the `gql` tag provided by the `graphql-tag` library.
20+
There are two common ways to write GraphQL schemas and queries. The first is to write queries into a `.graphql` file and import them into your other files for usage. The second is to write them wrapped them with the `gql` tag provided by the [graphql-tag library](https://github.com/apollographql/graphql-tag#graphql-tag).
2121

2222
We recommend doing the latter for a couple reasons. Most notably, it can save a build step. Using `.graphql` files requires a loader to parse the file and make it useful. This may not seem like a concern on the client, but it may be especially useful on the server, where there’s often not a build step.
2323

2424
Additionally, using the `gql` tag unlocks full syntax highlighting in most editors and auto-formatting support with [prettier](https://prettier.io/).
2525

26-
Here’s an example of how to use the `gql` tag to wrap your schemas
26+
<h2 id="structure">Structure</h2>
27+
28+
GraphQL schemas are at their best when they are designed around the needs of client applications. When a team is building their first GraphQL schema, they might be tempted to create literal mappings on top of existing database collections or tables using CRUD-like root fields but it’s important to consider how this could be disadvantageous.
29+
30+
While this literal database-to-schema mapping may be a fast way to get up and running, we strongly suggest avoiding it and instead building the schema around based on how the GraphQL API will be used by the front-end.
31+
32+
If a database has has fields or relationships that the client won’t need, don’t include them in the schema. Adding fields later is cheap, so additions to a schema should be made when the need arises. Likewise, if a connection between two types of data doesn’t currently exist in a database, that doesn’t mean it can’t be added later.
2733

28-
<!-- TODO: can insert the glitch apollo-launchpad example here? -->
34+
For example, if you have a REST endpoint exposing a list of events and their locations, but not weather information for the day of the event, that doesn’t mean you can’t design a schema like the following:
35+
36+
```graphql
37+
type Event {
38+
name: String
39+
date: String
40+
weather: WeatherInfo
41+
}
42+
43+
type WeatherInfo {
44+
temperature: Float
45+
description: String
46+
}
47+
```
2948

30-
<h2 id="interfaces">Using interfaces</h2>
49+
In scenarios like this, you would just need to fetch the weather information from another endpoint (or a 3rd party endpoint) in your resolvers.
50+
51+
<h2 id="interfaces">Utilizing interfaces</h2>
3152

3253
Interfaces are a powerful way to build and use GraphQL schemas through the use of _abstract types_. Abstract types can't be used directly in schema, but can be used as building blocks for creating explicit types.
3354

@@ -79,29 +100,27 @@ Furthermore, if we need to return fields which are only provided by either `Text
79100

80101
```graphql
81102
query GetBooks {
82-
schoolBooks {
83-
title
84-
... on TextBook {
85-
classes {
86-
name
103+
schoolBooks {
104+
title
105+
... on TextBook {
106+
classes {
107+
name
108+
}
87109
}
88-
}
89-
... on ColoringBook {
90-
colors {
91-
name
110+
... on ColoringBook {
111+
colors {
112+
name
113+
}
92114
}
93115
}
94116
}
95-
}
96117
```
97118

98-
To see an interface in practice, check out this [example]()
99-
100-
<h2 id="mutation-design">Designing mutations</h2>
119+
<h2 id="mutations">Designing mutations</h2>
101120

102121
Mutations are one of the core types in GraphQL. Just like you can make a query to fetch information, you can make a mutation to update or change information. Unlike REST, GraphQL mutations actually are executed in two parts: the mutation itself, and a subsequent query, which can return any kind of data that you normally could query for. A mutation definition for updating the age of a `User` could look like this:
103122

104-
```
123+
```graphql
105124
type Mutation {
106125
updateUserAge(id: ID!, age: Int!): User
107126
}
@@ -115,7 +134,7 @@ type User {
115134

116135
With that definition, you could then make the following mutation:
117136

118-
```
137+
```graphql
119138
mutation updateMyUser {
120139
updateUserAge(id: 1, age: 25){
121140
id
@@ -125,9 +144,9 @@ mutation updateMyUser {
125144
}
126145
```
127146

128-
With a response looking something like:
147+
With a response looking something like:
129148

130-
```
149+
```json
131150
{
132151
"data": {
133152
"updateUserAge": {
@@ -139,35 +158,61 @@ With a response looking something like:
139158
}
140159
```
141160

142-
The first thing to note is that it’s most common to return the thing you’re updating from a mutation. In our example, we were updating a `User` record, so we returned that updated user. There’s nothing _enforcing_ this practice, but it’s highly recommended, because it’s often needed to have an updated user to let the clients update any local cache with a new instance of that `User`. For this same reason, it’s useful design mutations to update only one entity. If you wanted to update two unrelated entities, it’s recommended to in separate mutations.
161+
The first thing to note is that it’s most common to return the thing you’re updating from a mutation. In our example, we were updating a `User` record, so we returned that updated user. There’s nothing _enforcing_ this practice, but it’s highly recommended, because it’s often needed to have an updated user to let the clients update any local cache with a new instance of that `User`. For this same reason, it’s useful design mutations to update only one entity. If you wanted to update two unrelated entities, it’s recommended to in separate mutations. For more details on how to handle errors and warnings in mutations, see the [mutation responses](#mutation-responses) section below.
143162

144-
But what if we wanted to update more than just a single or couple attributes on a user? Passing each thing we need as a single argument would get tedious. For this, we can use Input types.
163+
But what if we wanted to update more than just a single or couple attributes on a user? Passing each thing we need as a single argument would get tedious. Especially if multiple mutations used similar fields. For this, we can use input types, which are explained in the next section.
145164

146-
<h3 id="input">Input types</h3>
165+
<h3 id="mutation-input-types">Input types</h3>
147166

148-
Input types are a special type in GraphQL reserved for using as arguments to queries and (more commonly) mutations. You can think of them as being similar to object types for your arguments, on top of the other Scalars. Input types can
167+
Input types are a special type in GraphQL which are defined as arguments to queries and, more commonly, mutations. They can be thought of as object types for arguments, in addition to the other scalar types. Input types are especially useful when multiple mutations require similar information; for example, when creating a user and updating a user require the same fields, like `age` and `name`.
149168

169+
Input types are used like any other type and defining them is similar to a typical object type definitions, but with the `input` keyword rather than `type`.
150170

151-
1) unlike in REST, mutations can return values
171+
Here is an example of two mutations that operate on a `User`, _without_ using input types:
152172

153-
2) we recommend always returning the item you mutate in order to automatically update the Apollo Client cache (show what this would look like on the front end by requesting an id and the property you mutated)
173+
```
174+
type Mutation {
175+
createUser(name: String, age: Int, address: String, phone: String): User
176+
updateUser(id: ID!, name: String, age: Int, address: String, phone: String): User
177+
}
178+
```
179+
180+
To avoid the repetition of argument fields, this can be refactored to use an input type, as follows:
181+
182+
```
183+
type Mutation {
184+
createUser(user: UserInput): User
185+
updateUser(id: ID!, user: UserInput): User
186+
}
187+
188+
input UserInput {
189+
name: String
190+
age: Int
191+
address: String
192+
phone: String
193+
}
194+
```
154195

155-
<h3 id="responses">Mutation responses</h3>
196+
<h3 id="mutation-responses">Responses</h3>
156197

157-
When making a mutation, many things could go wrong. A common way for handling errors when making a mutation is to simply `throw` an error. While that's fine sometimes, other times it may be useful to get a partial response from a mutation. For example, if you're trying to update a user's `name` and `age` and you made the following mutation:
198+
Mutations have a higher chance of causing errors than queries since they are modifying data. A common way to handle errors during a mutation is to simply `throw` an error. While that's fine, throwing an error in the resolver will return an error to the caller and prevent a partial response, which could be useful in the event of a partial update. Consider the following mutation example, which tries to update a user's `name` and `age`:
158199

159200
```graphql
160201
mutation updateUser {
161-
updateUser(id: 1, user: { age: -1, name: "Foo Bar" }){
162-
name
163-
age
164-
}
202+
updateUser(id: 1, user: { age: -1, name: "Foo Bar" }){
203+
name
204+
age
205+
}
165206
}
166207
```
167208

168-
This would likely cause an error, since the age was a negative value. Here, you could throw an error in the resolver, but what if you want to still update the name and return the `user`. If you simply threw an error in the resolver, you couldn't get that partial response back, and there wouldn't be an easy way to tell the client an error occurred with part of the mutation.
209+
With validation in place, this mutation might cause an error since the `age` is a negative value. While it’s possible that the entire operation should be stopped, there’s an opportunity to partially update the user’s record with the new `name` and return the updated record with the `age` left untouched.
210+
211+
Luckily, the powerful structure of GraphQL mutations accommodates this use case and can return transactional information about the update alongside the records which have been changed which enables client-side updates to occur automatically.
169212

170-
Mutations are an incredibly powerful part of GraphQL as they can easily return both information about the data updating transaction, as well as the actual data that has changed very easily. One pattern that we recommend to make this consistent is to have a `MutationResponse` interface that can be easily implemented for any `Mutation` fields. The `MutationResponse` is designed to allow transactional information alongside returning valuable data to make client side updates automatic! The interface looks like this:
213+
In order to provide consistency across a schema, we suggest introducing a `MutationResponse` interface which can be implemented on every mutation response in a schema and enables transactional information to be returned in addition to the normal mutation response object.
214+
215+
A `MutationResponse` interface would look like this:
171216

172217
```graphql
173218
interface MutationResponse {
@@ -180,19 +225,38 @@ interface MutationResponse {
180225
An implementing type would look like this:
181226

182227
```graphql
183-
type AddPostMutationResponse implements MutationResponse {
228+
type UpdateUserMutationResponse implements MutationResponse {
184229
code: String!
185230
success: Boolean!
186231
message: String!
187-
post: Post
232+
user: User
233+
}
234+
```
235+
236+
Calling a mutation that returns that `UpdateUserMutationResponse` type would result in a response that looks something like this:
237+
238+
```json
239+
{
240+
"data": {
241+
"updateUser": {
242+
"code": "200",
243+
"success": true,
244+
"message": "User was successfully updated",
245+
"user": {
246+
"id": "1",
247+
"name": "Jane Doe",
248+
"age": 35
249+
}
250+
}
251+
}
188252
}
189253
```
190254

191-
Lets break this down by field:
255+
Let’s break this down, field by field:
192256

193-
* **code** is a string representing a transactional value explaining details about the status of the data change. Think of this like HTTP status codes.
194-
* **success** is a boolean telling the client if the update was successful. It is a coarse check that makes it easy for the client application to respond to failures
195-
* **message** is a string that is meant to be a human readable description of the status of the transaction. It is intended to be used in the UI of the product
196-
* **post** is added by the implementing type `AddPostMutationResponse` to return back the newly created post for the client to use!
257+
* `code` is a string representing a transactional value explaining details about the status of the data change. Think of this like an HTTP status code.
258+
* `success` is a boolean indicating whether the update was successful or not. This allows a coarse check by the client to know if there were failures.
259+
* `message` is a string that is meant to be a human-readable description of the status of the transaction. It is intended to be used in the UI of the product.
260+
* `post` is added by the implementing type `AddPostMutationResponse` to return back the newly created post for the client to use!
197261

198-
Following this pattern for mutations provides detailed information about the data that has changed and how the operation to change it went! Client developers can easily react to failures and fetch the information they need to update their local cache.
262+
Following this pattern for mutations provides detailed information about the data that has changed and feedback on whether the operation was successful or not. Armed with this information, developers can easily react to failures in the client and fetch the information they need to update their local cache.

0 commit comments

Comments
 (0)