├── .gitignore ├── README.md ├── get-going-with-graphql.epub ├── get-going-with-graphql.pdf └── get-going-with-graphql ├── content ├── about-the-author │ └── index.html ├── appendix-a │ └── index.html ├── changelog │ └── index.html ├── chapter-01 │ └── index.html ├── chapter-02 │ └── index.html ├── chapter-03 │ └── index.html ├── chapter-04 │ └── index.html ├── chapter-05 │ └── index.html ├── chapter-06 │ └── index.html ├── chapter-07 │ └── index.html ├── chapter-08 │ └── index.html ├── chapter-09 │ └── index.html ├── chapter-10 │ └── index.html └── preface │ └── index.html ├── css └── web.min.css ├── fonts ├── FiraCode-Regular.woff ├── FiraCode-Regular.woff2 ├── GentiumBasic-Bold.woff ├── GentiumBasic-Bold.woff2 ├── GentiumBasic-BoldItalic.woff ├── GentiumBasic-BoldItalic.woff2 ├── GentiumBasic-Italic.woff ├── GentiumBasic-Italic.woff2 ├── GentiumBasic-Regular.woff ├── GentiumBasic-Regular.woff2 ├── OpenSans-Bold.woff ├── OpenSans-Bold.woff2 ├── OpenSans-BoldItalic.woff ├── OpenSans-BoldItalic.woff2 ├── OpenSans-Italic.woff ├── OpenSans-Italic.woff2 ├── OpenSans-Regular.woff └── OpenSans-Regular.woff2 └── images ├── 8bp-logo-white.svg ├── 8bp-logo.png ├── 8bp-logo.svg ├── _drafts ├── apollo-studio-onboarding-1.png ├── apollo-studio-onboarding-2_OLD.png ├── apollo-studio-onboarding-2a.png ├── apollo-studio-onboarding-3.png ├── apollo-studio-onboarding-3_OLD.png ├── apollo-studio-onboarding-5.png ├── apollo-studio-onboarding-7.png ├── apollo-studio-sign-up-2.png ├── apollo-studio-sign-up-3.png ├── apollo-studio-sign-up-4.png ├── apollo-studio-sign-up-5.png ├── github-api-explorer-docs-1.png ├── github-api-explorer-docs-2.png ├── github-api-explorer-docs-3.png ├── github-api-explorer-docs-4.png ├── github-api-explorer-with-response.png └── react-app-update-review-form.png ├── diagrams ├── apollo-link.png ├── client-server-jwts.png ├── cursor-pagination-add-new.png ├── cursor-pagination.png ├── offset-pagination-add-new.png ├── offset-pagination.png ├── operation-parts.png └── schema-node-and-edges.png ├── favicon.ico └── screenshots ├── apollo-dev-tools-1.png ├── apollo-dev-tools-2.png ├── apollo-dev-tools-3.png ├── apollo-dev-tools-author-cache-item.png ├── apollo-dev-tools-book-cache-item.png ├── apollo-explorer-documentation.png ├── apollo-explorer-extract-variables.png ├── apollo-explorer-headers.png ├── apollo-explorer-use-variables.png ├── apollo-studio-onboarding-1.png ├── apollo-studio-onboarding-2.png ├── apollo-studio-onboarding-3.png ├── apollo-studio-onboarding-4.png ├── apollo-studio-sign-up.png ├── chrome-cookie.png ├── cra-boilerplate.png ├── cra-no-styles.png ├── github-api-explorer-initial.png ├── github-api-explorer-with-response.png ├── react-app-book-list.png ├── react-app-create-review-form.png ├── react-app-initial-index-page.png ├── react-app-library-book-list.png ├── react-app-login-form-1.png ├── react-app-login-form-2.png ├── react-app-login-form-3.png ├── react-app-new-book-form.png ├── react-app-review-list-action-buttons.png ├── react-app-search-results.png ├── react-app-single-book-authenticated-1.png ├── react-app-single-book-authenticated-2.png ├── react-app-single-book.png ├── react-app-with-main-layout.png └── tailwind-no-styles.png /.gitignore: -------------------------------------------------------------------------------- 1 | # misc 2 | 3 | .DS_Store 4 | .DS_Store? 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | ehthumbs.db 9 | *[Tt]humbs.db -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Get Going with GraphQL 2 | 3 | This repo contains PDF, EPUB, and HTML versions of the _Get Going with GraphQL_ book from 8-Bit Press. 4 | 5 | [Download the complete source code for the application built throughout this book here.](https://github.com/8bitpress/get-going-with-graphql-source-code) 6 | 7 | Happy coding! 8 | 9 | --- 10 | 11 | Copyright © 2022 8-Bit Press Inc. 12 | -------------------------------------------------------------------------------- /get-going-with-graphql.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql.epub -------------------------------------------------------------------------------- /get-going-with-graphql.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql.pdf -------------------------------------------------------------------------------- /get-going-with-graphql/content/about-the-author/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Get Going with GraphQL 10 | 18 | 19 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |

Get Going with GraphQL

30 |

Learn How to Build JavaScript Applications with Apollo Server and Apollo Client

31 |

Mandi Wise

32 |
33 | 56 |
57 |
58 |
59 | 66 |
67 |
68 |

About the Author

69 |

Mandi Wise has worked in developer education for over five years with a focus on full-stack web application development using JavaScript and GraphQL. During that time, she wrote curricula for and instructed numerous full-time and part-time courses and also developed software to support program delivery. She currently works as a Solutions Architect for Apollo Graph Inc.

70 |

Mandi first discovered her love for building things for the web 20 years ago while spending countless hours handcrafting beautiful table-based websites with spacer GIFs on GeoCities. She currently shares that enduring passion for the web by speaking at numerous developer conferences and meet-ups and actively volunteering in her local tech community.

71 | 74 |
75 |
76 | 164 | 165 | -------------------------------------------------------------------------------- /get-going-with-graphql/content/appendix-a/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Get Going with GraphQL 10 | 18 | 19 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |

Get Going with GraphQL

30 |

Learn How to Build JavaScript Applications with Apollo Server and Apollo Client

31 |

Mandi Wise

32 |
33 | 56 |
57 |
58 |
59 | 66 |
67 |
68 |

Appendix A: GraphQL Resources

69 |

Background

70 |

When first learning about GraphQL, the official GraphQL website maintained by The GraphQL Foundation is a great place to start. This website contains tutorials and links to third-party GraphQL resources to help you get started. You can also find current and past versions of the GraphQL specification here. At the time this book was written, the June 2018 version of the specification is the latest release.

71 |

For historical background on how GraphQL came into being at Facebook and the events leading up to its public release, you may be interested in watching GraphQL: The Documentary. And if GraphQL has piqued your interest in graph theory, then A Gentle Introduction To Graph Theory by Vaidehi Joshi is a good primer.

72 |

Apollo Resources

73 |

Apollo maintains extensive documentation on its open-source libraries and products. These sections are most relevant to this book:

74 | 79 |

Apollo also has beginner-level, interactive tutorials called Odyssey that can supplement what you learn throughout this book. For more video-based content covering a variety of topics related to GraphQL and Apollo, check out Apollo on Twitch and GraphQL Summit. And be sure to bookmark the Apollo Blog.

80 |

Performance

81 |

When deploying a GraphQL API into a production environment, performance is an essential consideration. First, the API should be protected from malicious queries by considering measures such as limiting query depth. For a more advanced implementation, query cost analysis can be used to tally up the “cost” of each field in a query document and reject queries that exceed a maximum budget (either on a per query basis or as a part of a rate limiting strategy). Query cost analysis can be quite nuanced and context-dependent, but there are example query cost plugins available as well as an academic paper on the subject here.

82 |

Another important consideration is solving the N+1 problem that GraphQL queries may create by using data loaders for field resolvers. And despite some common misconceptions about GraphQL query responses being difficult to cache, there are proven caching strategies that work for GraphQL APIs. The Caching & GraphQL: Setting the Story Straight talk from by Marc-André Giroux at GraphQL Summit 2019 provides a good introduction, as well as the Apollo Server documentation on Caching and Automatic Persisted Queries.

83 |

Testing

84 |

For a quick primer on client-side and server-side testing with Apollo, check out the Testing GraphQL talk from Jake Dawkins from GraphQL Summit 2018. The Apollo documentation also has an entry on testing React components and there is a detailed post on the Apollo blog by Khalil Stemmler called Testing Apollo Client Applications. You can also find information on how to do mocking and integration testing with Apollo Server in the documentation.

85 |

Authentication

86 |

The GraphQL API authentication and authorization system demonstrated in this book is largely for educational purposes and not suitable for a production environment. For many cases, you would likely to want to investigate options for using a third-party authentication service (or hire a team of experienced engineers to design a bespoke authentication system for you). To make an informed decision about how you implement authentication and authorization for a GraphQL API, it’s a good idea to familiarize yourself with general auth-related pitfalls and best practices:

87 | 95 |

Other Advanced GraphQL Topics

96 |

After mastering some GraphQL essentials, you may be interested in reading about more nuanced topics such as how to leverage nullability in a schema and different approaches to handling errors for GraphQL APIs. If you’d like to learn more about what the info parameter does in a resolver function, then it will be well worth 20 minutes of your time to watch the Resolve Info Deep Dive talk by William Lyon from GraphQL Summit 2019.

97 |

After you’ve spent some time working with monolithic GraphQL APIs, you may also be interested in learning about Apollo Federation, which provides a declarative approach to building distributed GraphQL architectures.

98 |

Lastly, to learn more about how GraphQL execution works, watch the GraphQL Under the Hood talk talk by Eric Baer from GraphQL Summit 2017, as well as this post on query lexing and parsing and this post on query validation (both by Christian Joudrey).

99 |

Public GraphQL APIs

100 |

For some GraphQL schema inspiration, you can check out the following public GraphQL APIs:

101 | 109 |

More Content from the Author

110 |

Finally, if you enjoyed this book, you can find conference talks and additional books by the author at these links:

111 | 117 | 120 |
121 |
122 | 210 | 211 | -------------------------------------------------------------------------------- /get-going-with-graphql/content/changelog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Get Going with GraphQL 10 | 18 | 19 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |

Get Going with GraphQL

30 |

Learn How to Build JavaScript Applications with Apollo Server and Apollo Client

31 |

Mandi Wise

32 |
33 | 56 |
57 |
58 |
59 | 66 |
67 |
68 |

Changelog

69 |

v1.0.0

70 | 73 | 76 |
77 |
78 | 166 | 167 | -------------------------------------------------------------------------------- /get-going-with-graphql/content/chapter-01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Get Going with GraphQL 10 | 18 | 19 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |

Get Going with GraphQL

30 |

Learn How to Build JavaScript Applications with Apollo Server and Apollo Client

31 |

Mandi Wise

32 |
33 | 56 |
57 |
58 |
59 | 78 |
79 |
80 |

Chapter 1: Up and Running with GraphQL and Apollo Server

81 |
82 |

In this chapter, we will:

83 |
    84 |
  • Try running a query against the GitHub GraphQL API
  • 85 |
  • Use built-in Scalar types, define Object types, and create non-null and list fields
  • 86 |
  • Set up an Apollo Server with some basic type definitions and corresponding resolvers
  • 87 |
  • Add arguments to fields
  • 88 |
  • Run queries against a locally-running GraphQL API using Apollo Studio Explorer
  • 89 |
90 |
91 |

Hello Schema, Nice to Meet You

92 |

The heart of any GraphQL is its schema. While a REST API provides a series of structured routes that can be used to read or write different resources based HTTP verbs, a GraphQL API will instead expose a single endpoint (often at /graphql) to query any of the data exposed by the API. If you’ve spent many years working with REST APIs, then you may immediately wonder how a single endpoint can do all of the heavy lifting for an API.

93 |

The answer is in the specially formatted query that we send to the GraphQL API endpoint. When sending requests to the API via HTTP, this query is often included in the body of a POST request. The GraphQL query language allows us to describe exactly what data we want to receive and in what shape we’d like to receive and—just like that—the GraphQL API will send a response back in precisely the format we requested as long as it’s compliant with API’s schema. If you’ve ever felt the pain of simultaneously over-fetching some data from an API while under-fetching the data you really wanted, and then subsequently needing to juggle various asynchronous requests and mash their responses together to finally populate all of the required data for a single view, then GraphQL will feel like a breath of fresh air for you.

94 |
95 |

Convention versus Specification

96 |

The GraphQL specification is a detailed document describing how to define a GraphQL schema, how to write queries against that schema, and how to build an execution engine (also known as a GraphQL server) to respond to those queries. It does not, however, specify anything about the transport mechanism used to shuttle requests to and from a GraphQL server. That said, HTTP is commonly chosen for the transport layer and GraphQL clients often send POST requests containing a serialized JSON representation of the query in the request body. Similarly, GraphQL servers often respond with JSON as well.

97 |

The POST verb is a common default for GraphQL requests because GET requests have size limits and complex queries may grow quite large. Additionally, a POST request’s body is encrypted over HTTPS, which will help limit potential exposure of any sensitive operation argument values included with the request. That said, tools like Automatic Persisted Queries allow you to hash GraphQL queries to ensure that they fit comfortably within GET requests limits, and thus allow you to take advantage of browser caches and CDNs.

98 |
99 |

GraphQL’s query language is very intuitive and the best way to learn it is to start writing queries. If you have a GitHub account, you can try sending a query to the GitHub GraphQL API using a browser-based tool available at https://docs.github.com/en/graphql/overview/explorer. After logging in with your GitHub account, the GraphiQL IDE will be active with a viewer query preloaded in it. Press the play button above the editor to view the response:

100 |

In-browser interface for exploring the GitHub GraphQL API
101 |

102 |

With an easy click of a button, you just made your first query to a GraphQL API! And as promised, you were able to query the API for a specific piece of information about the currently logged in-user (in this case, your username), and that data was returned as JSON and in a shape mirroring the original query.

103 |

Now that we’ve seen a query in action, let’s break it down piece by piece:

104 |

Diagram of GraphQL operation parts

105 |

The entirety of what you see above is known as a document in GraphQL speak and that document contains an operation. In this case, it is a query operation that performs a read-only fetch operation, but there are also mutation operations that write and fetch data and there are subscription operations that can fetch data in real-time in response to source events.1

106 |

When performing query operations only we can optionally omit the query keyword in front of the curly brackets as a shorthand:

107 |

108 |
109 |

GraphQL Query

110 |
111 |
{
112 |   viewer {
113 |     login
114 |   }
115 | }
116 | 
117 | 118 |

Inside the outermost curly brackets, we are then left with the selection set of fields that specify the precise data that we want to receive in the response. Lastly, note that GraphQL syntax also supports comments by preceding comment text with the # character.

119 |

We’ve just unpacked several important GraphQL features using this basic query, but some important questions still linger. For one, how would we know that we were able to query a viewer from the GitHub API in the first place? And how do we find out what other fields can be queried for a viewer?

120 |

The answer to those questions is the schema—a GraphQL schema defines what types and fields are queryable through the API, as well as the relationships between types. A GraphQL schema also defines the root operation types that it supports, including query (required), mutation (optional), and subscription (optional). We can think of the fields belonging to these root operation types as the entry points to a GraphQL API.

121 |

One popular way to define a GraphQL schema is with the schema definition language (SDL). For example, to support queries for a viewer field on the query root operation type, the GitHub API contains the following definition:

122 |
type Query {
123 |   viewer: User!
124 | }
125 | 
126 | 127 |

Above, the query root operation type is defined as an Object type called Query and that type contains a single viewer field (in reality, the GitHub API’s Query type contains many more fields, but we’ll start with one to keep things simple). But where does User come from, and what is its relation to the nested login field we saw earlier? And what does the exclamation point after User mean? To answer these questions, we’ll need to dive into GraphQL’s type system.

128 |

Basic Types of Types

129 |

According to the GraphQL specification, types are the “fundamental unit” of a schema.2 We just saw an example of an Object type when we defined type Query with its viewer field above, and there are five other named types available in GraphQL that we can use too.

130 |

Scalar Types

131 |

The most basic type in GraphQL is a Scalar type. We can define custom Scalar types, but we typically start with the ones built into GraphQL. Out of the box, we can use Int, Float, String, Boolean, and ID Scalar types in a schema. The first four of those Scalar types represent exactly what they suggest. The ID type is a special-purpose Scalar type to indicate that a field is a unique identifier and that it will typically not be human-readable. Even if the ID value is numeric it should always serialize to a string.

132 |

In practice, the built-in Scalar types are used to specify the types that correspond to fields in a schema as follows:

133 |
type User {
134 |   bio: String
135 |   id: ID
136 |   isViewer: Boolean
137 |   pinnedItemsRemaining: Int
138 | }
139 | 
140 | 141 |

Object Types

142 |

But wait! We haven’t addressed how we were able to arbitrarily define type User in the last example. We have already seen an example of an Object type when we defined type Query previously, but we can also define Object types that are specific to our schema.

143 |

Object types are very powerful because they allow us to express relationships (or edges) between the different data types (or nodes) within our graph. If we think of the query root operation type as an entry point to our API, then Scalar types are the outer nodes in our graph where we finally reach the data. By connecting Object types through their fields, we can facilitate deeper traversal across the graph to these outer nodes.

144 |

In our simplified GitHub API schema, we can move from the root Query type to a single authenticated User via the viewer field. With the following modification, we can go one level deeper:

145 |
type User {
146 |   bio: String
147 |   id: ID
148 |   isViewer: Boolean
149 |   pinnedItemsRemaining: Int
150 |   status: UserStatus
151 | }
152 | 
153 | type UserStatus {
154 |   emoji: String
155 |   id: ID
156 |   message: String
157 | }
158 | 
159 | 160 |

Here we see a powerful feature of GraphQL’s type system in action—the ability to define Object types as collections of related fields and then nest them within one another to define the relationships between those types that ultimately allow us to move from node to node across our data graph.

161 |

Much of a GraphQL schema will end up being composed of the Object types that you create, but there are still two specification-defined Object types that we should make note of before moving on. Those types are the Mutation and Subscription types that correspond to those root operations in an API. We’ll cover those types in-depth in Chapter 3 and Chapter 10 respectively.

162 |

And if you’re keeping count, you’ve probably noticed that we’ve only explored two of the six named types in GraphQL so far. Scalar and Object types will be all we need to begin building a functioning GraphQL API, so we’ll cover Input types in Chapter 3 and Enum, Interface, and Union types in Chapter 4 as we require them to build out additional API features.

163 |

Two Ways to Modify Types

164 |

By default, GraphQL expects that a field’s corresponding type is singular and that it may return null values. However, in addition to the named types we’ve already explored, the GraphQL specification outlines two different wrapping types that we can use “wrap” named types and modify their defaults.

165 |

Non-Null

166 |

To make a field non-null, we use an exclamation point. Making a field non-null means that a client consuming this API can always expect a value to be returned for this field, and if one is not available, then the query will return an error. We have already seen this where we defined the viewer field on the Query type:

167 |
type Query {
168 |   viewer: User!
169 | }
170 | 
171 | 172 |

As a further example, if we update the User type to reflect GitHub’s real API, then we would make the id, isViewer, and pinnedItemsRemaining fields non-null:

173 |
type User {
174 |   bio: String
175 |   id: ID!
176 |   isViewer: Boolean!
177 |   pinnedItemsRemaining: Int!
178 |   status: UserStatus
179 | }
180 | 
181 | 182 |
183 |

Best Practice: Think About Nullability Up Front

184 |

Non-null fields have the advantage of making API responses more predictable, but they come with the trade-off of making that API harder to evolve in some ways in the future. For example, if a non-null field is later transitioned to nullable or removed entirely from an Object type, then breaking changes may result for clients that assume the field value will be there.

185 |

The inherently evolvable nature of a GraphQL API combined with the risk of breaking changes where alterations to a field’s nullability are concerned is a good reason to take time to make an informed choice about whether a field should be nullable or non-null when it’s added to a schema.

186 |

When unforeseen circumstances require changes to field nullability in the future, you can use observability tooling to understand what clients are using that field in your API and then proactively help those affected client developers avoid breaking changes to user interfaces.

187 |
188 |

Lists

189 |

Fields can also output lists of types and we indicate such a list by wrapping the type name in brackets. If we wanted to add a non-null field to the User type that contained a list without any null items, we would update our code as follows:

190 |
type User {
191 |   bio: String
192 |   id: ID!
193 |   isViewer: Boolean!
194 |   pinnedItemsRemaining: Int!
195 |   status: UserStatus
196 |   organizationVerifiedDomainEmails: [String!]!
197 | }
198 | 
199 | 200 |

If we wanted to permit a potentially empty list or null items in the list, but still require an empty list to be returned (rather than a null value), then we would specify the field’s output type as [String]!. Similarly, if we wanted to allow the field itself to be nullable but disallow non-null values in the list, we would specify the field’s output type as [String!]. Lastly, to permit a nullable list of strings that may contain null values or be empty, we would specify the field’s output type as [String].

201 |

A GraphQL API to Call Our Own

202 |

We have now covered just enough GraphQL theory to roll up our sleeves and start building a GraphQL API from scratch with Apollo Server. To do that, we’ll need to define a schema and set up a GraphQL server to respond to client requests to our API.

203 |

Our objective is to build a new and improved version of the REST-based Bibliotech API using GraphQL. Under the hood, the new GraphQL API will use the existing REST endpoints as a data source—a step that many teams take to migrate to GraphQL within an organization. Later on, as the REST API is phased out, different data sources may be swapped in behind the GraphQL API and any clients presently making GraphQL requests will be none the wiser.

204 |

The Bibliotech API provides information about books, authors, and allows users to create a personal library of their favorite books, as well as rate and review them. In the final three chapters, we’ll also build out an MVP client application using React and Apollo Client to consume data from the new GraphQL API. The index page of the Bibliotech React app will look like this:

205 |

Book list rendered on the index page
206 |

207 |

We’ll leverage the GraphQL API to build out additional pages to display a user’s favorite books, book search results, and a single book view. We will also need to create forms to authenticate users, add and update reviews, and add new authors and books. To begin, let’s create a project directory, cd into it, and then run the following command to set up the server directory:

208 |
mkdir server && cd server
209 | 
210 | 211 |

Second, we’ll create a package.json file in the new server directory (the --yes flag creates the package.json file without asking any questions):

212 |
npm init --yes
213 | 
214 | 215 |

Next, we’ll install some initial dependencies:

216 |
npm i apollo-server@2.22.2 concurrently@5.3.0 dotenv@8.2.0 graphql@15.5.0 json-server@0.16.3 node-fetch@2.6.1 nodemon@2.0.7
217 | 
218 | 219 |

Let’s take a brief look at each of the packages we just installed:

220 | 229 |

As an additional piece of configuration in the package.json file, we’ll set the type field to module so that we can use ECMAScript module syntax in our files:

230 |

231 |
232 |

server/package.json

233 |
234 |
{
235 |   // ...
236 |   "type": "module",
237 |   // ...
238 | }
239 | 
240 | 241 |

But First, a (Mocked) REST API

242 |

Before we can use the Bibliotech REST API as a backing data source for the GraphQL API, we’ll need to create a file from which JSON Server can query data. Create a db.json file in the server directory and add the following code to it:

243 |

244 |
245 |

server/db.json

246 |
247 |
{
248 |   "authors": [
249 |     {
250 |       "id": 1,
251 |       "name": "Douglas Adams"
252 |     }
253 |   ],
254 |   "books": [
255 |     {
256 |       "id": 1,
257 |       "cover": "http://covers.openlibrary.org/b/isbn/9780671461492-L.jpg",
258 |       "summary": "After a Vogon constructor fleet destroys Earth to make way for a hyperspace bypass, the last surviving man, Arthur Dent, embarks on an interstellar adventure with his friend Ford Prefect (who, apparently, was an alien all along).",
259 |       "title": "The Hitchhiker's Guide to the Galaxy"
260 |     }
261 |   ],
262 |   "bookAuthors": [
263 |     {
264 |       "id": 1,
265 |       "authorId": 1,
266 |       "bookId": 1
267 |     }
268 |   ]
269 | }
270 | 
271 | 272 |
273 |

If you’re using version control in this project, you may wish to ignore the db.json file as we will be writing test data to this file as we build out the GraphQL API throughout this book.

274 |
275 |

JSON Server will automatically create an endpoint for each of the top-level properties in the db.json file. We’ll primarily use the /authors and /books endpoints for now, while the /bookAuthors endpoint will act like a join table would to capture the many-to-many relationship between authors and books. Next, we can make our lives easier by writing some custom routes for the REST API that will make the process of querying for all authors of a given book or all books by a given author a bit more intuitive than querying /bookAuthors directly. To do this, we’ll create a routes.json file in the server directory and add the following code to it:

276 |

277 |
278 |

server/routes.json

279 |
280 |
{
281 |   "/authors/:authorId/books": "/bookAuthors?authorId=:authorId&_expand=book",
282 |   "/books/:bookId/authors": "/bookAuthors?bookId=:bookId&_expand=author"
283 | }
284 | 
285 | 286 |

Lastly, we can remove the existing test script in the package.json file and replace it with a script to start up our brand new REST API:

287 |

288 |
289 |

server/package.json

290 |
291 |
{
292 |   // ...
293 |   "scripts": {
294 |     "server:rest": "json-server -w db.json -p 5000 -r routes.json -q"
295 |   },
296 |   // ...
297 | }
298 | 
299 | 300 |

Run npm run server:rest and try out the following endpoints to confirm they return the expected data before proceeding:

301 | 307 |

Initial Type Definitions

308 |

With our REST API up and running, it’s time to define the first few types in the Bibliotech schema. First, we’ll create a subdirectory to organize our GraphQL-related code, as well as a typeDefs.js file to house our type definitions. We’ll create a graphql directory in server, add the typeDefs.js file to it, and then set up the new file as follows:

309 |

310 |
311 |

server/src/graphql/typeDefs.js

312 |
313 |
import { gql } from "apollo-server";
314 | 
315 | const typeDefs = gql`
316 |   # GraphQL type definitions will go here...
317 | `;
318 | 
319 | export default typeDefs;
320 | 
321 | 322 |

We’re going to define our type definitions as a string inside of a JavaScript file using SDL and then wrap that string in the gql template literal tag to convert it into the format that Apollo Server is expecting. As a bonus, the gql tag will enable GraphQL syntax highlighting within the string when the Apollo GraphQL VS Code extension is installed.

323 |

Next, let’s add some initial type definitions for the Author and Book Object types, as well as the root Query type with some related fields for querying lists of authors and books:

324 |

325 |
326 |

server/src/graphql/typeDefs.js

327 |
328 |
import { gql } from "apollo-server";
329 | 
330 | const typeDefs = gql`
331 |   type Author {
332 |     id: ID!
333 |     books: [Book]
334 |     name: String!
335 |   }
336 | 
337 |   type Book {
338 |     id: ID!
339 |     authors: [Author]
340 |     title: String!
341 |   }
342 | 
343 |   type Query {
344 |     authors: [Author]
345 |     books: [Book]
346 |   }
347 | `;
348 | 
349 | export default typeDefs;
350 | 
351 | 352 |

Getting lists of authors and books is useful, but we’ll also need a way to query a single author or book based on a unique identifier. To do that, we’ll add some new query fields with arguments. In our previous example with User Object type from the GitHub API, we mocked the organizationVerifiedDomainEmails field on the type as it is in the real API, but left out one key detail. This field accepts a login argument that represents the login of the organization from which to match verified domains. The real field looks like this:

353 |
type User {
354 |   # ...
355 |   organizationVerifiedDomainEmails(login: String!): [String!]!
356 | }
357 | 
358 | 359 |

Parentheses are used after the field name to indicate that the field accepts arguments, and inside, each of the arguments is listed by name with its corresponding type. In this above example, the login argument expects a non-null String to be provided when the field is queried.

360 |

For our API, we’ll add author and book fields that each accept a non-null ID as an argument, and return a single Author or Book respectively:

361 |

362 |
363 |

server/src/graphql/typeDefs.js

364 |
365 |
import { gql } from "apollo-server";
366 | 
367 | const typeDefs = gql`
368 |   # ...
369 | 
370 |   type Query {
371 |     author(id: ID!): Author
372 |     authors: [Author]
373 |     book(id: ID!): Book
374 |     books: [Book]
375 |   }
376 | `;
377 | 
378 | export default typeDefs;
379 | 
380 | 381 |

The basic syntax for a query that contains field arguments is as follows:

382 |

383 |
384 |

GraphQL Query

385 |
386 |
query {
387 |   author(id: "1") {
388 |     name
389 |   }
390 | }
391 | 
392 | 393 |

Resolvers, aka Functions that Do the Data Fetching

394 |

With some type definitions in place, we now need a way to respond to API requests with real data. To do that, we must write resolver functions. The Apollo Server documentation3 provides the following concise definition of what a resolver function does:

395 |
396 |

A resolver is a function that’s responsible for populating the data for a single field in your schema. It can populate that data in any way you define, such as by fetching data from a back-end database or a third-party API.

397 |
398 |

Resolvers have the following function signature:

399 |
someField(parent, args, context, info) {
400 |   return "Hello, world!";
401 | }
402 | 
403 | 404 |

Let’s explore what each of the resolver’s parameters exposes in the function:

405 | 411 |
412 |

Good to Know!

413 |

Resolver functions can return promises, so it’s OK to use the async keyword with them.

414 |
415 |

We’ll keep our resolvers organized in a separate file, so create a resolvers.js file in the server/src/graphql directory. We’ll structure the resolver functions in an object so that Apollo Server will be able to understand what resolvers correspond to which fields. We’ll begin by adding the following code to resolvers.js:

416 |

417 |
418 |

server/src/graphql/resolvers.js

419 |
420 |
const resolvers = {
421 |   Query: {
422 |     async author(root, { id }, context, info) {
423 |       // Fetch an author by ID here...
424 |     },
425 |     async authors(root, args, context, info) {
426 |       // Fetch all authors here...
427 |     },
428 |     async book(root, { id }, context, info) {
429 |       // Fetch a book by ID here...
430 |     },
431 |     async books(root, args, context, info) {
432 |       // Fetch all books here...      
433 |     }
434 |   }
435 | };
436 | 
437 | export default resolvers;
438 | 
439 | 440 |

Note that each key nested in the object value of the Query key directly corresponds to a field name from the type definitions we just created. We have also marked each function as async because we must await a GET request to the mocked REST API to obtain the data that will be returned for the fields. Let’s update each resolver to fetch the appropriate data now:

441 |

442 |
443 |

server/src/graphql/resolvers.js

444 |
445 |
import fetch from "node-fetch";
446 | 
447 | const baseURL = "http://localhost:5000";
448 | 
449 | const resolvers = {
450 |   Query: {
451 |     async author(root, { id }, context, info) {
452 |       const res = await fetch(`${baseURL}/authors/${id}`).catch(
453 |         err => err.message === "404: Not Found" && null
454 |       );
455 |       return res.json();
456 |     },
457 |     async authors(root, args, context, info) {
458 |       const res = await fetch(`${baseURL}/authors`);
459 |       return res.json();
460 |     },
461 |     async book(root, { id }, context, info) {
462 |       const res = await fetch(`${baseURL}/books/${id}`).catch(
463 |         err => err.message === "404: Not Found" && null
464 |       );
465 |       return res.json();
466 |     },
467 |     async books(root, args, context, info) {
468 |       const res = await fetch(`${baseURL}/books`);
469 |       return res.json();
470 |     }
471 |   }
472 | };
473 | 
474 | export default resolvers;
475 | 
476 | 477 |

This code is all we need to take care of resolving data for fields on the Query type, but what about the Author and Book types? As you can imagine, if a schema contained many Object types with many different fields, then it could get very tedious to write resolvers for each field.

478 |

Luckily, if the parent object argument contains a property with a name that matches the field name, then Apollo Server will use a default resolver to automatically supply that value for the field. That means that id and name fields will be automatically resolved for the Author type and the id and title fields will be automatically resolved for the Book type because these properties are readily available in the objects fetched from the REST API.

479 |

However, because we expressed a many-to-many relationship between authors and books in our schema, we will need to provide a books field resolver for Author and an authors field resolver for Book. To do that, we’ll update our code:

480 |

481 |
482 |

server/src/graphql/resolvers.js

483 |
484 |
// ...
485 | 
486 | const resolvers = {
487 |   Author: {
488 |     async books(author, args, context, info) {
489 |       const res = await fetch(`${baseURL}/authors/${author.id}/books`);
490 |       const items = await res.json();
491 |       return items.map(item => item.book);
492 |     }
493 |   },
494 |   Book: {
495 |     async authors(book, args, context, info) {
496 |       const res = await fetch(`${baseURL}/books/${book.id}/authors`);
497 |       const items = await res.json();
498 |       return items.map(item => item.author);
499 |     }
500 |   },
501 |   Query: {
502 |     // ...
503 |   }
504 | };
505 | 
506 | export default resolvers;
507 | 
508 | 509 |

We now have all of the required code in place to resolve every field in the schema.

510 |

Wire Up Apollo Server

511 |

Now that we have type definitions and resolvers ready to go, we’re finally ready to create a new Apollo Server and start sending requests to the GraphQL API. We’ll instantiate an ApolloServer object in a top-level index.js file for our server application. Inside the server/src directory, create the index.js file first. To create an ApolloServer, we only need to pass in our typeDefs and resolvers and call its listen method to start it up (under the hood, Apollo Server runs on Express by default). We can do that in about a dozen lines of code in the new index.js file:

512 |

513 |
514 |

server/src/index.js

515 |
516 |
import { ApolloServer } from "apollo-server";
517 | 
518 | import resolvers from "./graphql/resolvers.js";
519 | import typeDefs from "./graphql/typeDefs.js";
520 | 
521 | const server = new ApolloServer({
522 |   typeDefs,
523 |   resolvers
524 | });
525 | 
526 | server.listen().then(({ url }) => {
527 |   console.log(`Server ready at ${url}`);
528 | });
529 | 
530 | 531 |

Just one last step! We’ll need a server:graphql script to start up the GraphQL API. In this script, we’ll set the --ignore option for Nodemon because we don’t need to restart the server every time a record is added, updated, or removed from db.json. For convenience, we’ll use Concurrently to create a top-level server script to start the REST API and GraphQL API together:

532 |

533 |
534 |

server/package.json

535 |
536 |
{
537 |   // ...
538 |   "scripts": {
539 |     "server": "concurrently -k npm:server:*",
540 |     "server:rest": "json-server -w db.json -p 5000 -r routes.json -q",
541 |     "server:graphql": "nodemon --ignore db.json ./src/index.js"
542 |   },
543 |   // ...
544 | }
545 | 
546 | 547 |

If we try running npm run server in the terminal now, then we should see both APIs start up. By default, the GraphQL API will be available at http://localhost:4000/ and we will access it at the /graphql path. We can take the GraphQL API for a quick test spin by opening another terminal tab or window and running this cURL command:

548 |
curl 'http://localhost:4000/graphql' -H 'Content-Type: application/json' -H 'Accept: application/json' --data-binary '{"query":"query { books { title id } }"}' --compressed
549 | 
550 | 551 |

The following response output should appear in the terminal:

552 |
{"data":{"books":[{"title":"The Hitchhiker's Guide to the Galaxy","id":"1"}]}}
553 | 
554 | 555 |

Congratulations! You are now the proud owner of a shiny, new GraphQL API.

556 |

Exploring the GraphQL API

557 |

Now that our API is up and running, it would be easier if we had a more visual way to experiment with it than cURL commands. There are many browser-based GraphQL IDEs to choose from today—we’ve already seen one such example when we used the embedded GraphiQL tool to try out the GitHub API. GraphQL Playground is another option, and it even comes bundled with Apollo Server. If you navigate to http://localhost:4000/graphql directly in your browser, you can explore your GraphQL API and run queries against it using GraphQL Playground right now.

558 |

Currently, one of the most feature-rich, browser-based IDEs designed for testing GraphQL operations is Apollo Studio Explorer. It includes a clickable query builder interface, operation history, and a spotlight-style search for browsing types and fields. Explorer is offered as a free feature of the Apollo Studio platform and examples throughout this book will demonstrate how to use it as a GraphQL IDE.

559 |

There are two different ways that you can access Explorer. If you would prefer to use it without creating an Apollo Studio account, you can navigate to https://sandbox.apollo.dev and update the sandbox URL to http://localhost:4000/graphql and you will be able to explore the Bibliotech GraphQL API immediately (skip to Page 21 for further directions on how to do so).

560 |

Alternatively, for a more feature-rich experience using Explorer (including a query history and additional customization settings), you can sign up for an Apollo Studio account by following these quick steps. First, navigate to https://studio.apollographql.com/ to register for a new account:

561 |

Apollo Studio login page
562 |

563 |

Click the “Create an account” link and then create a new account either using your email or GitHub account. Be sure to pick the “Local development” plan. Once your account has been created, you’ll have the option to choose a “Deployed” or “Development” graph. A deployed graph can take advantage of Apollo Studio features such as its schema registry, metrics reporting, and is typically meant for team collaboration. A development graph will poll your development server for schema updates as you work. For our purposes, we will choose a development graph. Be sure to give it a unique name and set the correct endpoint:

564 |

Create a new graph in Apollo Studio
565 |

566 |

After creating your graph, Explorer will load with a demo query operation in the editor:

567 |

Apollo Studio Explorer IDE
568 |

569 |

The auto update option will keep Explorer in sync with the schema in your local development environment.

570 |

Next, replace the demo query in the editor with the following operation document:

571 |
query {
572 |   authors {
573 |     id
574 |   }
575 | }
576 | 
577 | 578 |

Then click on authors: [Author] under the “Fields” subheading in the lefthand panel. Click the plus sign next to the name and books fields to automatically add them to the query (you can also type the field names into the editor if you prefer). Click and add more fields under the books field as well, and then press the “Run” button above the editor to execute the query:

579 |

Table view of response data in Apollo Studio Explorer
580 |

581 |

In the image above, the table view has been toggled to display the results in a table format instead of the default JSON format. Keep this feature in mind when viewing complex lists of results. Before you move on to the next chapter, be sure to try out some of Explorer’s other features, such as the spotlight-style search (accessible from the magnifying glass icon at the top of the lefthand panel). And if you signed up for an Apollo Studio account, try out some of the various settings you can configure (from the gear icon) such as dark mode and editor hints.

582 |

As a final feature to highlight, Apollo Studio also provides a convenient interface for exploring the different types in your schema. As schemas grow in size, it can become increasingly difficult to have a bird’s eye view of all of the types in it, especially if all of those type definitions are contained within a single file. You can access an overview of all of your type definitions organized by type by navigating to the “Schema” page (it’s the first item in the lefthand navigation menu):

583 |

Apollo Studio schema page
584 |

585 |
586 |

Introspection: The GraphQL Magic that Makes Tools Like Explorer Possible

587 |

You may wonder how it’s possible for tools such as Explorer to know so much about your GraphQL schema simply by sharing your API’s endpoint. A GraphQL schema can share information about itself via introspection. In practice, that means that a GraphQL API exposes a special __schema field on the query root operation type that allows us to request information about the API itself, such as the names of its various types and fields.

588 |

You can see introspection in action by running the following query:

589 |
{
590 |   __schema {
591 |     types {
592 |       name
593 |       fields {
594 |         name
595 |       }
596 |     }
597 |   }
598 | }
599 | 
600 | 601 |

Note that it’s considered a best practice to turn off introspection in production for non-public GraphQL APIs to prevent potential bad actors from learning about the detailed inner workings of the API by sending an introspection query to it.

602 |
603 |

Summary

604 |

We covered a lot of ground in this chapter, from learning how to write basic queries against a third-party GraphQL API, to creating type definitions for a schema of our own, and writing resolver functions to provide data for all of the fields in our API. We also learned about two named types in the GraphQL type system—Object and Scalar types—and learned how to add arguments to fields. Lastly, we saw how a visual tool like Apollo Studio Explorer can make it easy to experiment with a GraphQL API as we develop it.

605 |

In the next chapter, we’ll continue to add new Object types and additional query fields to the Bibliotech API while also adopting some best practices related to GraphQL API design and development.

606 |
607 |
608 |
    609 |
  1. https://spec.graphql.org/June2018/#sec-Language↩︎

  2. 610 |
  3. https://spec.graphql.org/June2018/#sec-Type-System↩︎

  4. 611 |
  5. https://www.apollographql.com/docs/apollo-server/data/resolvers/↩︎

  6. 612 |
613 |
614 | 617 |
618 |
619 | 707 | 708 | -------------------------------------------------------------------------------- /get-going-with-graphql/content/preface/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Get Going with GraphQL 10 | 18 | 19 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |

Get Going with GraphQL

30 |

Learn How to Build JavaScript Applications with Apollo Server and Apollo Client

31 |

Mandi Wise

32 |
33 | 56 |
57 |
58 |
59 | 77 |
78 |
79 |

Preface

80 |

Welcome to Get Going with GraphQL! I’m thrilled you’ve chosen this book to dive into learning about full-stack application development with GraphQL APIs.

81 |

GraphQL was invented at Facebook in 2012 and was publicly launched in 2015. Since then, it has opened up a world of new possibilities for API design and consumption. Today, it has been famously adopted by companies including The New York Times, Shopify, Coursera, AirBnB, GitHub, and Netflix. It has also inspired countless open source projects related to it, including the popular suite of Apollo tools for working with GraphQL APIs in both client and server applications.

82 |

It may seem like everyone is talking about these days, but if you’re new to GraphQL you may still be wondering what all of the fuss is about. If you visit the graphql.org homepage, you will see GraphQL described as “a query language for your API.” That’s pretty high-level, so from a more tactical perspective, the GraphQL specification outlines the mechanics of that query language as well as the runtime for responding to query requests received by an API. And despite what the “QL” in its name might suggest, it has nothing to do with any particular database implementation.

83 |

GraphQL’s appeal stems largely from its orientation toward making API consumption easier for clients. Unlike a REST API, a GraphQL API is exposed through a single endpoint and will often stand between a client and any number of backing data sources. A well-designed GraphQL schema can also help client developers avoid the common REST-related headache of simultaneously over-fetching and under-fetching the data needed to render a user interface. What’s more, GraphQL puts client developers in the driver’s seat to query only the data that they need and in the shape that they need it. And with the right schema governance practices and observability tools in place, a GraphQL API can be safely evolved without versioning. That means that a GraphQL schema can be iterated to support new product requirements as they emerge and increase the velocity at which new features are released to users.

84 |

With all of this in mind, it’s easy to see how GraphQL knowledge is a must-have for JavaScript developers today and this book has been thoughtfully designed to help you up ramp up quickly. Throughout this book, we’ll explore all of GraphQL’s essential features, as well as how Apollo Server and Apollo Client may be used to supercharge API and client application development.

85 |

What’s Inside

86 |

This book covers a wide range of topics relevant to developing web applications from scratch with JavaScript and does so using a practical, project-based approach. Working through this book from start to finish will leave you with a relatively full-featured React application powered by a GraphQL API.

87 |

Specific topics covered throughout the book include:

88 | 105 |

In its exploration of these topics, this book is divided into 10 chapters. Chapters 1 to 6 focus entirely on the server side concerns of designing and building a GraphQL API with Apollo Server. Chapters 7 to 9 shift gears to the client side and cover the development of a React web application that consumes the GraphQL API. Chapter 10 wraps up the book by configuring subscription operation support on both the server and client applications.

106 |

Who Should Read this Book

107 |

This book assumes you have little or no prior knowledge of GraphQL, but does assume you have some experience building React applications. Before proceeding, consider if you have at least:

108 | 114 |

If those qualifications sound like they describe you, then you’re in the right place. By the time you’ve finished this book, you’ll feel confident drawing on many of the features of Apollo Server and Client and applying them to real-world development scenarios.

115 |

Getting the Most from this Book

116 |

To get the most from your experience reading this book, I encourage you to work through the chapters chronologically because the code in each chapter builds on the previous one. This book and its chapters are structured around building out a single full-stack application, beginning with a GraphQL server and ending with a React application that consumes the API.

117 |

If you intend to build out the demonstration application as you work through the chapters, I also encourage you to explicitly type out as much of the code that you encounter as possible rather than simply copying and pasting snippets. In my experience, taking the time to intentionally type out code examples aids comprehension and builds mental muscle memory.

118 |

Of course, if you’re working on another GraphQL-based project right now and are in search of insights or code examples for any of the aforementioned topics, each chapter of this book also stands alone in building out a complete, logical chunk of functionality for the application. You will almost certainly find that various chapters serve as helpful reference points for that work too.

119 |

Formatting Conventions

120 |

Code Blocks

121 |

In favor of brevity, some code examples are truncated where code that was previously added to a file doesn’t change in the context of a new update to that file. Additionally, file diffs are highlighted in code examples to enhance readability and filenames are added above the code blocks in italics, where applicable. For example:

122 |

123 |
124 |

server/src/index.js

125 |
126 |
const typeDefs = gql`
127 |   # ...
128 | 
129 |   type Query {
130 |     author(id: ID!): Author
131 |     authors: [Author]
132 |     book(id: ID!): Book
133 |     books: [Book]
134 |   }
135 | `;
136 | 
137 | 138 |

Inline Code

139 |

Code and filenames that are referenced inline (for example, within a paragraph or list item) are rendered in this monospaced font to distinguish them from the other text.

140 |

Info Boxes

141 |

Supplementary notes and additional tips related to the main content are presented in gray boxes throughout the chapters as follows:

142 |
143 |

You can find a current version of the GraphQL specification here: http://spec.graphql.org/

144 |
145 |

Quotes

146 |

Longer sections of text directly quoted from another source are delineated with a gray border to the left of the quote:

147 |
148 |

The best programs are written so that computing machines can perform them quickly and so that human beings can understand them clearly. A programmer is ideally an essayist who works with traditional aesthetic and literary forms as well as mathematical concepts, to communicate the way that an algorithm works and to convince a reader that the results will be correct.

149 |

—Donald E. Knuth, Selected Papers on Computer Science

150 |
151 |

Package Versions

152 |

For compatibility purposes, package versions are specified wherever you are instructed to install a package from npm. You may wish to experiment with updated package versions as they become available, but compatibility with the other code in this book cannot be guaranteed.

153 |

Reference Source Code

154 |

The complete source code for the application built throughout this book may be accessed at:

155 |

https://github.com/8bitpress/get-going-with-graphql-source-code

156 |

Required Software

157 |

To build the application as outlined in the chapters that follow you will need to have Node.js 14.13.0 or higher installed on your computer. This version is required because it was the first version of Node.js that provided support for importing named exports from CommonJS modules using ES module syntax. You can download the latest LTS version of Node.js for your operating system here:

158 |

https://nodejs.org/en/download/

159 |

Optional Software

160 |

Optionally, if you use VS Code as an editor, then also consider installing the Apollo GraphQL extension to enhance your development experience with features such as GraphQL syntax highlighting:

161 |

https://marketplace.visualstudio.com/items?itemName=apollographql.vscode-apollo

162 |

And to facilitate client-side development, you may also wish to install the Apollo Client Developer Tools for Chrome or Firefox:

163 |

https://www.apollographql.com/docs/react/development-testing/developer-tooling/#apollo-client-devtools

164 |

The Game Plan

165 |

In the chapters that follow, we’ll build out a GraphQL API to be consumed by React application called Bibliotech. The client application will allow unauthenticated users to view details and reviews about books contained within the Bibliotech catalog. Users can also sign up for an account and create libraries of their favorite books and also submit and update reviews of their own when logged in.

166 |

We have a lot of code to write to get from zero to full-stack application at the end of this book, so fire up your code editor and let’s go!

167 | 170 |
171 |
172 | 260 | 261 | -------------------------------------------------------------------------------- /get-going-with-graphql/css/web.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:"Fira Code";src:url("../fonts/FiraCode-Regular.woff2") format("woff2"),url("../fonts/FiraCode-Regular.woff") format("woff");font-style:normal}@font-face{font-family:"Gentium Basic";src:url("../fonts/GentiumBasic-Regular.woff2") format("woff2"),url("../fonts/GentiumBasic-Regular.woff") format("woff");font-weight:normal;font-style:normal}@font-face{font-family:"Gentium Basic";src:url("../fonts/GentiumBasic-Italic.woff2") format("woff2"),url("../fonts/GentiumBasic-Italic.woff") format("woff");font-weight:normal;font-style:italic}@font-face{font-family:"Gentium Basic";src:url("../fonts/GentiumBasic-Bold.woff2") format("woff2"),url("../fonts/GentiumBasic-Bold.woff") format("woff");font-weight:bold;font-style:normal}@font-face{font-family:"Gentium Basic";src:url("../fonts/GentiumBasic-BoldItalic.woff2") format("woff2"),url("../fonts/GentiumBasic-BoldItalic.woff") format("woff");font-weight:bold;font-style:italic}@font-face{font-family:"Open Sans";src:url("../fonts/OpenSans-Regular.woff2") format("woff2"),url("../fonts/OpenSans-Regular.woff") format("woff");font-weight:normal;font-style:normal}@font-face{font-family:"Open Sans";src:url("../fonts/OpenSans-Italic.woff2") format("woff2"),url("../fonts/OpenSans-Italic.woff") format("woff");font-weight:normal;font-style:italic}@font-face{font-family:"Open Sans";src:url("../fonts/OpenSans-Bold.woff2") format("woff2"),url("../fonts/OpenSans-Bold.woff") format("woff");font-weight:bold;font-style:normal}@font-face{font-family:"Open Sans";src:url("../fonts/OpenSans-BoldItalic.woff2") format("woff2"),url("../fonts/OpenSans-BoldItalic.woff") format("woff");font-weight:bold;font-style:italic}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:"";content:none}table{border-collapse:collapse;border-spacing:0}html{box-sizing:border-box;font-size:18px;scroll-behavior:smooth}*,*:before,*:after{box-sizing:inherit}body{background:#fff;color:#000;font-family:"Gentium Basic",serif;font-size:1rem;line-height:1.35;text-align:left}b,strong{font-weight:bold}i,em{font-style:italic}h1,h2,h3,h4,h5,h6{font-family:"Open Sans",sans-serif;line-height:1.25;text-align:left}h1{font-size:2.25rem;font-weight:bold;margin:2rem 0}h2{font-size:1.75rem;font-weight:bold;margin:2rem 0}h3{font-size:1.33rem;font-weight:bold;margin:2rem 0}h4{font-size:1.15rem;font-weight:bold;margin-bottom:1rem}h5{font-size:1rem;font-style:italic;margin-bottom:1rem}h6{font-size:0.85rem;font-style:italic}p{margin-bottom:1rem}ol,ul{margin-bottom:1rem}li{line-height:1.35;margin-bottom:0.25rem}ol{padding-left:1.25rem}ol li{list-style-type:decimal}ul{padding-left:1rem}ul li{list-style-type:disc}blockquote{border-left:0.15rem solid lightgray;padding-left:1rem}code{font-family:"Fira Code",monospace;font-size:0.85rem}h1 code,h2 code,h3 code{font-size:inherit}pre{font-family:"Fira Code",monospace;font-size:0.8rem;line-height:1.5;margin-bottom:1rem;overflow:auto;white-space:pre-wrap}a{color:#07a0d2}a:hover{color:#735192;text-decoration:none}img{height:auto;max-width:100%}figure{margin:0 0 2rem 0;text-align:center}figcaption{font-style:italic;margin-top:.5rem}nav#toc ol,nav#landmarks ol{padding:0;margin-left:1em}nav#toc ol li,nav#landmarks ol li{list-style-type:none;margin:0;padding:0}a.footnote-ref{vertical-align:super}em,em em em,em em em em em{font-style:italic}em em,em em em em{font-style:normal}code{white-space:pre-wrap}span.smallcaps{font-variant:small-caps}span.underline{text-decoration:underline}q{quotes:"“" "”" "‘" "’"}div.column{display:inline-block;vertical-align:top;width:50%}div.hanging-indent{margin-left:1.5em;text-indent:-1.5em}.boxout{background-color:#f7f7f7;padding:1rem 1rem 0.01rem;margin-bottom:1rem}.break-line-anywhere{line-break:anywhere}img.center,img.center-no-caption{display:block;margin:0 auto}.code-context p{font-style:italic;margin-bottom:.25rem}.screen-reader,.content h1,[id="masthead-open"] span,[id="masthead-close"] span{border-width:0;clip:rect(0, 0, 0, 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}[id="chapter"]{margin:0 auto;max-width:1200px;padding:0 1rem;width:100%}@media (min-width: 960px){.chapter-nav{bottom:0;overflow-y:scroll;overflow-x:hidden;padding-right:1rem;position:fixed;top:0;z-index:10}}.chapter-nav nav{margin-bottom:1rem}.content{margin:2rem 0}@media (min-width: 960px){.content{margin-left:calc(30% + (1rem * 3))}}[id="masthead"]{background-color:#b81882;color:white;height:100%;left:0;margin-left:-100%;max-width:480px;overflow-x:hidden;padding:2rem 2.5rem;position:fixed;top:0;transition:0.33s;width:100%;z-index:100}[id="masthead"] [id="title-block-header"]{padding-bottom:1rem}[id="masthead"] h1.title{font-size:2rem}[id="masthead"] .subtitle{font-family:"Open Sans",sans-serif;font-size:1.15rem}[id="masthead"] .author{font-family:"Open Sans",sans-serif;font-size:0.85rem}[id="masthead"] a{color:white}[id="masthead"] a:hover{text-decoration:none}.close-button-wrapper{display:flex;justify-content:flex-end}[id="masthead-open"],[id="masthead-close"]{background-color:transparent;border:0;cursor:pointer;font-size:0.85rem;padding:0}[id="masthead-open"]:focus,[id="masthead-close"]:focus{outline:none}[id="masthead-open"]:after,[id="masthead-close"]:after{background-size:32px 32px;content:"";display:block;height:32px;width:32px}[id="masthead-open"]{margin-top:2rem}[id="masthead-open"]:after{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlklEQVR4nO3ZwQkCMQAEwFV8+bEPe7Aoe7MKsQM78GcF2oDg4RFWzxnIL4TNfhKSBAAAgJcuSR4/Ok7vNrf+qJIFUUA7QJsC2gHaNhPmHJPsRgcZ5NYOAAB8r9WEOfsk29FBBrknuc5dxHvAkimgHaBNAe0AbY5BAOBvTbkIHfLbHyPnuYt4D1gyBbQDtCmgHQAAAICCJzBKlYZ09zQzAAAAAElFTkSuQmCC")}[id="masthead-close"]:after{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAACDUlEQVR4nO2ZPU7DQBBGx5aoED0SVY4RiYIGiQMgSn4OQZWSjkMAt+EYCKq0gJQCEB8FHukrnNhrr3dnrH11MjPvJVIkR6RQKBQK3QBYAVjmvqMLAOcAbmIPvcM/75YjNPJfAH4AXMcaqvKwHIHklfERWuRNRmiR5whXQ4dukzcVYYc8R7gMHbrqkDcRoYc8R7gIGbxs5MxGCJAHgFcAi9AFZiNMLk+LzEVIJk8LzURILk+Ls0fIJk8HZIuQXZ4OSR7BjDwdlCyCOXk6bPIIZuXpwMkimJenQ6NHcCNPB0eL4E5eiRHBrbwyJoJ7eWVIhNnIK4ERNgC+ZyOvBEaYl7wSMYI/eSVCBL/yyogI/uWVJsImQH6dSr5OsUREjkRkL+D1+yJyONEtaUHY7zxj4n+HUYyQ9x8hgrzfCBHl/UUIlF8D+JxNhED5VwALGHjkHoUh8vRe3xHGyNMMnxFiyNMsXxFiytNMHxGmkKfZtiNMKU87bEZIIU+7bEVIKU87bUTIIU+780bIKU835IlgQZ5uSRvBkjzdlCaCRXm6bdoIluXpxigRtj0UPZV+DzHfROSkqqqXoSJDqarqWUTOROSjx8sPROS493AANYAHi598y61d34RfAKshg3dFMCGv7IgwTJ4G1wAeLcsrLRHGydPgGsCTZXmFIsSRp8E1gHvL8koT4Tb3HYVCoeCCP2aIN1tW+EaOAAAAAElFTkSuQmCC")}.book-nav{border-top:1px solid white;padding-top:2rem}.book-nav p{font-family:"Open Sans",sans-serif;font-size:1.25rem;font-weight:700}.book-nav p a{text-decoration:none}.book-nav p a:hover{text-decoration:underline}.front-matter-content,.back-matter-content{padding-left:0}.front-matter-content li,.back-matter-content li{list-style-type:none}.front-matter{margin-bottom:2rem}.main-matter-content{margin-bottom:2rem;margin-left:-.25rem;padding-left:0}.main-matter-content li{padding-left:.25rem}.back-matter-content{margin-bottom:3rem}.logo{width:125px}.chapter-nav h2{border-width:0;clip:rect(0, 0, 0, 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.chapter-nav nav ul{margin-top:1rem;padding-left:0}.chapter-nav nav ul li{font-family:"Open Sans", sans-serif;font-size:1.25rem;font-weight:bold;list-style-type:none}.chapter-nav nav ul li a{color:black;text-decoration:none}.chapter-nav nav ul li a:hover{text-decoration:underline}.chapter-nav nav ul li ul{padding-left:1rem}.chapter-nav nav ul li ul li{font-family:"Gentium Basic", serif;font-size:1rem;font-weight:normal;list-style-type:disc}.chapter-nav nav ul li ul li a{text-decoration:underline}.chapter-nav nav ul li ul li a:hover{text-decoration:none}.content footer{margin-top:3rem}.copyright a{color:black}.highlight{margin-left:-0.5rem;margin-right:-0.5rem;padding-left:0.25rem;padding-right:0.25rem;position:relative}.highlight pre{background-color:#f7f7f7;color:#2f1e2e;padding:0.75rem}.highlight pre>span{box-sizing:content-box}.highlight .hll{background-color:#efefef;border-left:0.15rem solid #5bc4bf;display:block;margin-left:-0.74rem;padding-left:0.6rem;padding-right:0.75rem;width:100%}.highlight:hover .highlight-copy-btn{visibility:visible}.highlight-copy-btn{background-color:#999;border:0;color:#fff;font-size:0.7em;line-height:1.6;padding:1px 5px;position:absolute;right:9px;text-align:center;top:5px;visibility:hidden}.highlight-copy-btn:hover{background-color:#666;cursor:pointer}.highlight .c{color:#8d8687}.highlight .err{color:#ef6155}.highlight .k{color:#815ba4}.highlight .l{color:#f99b15}.highlight .n{color:#2f1e2e}.highlight .o{color:#5bc4bf}.highlight .p{color:#2f1e2e}.highlight .cm{color:#8d8687}.highlight .cp{color:#8d8687}.highlight .c1{color:#8d8687}.highlight .cs{color:#8d8687}.highlight .gd{color:#ef6155}.highlight .ge{font-style:italic}.highlight .gh{color:#2f1e2e;font-weight:bold}.highlight .gi{color:#48b685}.highlight .gp{color:#8d8687;font-weight:bold}.highlight .gs{font-weight:bold}.highlight .gu{color:#5bc4bf;font-weight:bold}.highlight .kc{color:#815ba4}.highlight .kd{color:#815ba4}.highlight .kn{color:#5bc4bf}.highlight .kp{color:#815ba4}.highlight .kr{color:#815ba4}.highlight .kt{color:#fec418}.highlight .ld{color:#48b685}.highlight .m{color:#f99b15}.highlight .s{color:#48b685}.highlight .na{color:#06b6ef}.highlight .nb{color:#2f1e2e}.highlight .nc{color:#fec418}.highlight .no{color:#ef6155}.highlight .nd{color:#5bc4bf}.highlight .ni{color:#2f1e2e}.highlight .ne{color:#ef6155}.highlight .nf{color:#06b6ef}.highlight .nl{color:#2f1e2e}.highlight .nn{color:#fec418}.highlight .nx{color:#06b6ef}.highlight .py{color:#2f1e2e}.highlight .nt{color:#5bc4bf}.highlight .nv{color:#ef6155}.highlight .ow{color:#5bc4bf}.highlight .w{color:#2f1e2e}.highlight .mf{color:#f99b15}.highlight .mh{color:#f99b15}.highlight .mi{color:#f99b15}.highlight .mo{color:#f99b15}.highlight .sb{color:#48b685}.highlight .sc{color:#2f1e2e}.highlight .sd{color:#8d8687}.highlight .s2{color:#48b685}.highlight .se{color:#f99b15}.highlight .sh{color:#48b685}.highlight .si{color:#f99b15}.highlight .sx{color:#48b685}.highlight .sr{color:#48b685}.highlight .s1{color:#48b685}.highlight .ss{color:#48b685}.highlight .bp{color:#2f1e2e}.highlight .vc{color:#ef6155}.highlight .vg{color:#ef6155}.highlight .vi{color:#ef6155}.highlight .il{color:#f99b15} 2 | -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/FiraCode-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/FiraCode-Regular.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-Bold.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-Bold.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-BoldItalic.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-BoldItalic.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-Italic.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-Italic.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-Regular.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/GentiumBasic-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/GentiumBasic-Regular.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-Bold.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-Bold.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-BoldItalic.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-BoldItalic.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-Italic.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-Italic.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-Regular.woff -------------------------------------------------------------------------------- /get-going-with-graphql/fonts/OpenSans-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/fonts/OpenSans-Regular.woff2 -------------------------------------------------------------------------------- /get-going-with-graphql/images/8bp-logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 19 | 20 | 24 | 25 | 27 | 31 | 33 | 37 | 42 | 47 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /get-going-with-graphql/images/8bp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/8bp-logo.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/8bp-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 19 | 20 | 24 | 25 | 27 | 31 | 33 | 37 | 42 | 47 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-1.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-2_OLD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-2_OLD.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-2a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-2a.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-3.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-3_OLD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-3_OLD.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-5.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-onboarding-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-onboarding-7.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-sign-up-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-sign-up-2.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-sign-up-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-sign-up-3.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-sign-up-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-sign-up-4.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/apollo-studio-sign-up-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/apollo-studio-sign-up-5.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/github-api-explorer-docs-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/github-api-explorer-docs-1.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/github-api-explorer-docs-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/github-api-explorer-docs-2.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/github-api-explorer-docs-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/github-api-explorer-docs-3.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/github-api-explorer-docs-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/github-api-explorer-docs-4.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/github-api-explorer-with-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/github-api-explorer-with-response.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/_drafts/react-app-update-review-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/_drafts/react-app-update-review-form.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/apollo-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/apollo-link.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/client-server-jwts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/client-server-jwts.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/cursor-pagination-add-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/cursor-pagination-add-new.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/cursor-pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/cursor-pagination.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/offset-pagination-add-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/offset-pagination-add-new.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/offset-pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/offset-pagination.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/operation-parts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/operation-parts.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/diagrams/schema-node-and-edges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/diagrams/schema-node-and-edges.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/favicon.ico -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-dev-tools-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-dev-tools-1.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-dev-tools-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-dev-tools-2.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-dev-tools-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-dev-tools-3.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-dev-tools-author-cache-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-dev-tools-author-cache-item.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-dev-tools-book-cache-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-dev-tools-book-cache-item.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-explorer-documentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-explorer-documentation.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-explorer-extract-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-explorer-extract-variables.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-explorer-headers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-explorer-headers.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-explorer-use-variables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-explorer-use-variables.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-studio-onboarding-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-studio-onboarding-1.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-studio-onboarding-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-studio-onboarding-2.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-studio-onboarding-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-studio-onboarding-3.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-studio-onboarding-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-studio-onboarding-4.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/apollo-studio-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/apollo-studio-sign-up.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/chrome-cookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/chrome-cookie.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/cra-boilerplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/cra-boilerplate.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/cra-no-styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/cra-no-styles.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/github-api-explorer-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/github-api-explorer-initial.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/github-api-explorer-with-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/github-api-explorer-with-response.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-book-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-book-list.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-create-review-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-create-review-form.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-initial-index-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-initial-index-page.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-library-book-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-library-book-list.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-login-form-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-login-form-1.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-login-form-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-login-form-2.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-login-form-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-login-form-3.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-new-book-form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-new-book-form.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-review-list-action-buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-review-list-action-buttons.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-search-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-search-results.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-single-book-authenticated-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-single-book-authenticated-1.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-single-book-authenticated-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-single-book-authenticated-2.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-single-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-single-book.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/react-app-with-main-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/react-app-with-main-layout.png -------------------------------------------------------------------------------- /get-going-with-graphql/images/screenshots/tailwind-no-styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/8bitpress/get-going-with-graphql/d220637b12dde56483d64eb49d6e39ecb9eb6ad3/get-going-with-graphql/images/screenshots/tailwind-no-styles.png --------------------------------------------------------------------------------