
Using Strapi CMS GraphQL with Nextjs Pages Router
We explore how to leverage Strapi's GraphQL capabilities within Next.js, allowing you to manage content and deliver updates to the web application.
Building dynamic, content-driven web applications is the new norm, providing extra value to the end user by offering personalized experiences, real-time updates, and interactive features that keep them engaged. This combination of Next.js,and Strapi, an open-source Headless CMS (Content Management System) offers a compelling solution, especially for developers who favor open-source tools.
Strapi introduction
Strapi, an open-source headless CMS, allows you to create, manage, and publish content independently of your front-end application. This platform offers a friendly UI for defining content models, managing user permissions, and leveraging GraphQL for optimized data fetching. Strapi can be utilized through a self-hosted option or as a Software as a Service (SaaS) uti; This flexibility makes Strapi a compelling choice for developers who value transparency, control and customization. In terms of support,the Strapi community is very active on Discord! Their Discord server offers friendly assistance from fellow Strapi enthusiasts. In addition, the Strapi core developers themselves host daily open office hours – mornings at 6 AM CST and afternoons at 12:30 PM CST. The open-office-hours voice channel is a great place for your questions, advice, or simply sharing your Strapi experiences. Dive in and get the most out of Strapi with the supportive Strapi community!
Assumptions
To follow along comfortably, we'll assume you have a Next.js project and a basic understanding of:
Next.js: Familiarity with Next.js concepts like components, pages, and data fetching methods like getStaticProps will be beneficial. For this guide you should have a Next.js project with the pages router setup.
React: A working knowledge of React fundamentals like components, state management, and JSX syntax is helpful.
GraphQL: Understanding the core principles of GraphQL queries, and schemas will be advantageous.
Building a web application with Nextjs and Strapi
In this following section, we'll walk you through building a web application with Next.js and Strapi. We'll cover setup, fetching data from Strapi using GraphQL, and explore alternative approaches. Plus, we'll point you to some helpful resources to take your project even further.
By default Strapi creates REST endpoints for each of your content-types, we’ll need to add the GraphQL plugin, to create a GraphQL endpoint to fetch and mutate your content. To do that, open your terminal and run the following command in your Strapi project:
1
npm run strapi install graphql
Start your app and open your browser to http://localhost:1337/graphql. You should now be able to access the GraphQL Playground that will help you to write your GraphQL queries and mutations.
Strapi GraphQL Playground
Once you've installed the Strapi plugin and navigate to /graphql, you should be greeted by the GraphQL playground above. But before you start fetching data, there's one crucial step. To ensure secure access, you'll need to add your access token as a bearer token authorization header in the HTTP Headers section in the image above. Your access token should be the same as your REST api token. To review or create new Strapi API tokens, navigate to /admin/settings/api-tokens
.
Authorization Header example:
{
"Authorization":"bearer_token_example"
}
On the right side of the GraphQL Playground, you'll find two tabs: Docs and Schema. Clicking the Docs tab reveals your available types and their structure, allowing you to easily write GraphQL requests directly within the Playground by following along with the structure.
On our CMS end, here are some example fields of the Article content type that we will be querying using GraphQL.
Let's leverage the GraphQL Playground to create our first basic query! We'll target a single article using its ID and fetch specific fields: id, title, and the related categories.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
query { document(id: 1) { data { id attributes { title categories { data { id attributes { name } } } } }
We can extend our capabilities by searching articles based on specific fields. Let's explore how to find articles with a particular slug, sort the results according to your criteria, and even retrieve the total number of pages returned by the request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
query { articles( filters: { slug: { eq: “test_slug” } } } sort: "createdAt:desc" pagination: { page: 1, pageSize: 10} ) { data { id attributes { title subtitle updatedAt createdAt publishedAt excerpt deck slug tags { data { attributes { title slug } } } } } meta { pagination { pageCount } } }
With our initial queries successfully crafted in the GraphQL Playground, we've laid the groundwork for integrating this functionality into our Next.js project. Let's dive into the process of fetching data from Strapi using GraphQL within your Next.js application.
Package - Apollo
Apollo Client is a popular library for managing GraphQL interactions within Javascript applications. It gives you tools to aid in fetching data from your Strapi GraphQL endpoint, managing the state of your data, and handling mutations (updates) sent back to your CMS, and even caching query results for faster response times.
Next.js Apollo setup
To get started install the Apollo client package into your Nextjs application:
npm i @apollo/client
Create a file in your project for the apollo client, tools and functions needed for setup../utilities/apolloClient.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
import { ApolloClient, HttpLink, InMemoryCache, from } from '@apollo/client'; import { onError } from '@apollo/client/link/error'; import { concatPagination } from '@apollo/client/utilities'; import merge from 'deepmerge'; import isEqual from 'lodash/isEqual'; import { useMemo } from 'react'; export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__'; let apolloClient: any; const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.forEach(({ message, locations, path }) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, ), ); if (networkError) console.log(`[Network error]: ${networkError}`); }); const httpLink = new HttpLink({ uri: `${process.env.STRAPI_ENDPOINT}`, // Server URL (must be absolute) credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers` headers: { Authorization: `bearer ${process.env.STRAPI_KEY}`, }, }); function createApolloClient() { return new ApolloClient({ ssrMode: typeof window === 'undefined', link: from([errorLink, httpLink]), cache: new InMemoryCache({ typePolicies: { Query: { fields: { allPosts: concatPagination(), }, }, }, }), }); } export function initializeApollo(initialState = null) { const _apolloClient = apolloClient ?? createApolloClient(); // If your page has Next.js data fetching methods that use Apollo Client, the initial state // gets hydrated here if (initialState) { // Get existing cache, loaded during client side data fetching const existingCache = _apolloClient.extract(); // Merge the initialState from getStaticProps/getServerSideProps in the existing cache const data = merge(existingCache, initialState, { // combine arrays using object equality (like in sets) arrayMerge: (destinationArray, sourceArray) => [ ...sourceArray, ...destinationArray.filter((d) => sourceArray.every((s) => !isEqual(d, s))), ], }); // Restore the cache with the merged data _apolloClient.cache.restore(data); } // For SSG and SSR always create a new Apollo Client if (typeof window === 'undefined') return _apolloClient; // Create the Apollo Client once in the client if (!apolloClient) apolloClient = _apolloClient; return _apolloClient; } // Allows the apollo state to be accessiable via the useApollo hook export function addApolloState(client: any, pageProps: any) { if (pageProps?.props) { pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract(); } return pageProps; } export function useApollo(pageProps: any) { const state = pageProps[APOLLO_STATE_PROP_NAME]; const store = useMemo(() => initializeApollo(state), [state]); return store; }
Having built the essential functions, it's time to consolidate! First copy your API token you have into the .env of your Next.js project and call it “STRAPI_KEY”. Now let's copy our GraphQL requests from the playground and store them centrally for easy access across our Next.js pages. In the following code, you'll see how we've replaced the original filtering methods with variables. By wrapping these queries in functions, we unlock more dynamic usage. This allows for flexible data fetching based on your needs throughout the Next.js application.
articles.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
export const GET_ARTICLE_BY_SLUG = gql` query getarticlebyslug($slug: String) { articles( filters: { slug: { eq: $slug } } pagination: { page: 1, pageSize: 10 } ) { data { id attributes { title subtitle slug } } } } `; export const GET_ARTICLE_BY_ID = gql` query { article(id: $id) { data { id attributes { title categories { data { id attributes { name } } } }`;
Using GraphQL in Nextjs
We've explored crafting GraphQL queries in the playground and the benefits of using variables for dynamic filtering. Now, let's delve into integrating these queries within your Next.js application. We'll begin with a fundamental approach: fetching data using getStaticProps.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
export default function Home() { return ( <> <main> </main> </> ); } export async function getStaticProps() { const apolloClient = initializeApollo(); apolloClient.query({ query: GET_ARTICLE_BY_SLUG, variables: { slug:"home_article", } }) return addApolloState(apolloClient); }
Let's break down the steps involved in using getStaticProps to fetch data with GraphQL:
Setting Up Apollo Client: We'll begin by initializing the Apollo Client instance using the function we imported earlier
Constructing the Query: Next, we'll leverage the Apollo Client to execute the GraphQL query we copied from the Playground. This query will likely contain variables to handle dynamic filtering, such as the specific article slug you want to retrieve
Return ApolloState: The final step involves utilizing a helper function called addApolloState (likely a custom function you've created) to add data to the pageProps. In addition, this function is likely responsible for attaching the Apollo Client instance (client) to the data fetched from the API. By returning the result of addApolloState with the attached client, you ensure that the data and the client needed to interact with it are passed together as props to your page component to build out the page.
Retrieving data inside of your component
In the following section, we'll delve into the specifics of using an Apollo hook within your Next.js component to fetch data from props and potentially make additional API requests based on user interactions.
1 2 3 4 5
const { data: articles } = useQuery(GET_ARTICLE_BY_SLUG, { variables: { slug: "home_article" }, });
Import the useQuery hook from @apollo/client. Inside your component, call useQuery and pass two arguments:
The same GraphQL query you used in getStaticProps (or any query you desire).
An object containing the variables used in getStaticProps (e.g., { slug: props.slug }).
With the useQuery hook in place, you've successfully fetched data from Strapi.
Synopsis
By leveraging Strapi's and Next.js's capabilities, this guide empowers you to create dynamic and data-driven web applications. We've explored setting up Strapi, crafting GraphQL queries, and integrating them into your Next.js application using both static and dynamic data fetching methods. With this knowledge, you're well-equipped to craft user-centric experiences that leverage the power of Strapi and Next.js.
Happy building!
Helpful Resources
Strapi Discord Link: https://discord.strapi.io/
Strapi Forums: https://forum.strapi.io/
Nextjs Examples: https://github.com/vercel/next.js/tree/canary/examples