diff --git a/docs/contributing/docs-contributions.md b/docs/contributing/docs-contributions.md index c33e64764c3b2..3608336e098f5 100644 --- a/docs/contributing/docs-contributions.md +++ b/docs/contributing/docs-contributions.md @@ -191,7 +191,7 @@ Sometimes it makes sense to move or rename a file as part of docs restructuring ```yaml:title=www/redirects.yaml - fromPath: /docs/source-plugin-tutorial/ - toPath: /tutorial/pixabay-source-plugin-tutorial/ + toPath: /tutorial/source-plugin-tutorial/ ``` ## Claim your swag diff --git a/docs/docs/creating-a-source-plugin.md b/docs/docs/creating-a-source-plugin.md index d9e816efd0113..ffff109d76463 100644 --- a/docs/docs/creating-a-source-plugin.md +++ b/docs/docs/creating-a-source-plugin.md @@ -482,5 +482,5 @@ Then the new data needs to be pulled in via a live update like a websocket (in t ## Additional resources - Working example repository on [creating source plugins](https://github.com/gatsbyjs/gatsby/tree/master/examples/creating-source-plugins) with the features in this guide implemented -- Tutorial on [Creating a Pixabay Image Source Plugin](/tutorial/pixabay-source-plugin-tutorial/) +- Tutorial on [Creating a Source Plugin](/tutorial/source-plugin-tutorial/) - [`gatsby-node-helpers`](https://github.com/angeloashmore/gatsby-node-helpers), a community-made npm package with helper functions to generate Node objects with required fields like IDs and the `contentDigest` MD5 hash. diff --git a/docs/docs/gatsby-vendor-partnership.md b/docs/docs/gatsby-vendor-partnership.md index c500b2ab661e4..a2ee1816beedb 100644 --- a/docs/docs/gatsby-vendor-partnership.md +++ b/docs/docs/gatsby-vendor-partnership.md @@ -54,7 +54,7 @@ Current partners have reported development timelines of 2-3 days when the conten If you have a GraphQL-based API, you **may not need to build an integration at all** -- Gatsby supports integration with GraphQL APIs via so-called ["schema stitching"](/blog/2018-09-25-announcing-graphql-stitching-support/). -If you have questions while building your Gatsby integrations, try reading other supporting documentation such as the [general plugin authoring guide](/docs/creating-plugins/) and [source plugin tutorial](/tutorial/pixabay-source-plugin-tutorial/). +If you have questions while building your Gatsby integrations, try reading other supporting documentation such as the [general plugin authoring guide](/docs/creating-plugins/) and [source plugin tutorial](/tutorial/source-plugin-tutorial/). If you still have questions, please [raise an issue on GitHub](https://github.com/gatsbyjs/gatsby/issues), ask a question in [Discord chat](https://gatsby.dev/discord), or reach out to our team at [developer-relations@gatsbyjs.com](mailto:developer-relations@gatsbyjs.com). diff --git a/docs/docs/recipes/sourcing-data.md b/docs/docs/recipes/sourcing-data.md index 0ecc4151daae0..26058f7da7fee 100644 --- a/docs/docs/recipes/sourcing-data.md +++ b/docs/docs/recipes/sourcing-data.md @@ -59,7 +59,7 @@ query MyPokemonQuery { - Walk through an example using the `gatsby-source-filesystem` plugin in [tutorial part five](/tutorial/part-five/#source-plugins) - Search available source plugins in the [Gatsby library](/plugins/?=source) -- Understand source plugins by building one in the [Pixabay source plugin tutorial](/tutorial/pixabay-source-plugin-tutorial/) +- Understand source plugins by building one in the [source plugin tutorial](/tutorial/source-plugin-tutorial/) - The createNode function [documentation](/docs/actions/#createNode) ## Sourcing Markdown data for blog posts and pages with GraphQL diff --git a/docs/docs/schema-gql-type.md b/docs/docs/schema-gql-type.md index e261922c25aed..269cbf70f9964 100644 --- a/docs/docs/schema-gql-type.md +++ b/docs/docs/schema-gql-type.md @@ -106,7 +106,7 @@ Now we can create a GraphQL Field declaration whose type is `AuthorYaml` (which #### Foreign Key reference (`___NODE`) -If not a mapping field, it might instead end in `___NODE`, signifying that its value is an ID that is a foreign key reference to another node in redux. Check out the [Source Plugin Tutorial](/tutorial/pixabay-source-plugin-tutorial/) for how this works from a user point of view. Behind the scenes, the field inference is handled by [inferFromFieldName](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/infer-graphql-type.js#L204). +If not a mapping field, it might instead end in `___NODE`, signifying that its value is an ID that is a foreign key reference to another node in redux. Check out the [Source Plugin Tutorial](/tutorial/source-plugin-tutorial/) for how this works from a user point of view. Behind the scenes, the field inference is handled by [inferFromFieldName](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/infer-graphql-type.js#L204). This is actually quite similar to the mapping case above. We remove the `___NODE` part of the field name. E.g. `author___NODE` would become `author`. Then, we find our `linkedNode`. I.e given the example value for `author` (which would be an ID), we find its actual node in redux. Then, we find its type in processed types by its `internal.type`. Note, that also like in mapping fields, we can define the `linkedField` too. This can be specified via `nodeFieldname___NODE___linkedFieldName`. E.g. for `author___NODE___name`, the linkedField would be `name` instead of `id`. diff --git a/docs/docs/sourcing-from-private-apis.md b/docs/docs/sourcing-from-private-apis.md index 340fa2bfe11c3..47189365e4089 100644 --- a/docs/docs/sourcing-from-private-apis.md +++ b/docs/docs/sourcing-from-private-apis.md @@ -10,7 +10,7 @@ There are 3 approaches that you can use to source data from your private API: 1. If your private API is a GraphQL API, you can use [`gatsby-source-graphql`](/packages/gatsby-source-graphql/). 2. If your private API is not a GraphQL API and you are new to GraphQL, treat the data as unstructured data and fetch it during build time, as described by the guide "[Using Gatsby without GraphQL](/docs/using-gatsby-without-graphql/)". However, as highlighted in the guide, this approach comes with some tradeoffs. -3. Create a source plugin, as described in the tutorial "[Source plugin tutorial](/tutorial/pixabay-source-plugin-tutorial/)". +3. Create a source plugin, as described in the tutorial "[Source plugin tutorial](/tutorial/source-plugin-tutorial/)". ## Other considerations diff --git a/docs/tutorial/pixabay-source-plugin-tutorial.md b/docs/tutorial/pixabay-source-plugin-tutorial.md deleted file mode 100644 index d751e83eec726..0000000000000 --- a/docs/tutorial/pixabay-source-plugin-tutorial.md +++ /dev/null @@ -1,390 +0,0 @@ ---- -title: "Creating an Image Source Plugin" ---- - -## What this tutorial covers - -In this tutorial you'll create your own source plugin. Your plugin will source data from [pixabay.com](https://pixabay.com) allowing you to add Pixabay images to any Gatsby site. - -## What is a source plugin? - -Source plugins "source" data from remote or local locations into what Gatsby calls [nodes](/docs/node-interface/). - -For more background on source plugins, check out [Gatsby's source plugin documentation](/docs/creating-a-source-plugin/) - -## Why create a source plugin? - -Source plugins convert data from any source into a format that can be processed by Gatsby. Your Gatsby site could use several source plugins to combine data in interesting ways. - -If you can't find a plugin for your data source you can create your own. - -_**NOTE:** if your data is local i.e. on your file system and part of your site's repo, then you generally don't want to create a new source plugin. Instead you want to use [gatsby-source-filesystem](/packages/gatsby-source-filesystem/) which handles reading and watching files for you. You can then use [transformer plugins](/plugins/?=gatsby-transformer) like [gatsby-transformer-yaml](/packages/gatsby-transformer-yaml/) to make queryable data from files._ - -## How to create a source plugin - -### Overview - -Your plugin is going to source images from Pixabay. You'll be able to configure your plugin in your site's `gatsby-config.js` file and write GraphQL queries to access your plugin's data. - -> **NOTE:** You'll need a Pixabay API key which you can get by [registering for a Pixabay account](https://pixabay.com/en/accounts/register/). Your API key is in the [“Search Images“ section of the Pixabay API docs](https://pixabay.com/api/docs/#api_search_images). - -### An example API request - -Pixabay's [API documentation](https://pixabay.com/api/docs/#api_search_images) describes how their API works. Here's an example that uses a few options to search for photos: - -`https://pixabay.com/api/?q=yellow+flowers&editors_choice=true&pretty=true&key=` - -Take the above URL and paste it into a browser to see Pixabay's response to your query. It gives you a list of photos matching the query "yellow flowers" that have received an Editor's Choice award. - -> **NOTE:** You should replace `` with your Pixabay API key. - -### Plugin behavior - -Your plugin will have the following behavior: - -- Accept config options like a Pixabay API key and a search query -- Make an API request using the provided config options -- Convert the data in the API response to Gatsby's node system - -### Setup a new Gatsby site - -Create a new Gatsby project and change directories into the new project you just created. - -```shell -gatsby new source-tutorial-site https://github.com/gatsbyjs/gatsby-starter-default -cd source-tutorial-site -``` - -You're going to build your plugin as a "local" plugin that only exists for your project. Later on you'll learn how to publish a plugin to [npm](https://npmjs.com) so anyone can use it, but for now create a `plugins` directory and change into that directory: - -```shell -mkdir plugins -cd plugins -``` - -### Create a `plugins` folder - -The bare essentials of a plugin are a directory named after your plugin, which contains a `package.json` file and a `gatsby-node.js` file: - -```text -|-- plugins - |-- gatsby-source-pixabay - |-- gatsby-node.js - |-- package.json -``` - -Start by creating the directory and changing into it: - -```shell -mkdir gatsby-source-pixabay -cd gatsby-source-pixabay -``` - -### Create a `package.json` file - -Now create a `package.json` file, this describes your plugin and any third-party code it might depend on. npm has a command to create this file for you. Run: - -```shell -npm init --yes -``` - -to create the file using default options. - -> **NOTE:** You can omit `--yes` if you'd like to specify the options yourself. - -### Add dependencies - -You'll use a couple of modules from npm to add some helper functionality. Install them with: - -```shell -npm install node-fetch query-string --save -``` - -Open your `package.json` file and you'll see `node-fetch` and `query-string` have been added to a `dependencies` section at the end: - -```json:title=package.json - "dependencies": { - "node-fetch": "^2.2.0", - "query-string": "^6.1.0" - } -``` - -With the setup done, move on to adding the plugin's functionality. - -### Create a `gatsby-node.js` file - -Create a new file called `gatsby-node.js` in your `gatsby-source-pixabay` directory, and add the following: - -```js:title=gatsby-node.js -const fetch = require("node-fetch") -const queryString = require("query-string") - -exports.sourceNodes = ( - { actions, createNodeId, createContentDigest }, - configOptions -) => { - const { createNode } = actions - - // Gatsby adds a configOption that's not needed for this plugin, delete it - delete configOptions.plugins - - // plugin code goes here... - console.log("Testing my plugin", configOptions) -} -``` - -### Step by step through your `gatsby-node.js` file - -What did you do by adding this code? You started by importing the dependencies that you added earlier (along with one built in dependency): - -```js:title=gatsby-node.js -const fetch = require("node-fetch") -const queryString = require("query-string") -``` - -Then you implemented Gatsby's [`sourceNodes` API](/docs/node-apis/#sourceNodes) which Gatsby will run as part of its bootstrap process. Gatsby expects sourceNodes to return either a promise or a callback (3rd parameter). This is important as it tells Gatsby to wait to move on to next stages until your nodes are sourced, ensuring your nodes are created before the schema is generated. - -```js:title=gatsby-node.js -exports.sourceNodes = ({ actions, createNodeId, createContentDigest }, configOptions) => { -``` - -You do some initial setup: - -```js:title=gatsby-node.js -const { createNode } = actions - -// Gatsby adds a configOption that's not needed for this plugin, delete it -delete configOptions.plugins -``` - -And finally add a placeholder message: - -```js:title=gatsby-node.js -// plugin code goes here... -console.log("Testing my plugin", configOptions) -``` - -### Add the plugin to your site - -The skeleton of your plugin is in place which means you can now add it to your project and check your progress so far. - -Open `gatsby-config.js` from the root directory of your tutorial site, and add the `gatsby-source-pixabay` plugin: - -```js:title=gatsby-config.js -module.exports = { - siteMetadata: { - title: "Gatsby Default Starter", - }, - plugins: [ - "gatsby-plugin-react-helmet", - { - resolve: "gatsby-source-pixabay", - options: { - key: "", - q: "yellow flowers", - }, - }, - ], -} -``` - -Open a new terminal in the root directory of your tutorial site, then start Gatsby's development mode: - -```shell -gatsby develop -``` - -Check the lines after `success on PreBootstrap`, you should see your "Testing my plugin" message along with the `key` from your `gatsby-config.js` file: - -```shell -success onPreBootstrap — 0.048 s -⠁ Testing my plugin { key: '' } -warning The gatsby-source-pixabay plugin has generated no Gatsby nodes. Do you need it? -success source and transform nodes — 0.057 s -``` - -Note that Gatsby is warning that your plugin doesn't do anything yet. Time to fix that. - -### Fetch remote data from Pixabay - -Update `gatsby-node.js` in your `plugins/gatsby-source-pixabay/` directory: - -```js:title=gatsby-node.js -const fetch = require("node-fetch") -const queryString = require("query-string") - -exports.sourceNodes = ( - { actions, createNodeId, createContentDigest }, - configOptions -) => { - const { createNode } = actions - - // Gatsby adds a configOption that's not needed for this plugin, delete it - delete configOptions.plugins - - // highlight-start - // Convert the options object into a query string - const apiOptions = queryString.stringify(configOptions) - - // Join apiOptions with the Pixabay API URL - const apiUrl = `https://pixabay.com/api/?${apiOptions}` - - // Gatsby expects sourceNodes to return a promise - return ( - // Fetch a response from the apiUrl - fetch(apiUrl) - // Parse the response as JSON - .then(response => response.json()) - // Process the JSON data into a node - .then(data => { - // For each query result (or 'hit') - data.hits.forEach(photo => { - console.log("Photo data is:", photo) - }) - }) - ) - // highlight-end -} -``` - -You've added code that fetches photo data from the Pixabay API. For now, your plugin logs that data but doesn't do anything else. Check that you can see the logged photo data by restarting `npm run develop`. This time you should see a series of results like: - -```shell -success onPreBootstrap — 0.035 s -⠠ source and transform nodesresponse Response { size: 0, timeout: 0 } -Photo data is: { largeImageURL: 'https://pixabay.com/get/ea36b70d29fd073ed1584d05fb1d4e9ee570e4d510ac104497f5c071a3efb6bd_1280.jpg', - webformatHeight: 426, - webformatWidth: 640, - likes: 17, - imageWidth: 5184, - id: 3362196, - user_id: 5598375, - views: 263, - comments: 24, - pageURL: 'https://pixabay.com/en/dandelion-flower-yellow-nature-3362196/', - imageHeight: 3456, - - ...(more data follows)... -``` - -You're ready to add the final step of your plugin - converting this data into a Gatsby node. - -### Use `createNode` function - -You're adding a helper function on lines 13 to 28 and processing the data into a node on lines 46 to 49: - -```js:title=gatsby-node.js -const fetch = require("node-fetch") -const queryString = require("query-string") - -exports.sourceNodes = ( - { actions, createNodeId, createContentDigest }, - configOptions -) => { - const { createNode } = actions - - // Gatsby adds a configOption that's not needed for this plugin, delete it - - delete configOptions.plugins - // highlight-start - // Helper function that processes a photo to match Gatsby's node structure - const processPhoto = photo => { - const nodeId = createNodeId(`pixabay-photo-${photo.id}`) - const nodeContent = JSON.stringify(photo) - - const nodeData = Object.assign({}, photo, { - id: nodeId, - parent: null, - children: [], - internal: { - type: `PixabayPhoto`, - content: nodeContent, - contentDigest: createContentDigest(photo), - }, - }) - - return nodeData - } - // highlight-end - - // Convert the options object into a query string - const apiOptions = queryString.stringify(configOptions) - - // Join apiOptions with the Pixabay API URL - const apiUrl = `https://pixabay.com/api/?${apiOptions}` - - // Gatsby expects sourceNodes to return a promise - return ( - // Fetch a response from the apiUrl - fetch(apiUrl) - // Parse the response as JSON - .then(response => response.json()) - // Process the response data into a node - .then(data => { - // For each query result (or 'hit') - data.hits.forEach(photo => { - // highlight-start - // Process the photo data to match the structure of a Gatsby node - const nodeData = processPhoto(photo) - // Use Gatsby's createNode helper to create a node from the node data - createNode(nodeData) - // highlight-end - }) - }) - ) -} -``` - -### Query for results - -Your plugin is ready. Restart `npm run develop` and open a browser at `http://localhost:8000/___graphql`. The Pixabay data can be queried from here. try: - -```graphql -{ - allPixabayPhoto(limit: 10) { - edges { - node { - largeImageURL - pageURL - tags - user - } - } - } -} -``` - -Or [open the query from this link](). - -Experiment with different options in your `gatsby-config.js` file to see how that affects your query results. The [Pixabay API docs](https://pixabay.com/api/docs/#api_search_images) might be a useful reference. - -## Publishing a plugin - -Don't publish this particular plugin to npm or the Gatsby Plugin Library, because it's just a sample plugin for the tutorial. However, if you've built a local plugin for your project, and want to share it with others, npm allows you to publish your plugins. Check out the npm docs on [How to Publish & Update a Package](https://docs.npmjs.com/getting-started/publishing-npm-packages) for more info. - -> **NOTE:** Once you have published your plugin on npm, don't forget to edit your plugin's `package.json` file to include info about your plugin. If you'd like to publish a plugin to the [Gatsby Plugin Library](/plugins/) (please do!), please [follow these steps](/contributing/submit-to-plugin-library/). - -## Summary - -You've written a local Gatsby plugin that: - -- can be configured with an entry in your `gatsby-config.js` file -- requests data from a third-party API -- pulls the API data into Gatsby's node system -- allows the data to be queried with GraphQL - -Congratulations! - -## Where next? - -Your plugin has been adapted from Jason Lengstorf's [gatsby-source-pixabay plugin](https://www.npmjs.com/package/gatsby-source-pixabay). Check out [the source on GitHub](https://github.com/jlengstorf/gatsby-source-pixabay). - -Try adding new features to your plugin, for example - download images from Pixabay, improve error handling, add documentation or automated tests. - -Check out Gatsby's docs on [plugin authoring](/docs/creating-plugins/) and [creating a source plugin](/docs/creating-a-source-plugin/). - -### A note on JavaScript versions - -In this tutorial you've written code in a version of JavaScript that's compatible with Node.js version 6 and above. - -Jason's version of [the plugin](https://github.com/jlengstorf/gatsby-source-pixabay/blob/master/src/gatsby-node.js) uses newer JavaScript features with [babel](https://babeljs.io/) to provide compatibility for older versions of Node. Compare your code with Jason's to see how newer JavaScript features allow for more succinct code. diff --git a/docs/tutorial/source-plugin-tutorial.md b/docs/tutorial/source-plugin-tutorial.md new file mode 100644 index 0000000000000..99df984d2500d --- /dev/null +++ b/docs/tutorial/source-plugin-tutorial.md @@ -0,0 +1,977 @@ +--- +title: "Creating a Source Plugin" +tableOfContentsDepth: 2 +--- + +In this tutorial, you'll create your own source plugin that will gather data from an API. The plugin will source data, optimize remote images, and create foreign key relationships between data sourced by your plugin. + +## What is a source plugin? + +Source plugins "source" data from remote or local locations into what Gatsby calls [nodes](/docs/node-interface/). This tutorial uses a demo API so that you can see how the data works on both the frontend and backend, but the same principles apply if you would like to source data from another API. + +For more background on source plugins, check out [Gatsby's source plugin documentation](/docs/creating-a-source-plugin/). + +## Why create a source plugin? + +Source plugins convert data from any source into a format that Gatsby can process. Your Gatsby site can use several source plugins to combine data in interesting ways. + +There may not be [an existing plugin](/plugins/?=gatsby-source) for your data source, so you can create your own. + +_**NOTE:** if your data is local i.e. on your file system and part of your site's repo, then you generally don't want to create a new source plugin. Instead you want to use [gatsby-source-filesystem](/packages/gatsby-source-filesystem/) which handles reading and watching files for you. You can then use [transformer plugins](/plugins/?=gatsby-transformer) like [gatsby-transformer-yaml](/packages/gatsby-transformer-yaml/) to make queryable data from files._ + +## How to create a source plugin + +### Overview + +The plugin in this tutorial will source blog posts and authors from the demo API, link the posts and authors, and take image URLs from the posts and optimize them automatically. You'll be able to configure your plugin in your site's `gatsby-config.js` file and write GraphQL queries to access your plugin's data. + +This tutorial builds off of an existing Gatsby site and some data. If you want to follow along with this tutorial, you can find the codebase inside [the examples folder of the Gatsby repository](https://github.com/gatsbyjs/gatsby/tree/master/examples/creating-source-plugins). Once you clone this code, make sure to delete the `source-plugin` and `example-site` folders. Otherwise, the tutorial steps will already be completed. + +#### An example API request + +To see the API in action, you can run it locally by navigating into the `api` folder, installing dependencies with `npm install`, and starting the server with `npm start`. You will then be able to navigate to a GraphQL playground running at `http://localhost:4000`. This is a GraphQL server running in Node.js and is _separate from Gatsby_, this server could be replaced with a different backend or data source and the patterns in this tutorial would remain the same. Other possible examples could be a REST API, local files, or even a database, so long as you can access data it can be sourced. + +If you paste the following query into the left side of the window and press the play button, you should see data for posts with their IDs and descriptions returned: + +```graphql +query { + posts { + id + description + } +} +``` + +This data is an example of the data you will source with your plugin. + +_You can also see a running version of the GraphQL playground associated with a distinct API at [https://gatsby-source-plugin-api.glitch.me/](https://gatsby-source-plugin-api.glitch.me/), which is running the `api` folder in a Glitch project, like you would when you run `npm start` on your own computer._ + +#### Plugin behavior + +Your plugin will have the following behavior: + +- Make an API request to the demo API. +- Convert the data in the API response to Gatsby's node system. +- Link the nodes together so you can query for an author on each post. +- Accept plugin options to customize how your plugin works. +- Optimize images from Unsplash URLs so they can be used with `gatsby-image`. + +### Set up projects for plugin development + +You'll need to set up an example site and create a plugin inside it to begin building. + +#### Set up an example site + +Create a new Gatsby site with the `gatsby new` command, based on the hello world starter. + +```shell +gatsby new example-site https://github.com/gatsbyjs/gatsby-starter-hello-world +``` + +This site generated by the `new` command is where the plugin will be installed, giving you a place to test the code for your plugin. + +#### Set up a source plugin + +Create a new Gatsby plugin with the `gatsby new` command, this time based on the plugin starter. + +```shell +gatsby new source-plugin https://github.com/gatsbyjs/gatsby-starter-plugin +``` + +This will create your plugin in a separate project from your example site, but you could also include it in your site's [`plugins` folder](/docs/loading-plugins-from-your-local-plugins-folder/). + +Your plugin starts with a few files from the starter, which can be seen in the snippet below: + +```text +/example-site +/source-plugin +├── .gitignore +├── gatsby-browser.js +├── gatsby-node.js +├── gatsby-ssr.js +├── index.js +├── package.json +└── README.md +``` + +The biggest changes will be in **`gatsby-node.js`**. This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](/docs/node-apis/). These allow customization/extension of default Gatsby settings affecting pieces of the site build process. All the logic for sourcing data will live in this file. + +#### Install your plugin in the example site + +You need to install your plugin in the site to be able to test that your code is running. Gatsby only knows to run plugins that are included in its `gatsby-config.js` file. Open up the `gatsby-config.js` file in the `example-site` and [add your plugin using `require.resolve`](/docs/creating-a-local-plugin/#using-requireresolve-and-a-filepath). If you decide to publish your plugin it can be installed with an `npm install ` and including the name of the plugin in the config instead of `require.resolve`. + +```javascript:title=example-site/gatsby-config.js +module.exports = { + plugins: [require.resolve(`../source-plugin`)], +} +``` + +_You can include the plugin by using its name if you are using [npm link or yarn workspaces](/docs/creating-a-local-plugin/#using-npm-link-or-yarn-link) or place your `source-plugin` in [`example-site/plugins`](/docs/creating-a-local-plugin/) instead of being in a folder a step above and using `require.resolve`._ + +You can now navigate into the `example-site` folder and run `gatsby develop`. You should see a line in the output in the terminal that shows your plugin loaded: + +```shell +$ gatsby develop +success open and validate gatsby-configs - 0.033s +success load plugins - 0.074s +Loaded gatsby-starter-plugin // highlight-line +success onPreInit - 0.016s +... +``` + +If you open the `gatsby-node.js` file in your `source-plugin` folder, you will see the `console.log` that produces that output in the terminal. + +```javascript:title=source-plugin/gatsby-node.js +exports.onPreInit = () => console.log("Loaded gatsby-starter-plugin") +``` + +### Source data and create nodes + +Data is sourced in the `gatsby-node.js` file of source plugins or Gatsby sites. Specifically, it's done by calling a Gatsby function called `createNode` inside of the `sourceNodes` API in the `gatsby-node.js` file. + +#### Create nodes inside of `sourceNodes` with the `createNode` function + +Open up the `gatsby-node.js` file in the `source-plugin` project and add the following code to create nodes from a hardcoded array of data : + +```javascript:title=source-plugin/gatsby-node.js +// constants for your GraphQL Post and Author types +const POST_NODE_TYPE = `Post` + +exports.sourceNodes = async ({ + actions, + createContentDigest, + createNodeId, + getNodesByType, +}) => { + const { createNode } = actions + + const data = { + posts: [ + { id: 1, description: `Hello world!` }, + { id: 2, description: `Second post!` }, + ], + } + + // loop through data and create Gatsby nodes + data.posts.forEach(post => + createNode({ + ...post, + id: createNodeId(`${POST_NODE_TYPE}-${post.id}`), + parent: null, + children: [], + internal: { + type: POST_NODE_TYPE, + content: JSON.stringify(post), + contentDigest: createContentDigest(post), + }, + }) + ) + + return +} +``` + +This code creates Gatsby nodes that are queryable in a site. The following bullets break down what is happening in the code: + +- You implemented Gatsby's [`sourceNodes` API](/docs/node-apis/#sourceNodes), which Gatsby will run as part of its bootstrap process, and pulled out some Gatsby helpers (like `createContentDigest` and `createNodeId`) to facilitate creating nodes. +- You provided the required fields for the node like creating a node ID and a content digest (which Gatsby uses to track dirty nodes—or nodes that have changed). The content digest should include the whole content of the item (`post`, in this case). +- Then you stored some data in an array and looped through it, calling `createNode` on each post in the array. + +If you run the `example-site` with `gatsby develop`, you can now open up `http://localhost:8000/___graphql` and query your posts with this query: + +```graphql +query { + allPost { + nodes { + id + description + } + } +} +``` + +The problem with this data is that it is _not coming from the API_, it is hardcoded into an array. The declaration of the `data` array needs to be updated to pull data from a different location. + +### Querying and sourcing data from a remote location + +You can query data from any location to source at build time using functions and libraries like Node.js's built-in `http.get`, `axios`, or `node-fetch`. This tutorial uses a GraphQL client so that the source plugin can support GraphQL subscriptions when it fetches data from the demo API, and can proactively update your data in the site when information on the API changes. + +#### Adding dependencies + +You'll use several modules from npm to making fetching data with GraphQL easier. Install them in the `source-plugin` project with: + +```shell +npm install apollo-cache-inmemory apollo-client apollo-link apollo-link-http apollo-link-ws apollo-utilities graphql graphql-tag node-fetch ws subscriptions-transport-ws +``` + +_Note: The libraries used here are specifically chosen so that the source plugin can support [GraphQL subscriptions](https://www.apollographql.com/docs/react/data/subscriptions/). You can fetch data the same way you would in any other Node.js app or however you are most comfortable._ + +Open your `package.json` file after installation and you'll see the packages have been added to a `dependencies` section at the end of the file. + +#### Configure an Apollo client to fetch data + +Import the handful of Apollo packages that you installed to help set up an Apollo client in your plugin: + +```javascript:title=source-plugin/gatsby-node.js +// highlight-start +const { ApolloClient } = require("apollo-client") +const { InMemoryCache } = require("apollo-cache-inmemory") +const { split } = require("apollo-link") +const { HttpLink } = require("apollo-link-http") +const { WebSocketLink } = require("apollo-link-ws") +const { getMainDefinition } = require("apollo-utilities") +const fetch = require("node-fetch") +const gql = require("graphql-tag") +const WebSocket = require("ws") +// highlight-end + +const POST_NODE_TYPE = `Post` + +exports.sourceNodes = async ({ +// ... +``` + +Then you can copy this code that sets up the necessary pieces of the Apollo client and paste it after your imports: + +```javascript:title=source-plugin/gatsby-node.js +// ... imports +const WebSocket = require("ws") + +const POST_NODE_TYPE = `Post` + +// highlight-start +const client = new ApolloClient({ + link: split( + ({ query }) => { + const definition = getMainDefinition(query) + return ( + definition.kind === "OperationDefinition" && + definition.operation === "subscription" + ) + }, + new WebSocketLink({ + uri: `ws://localhost:4000`, // or `ws://gatsby-source-plugin-api.glitch.me/` + options: { + reconnect: true, + }, + webSocketImpl: WebSocket, + }), + new HttpLink({ + uri: "http://localhost:4000", // or `https://gatsby-source-plugin-api.glitch.me/` + fetch, + }) + ), + cache: new InMemoryCache(), +}) +// highlight-end + +exports.sourceNodes = async ({ +// ... +``` + +You can read about each of the packages that are working together in [Apollo's docs](https://www.apollographql.com/docs/react/). The end result is creating a `client` that you can use to call methods like `query` to get data from the source it's configured to work with. In this case, that is `localhost:4000` where you should have the API running. If you can't configure the API to run locally, you can update the URLs for the client to use `gatsby-source-plugin-api.glitch.me` where a version of the API is deployed, instead of `localhost:4000`. + +#### Query data from the API + +Now you can replace the hardcoded data in the `sourceNodes` function with a GraphQL query: + +```diff:title=source-plugin/gatsby-node.js + exports.sourceNodes = async ({ + actions, + createContentDigest, + createNodeId, + getNodesByType, + }) => { + const { createNode, touchNode, deleteNode } = actions + +- const data = { +- posts: [ +- { id: 1, description: `Hello world!` }, +- { id: 2, description: `Second post!` }, +- ], +- } ++ const { data } = await client.query({ ++ query: gql` ++ query { ++ posts { ++ id ++ description ++ } ++ } ++ `, ++ }) + + // ... +``` + +Now you're creating nodes based on data coming from the API. Neat! However, only the `id` and `description` fields are coming back from the API and being saved to each node, so add the rest of the fields to the query so that the same data is available to Gatsby. + +This is also a good time to add data to your query so that it also returns authors. + +```javascript:title=source-plugin/gatsby-node.js +const { data } = await client.query({ + query: gql` + query { + posts { + id + description + // highlight-start + slug + imgUrl + imgAlt + author { + id + name + } + } + authors { + id + name + } + // highlight-end + } + `, +}) +``` + +With the new data, you can also loop through the authors to create Gatsby nodes from them by adding another loop to `sourceNodes`: + +```javascript:title=source-plugin/gatsby-node.js +const POST_NODE_TYPE = `Post` +const AUTHOR_NODE_TYPE = `Author` // highlight-line + +exports.sourceNodes = async ({ + actions, + createContentDigest, + createNodeId, + getNodesByType, +}) => { + const { createNode, touchNode, deleteNode } = actions + + const { data } = await client.query({ + query: gql` + query { + posts { + id + slug + description + imgUrl + imgAlt + author { + id + name + } + } + authors { + id + name + } + } + `, + }) + + // loop through data returned from the api and create Gatsby nodes for them + data.posts.forEach(post => + createNode({ + ...post, + id: createNodeId(`${POST_NODE_TYPE}-${post.id}`), // hashes the inputs into an ID + parent: null, + children: [], + internal: { + type: POST_NODE_TYPE, + content: JSON.stringify(post), + contentDigest: createContentDigest(post), + }, + }) + ) + // highlight-start + data.authors.forEach(author => + createNode({ + ...author, + id: createNodeId(`${AUTHOR_NODE_TYPE}-${author.id}`), // hashes the inputs into an ID + parent: null, + children: [], + internal: { + type: AUTHOR_NODE_TYPE, + content: JSON.stringify(author), + contentDigest: createContentDigest(author), + }, + }) + ) + // highlight-end + + return +} +``` + +At this point you should be able to run `gatsby develop` in your `example-site`, open up GraphiQL at `http://localhost:8000/___graphql` and query both posts and authors. + +```graphql +query { + allPost { + nodes { + id + description + imgUrl + } + } + allAuthor { + nodes { + id + name + } + } +} +``` + +### Optimize remote images + +Each node of post data has an `imgUrl` field with the URL of an image on Unsplash. You could use that URL to load images on your site, but they will be large and take a long time to load. You can optimize the images with your source plugin so that a site using your plugin already has data for `gatsby-image` ready to go! + +You can read about [how to use Gatsby Image to prevent image bloat](/docs/using-gatsby-image/) if you are unfamiliar with it. + +#### Create `remoteFileNode`'s from a URL + +To create optimized images from URLs, `File` nodes for image files need to be added to your site's data. Then, you can install `gatsby-plugin-sharp` and `gatsby-transformer-sharp` which will automatically find image files and add the data needed for `gatsby-image`. + +Start by installing `gatsby-source-filesystem` in the `source-plugin` project: + +```shell:title=source-plugin +npm install gatsby-source-filesystem +``` + +Now in your plugin's `gatsby-node.js` file, you can implement a new API, called `onCreateNode`, that gets called every time a node is created. You can check if the node created was one of your `Post` nodes, and if it was, create a file from the URL on the `imgUrl` field. + +Import the `createRemoteFileNode` helper from `gatsby-source-filesystem`, which will download a file from a remote location and create a `File` node for you. + +```javascript:title=source-plugin/gatsby-node.js +const { ApolloClient } = require("apollo-client") +const { InMemoryCache } = require("apollo-cache-inmemory") +const { split } = require("apollo-link") +const { HttpLink } = require("apollo-link-http") +const { WebSocketLink } = require("apollo-link-ws") +const { getMainDefinition } = require("apollo-utilities") +const fetch = require("node-fetch") +const gql = require("graphql-tag") +const WebSocket = require("ws") +// highlight-start +const { createRemoteFileNode } = require(`gatsby-source-filesystem`) +// highlight-end +``` + +Then export a new function `onCreateNode`, and call `createRemoteFileNode` in it whenever a node of of type `Post` is created: + +```javascript:title=source-plugin/gatsby-node.js +// called each time a node is created +exports.onCreateNode = async ({ + node, // the node that was just created + actions: { createNode }, + createNodeId, + getCache, +}) => { + if (node.internal.type === POST_NODE_TYPE) { + const fileNode = await createRemoteFileNode({ + // the url of the remote image to generate a node for + url: node.imgUrl, + parentNodeId: node.id, + createNode, + createNodeId, + getCache, + }) + + if (fileNode) { + node.remoteImage___NODE = fileNode.id + } + } +} +``` + +This code is called every time a node is created, e.g. when `createNode` is invoked. Each time it is called in the `sourceNodes` step, the condition will check if the node was a `Post` node. Since those are the only nodes with an image associated with them, that is the only time images need to be optimized. Then a remote node is created, if it's successful, the `fileNode` is returned. The next few lines are important: + +```javascript:title=source-plugin/gatsby-node.js +if (fileNode) { + // save the ID of the fileNode on the Post node + node.remoteImage___NODE = fileNode.id +} +``` + +By assigning a field called `remoteImage___NODE` to the ID of the `File` node that was created, Gatsby will be able to [infer](/docs/glossary#inference) a connection between this field and the file node. This will allow fields on the file to be queried from the post node. + +```graphql +# leaving off the ___NODE field will give you: +query { + allPost { + nodes { + id + remoteImage # returns an ID like "ecd83d94-7111-5386-bd3f-0066248b6fa9" + } + } +} +# instead of an entire node you can query more fields on: +query { + allPost { + nodes { + id + remoteImage { + id + relativePath + } + } + } +} +``` + +_**Note**: you can use [schema customization APIs](/docs/schema-customization) to create these kinds of connections between nodes as well as sturdier and more strictly typed ones._ + +At this point you have created local image files from the remote locations and associated them with your posts, but you still need to transform the files into optimized versions. + +#### Transform `File` nodes with sharp plugins + +Sharp plugins make optimization of images possible at build time. + +Install `gatsby-plugin-sharp` and `gatsby-transformer-sharp` in the `example-site` (_not_ the plugin): + +```shell:title=example-site +npm install gatsby-plugin-sharp gatsby-transformer-sharp +``` + +Then include the plugins in your `gatsby-config`: + +```javascript:title=example-site/gatsby-config.js +module.exports = { + plugins: [ + require.resolve(`../source-plugin`), + // highlight-start + `gatsby-plugin-sharp`, + `gatsby-transformer-sharp`, + // highlight-end + ], +} +``` + +By installing the sharp plugins in the site, they'll run after the source plugin and transform the file nodes and add fields for the optimized versions at `childImageSharp`. The transformer plugin looks for `File` nodes with extensions like `.jpg` and `.png` to create optimized images and creates the GraphQL fields for you. + +Now when you run your site, you will also be able to query a `childImageSharp` field on the `post.remoteImage`: + +```graphql +query { + allPost { + nodes { + remoteImage { + // highlight-start + childImageSharp { + id + } + // highlight-end + } + } + } +} +``` + +With data available, you can now query optimized images to use with the `gatsby-image` component in a site! You will need to install `gatsby-image` before you can use it. + +### Create foreign key relationships between data + +To link the posts to the authors, Gatsby needs to be aware that the two are associated, and how. You have already implemented one example of this when Gatsby inferred a connection between a `remoteImage` and the remote file from Unsplash. + +The best approach for connecting related data is through customizing the GraphQL schema. By implementing the `createSchemaCustomization` API, you can specify the exact shape of a node's data. While defining that shape, you can optionally link a node to other nodes to create a relationship. + +Copy this code and add it to the `source-plugin` in the `gatsby-node.js` file: + +```javascript:title=source-plugin/gatsby-node.js +exports.createSchemaCustomization = ({ actions }) => { + const { createTypes } = actions + createTypes(` + type Post implements Node { + id: ID! + slug: String! + description: String! + imgUrl: String! + imgAlt: String! + # create relationships between Post and File nodes for optimized images + remoteImage: File @link + # create relationships between Post and Author nodes + author: Author @link(from: "author.name" by: "name") + } + type Author implements Node { + id: ID! + name: String! + }`) +} +``` + +The `author: Author @link(from: "author.name" by: "name")` line tells Gatsby to look for the value on the `Post` node at `post.author.name` and relate it with an `Author` node with a matching `name`. This demonstrates the ability to link using more than just an ID. + +The line `remoteImage: File @link` tells Gatsby to look for a `remoteImage` field on a `Post` node and link it to a `File` node with the ID there. + +Now, instead of using inference in for the `remoteImage` field, you can take off the `___NODE` suffix. You can update the code in the `onCreateNode` API now like this: + +```diff:title=source-plugin/gatsby-node.js + if (fileNode) { +- node.remoteImage___NODE = fileNode.id ++ node.remoteImage = fileNode.id + } +``` + +Now running the site will allow you to query authors and remoteImages from the post nodes! + +```graphql +query { + allPost { + nodes { + id + // highlight-start + author { + name + } + remoteImage { + id + } + // highlight-end + } + } +} +``` + +### Using data from the source plugin in a site + +In the `example-site`, you can now query data from pages. + +Add a file at `example-site/src/pages/index.js` and copy the following code into it: + +Ensure you have `gatsby-image` installed in the site by running `npm install gatsby-image`. It provides a component that can take the optimized image data and render it. + +```javascript:title=example-site/src/pages/index.js +import React from "react" +import { graphql } from "gatsby" +import Img from "gatsby-image" + +export default ({ data }) => ( + <> +

Posts

+
+ {data.allPost.nodes.map(post => ( +
+

{post.slug}

+ By: {post.author.name} +

{post.description}

+ {post.imgAlt} +
+ ))} +
+ +) + +export const query = graphql` + { + allPost { + nodes { + id + slug + description + imgAlt + author { + id + name + } + remoteImage { + id + childImageSharp { + id + fluid { + ...GatsbyImageSharpFluid + } + } + } + } + } + } +` +``` + +This code uses a [page query](/docs/page-query/) to fetch all posts and provide them to the component in the `data` prop at build time. The JSX code loops through the posts so they can be rendered to the DOM. + +### Using plugin options to customize plugin usage + +You can pass options into a plugin through a `gatsby-config.js` file. Update the code where your plugin is installed in the `example-site`, changing it from a string, to an object with a `resolve` and `options` key. + +```javascript:title=example-site/gatsby-config.js +module.exports = { + plugins: [ + // highlight-start + { + resolve: require.resolve(`../source-plugin`), + options: { + previewMode: true, + }, + }, + // highlight-end + `gatsby-plugin-sharp`, + `gatsby-transformer-sharp`, + ], +} +``` + +Now the options you designated (like `previewMode: true`) will be passed into each of the Gatsby Node APIs like `sourceNodes`, making options accessible inside of Gatsby APIs. Add an argument called `pluginOptions` to your `sourceNodes` function. + +```javascript:title=source-plugin/gatsby-node.js +exports.sourceNodes = async ( + { actions, createContentDigest, createNodeId, getNodesByType }, + pluginOptions // highlight-line +) => { + const { createNode, touchNode, deleteNode } = actions + // highlight-start + console.log(pluginOptions.previewMode) // true + // highlight-end + + // ... other code removed for brevity +} +``` + +Options can be a good way of providing conditional paths to logic that you as a plugin author want to provide or limit. + +### Proactively updating data with subscriptions + +The data sourced for your site was fetched using Apollo Client, which supports subscriptions. GraphQL subscriptions _listen_ for changes in data and return changes to the GraphQL client. Your source plugin is able to listen—or subscribe—to the new data that is incoming. That means if a post has something on it updated, your source plugin can listen for that change and update the data in your site without having to restart your site, neat! + +The API you connect to needs to provide support for live changes to data in order for this to be possible. You can read about other options for live data updates in the [creating a source plugin guide](/docs/creating-a-source-plugin/). + +You already set up your client to handle subscriptions by providing a websocket link (`ws://localhost:4000` or `ws://gatsby-source-plugin-api.glitch.me/`). Now you need to add some logic to your `sourceNodes` function to handle updating and deleting nodes, rather than just creating them. The first step will be touching nodes, to make sure that Gatsby doesn't discard the nodes that don't get updated when `sourceNodes` gets called. + +```javascript:title=source-plugin/gatsby-node.js +exports.sourceNodes = async ( + { actions, createContentDigest, createNodeId, getNodesByType }, + pluginOptions +) => { + const { createNode, touchNode, deleteNode } = actions + + // highlight-start + // touch nodes to ensure they aren't garbage collected + getNodesByType(POST_NODE_TYPE).forEach(node => touchNode({ nodeId: node.id })) + getNodesByType(AUTHOR_NODE_TYPE).forEach(node => + touchNode({ nodeId: node.id }) + ) + // highlight-end + + // rest of code that creates nodes +} +``` + +You can use the `getNodesByType` function to gather up the post and author nodes, loop through each, and call `touchNode` on each node. + +Then, you can use the plugin option provided in the previous section for `previewMode` to only turn on subscriptions when it's set to `true`. + +```javascript:title=source-plugin/gatsby-node.js +exports.sourceNodes = async ( + { actions, createContentDigest, createNodeId, getNodesByType }, + pluginOptions +) => { + const { createNode, touchNode, deleteNode } = actions + + // touch nodes to ensure they aren't garbage collected + getNodesByType(POST_NODE_TYPE).forEach(node => touchNode({ nodeId: node.id })) + getNodesByType(AUTHOR_NODE_TYPE).forEach(node => + touchNode({ nodeId: node.id }) + ) + + // highlight-start + if (pluginOptions.previewMode) { + console.log("Subscribing to content updates...") + } + // highlight-end + + // rest of code that creates nodes +} +``` + +Using Apollo Client, you can create a subscription with almost all of the same fields as your query. Note this string of fields in the GraphQL subscription also includes a `status`. This status is returned by the subscriptions on the backend as a mechanism to allow Gatsby to know what to do with each piece of updated data. + +```javascript:title=source-plugin/gatsby-node.js +exports.sourceNodes = async ( + { actions, createContentDigest, createNodeId, getNodesByType }, + pluginOptions +) => { + const { createNode, touchNode, deleteNode } = actions + + // touch nodes to ensure they aren't garbage collected + getNodesByType(POST_NODE_TYPE).forEach(node => touchNode({ nodeId: node.id })) + getNodesByType(AUTHOR_NODE_TYPE).forEach(node => + touchNode({ nodeId: node.id }) + ) + + if (pluginOptions.previewMode) { + console.log("Subscribing to content updates...") + // highlight-start + const subscription = await client.subscribe({ + query: gql` + subscription { + posts { + id + slug + description + imgUrl + imgAlt + author { + id + name + } + status + } + } + `, + }) + // highlight-end + } + + // rest of code that creates nodes +} +``` + +Now you can write a function to subscribe to the data updates (like when a post is updated), and handle the new data coming in with a switch statement. + +```javascript:title=source-plugin/gatsby-node.js +exports.sourceNodes = async ( + { actions, createContentDigest, createNodeId, getNodesByType }, + pluginOptions +) => { + const { createNode, touchNode, deleteNode } = actions + + // touch nodes to ensure they aren't garbage collected + getNodesByType(POST_NODE_TYPE).forEach(node => touchNode({ nodeId: node.id })) + getNodesByType(AUTHOR_NODE_TYPE).forEach(node => + touchNode({ nodeId: node.id }) + ) + + if (pluginOptions.previewMode) { + console.log("Subscribing to content updates...") + const subscription = await client.subscribe({ + query: gql` + subscription { + posts { + id + slug + description + imgUrl + imgAlt + author { + id + name + } + status + } + } + `, + }) + // highlight-start + subscription.subscribe(({ data }) => { + console.log(`Subscription received:`) + console.log(data.posts) + data.posts.forEach(post => { + const nodeId = createNodeId(`${POST_NODE_TYPE}-${post.id}`) + switch (post.status) { + case "deleted": + deleteNode({ + node: getNode(nodeId), + }) + break + case "created": + case "updated": + default: + // created and updated can be handled by the same code path + // the post's id is presumed to stay constant (or can be inferred) + createNode({ + ...post, + id: createNodeId(`${POST_NODE_TYPE}-${post.id}`), + parent: null, + children: [], + internal: { + type: POST_NODE_TYPE, + content: JSON.stringify(post), + contentDigest: createContentDigest(post), + }, + }) + break + } + }) + }) + // highlight-end + } + + // rest of code that creates nodes +} +``` + +Posts that are changed on the backend while Gatsby is running will be created if they are new or updated, and deleted if they were deleted on the backend. + +You can test that this is working by running the site again and updating one of the posts. When you run the site this time you should see a message logged in the console: `Subscribing to content updates...`. Now, running an `updatePost` or `deletePost` mutation on the GraphQL server will send information to the subscription because it is now listening. + +Follow these steps to test it out: + +1. Open up your site at `localhost:8000` after you run `gatsby develop` +2. Open up the GraphQL playground at `localhost:4000` (if you are running the `api` folder locally) or `https://gatsby-source-plugin-api.glitch.me/` and first run a query for posts: + +```graphql +query { + posts { + id + description + } +} +``` + +3. Copy the ID from the post that you would like to update +4. Inside the GraphQL playground, run an update post mutation, replacing `` with the ID you just copied + +```graphql +mutation { + updatePost(id: "", description: "These are my changes!") { + id + } +} +``` + +5. When you run the mutation, the data will be updated on the backend, the subscription will recognize the change, Gatsby will update the node, and your page query will render the new data. + +It's so fast that it's a blink and you'll miss it kind of moment, so try running another mutation or even run a `deletePost` mutation to make it easier to see the changes! + +## Publishing a plugin + +Don't publish this particular plugin to npm or the Gatsby Plugin Library, because it's just a sample plugin for the tutorial. However, if you've built a local plugin for your project, and want to share it with others, npm allows you to publish your plugins. Check out the npm docs on [How to Publish & Update a Package](https://docs.npmjs.com/getting-started/publishing-npm-packages) for more info. + +> **NOTE:** Once you have published your plugin on npm, don't forget to edit your plugin's `package.json` file to include info about your plugin. If you'd like to publish a plugin to the [Gatsby Plugin Library](/plugins/) (please do!), please [follow these steps](/contributing/submit-to-plugin-library/). + +## Summary + +You've written a Gatsby plugin that: + +- can be configured with an entry in your `gatsby-config.js` file +- requests data from an API +- pulls the API data into Gatsby's node system +- allows the data to be queried with GraphQL +- optimizes images from a remote location automatically +- links data types with a customized GraphQL schema +- updates new data without needing to restart your Gatsby site + +Congratulations! + +## Additional resources + +- [Example repository](https://github.com/gatsbyjs/gatsby/tree/master/examples/creating-source-plugins) with all of this code implemented +- Creating a [first class source plugin for Gatsby Cloud](https://www.gatsbyjs.com/docs/integration-guide/source-plugin/) diff --git a/examples/using-local-plugins/README.md b/examples/using-local-plugins/README.md index 08b2ff48800fc..07e5e0aedad00 100755 --- a/examples/using-local-plugins/README.md +++ b/examples/using-local-plugins/README.md @@ -2,7 +2,7 @@ This example demonstrates usage of a local plugin -- in this case a source plugin. -You might also be interested in the docs section on [local plugins](/docs/creating-a-local-plugin/), or the [source plugin tutorial](/tutorial/pixabay-source-plugin-tutorial/). +You might also be interested in the docs section on [local plugins](/docs/creating-a-local-plugin/), or the [source plugin tutorial](/tutorial/source-plugin-tutorial/). ## Using Gatsby's GraphQL integration layer diff --git a/packages/gatsby-cli/src/create-cli.js b/packages/gatsby-cli/src/create-cli.js index bbca1543a4795..aa2534c5db9df 100644 --- a/packages/gatsby-cli/src/create-cli.js +++ b/packages/gatsby-cli/src/create-cli.js @@ -417,7 +417,7 @@ Creating a plugin: - Creating a Source Plugin (https://www.gatsbyjs.org/docs/creating-a-source-plugin/) - Creating a Transformer Plugin (https://www.gatsbyjs.org/docs/creating-a-transformer-plugin/) - Submit to Plugin Library (https://www.gatsbyjs.org/contributing/submit-to-plugin-library/) -- Pixabay Source Plugin Tutorial (https://www.gatsbyjs.org/tutorial/pixabay-source-plugin-tutorial/) +- Source Plugin Tutorial (https://www.gatsbyjs.org/tutorial/source-plugin-tutorial/) - Maintaining a Plugin (https://www.gatsbyjs.org/docs/maintaining-a-plugin/) - Join Discord #plugin-authoring channel to ask questions! (https://gatsby.dev/discord/) `) diff --git a/www/redirects.yaml b/www/redirects.yaml index 2c8f286338e59..c8795c665e80d 100644 --- a/www/redirects.yaml +++ b/www/redirects.yaml @@ -81,7 +81,7 @@ - fromPath: /docs/remark-plugin-tutorial/ toPath: /tutorial/remark-plugin-tutorial/ - fromPath: /docs/source-plugin-tutorial/ - toPath: /tutorial/pixabay-source-plugin-tutorial/ + toPath: /tutorial/source-plugin-tutorial/ - fromPath: /docs/how-plugins-work/ toPath: /docs/plugins/ - fromPath: /blog/2018-2-16-how-to-build-a-website-with-react/ @@ -148,3 +148,5 @@ toPath: /contributing/blog-contributions/ - fromPath: /contributing/docs-and-website-components/ toPath: /contributing/docs-and-blog-components/ +- fromPath: /docs/pixabay-source-plugin-tutorial + toPath: /docs/source-plugin-tutorial diff --git a/www/src/data/sidebars/tutorial-links.yaml b/www/src/data/sidebars/tutorial-links.yaml index a53941be8548a..df7cbbf98c326 100644 --- a/www/src/data/sidebars/tutorial-links.yaml +++ b/www/src/data/sidebars/tutorial-links.yaml @@ -113,8 +113,8 @@ - title: Plugin & Theme Tutorials link: /tutorial/plugin-and-theme-tutorials/ items: - - title: Creating an Image Source Plugin - link: /tutorial/pixabay-source-plugin-tutorial/ + - title: Creating a Source Plugin + link: /tutorial/source-plugin-tutorial/ - title: Creating a Remark Transformer Plugin link: /tutorial/remark-plugin-tutorial/ - title: Using a Theme