Update all code samples

This commit is contained in:
Peggy Rayzis
2018-11-07 06:53:07 -08:00
parent 04bd2e6490
commit d07efee1f7
11 changed files with 85 additions and 53 deletions

View File

@@ -12,12 +12,12 @@ While Apollo Client works with any view layer, it's most commonly used with Reac
For this half of the tutorial, we will be working in the `client/` folder of the project. You should have the project already from the server portioned, but if you don't, make sure to clone [the tutorial](https://github.com/apollographql/fullstack-tutorial/). From the root of the project, run:
```bash
cd client && npm install
cd start/client && npm install
```
Now, our dependencies are installed. Here are the packages we will be using to build out our frontend:
- `apollo-client@next`: A complete data management solution with an intelligent cache. In this tutorial, we will be using the Apollo Client 3.0 preview since it includes local state management capabilities and sets your cache up for you.
- `apollo-client@alpha`: A complete data management solution with an intelligent cache. In this tutorial, we will be using the Apollo Client 3.0 preview since it includes local state management capabilities and sets your cache up for you.
- `react-apollo`: The view layer integration for React that exports components such as `Query` and `Mutation`
- `graphql-tag`: The tag function `gql` that we use to wrap our query strings in order to parse them into an AST
@@ -36,6 +36,12 @@ Our key is now stored under the environment variable `ENGINE_API_KEY`. Apollo VS
Next, create an Apollo config file called `apollo.config.js`. This config file is how you configure both the Apollo VSCode extension and CLI. Paste the snippet below into the file:
```js
module.exports = {
client: {
name: 'Space Explorer [web]',
service: 'space-explorer',
},
};
```
Great, we're all set up! Let's dive into building our first client.

View File

@@ -60,11 +60,10 @@ _src/schema.js_
```graphql
type Launch {
id: ID!
year: String
mission: Mission!
site: String
mission: Mission
rocket: Rocket
launchSuccess: Boolean
isBooked: Boolean
isBooked: Boolean!
}
```
@@ -77,18 +76,17 @@ launchReducer(launch) {
return {
id: launch.flight_number || 0,
cursor: `${launch.launch_date_unix}`,
site: launch.launch_site && launch.launch_site.site_name,
mission: {
name: launch.mission_name,
missionPatchSmall: launch.links.mission_patch_small,
missionPatchLarge: launch.links.mission_patch
missionPatchLarge: launch.links.mission_patch,
},
year: launch.launch_year,
rocket: {
id: launch.rocket.rocket_id,
name: launch.rocket.rocket_name,
type: launch.rocket.rocket_type,
},
launchSuccess: launch.launch_success,
};
}
```

View File

@@ -24,7 +24,10 @@ Why do you need a graph? Today, one of the most difficult parts of building an a
In this tutorial, we'll build an interactive app for reserving your spot on an upcoming Space-X launch. You can think of it as an Airbnb for space travel! All of the data is real, thanks to the [SpaceX-API](https://github.com/r-spacex/SpaceX-API).
Here's what the finished app will look like:
<!-- TODO: Add screenshot -->
<div style="text-align:center">
![Space explorer](../images/space-explorer.png)
</div>
The app has five screens: a login screen, a list of launches, a launch detail, a profile page, and a cart. The graph API powering our space app connects to a REST API and a SQLite database. Don't worry if you're unfamiliar with those technologies, you don't need to know how to build a REST API or SQLite database from scratch in order to complete the tutorial.
@@ -55,9 +58,11 @@ Next, in your terminal, clone this repository:
git clone https://github.com/apollographql/fullstack-tutorial/
```
There are two folders: one for the server and one for the client. We will be working in the server folder first. If you're comfortable with building a graph API already and you want to skip to the client portion, navigate to the [last half of the tutorial](./client.html).
There are two folders: one for the starting point (`start/`) and one for the final version (`final`). Within each directory are two folders: one for the server and one for the client. We will be working in the server folder first. If you're comfortable with building a graph API already and you want to skip to the client portion, navigate to the [last half of the tutorial](./client.html).
<h3 id="vscode">Configure Apollo VSCode</h3>
<!--
TODO: Add in this section after Apollo VSCode works for server development
<h3 id="vscode">Configure Apollo VSCode</h3> -->
<h3 id="help">Where can I get help?</h3>

View File

@@ -42,9 +42,9 @@ We can also add local fields to server data by extending types from our server.
<h2 id="store-initializers">Initialize the store</h2>
Now that we've created our client schema, let's learn how to initialize the store. Since queries execute as soon as the component mounts, it's important for us to warm the Apollo cache with some default state so those queries don't error out. We will need to create `storeInitializers` for both `isLoggedIn` and `cartItems` to prevent these two local queries from erroring out:
Now that we've created our client schema, let's learn how to initialize the store. Since queries execute as soon as the component mounts, it's important for us to warm the Apollo cache with some default state so those queries don't error out. We will need to create `initializers` for both `isLoggedIn` and `cartItems` to prevent these two local queries from erroring out:
Jump to `src/index.js` and specify your `storeInitializers` on the `ApolloClient` constructor:
Jump to `src/index.js` and specify your `initializers` on the `ApolloClient` constructor:
_src/index.js_
@@ -57,14 +57,14 @@ const client = new ApolloClient({
authorization: localStorage.getItem('token'),
},
}),
storeInitializers: {
initializers: {
isLoggedIn: () => !!localStorage.getItem('token'),
cartItems: () => [],
},
});
```
These `storeInitializers` will be called as soon as `ApolloClient` is created. They will also run if the store is reset.
These `initializers` will be called as soon as `ApolloClient` is created. They will also run if the store is reset.
Now that we've added default state to the Apollo cache, let's learn how to query local data from within our React components.
@@ -138,7 +138,7 @@ export default function Cart() {
<Fragment>
<Header>My Cart</Header>
{!data.cartItems || !data.cartItems.length ? (
<p>No items in your cart</p>
<p data-testid="empty-message">No items in your cart</p>
) : (
<Fragment>
{data.cartItems.map(launchId => (
@@ -291,9 +291,11 @@ export default function BookTrips({ cartItems }) {
>
{(bookTrips, { data, loading, error }) =>
data && data.bookTrips && !data.bookTrips.success ? (
<p>{data.bookTrips.message}</p>
<p data-testid="message">{data.bookTrips.message}</p>
) : (
<Button onClick={bookTrips}>Book All</Button>
<Button onClick={bookTrips} data-testid="book-button">
Book All
</Button>
)
}
</Mutation>
@@ -318,7 +320,7 @@ export const resolvers = {
const { cartItems } = cache.readQuery({ query: GET_CART_ITEMS });
const data = {
cartItems: cartItems.includes(id)
? cartItems.filter(i => !i)
? cartItems.filter(i => i !== id)
: [...cartItems, id],
};
cache.writeQuery({ query: GET_CART_ITEMS, data });
@@ -383,10 +385,17 @@ export default function ActionButton({ isBooked, id, isInCart }) {
},
]}
>
{(mutate, { data, loading, error }) => {
{(mutate, { loading, error }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>An error occurred</p>;
return (
<div>
<Button onClick={mutate} isBooked={isBooked}>
<Button
onClick={mutate}
isBooked={isBooked}
data-testid={'action-button'}
>
{isBooked
? 'Cancel This Trip'
: isInCart

View File

@@ -59,7 +59,7 @@ In our `onCompleted` handler, we also call `client.writeData` to write local dat
_src/pages/login.js_
```js lines=3,4,7-10,16
```js lines=3,4,7-10,22
export default function Login() {
return (
<ApolloConsumer>
@@ -71,7 +71,14 @@ export default function Login() {
client.writeData({ data: { isLoggedIn: true } });
}}
>
{(login, { data }) => <LoginForm login={login} />}
{(login, { loading, error }) => {
// this loading state will probably never show, but it's helpful to
// have for testing
if (loading) return <Loading />;
if (error) return <p>An error occurred</p>;
return <LoginForm login={login} />;
}}
</Mutation>
)}
</ApolloConsumer>

View File

@@ -330,15 +330,19 @@ export default function Profile() {
return (
<Fragment>
<Header>My Trips</Header>
{data.me.trips.length ? data.me.trips.map(launch => (
<LaunchTile key={launch.id} launch={launch} />
)) : <p>You haven't booked any trips</p>}
{data.me.trips.length ? (
data.me.trips.map(launch => (
<LaunchTile key={launch.id} launch={launch} />
))
) : (
<p>You haven't booked any trips</p>
)}
</Fragment>
);
}}
</Query>
);
};
}
```
If you try to render this query, you'll notice that it returns null. This is because we need to implement our login feature first. We're going to tackle login in the next section.

View File

@@ -173,6 +173,9 @@ const { paginateResults } = require('./utils');
Query: {
launches: async (_, { pageSize = 20, after }, { dataSources }) => {
const allLaunches = await dataSources.launchAPI.getAllLaunches();
// we want these in reverse chronological order
allLaunches.reverse();
const launches = paginateResults({
after,
pageSize,
@@ -223,11 +226,12 @@ _src/resolvers.js_
```js
Mission: {
missionPatch: (mission, { size }) => {
// make sure the default size is 'large' in case user doesn't specify
missionPatch: (mission, { size } = { size: 'LARGE' }) => {
return size === 'SMALL'
? mission.missionPatchSmall
: mission.missionPatchLarge;
}
},
},
```
@@ -274,19 +278,17 @@ Let's open up `src/index.js` and update the `context` function on `ApolloServer`
_src/index.js_
```js line=4,11,15
```js line=4,8,10
const server = new ApolloServer({
context: async ({ req }) => {
// simple auth check on every request
const auth = (req.headers && req.headers.authorization) || '';
const email = new Buffer(auth, 'base64').toString('ascii');
// if the email isn't formatted validly, return null for user
if (!isEmail.validate(email)) return { user: null };
// find a user by their email
// find a user by their email
const users = await store.users.findOrCreate({ where: { email } });
const user = users && users[0] ? users[0] : null;
return { user: { ...user.dataValues } };
@@ -393,6 +395,4 @@ Next, paste our authorization header into the HTTP Headers box at the bottom:
}
```
Then, run the mutation. You should see a success message, along with the ids of the mutations we just booked. Testing mutations manually in the playground is a good way to explore our API, but in a real-world application, we should run automated tests so we can safely refactor our code. In the next section, you'll learn all about testing your graph.
<h2 id="testing">Test your graph</h2>
Then, run the mutation. You should see a success message, along with the ids of the mutations we just booked. Testing mutations manually in the playground is a good way to explore our API, but in a real-world application, we should run automated tests so we can safely refactor our code. In the next section, you'll learn all about testing your graph.

View File

@@ -12,7 +12,7 @@ Before we write our schema, we need to set up our graph API's server. **Apollo S
From the root, let's install our projects dependencies:
```bash
cd server && npm install
cd start/server && npm install
```
The two packages you need to get started with Apollo Server are `apollo-server` and `graphql`, which we've already installed for you. Now, let's navigate to `src/index.js` so we can create our server. Copy the code below into the file.
@@ -86,11 +86,10 @@ _src/schema.js_
```graphql
type Launch {
id: ID!
year: String
mission: Mission!
site: String
mission: Mission
rocket: Rocket
launchSuccess: Boolean
isBooked: Boolean
isBooked: Boolean!
}
```
@@ -101,15 +100,10 @@ The `Mission` and `Rocket` types represent other object types. Let's define the
_src/schema.js_
```graphql
type Mission {
name: String!
missionPatch(size: PatchSize): String
}
type Rocket {
id: ID!
name: String!
type: String!
name: String
type: String
}
type User {
@@ -118,6 +112,11 @@ type User {
trips: [Launch]!
}
type Mission {
name: String
missionPatch(size: PatchSize): String
}
enum PatchSize {
SMALL
LARGE
@@ -136,9 +135,13 @@ _src/schema.js_
```graphql
type Mutation {
bookTrip(launchId: ID!): TripUpdateResponse!
# if false, signup failed -- check errors
bookTrips(launchIds: [ID]!): TripUpdateResponse!
# if false, cancellation failed -- check errors
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): String
login(email: String): String # login token
}
```
@@ -150,7 +153,7 @@ _src/schema.js_
type TripUpdateResponse {
success: Boolean!
message: String
launch: Launch
launches: [Launch]
}
```