A gatsby plugin that enables fetching JSON data from an external REST API. Responses are converted to graphql nodes which can then be accessed via Gatsby's graphql API. Optionally, a schema can be provided to ensure graphql will not fail in the event the API fails to return data.
Note that the plugin is data/API agnostic but it is only designed to work
correctly with JSON data. So although the plugin can query data of any response
type, you can expect non-deterministic behaviour if you attempt to create
non-JSON graphql nodes. If your API does not return JSON data, you can either use
the serialize
or serializeAll
callback functions (see below for details) to
transform your data into a valid JSON format so that it can be read by the
graphql node creator successfully.
- Install the package from npm
- Add the plugin to your
gatsby-config.js
npm install @singlestore/gatsby-source-api -D
# or
yarn add @singlestore/gatsby-source-api -D
Note that @singlestore/gatsby-source-api
also requires gatsby
as a peer dependency. If you do not
already have gatsby
installed, you can do so with the following command:
npm install gatsby -D
# or
yarn add gatsby -D
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "http-api",
endpoint: "http://httpbin.org/get",
}
}
]
}
Do you need to request data from more than one API? Use multiple instances of the
plugin. Each instance is self contained. The only thing to remember is that each
instance of the plugin must have a unique name
field.
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "http-api",
endpoint: "http://httpbin.org/get",
},
},
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "github-repos",
endpoint: "https://api.github.com/repos/gatsby",
},
},
],
};
Do you need to make multiple requests to the same API? You can specify an array
of requests
where each request is an object containing any of the
parameters allowed in the root options
object. You can alternatively use
options.getRequests()
to programmatically generate the request objects used to
generate source nodes.
// gatsby-config.js
const repositoryNames = [
["gatsbyjs", "gatsby"],
["facebook", "react"],
["microsoft", "TypeScript"],
];
module.exports = {
plugins: [
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "github-repos",
fetchOptions: {
headers: {
authorization: "token <github_token>"
}
},
requests: repositoryNames.map(([orgName, repoName]) => ({
endpoint: `https://api.github.com/${orgName}/${repoName}`;
})),
},
},
],
};
Do you need to programmatically create one or more request option objects? For example, do
you need to login to your API before using it? You can use options.getRequest()
or options.getRequests()
to create a RequestOptions object or a set of
RequestOptions objects (respectively) dynamically.
// gatsby-config.js
const sources = ["blog", "events"];
module.exports = {
plugins: [
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "my-custom-api",
getRequest: async () => {
const token = await myCustomAPI.login();
return {
fetchOptions: {
endpoint: myCustomAPI.blogEndpoint,
headers: {
authorization: `Bearer ${token}`,
},
},
};
},
},
},
// ========
// OR
// ========
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "my-custom-api",
getRequests: async () => {
return Promise.all(
sources.map(async source => {
const token = await myCustomAPI[source].login();
return {
fetchOptions: {
endpoint: myCustomAPI[source].endpoint,
headers: {
authorization: `Bearer ${token}`,
},
},
};
})
);
},
},
},
],
};
Do you want a third-party library to handle fetching data for you? You can use
options.fetch()
to customize how the fetch works in any way you want. The
resulting data will be processed by this plugin the same way as every other
request that this plugin handles. Use this if you want to continue using
advanced features like options.requests
or options.getRequests
but don't
want to have to install yet another plugin to handle third-party fetching
use-cases.
For example, lets say you want to fetch class data from this example Google
Sheet using the Google Sheets API stored within the googleapis
library. Using
a custom fetch function, you can fetch the data yourself but then allow the
plugin to handle creating graphql nodes for you. This example also shows how to
serialize
the return data such that it is stored as an array of objects instead
of the default google sheet format (which returns an array of arrays).
// gatsby-config.js
const { google } = require("googleapis");
module.exports = {
plugins: [
{
resolve: "@singlestore/gatsby-source-api",
options: {
name: "sheets",
fetch: async () => {
const auth = new google.auth.GoogleAuth({
keyFile: "/path/to/you/service-acc/credentials.json",
scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"],
});
const sheets = google.sheets({
version: "v4",
auth,
});
// https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0
return sheets.spreadsheets.values.get({
spreadsheetId: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
range: "Class Data!A2:F31",
})
},
serialize: ({ data: rows }) => {
return rows.map(row => {
const [name, gender, class_level, home_state, major, activity] = row;
return {
name,
gender,
class_level,
home_state,
major,
activity,
};
});
},
},
},
],
};
-
name <string>
- required- A unique name for the request. The graphQL node name will be the camelCase
version of the name given. The name can optionally be used as a key in
cases where the returned data is an array of primitives (see
listKey
). The name is also used in all logging and tracing outputs. - Note that although using a non-unique
name
will not cause the plugin to break, it will mean the plugin will not be able to use Gatsby's built-in cache thus causing slower builds overall.
- A unique name for the request. The graphQL node name will be the camelCase
version of the name given. The name can optionally be used as a key in
cases where the returned data is an array of primitives (see
-
endpoint <string|URL>
- The API endpoint to use to obtain the JSON data.
-
fetchOptions <object>
-
Any additional options to pass to the fetch request. This can include options like headers, authentication details, post data, query params etc... See the list of available options in the got package
-
fetchOptions.endpoint <string|URL>
- See endpoint description above. If both
endpoint
and this option are set, this option will override the rootendpoint
option above.
- See endpoint description above. If both
-
fetchOptions.method <oneOf(head|options|get|post)>
- Default:
get
- Default:
-
-
fetch <(request: RequestOptions, pluginContext: PluginContext) => array|object>
- A custom fetch function to replace the default fetch implementation. Can be used to fetch from other sources such as GraphQL or use third-party libraries to fetch data for you.
-
entryPoint <string|(string|number)[]>
- A path to the data returned by the API. See lodash's get path syntax for more examples of what the path should look like
-
serialize <(response: FetchResponse, request: RequestOptions, pluginContext: PluginContext) => array|object>
- Modifies the response data written to graphql nodes.
-
metadata <object>
- Any additional metadata to pass along with the request's configuration
options. The metadata is not used by the fetch function. Use
fetchOptions
to pass additional options to the fetch request. This can be useful when using one of theserialize
functions as the metadata is exposed along with the response in the serializer.
- Any additional metadata to pass along with the request's configuration
options. The metadata is not used by the fetch function. Use
-
schema <Schema | (request: RequestOptions, pluginContext: PluginContext) => Schema>
- Optionally provide one or more schema(s) that denote the shape of the data returned by your API call. This is useful if you know you will need to query for fields that may not always be present in the queried data. See the schema data type for more details or see Gatsby's full schema explaination for an in-depth guide for how the schema works
- Optionally you can provide a function that resolves to a valid schema parameter
-
listKey <string>
- A string that is used as a node name in the case that the API returns a
nested array or an array of primitives. In most cases, this will not be used
as most rest APIs will return an array of objects or a single object. Will
use the request's
name
if not set.
- A string that is used as a node name in the case that the API returns a
nested array or an array of primitives. In most cases, this will not be used
as most rest APIs will return an array of objects or a single object. Will
use the request's
-
typePrefix <string>
- A prefix applied to the graphql node name such that the node appears as
<typePrefix><name>
(without carrets). - Default:
"external"
- A prefix applied to the graphql node name such that the node appears as
-
killOnRequestError <boolean>
- Whether to throw an error in the case of a failed request. This is useful if you want to combine the result of multiple requests in the serialized output and it is ok if one or more of the individual requests fail.
- Default:
true
-
requests <RequestOptions[]>
- An array of request options which can be used to make multiple requests to
same set of fetch or sheet options. Accepts an array of objects whose
properties can be found in #RequestOptions above. Every request requires a
unique
name
property and should either have differeing endpoints, fetchOptions or sheetOptions. Each request's options will be merged with the root plugin options. Should not be used withgetRequest
orgetRequests
- An array of request options which can be used to make multiple requests to
same set of fetch or sheet options. Accepts an array of objects whose
properties can be found in #RequestOptions above. Every request requires a
unique
-
getRequest <(pluginOptions: PluginOptions, pluginContext: PluginContext) => RequestOptions>
- Allows an asynchronous function to get the request configuration options to
use. The request requires a unique
name
property. The request's options will be merged with the root plugin options. Should return a single request object. Should not be used withrequests
orgetRequests
- Allows an asynchronous function to get the request configuration options to
use. The request requires a unique
-
getRequests <(pluginOptions: PluginOptions, pluginContext: PluginContext) => RequestOptions[]>
- Allows an asynchronous function to get an array of request configuration
options. Every request requires a
unique
name
property and should either have differeing endpoints, fetchOptions or sheetOptions. Applies the same merging as therequests
option. Should not be used withrequests
orgetRequest
- Allows an asynchronous function to get an array of request configuration
options. Every request requires a
unique
-
serializeAll <(responses: NonSerializedResponseContext[], pluginContext: PluginContext) => array|object>
- Modifies the response data that is written to JSON. The first argument contains an array of all the
responses for each request made. Should only be used when multiple requests
have been made, otherwise use
serialize
.
- Modifies the response data that is written to JSON. The first argument contains an array of all the
responses for each request made. Should only be used when multiple requests
have been made, otherwise use
Contains all of the helpers included in the Gatsby Node API helpers gatsby context in addition to the following:
instance <string>
- A colorized string containing the plugin name and the name of the current request. Can be used in logging messages.
Contains the original request, the response's headers and data and the serialized output of
entryPoint
, serialize()
or serializeAll()
. The data that gets written to
gatsby nodes is the final result of the serialized
key. If no serialize()
function is provided, serialized
will be filled in for youtypes based on the
body of the original response.
type SimpleResponse = Record<string, unknown>
type SerializedResponse = SimpleResponse[] | SimpleResponse;
interface SerializedResponseContext {
request: RequestOptions;
response: {
headers: ResponseHeaders,
data: ResponseData;
},
serialized: SerializedResponse
}
The provided schema gets fed directly to Gatsby's createTypes()
helper which
accepts schemas in a number of different data formats. We recommend you check out
the following Gatsby documentation on Gatsby Schemas for help on creating your
own schema for this plugin.
The following is the type definition for the allowed values of Schema
as
definied by Gatsby's createTypes
helper function.
type Schema =
| string
| GraphQLOutputType
| GatsbyGraphQLType
| Array<string | GraphQLOutputType | GatsbyGraphQLType>;
TODO
The plugin provides its own typescript definition files out of the box. These can be imported as named exports directly from the package itself. For example:
import { RequestOptions as GatsbySourceAPIRequestOptions } from "@singlestore/gatsby-source-api";
const requestOptions: GatsbySourceAPIRequestOptions = {
name: "my-unique-request-name",
endpoint: "http://httpbin.org/get",
};
- Add support for gatsby image plugin
- Possibly use system like
entryPoint
for determing where images are in data
- Possibly use system like
- Support a login request that can obtain an OAuth token or similar
- This is currently possible using the
getRequest()
function
- This is currently possible using the
- Support paginated requests where the user defines a key that should be used as a pagination token and automatically fetches X pages up to a user specified amount