Step 1 - Setting up the store code for Dynamic Content
Now that you have an overview of the Dynamic Content feature, let's start the feature implementation by setting up your store code.
Instructions
1. Mapping pages and data-fetching functions
Create a file to map the home and landing Pages to their data-fetching functions. This file maps the home or the landing pages slug to a respective data fetch function, which will fetch, handle, and return the desired server-side data to display the content in a store section. For example, you can add an image from a third-party source in a Hero component.
-
Open your FastStore project in any code editor that you prefer.
-
In the
src
folder, create thedynamicContent
folder. -
Inside the
dynamicContent
folder, create theindex.ts
file. -
Inside
index.ts
, add the following code:- First, we define the
fetchDataMyLandingPage
, which calls for an external API (https://fakestoreapi.com/products
) to fetch the product information. - The
data
object returns the information. - The
dynamicContent
relates the slug that we will define in the Headless CMS.
- First, we define the
Let’s dive deep into
dynamicContent
and the pages allowed to be customized with the Dynamic Content feature: home and landing pages:dynamicContent
The
dynamicContent
object stores key-value pairs:- The key represents the page slug (e.g.,
home
for the Home page andmy-landing-page
for a specific Landing page). - The value is the corresponding data-fetching function responsible for fetching data for that page. In this example,
fetchDataMyLandingPage
.
Home
This page is a singleton page, meaning it is unique in your store. This page is mapped using the key
home
for the Dynamic Content feature. The value of the home
key is a data-fetching function that returns the necessary data for the Home.Landing page
Landing pages can represent different pages and URLs. To identify the landing page, use the slug from the Headless CMS SEO Path input in the Settings tab as the key to the
dynamicContent
object without the initial /
. In the example below, the value /my-landing-page
.Now that we've established the file for mapping pages to data fetching functions let's define those functions to customize your component using third-party sources.
2. Creating Dynamic Content via API fetching
After mapping pages and data-fetching functions, you need to define these functions and create the logic to fetch the data from other data sources. There are two ways for fetching data:
- Using API extensions: Leverage the existing API Extension feature to fetch the data.
- Using fetch API or any requesting library: Request Fetch API or any HTTP library to directly fetch the data (Axios, fetch, etc).
Using API Extensions
-
In the store code, create the folder
graphql
in the' src' folder. -
Inside
graphql
, create the folderthirdParty
. This folder will contain the external data that we want to obtain. -
Create two other folders inside the
thirdParty
:resolvers
andtypeDefs
. -
Inside
typeDefs
create theextra.graphql
file. -
Add the following code:_35type Rating {_35rate: String_35count: Int_35}_35_35type ProductsExtraData {_35id: Int_35title: String_35price: Float_35description: String_35category: String_35image: String_35rating: Rating_35}_35_35type ExtraData {_35"""_35Data customizing ExtraData_35"""_35data: [ProductsExtraData]_35customFieldFromRoot: String_35customField: String_35}_35_35type NamedExtraData {_35"""_35Data customizing NamedExtraData_35"""_35data: String!_35}_35_35type Query {_35extraData: ExtraData_35namedExtraData(name: String!): NamedExtraData_35}
-
The field
ProductsExtraData
represents product data, with fields for id, title, price, description, category, image, and rating. -
The field
ExtraData
combines a list ofProductsExtraData
and additional custom fields. -
The field
Query
defines queries for fetching extraData and namedExtraData.
Now, create the logic to fill these fields: -
-
Create a file called
query.ts
inside the' resolvers' folder. -
Add the following code to it:_28_28export type ProductsExtraData = {_28id: number;_28title: string;_28price: number;_28description: string;_28category: string;_28image: string;_28rating: {_28rate: number;_28count: number;_28};_28};_28_28async function getProductsFromAPI() {_28const response = await fetch("https://fakestoreapi.com/products");_28return response.json();_28}_28_28export const Query = {_28extraData: async (): Promise<{ data: ProductsExtraData[] }> => {_28const products = await getProductsFromAPI();_28return { data: products };_28},_28namedExtraData: (_: unknown, { name }: { name: string }) => ({_28data: `Named extra data: ${name}`,_28}),_28};
getProductsFromAPI
fetches product data from an external API.extraData
resolves theextraData
query by fetching and returning products.namedExtraData
resolves thenamedExtraData
query by returning a custom message.
-
Create a file called
extra.ts
inside the' resolvers' folder. -
Add the following code to it:_13import { ExtraDataRoot } from "./query";_13_13export const ExtraData = {_13data: (root: ExtraDataRoot) => root.data,_13customFieldFromRoot: (root: ExtraDataRoot) => root?.data?.[0]?.image ?? "",_13customField: async (_: ExtraDataRoot) => {_13const res = await fetch(_13"https://fakestoreapi.com/products/category/jewelery"_13);_13const customField = await res.json();_13return (customField?.[0]?.title as string) ?? "";_13},_13};
- The
Query
function returns the API data in the fieldextraData
through a request on an external API.
- The
-
Inside the
resolvers
folder, create a file calledindex.ts
and add the following code:_10import { ExtraData } from "./extraData";_10import { Query } from "./query";_10_10const resolvers = {_10ExtraData,_10Query,_10};_10_10export default resolvers; -
Combine your resolvers and queries into the store's dynamic content in the
index.tsx
file inside thedynamicContent
folder._40import { gql } from "@faststore/core/api";_40import { execute_unstable as execute } from "@faststore/core/experimental";_40_40const query = gql(`_40query ServerDynamicContent($name: String!){_40extraData {_40data {_40title_40rating {_40rate_40count_40}_40}_40customField_40customFieldFromRoot_40}_40namedExtraData(name: $name) {_40data_40}_40}_40`);_40_40async function fetchDataUsingApiExtension() {_40try {_40const result = await execute({_40variables: { name: "example-name" },_40operation: query,_40});_40return { data: result.data };_40} catch (error) {_40return { data: null, errors: ["Error fetching data from API Extensions"] };_40}_40}_40_40// Map slugs to data-fetching functions_40const dynamicContent = {_40"my-landing-page-api-extensions": fetchDataUsingApiExtension,_40};_40_40export default dynamicContent;- Imports:
- Import
gql
for defining GraphQL queries. - Import execute_unstable for running server-side GraphQL queries.
- Import
query
Defining the GraphQL Query:- Query named
ServerDynamicContent
accepts aname
parameter. - Fetches fields under
extraData
including nestedrating
. - Fetches
namedExtraData
based on the name parameter.
- Query named
fetchDataUsingApiExtension
:- It executes to run the GraphQL query with specified variables.
- Returns the result data or logs and returns an error if it fails.
dynamicContent
:- Maps the slug
my-landing-page-api-extensions
to thefetchDataUsingApiExtension function
. - This slug identifies in the code which content we want to bring to the page.
- Maps the slug
- Imports:
You can render this data in your store sections once you've set up your dynamic content fetching functions and mappings. See the Step 2 - Handling Dynamic Content within custom sections guide for more information on using this data in a new section.
Using Fetch API or any request library
Single request
You can use Fetch API to directly fetch data from an endpoint.
_13_13async function fetchDataMyLandingPage() {_13const response = await fetch("https://fakestoreapi.com/products");_13const data = await response.json();_13return { data };_13}_13_13// Mapping slug to the data-fetching function_13const dynamicContent = {_13"my-landing-page": fetchDataMyLandingPage,_13};_13_13export default dynamicContent;
fetchDataMyLandingPage
:- Fetches data from https://fakestoreapi.com/products.
- Returns the fetched data as an object with a data key.
Multiple Requests Example
Use
Promise.all
to fetch data from multiple endpoints concurrently.
_25async function fetchDataHomepage() {_25try {_25 const [apiData1, apiData2] = await Promise.all([_25 fetch("https://fakestoreapi.com/products/1").then(res => res.json()),_25 fetch("https://fakestoreapi.com/products/2").then(res => res.json())_25 ]);_25_25 return {_25 data: {_25 product1: apiData1,_25 product2: apiData2,_25 },_25 };_25} catch (error) {_25 return { data: null, error: "Error fetching data from APIs" };_25}_25}_25_25// Mapping slugs to their respective data-fetching functions_25const dynamicContent = {_25home: fetchDataHomepage,_25"my-landing-page": fetchDataMyLandingPage,_25};_25_25export default dynamicContent;
fetchDataHomepage
:- Concurrently fetches data from two endpoints.
- Returns the data from both endpoints in a single data object with separate keys for each product.
Once you've set up your dynamic content fetching functions and mappings, you can render this data in your store sections. For more information on using this data in a new section, see the Step 2 - Handling Dynamic Content within custom sections guide.
Returning the data Object
For both approaches, data fetching using Fetch API or Data Fetching using API extensions, the data-fetching function must return an object with data as the root key in the following format:
_10return {_10data: {_10 // data fetched_10},_10};
The data object can contain other objects inside:
_10return {_10data: {_10 key1: result1,_10 key2: result2,_10},_10 ;