Full-stack tutorial from Apollo's new learning platform Odyssey.
Here is how it'll look like when we're finished
git clone https://github.com/apollographql/odyssey-lift-off-part1 apollographql
In apollographql/
,
π¦ catstronauts-demo
β£ π client
β β£ π public
β β£ π rc
β β β£ π assets
β β β£ π components
β β β£ π containers
β β β£ π pages
β β β£ π utils
β β β£ π index.js
β β β π styles.js
β β£ π README.md
β β£ π package.json
β£ π server
β β£ π src
β β β£ π index.js
β β£ π README.md
β β£ π package.json
β π README.md
In both server/
and client/
folder:
npm install
Inside apollographql
folder,
Init npm:
npm init -y
Install concurrently package:
npm i -D concurrently
Add the script below in package.json
:
"scripts": {
"start": "concurrently \"cd client && npm start\" \"cd server && npm run start\""
}
Ready to run client/
and server/
at the same time.
In apollographql/
:
npm start
The mockup
Define a SpaceCat
type with the following fields: name
of type String
(non null), age
of type Int
, and missions
of type List of Mission
const typeDefs = gql`
"""
block description
"""
type SpaceCat {
"normal description"
name: String!
age: Int
missions: [Mission]
}
`;
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query {
"Query to get tracks array for the homepage grid"
tracksForHome: [Track!]!
}
"A track is a group of Modules that teaches about a specific topic"
type track {
id: ID!
"the track's title"
title: String!
"the track's main author"
author: Author!
"the tracks' main illustration to display in track card or track page detail"
thumbnail: String
"the track's approximate length to complete, in minutes"
length: Int
"the number of modules this track contains"
modulesCount: Int
}
"Author of a complete Track"
type Author {
id: ID!
"Author's first and last name"
name: String!
"Author's profile picture url"
photo: String
}
`;
module.exports = typeDefs;
const { ApolloServer, MockList } = require('apollo-server');
const typeDefs = require('./schema');
const mocks = {
Query: () => ({
tracksForHome: () => new MockList([6, 9]),
}),
Track: () => ({
id: () => 'track_01',
title: () => 'Astro Kitty, Space Explorer',
author: () => {
return {
name: 'Grumpy Cat',
photo:
'https://res.cloudinary.com/dety84pbu/image/upload/v1606816219/kitty-veyron-sm_mctf3c.jpg',
};
},
thumbnail: () =>
'https://res.cloudinary.com/dety84pbu/image/upload/v1598465568/nebula_cat_djkt9r.jpg',
length: () => 1210,
modulesCount: () => 6,
}),
};
const server = new ApolloServer({
typeDefs,
mocks,
});
server.listen().then(({ url }) => {
console.log(`
π Server is running!
π Listening at ${url}
π Query at https://studio.apollographql.com/dev
`);
});
πclient
β£ πsrc
β β£ πassets
β β£ πcomponents
β β£ πcontainers
β β£ πpages
β β£ ...
β£ ...
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import GlobalStyles from './styles';
import Pages from './pages';
import { ApolloProvider, ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
url: 'http://localhost:4000',
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<GlobalStyles />
<Pages />
</ApolloProvider>,
document.getElementById('root')
);
// client/src/pages/tracks.js
import React from 'react';
import { Layout } from '../components';
import { gql } from '@apollo/client';
const TRACKS = gql`
query getTracks {
tracksForHome {
id
title
thumbnail
length
modulesCount
author {
id
name
photo
}
}
}
`;
/**
* Tracks Page is the Catstronauts home page.
* We display a grid of tracks fetched with useQuery with the TRACKS query
*/
const Tracks = () => {
return <Layout grid> </Layout>;
};
export default Tracks;
// client/src/pages/tracks.js
import React from 'react';
import { Layout } from '../components';
import { useQuery, gql } from '@apollo/client';
import TrackCard from '../containers/track-card';
import QueryResult from '../components/query-result';
const TRACKS = gql`
query getTracks {
tracksForHome {
id
title
thumbnail
length
modulesCount
author {
id
name
photo
}
}
}
`;
/**
* Tracks Page is the Catstronauts home page.
* We display a grid of tracks fetched with useQuery with the TRACKS query
*/
const Tracks = () => {
const { loading, error, data } = useQuery(TRACKS);
if (loading) return 'Loading...';
if (error) return `ERROR! ${error.message}`;
return (
<Layout grid>
<QueryResult error={error} loading={loading} data={data}>
{data?.tracksForHome?.map((track) => (
<TrackCard key={track.id} track={track} />
))}
</QueryResult>
</Layout>
);
};
export default Tracks;
It is the end of the first part of the Catstronauts
*The resources used in this tutorial are from Odyssey.