Getting started with Next.js and eZ Platform

In my previous blog post I discussed about how you can run two applications on an eZ Platform Cloud project. As an example I used a clean install of Next.js, a JavaScript framework. Now let's take this a bit further and learn how integrate eZ Platform with Next.

Next.js is an open source JavaScript framework for building web sites and web applications. The first version of the project was launched in October 2016. Development is lead by Vercel, who also offer an integrated hosting solution for it. In the four years since its initial release, the product has matured and an active community has formed around Next.js.

Adopting a technology is an engineering choice, but also a business decision. The longevity of a technology is a significant factor when building something that will have a lifespan anywhere from multiple years to a decade or more. This is especially true when selecting an application framework like Next.js whose replacement could carry a significant cost.

In terms of future prospects, Next.js has a lot going for it.  It has widely used technologies as its foundation and has a wide range of references from household names such as Audible, Hulu and Ticketmaster to cutting edge technology companies like Docker, Elastic and TikTok.

While there are no guarantees, as a whole Next.js seems like safe bet in 2020. The product is mature and the user base is growing. The continued worldwide momentum of the JavaScript ecosystem ensures an ample supply of developers for years to come.

What is a web framework anyway?

In the above section, I mention the word "framework" a few times. It is a somewhat vague term, so let's clarify a bit what we mean in the context of Next.js. It is a product that integrates third party software libraries (code that does a very specific task) and provides conventions and functionality for common tasks when building web applications. Compared to many common web frameworks that target the client or the server, Next.js runs both on the server and browser.

As you may know, eZ Platform is built on another web framework, Symfony. While there some are overlapping parts between the two, they are actually quite complementary technologies. Symfony runs completely on the server side and offers tools for database connectivity, building sophisticated APIs and more. All features that we leverage to build a complete DXP suite.

Next.js excels at providing great user experiences with high performance. Using React.js on server and client side allows it to offer fast loading times, building fluent user interfaces with a single technology. Also, Next does not provide any tooling for database connectivity out of the box, instead opting to provide examples to connect to remote backends using web APIs

In essence Next.js is ideal for providing the "head" when implementing a service using a headless architecture. In our case eZ Platform handles management and delivery of data and Next.js provides a decoupled platform for creating the user experience.

Integrating Next.js and eZ Platform using GraphQL

Now let's learn how to integrate the two. We will start with the installation done in the article Running a Node.js Application on eZ Platform Cloud. You should have an understanding of eZ Platform and contemporary JavaScript (and React.js) to get the most out of this introduction. Our scope is simple: Next.js will list and display articles from eZ Platform using the GraphQL API.

The core API of Next.js exposes React.js components from the pages directory for display to the browser. The default installation comes with just a single page: the front page located in pages/index.js. Next.js comes with built-in CSS support, but for brevity our app will be unstyled.

To replace the standard page, replace the contents of the file with the following:

import Head from 'next/head'

export default function Home() {
  return (
    <div>
      <Head>
        <title>All articles</title>
      </Head>
      <main>
        <h1>All articles</h1>
        <ul>
          <li>Article 1</li>
          <li>Article 2</li>
          <li>Article 3</li>
        </ul>
      </main>
    </div>
  )
}

The above contains the logic and markup to render our article listing page. It makes use of the default head component, but other than that it's static. To populate it with articles from eZ Platform we will use the SWR library. Add it as a dependency to our Next.js app with with Yarn:

$ yarn add swr

We will use SWR to connect to our eZ Platform backend with GraphQL. To allow cross-domain requests, remember to enable CORS in your eZ Platform instance. Once CORS is enabled and you have the library included in your project, import and initialize SWR in the index component: 

import useSWR from 'swr'

let graphqlApiUrl = 'https://admin.master-7th15-d0esN07eXi57.eu-2.platformsh.site/graphql'

const fetcher = (query) =>
  fetch(graphqlApiUrl, {
    method: 'POST',
    headers: {
      'Content-type': 'application/json',
    },
    body: JSON.stringify({ query }),
  })
    .then((res) => res.json())
    .then((json) => json.data)

To learn about the eZ Platform GraphQL API, read the documentation and blog post on GraphQL. In our app, the queries are performed from within the component function. To fetch and view the items update the component to include the GraphQL query and template logic:

export default function Home() {

  const { data, error } = useSWR(`
  
  {content
    {articles  
      {edges
        {node
          {
            title
            intro {
              html5
            }
            _content {
              id
            }
          }
        }
      }
    }
  }

  `, fetcher)

  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>

  const { content } = data;

  return (
    <div>
      <Head>
        <title>All articles</title>
      </Head>
      <main>
        <h1>All articles</h1>
        <ul>
        {content.articles.edges.map((article, i) => (
            <li key={i}>
              {article.node.title}
            </li>
          ))}
        </ul>
      </main>
    </div>
  )
}

In the browser the above results in a list of article titles. To enable linking and displaying of individual articles we will need to use dynamic routes and the link component. To add the dynamic route for loading an article based on an ID, create a new file as pages/article/[id].js. This will allow routing requests with a dynamic parameter. Add the following to the file:

import Head from 'next/head'
import { useRouter } from 'next/router'

export default function Article() {

  const router = useRouter()
  const { id } = router.query

  return (
    <div>
      <Head>
        <title>Article {id}</title>
      </Head>
      <main>
        <h1>Article {id}</h1>
        <p>Content here.</p>
      </main>
    </div>
  )
}

The above component will print out a simple page with the dynamic ID parameter from the URL (e.g. https://example.com/article/123). To enable the dynamic route to view an individual article, import and initialize SWR and run a query and render the content as shown below:

  const router = useRouter()
  const { id } = router.query

  const { data, error } = useSWR(`
  
  {content
    {article(id: ${id})
      {title,
        intro{html5}
        body{html5}
      }
    }
  }

  `, fetcher)

  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>

  const { content } = data;
  
  return (
    <div>
      <Head>
        <title>{content.article.title}</title>
      </Head>
      <main>
        <h1>{content.article.title}</h1>
        <div dangerouslySetInnerHTML={{__html: content.article.body.html5}}></div>
      </main>
    </div>
  )

Note: for the richtext field we need to use of React's dangerouslySetInnerHTML attribute to inject the raw HTML content, as it would be escaped otherwise.

Now we've got the dynamic listing of all articles and a the display of a single page using. To tie them together we need to link them together. The method for linking in Next.js is the link component that allows linking to both standard and dynamic routes by passing parameters.

Let's jump back to our index component. After importing the link you can wrap the title in it and pass in the href and id attributes to the element to generate links to all articles automatically:

import Link from 'next/link'

...

<ul>
{content.articles.edges.map((article, i) => (
  <li key={i}>
    <Link href="/article/[id]" as={`/article/${article.node._content.id}`}>
      <a>{article.node.title}</a>
    </Link>
  </li>
))}
</ul>

With that in place, Next.js renders the correct links to the articles. So we've accomplished the functionality we set to build:

  • Display a listing of articles
  • Link to articles
  • Display an individual article.

The complete code for the app is available on GitHub: https://github.com/janit/ezplatform-nextjs

To learn more about the opportunities and challenges of using headless architectures in real world projects, download our free eBook Headless CMS in the Enterprise

Discover which skills and technology are required to successfully implement Headless CMS in the Enterprise

Headless CMS in the Enterprise

This eBook covers the considerations you should take into account to have the right skills and technology in place as well as what to pay attention to when it comes to the cost of ownership to ensure successful digital transformation in your organization.

Download eBook now
Headless CMS in the Enterprise

Insights and News