Dynamic configuration from the browser
Prerequisites
In the developing a data source extension article, we described how you could create a data source and then configure it in the Studio. But, you might need to add additional dynamic configuration options in the browser when requesting a data source. For example, for pagination or a user-provided search string.
In this article, we'll show you how to do that. For our example, we'll continue using our Star Wars API, this time for characters.
Accessing query parameters in the extension
Our data source schema to return a list of all the Star Wars characters would look like this:
{"customDataSourceType": "example/star-wars-character-search","name": "Star wars character search","category": "Content","icon": "source","schema": []}
You can store the schema.json
file wherever you like, because it's only required in the Studio and not by the code.
The parameters for the custom data source are added to the query of the page URL (for example pageSize
in https://yoursite.com/page?pageSize=20
). You can access these parameters as context.request.query
inside the extension implementation:
import { DataSourceConfiguration, DataSourceContext, DataSourceResult } from '@frontastic/extension-types';import axios from 'axios';export default {'data-sources': {'example/star-wars-character-search': async (config: DataSourceConfiguration,context: DataSourceContext,): Promise<DataSourceResult> => {const pageSize = context.request.query.pageSize || 10;const after = context.request.query.cursor || null;return await axios.post('https://swapi-graphql.netlify.app/.netlify/functions/index', {query: `{allPeople(first: ${pageSize}, after: ${JSON.stringify(after)}) {totalCountpageInfo {hasNextPageendCursor}people {idnamespecies {name}}}}`,}).then((response): DataSourceResult => {return {dataSourcePayload: response.data?.data?.allPeople || {},} as DataSourceResult;},).catch((reason) => {return {dataSourcePayload: {ok: false,error: reason.toString(),},} as DataSourceResult;});}}};
Setting parameters in the component
You can then use the router inside a Frontend component to change the query parameters:
import React from 'react';import { useRouter } from 'next/router';import Link from 'next/link';const StarWarsCharacterSearchTastic = ({ data }) => {const { totalCount, pageInfo, people } = data.data.dataSource;const router = useRouter();const { slug, ...queryWithoutSlug } = router.query;return (<div><h1 className="text-2xl mt-8 font-sans">Star Wars Characters</h1><p className="mt-2">{totalCount} total characters found</p>{people.map((character) => (<div key={character.id}><h2 className="text-lg mt-6 font-sans">{character.name}</h2>{character.species !== null && (<p className="mt-2">Species: {character.species.name}</p>)}</div>))}{pageInfo.hasNextPage && (<div className="mt-6"><Linkhref={{pathname: router.asPath.split('?')[0],query: {...queryWithoutSlug,cursor: pageInfo.endCursor,},}}><a className="bg-primary-500 px-4 py-2 text-white">Next Page</a></Link></div>)}</div>);};export default StarWarsCharacterSearchTastic;
Clash of parameters
This query is shared between all the data sources available on the same page.
So, you need to ensure you use query parameter names that don't clash between the different data sources. If 2 or more data sources use the same query parameter name, or if multiple instances of the data source are used on the same page with the same query parameter name, they'll clash.
The dataSourceId
can be used to distinguish different data sources since it's guaranteed to have a unique value for each data source.