├── .gitignore ├── LICENSE ├── README.md ├── gatsby-node.js ├── package.json ├── src └── templates │ ├── ability.js │ ├── all-pokemon.js │ └── pokemon.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .cache/ 61 | public 62 | yarn-error.log 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 gatsbyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Can you use Gatsby without GraphQL? 2 | 3 | Yes! 4 | 5 | This is a small example that loads data from the [PokéAPI](https://www.pokeapi.co/)’s REST endpoints, then creates pages (and nested pages) using [Gatsby’s `createPages` API](https://www.gatsbyjs.org/docs/node-apis/#createPages). 6 | 7 | ## What are the trade-offs? 8 | 9 | ### The upsides of using REST: 10 | 11 | - The approach is familiar and comfortable, _especially_ if you’re new to GraphQL 12 | - There’s no intermediate step: you fetch some data, then build pages with it 13 | 14 | ### The downsides of using REST: 15 | 16 | - There are lots of calls required, and each nested call relies on data from the previous call; this won’t scale well 17 | - All of the data for the page needs to be explicitly passed into the context object, which makes it a little harder to understand what data is being passed to the page component 18 | - The relationships between items are harder to understand; we need to make three separate requests, resulting in three separate data objects that we have to manually combine in the front-end 19 | 20 | ## What would this look like querying from a GraphQL API? 21 | 22 | Great question! There’s not a stable Pokémon GraphQL API that I’ve seen, but if there was, the query might look like this: 23 | 24 | ```jsx 25 | const data = await graphql(` 26 | { 27 | Pokemon { 28 | edges { 29 | node { 30 | name 31 | abilities { 32 | name 33 | effect 34 | } 35 | } 36 | } 37 | } 38 | } 39 | `); 40 | 41 | // Use createPage to turn the data into pages, just like the REST version. 42 | ``` 43 | 44 | This one query accomplishes the same thing as the three different REST calls, and it shows more clearly how the data is related (e.g. each Pokémon has abilities). 45 | 46 | ## What would this look like using Gatsby's GraphQL integration layer? 47 | 48 | For quick and easy comparison, the [using-gatsby-data-layer](https://github.com/jlengstorf/gatsby-with-unstructured-data/tree/using-gatsby-data-layer) branch illustrates how you can accomplish this using Gatsby's integration layer, rather than using the unstructured data approach. 49 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const get = endpoint => axios.get(`https://pokeapi.co/api/v2${endpoint}`); 4 | 5 | const getPokemonData = names => 6 | Promise.all( 7 | names.map(async name => { 8 | const { data: pokemon } = await get(`/pokemon/${name}`); 9 | const abilities = await Promise.all( 10 | pokemon.abilities.map(async ({ ability: { name: abilityName } }) => { 11 | const { data: ability } = await get(`/ability/${abilityName}`); 12 | 13 | return ability; 14 | }) 15 | ); 16 | 17 | return { ...pokemon, abilities }; 18 | }) 19 | ); 20 | 21 | exports.createPages = async ({ actions: { createPage } }) => { 22 | const allPokemon = await getPokemonData(['pikachu', 'charizard', 'squirtle']); 23 | 24 | // Create a page that lists all Pokémon. 25 | createPage({ 26 | path: `/`, 27 | component: require.resolve('./src/templates/all-pokemon.js'), 28 | context: { allPokemon } 29 | }); 30 | 31 | // Create a page for each Pokémon. 32 | allPokemon.forEach(pokemon => { 33 | createPage({ 34 | path: `/pokemon/${pokemon.name}/`, 35 | component: require.resolve('./src/templates/pokemon.js'), 36 | context: { pokemon } 37 | }); 38 | 39 | // Create a page for each ability of the current Pokémon. 40 | pokemon.abilities.forEach(ability => { 41 | createPage({ 42 | path: `/pokemon/${pokemon.name}/ability/${ability.name}/`, 43 | component: require.resolve('./src/templates/ability.js'), 44 | context: { pokemon, ability } 45 | }); 46 | }); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-source-plugin-workshop", 3 | "description": "Workshop source for a source plugin workshop", 4 | "license": "MIT", 5 | "scripts": { 6 | "develop": "gatsby develop", 7 | "build": "gatsby build", 8 | "serve": "gatsby serve" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.18.0", 12 | "gatsby": "^2.0.0", 13 | "react": "^16.5.1", 14 | "react-dom": "^16.5.1" 15 | }, 16 | "version": "1.0.0", 17 | "main": "index.js", 18 | "author": "Jason Lengstorf " 19 | } 20 | -------------------------------------------------------------------------------- /src/templates/ability.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | 4 | const getName = ability => 5 | ability.names.find(({ language }) => language.name === 'en').name; 6 | 7 | export default ({ pageContext: { pokemon, ability } }) => ( 8 |
9 |

10 | {pokemon.name} 11 | ’s {getName(ability)} ability 12 |

13 | {pokemon.name} 14 |

{ability.effect_entries[0].effect}

15 | Back to {pokemon.name} 16 |
17 | ); 18 | -------------------------------------------------------------------------------- /src/templates/all-pokemon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | 4 | export default ({ pageContext: { allPokemon } }) => ( 5 |
6 |

Choose a Pokémon!

7 | 24 |
25 | ); 26 | -------------------------------------------------------------------------------- /src/templates/pokemon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'gatsby'; 3 | 4 | export default ({ pageContext: { pokemon } }) => ( 5 |
6 |

{pokemon.name}

7 | {pokemon.name} 8 |

Abilities

9 | 18 | Back to all Pokémon 19 |
20 | ); 21 | --------------------------------------------------------------------------------