Building a content-rich application is a complicated task, especially when you’re using a modern tech stack. In some cases, you might need granular permissions support and custom workflows to localize your application. In others, you might want marketing content features that are intuitive for non-technical users, or you might need multi-tenancy support for lots of external content creators.
With a modern content platform like Hygraph, it’s a simple matter to tackle all of the above. Essentially a GraphQL content platform, Hygraph allows you to easily create custom schemas with its low-code schema builder and then access its stored data via the GraphQL API.
Let’s take Hygraph for a test run for the next few minutes, and see what it takes to build a car dealership website using Hygraph as a backend. This tutorial demonstrates how schemas are made and how easy it is to fetch data from a third-party API source with Hygraph. We’ll build the application frontend using Next.js.
#Building a car dealership website using Hygraph
Before getting into the nuts and bolts of how to build a car dealership website, you need to have some prerequisites in place:
- A basic understanding of Next.js and Node.js
- Node.js and NPM installed on your machine
- A Hygraph account
- An Auto.dev account to fetch car details
Obtaining an Auto.dev API endpoint
Let’s start by obtaining the Auto.dev API keys and endpoint. Auto.dev is an automotive API provider that facilitates accessing vehicle data, such as the VIN, or listing all vehicles filtered according to your parameters. It’s simple to use and free.
Navigate to Auto.dev and create a new account by clicking Get your free API key. After creating an account, you can obtain the API endpoint and the API key from the dashboard.
Keep this tab open or save the keys somewhere—you’ll need them later.
Creating a Hygraph account
Visit the Hygraph website and click Get Started. After successfully signing up on the registration page, you can either add a project or choose a template.
For this tutorial, add a new project from scratch. Click Add Project and fill out the form with the necessary details, like the name of your project, a description, and the data storage region.
When your details are to your satisfaction, click Add Project to view a menu like this:
- Schema. Allows you to create custom schemas from the intuitive GUI of Hygraph.
- Content. Lets you add content to these schemas.
- Assets. Lets you manage static assets like images.
- API Playground. Enables you to explore the GraphQL API of your project from the dashboard.
Schemas are the heart of Hygraph. They let you create content models, add remote sources, and create enumerators depending on your preferences. So let’s create a few.
Creating schemas in Hygraph
Click Schema in the menu. You can create any type of schema you want, but for this tutorial, let’s create a model for blog posts.
Click Add next to the Models option. In the modal that opens, enter a few details about the model, like its name, the API ID you'll use to call the model, the plural form of the model, and its description.
After successfully creating the model, add the fields that this particular model offers: title, summary, and the post itself.
If you click Content from the menu at this point, you should see an option to add a new blog post.
Now that you know how to create a basic schema, let’s add a remote source for fetching vehicle details.
Adding Remote Sources to Hygraph and creating a vehicle schema
Hygraph provides a feature called Content Federation, which integrates external data sources into your Hygraph account. This enables you to easily access all your data from a single GraphQL endpoint.
This tutorial integrates the Auto.dev data source in order to access the vehicle data from the Hygraph endpoint itself. In other words, when an admin inputs a VIN of a particular vehicle into the Hygraph CMS, all the data available in Auto.dev for that vehicle can be retrieved from the GraphQL endpoint.
To achieve this, let’s add a remote source. Click Schema > Components > Add from the menu, and a new form opens up. Fill in the basic details like the name and description of the remote source, and then choose the type of source. This tutorial selects REST.
Add the base URL of the Auto.dev endpoint, which in this case is https://auto.dev/api/vin/
. Add the Authorization header to authorize your requests, and add the Authorization
key and the value of Bearer API_KEY
so that it looks like this:
Replace API_KEY
with the key that you received from the Auto.dev dashboard. You can also define custom type definitions for the API data, but let’s skip adding it for this tutorial. Click Add, and your remote source is ready.
You can now create a model to use the remote source. Click Add next to the Models option, and name it Car Detail
. This model holds the car's name, a gallery for images, a description, price, and condition, along with the car details received from the API.
Add a single-line field called VIN
to pass to the remote source to decode the car detail, select the REST option from the model’s sidebar, and name it something appropriate, like car info
for example.
Follow this pattern to fill out the rest of the form:
- Remote source. This option lets you choose which remote source you’re referencing here in case you have multiple sources. The Auto.dev API is accessible through GET request only, so there’s no need to change the default.
- Input arguments. This field allows you to pass input arguments in the API call.
- Path. This option allows you to append path data to the base URL. In this case, you need to append the VIN to the base URL. Using
{{doc.vin}}
sets the VIN of the document as a path variable. Thedoc
object allows you to reference any field from the model. Because the API ID of VIN isvin
, it’s referenced asdoc.vin
.
The complete model is shown below.
Adding content to Hygraph
With your model for adding car details ready, let’s add some content from the Hygraph content menu.
Click Content > Car Detail from the content menu, and then Add Entry. Let’s say you want to add a Porsche 911. The VIN of this particular model is WP0AB2A93KS114315
. Fill in the form and click Save, then Publish to publish your content.
Editor's Note
Setting Hygraph permissions
Once you’ve added as many vehicles as you like, let’s set up Hygraph permissions to access the data in your application.
Hygraph provides you with the required endpoints, but by default, you can’t access them without setting proper permissions. To make the endpoints accessible, click Project Settings > Access > API Access. The content API endpoint is the endpoint for accessing the data in this case. You’ll need this string later.
To create a permanent auth token to use for accessing the data, scroll down the API Access page and click Add Token under under the Permanent Auth Token.
Give the token a name and a description, set the stage as Published, and click Add & configure permissions. On the permissions page, copy the bearer token. This tutorial leaves the values in the Content API section set to their default.
Your Hygraph setup is complete, but keep a note of the content endpoint and the bearer token. You’ll need them to connect the frontend.
Designing the application’s frontend
If you’re unfamiliar with Next.js, their documentation is a good place to start. If you’re already familiar with Next.js, you can jump directly to the code for this tutorial in GitHub.
Creating a new Next.js project is simple. Type the following command in the terminal:
npx create-next-app@latest APP_NAME
Replace APP_NAME
with the name of your application. To keep this article simple, choose options similar to the image below when configuring the template.
Editor's Note
Create a components folder in the root directory and add a new file called CarCard.jsx
for displaying the car details on the homepage. Paste the following code in this component:
import styles from '@/styles/Car.module.css';import Image from 'next/image';import Link from 'next/link';const Car = ({ id, imageUrl, description, name, make, price, condition }) => {return (<div className={styles.container}><Imagesrc={imageUrl}alt={`${make} ${name}`}className={styles.image}width={200}height={200}/><div className={styles.description}><h2 className={styles.name}>{name}</h2><p className={styles.make}>{make}</p><p className={styles.details}>{description}</p><p className={styles.price}>Price: {price}</p><p className={styles.condition}>Condition: {condition}</p><button className={styles.button}><Link href={`/${id}`}>View Details</Link></button></div></div>);};export default Car;
This component takes a few props for displaying the image, description, name, price, condition and the make of the car.
Create a new file called Car.module.css
inside the styles folder and paste the following code inside it:
.container {display: flex;flex-direction: row;align-items: center;margin-bottom: 20px;}.image {width: 200px;height: 150px;margin-right: 20px;object-fit: cover;border-radius: 5px;}.description {display: flex;flex-direction: column;}.name {font-size: 1.5rem;font-weight: bold;margin: 0;}.make {font-size: 1rem;color: gray;margin: 5px 0;}.details {font-size: 1rem;margin: 0;}.price {font-size: 1rem;color: gray;}.condition {font-size: 1rem;color: gray;}.button {width: 7rem;}
And your car card is ready. To display the details of the car on a separate page, create a new file called [id].jsx
inside the pages folder. This page will work as a dynamic route.
Paste the following code inside this route:
import Image from 'next/image';import styles from '@/styles/CarDetails.module.css';import { useRouter } from 'next/router';import { useQuery, gql } from '@apollo/client';const CarDetails = () => {const router = useRouter();const { id } = router.query;return (<div className={styles.container}>{loading ? (<p>Loading...</p>) : (<div><div className={styles.imageContainer}><Imagesrc={values.gallery[0].url}alt={`${values.carInfo.make.name} ${values.carInfo.model.name}`}className={styles.image}height={400}width={400}/></div><div className={styles.detailsContainer}><h1className={styles.name}>{`${values.carInfo.make.name} ${values.carInfo.model.name}`}</h1><p className={styles.doors}>Number of Doors: {values.carInfo.numOfDoors}</p><p className={styles.model}>Make: {values.carInfo.model.name}</p><p className={styles.model}>Engine Compressor Type: {values.carInfo.engine.compressorType}</p><div className={styles.description}><h2>Description</h2><p>{values.description}</p></div></div></div>)}</div>);};export default CarDetails;
Create a new file called CarDetails.module.css
under the styles folder and paste in the following code:
.container {display: flex;flex-direction: row;align-items: center;margin-bottom: 20px;}.imageContainer {flex-basis: 40%;margin-right: 20px;}.image {width: 100%;height: auto;object-fit: cover;border-radius: 5px;}.detailsContainer {flex-basis: 60%;}.name {font-size: 2rem;margin: 0;}.makeYear {font-size: 1.5rem;color: gray;margin: 10px 0;}.model {font-size: 1.5rem;margin: 0;}.transmission {font-size: 1rem;color: gray;margin-bottom: 20px;}.description {margin-top: 20px;}.description h2 {font-size: 1.5rem;margin: 0;}.description p {font-size: 1rem;margin: 10px 0;}
Editor's Note
Add media.graphassets.com
to your next.config.js
file, or else your images won’t display. Replace your next.config.js
file contents with:
/** @type {import('next').NextConfig} */const nextConfig = {reactStrictMode: true,images: {domains: ['media.graphassets.com'],},};module.exports = nextConfig;
With design complete, let’s connect Hygraph with your application.
Connecting Hygraph with the frontend
To connect Hygraph with the frontend, first install two dependencies—graphql
and @apollo/client
—by running the following command:
npm i @apollo/client graphql
Create a new file called .env.local
in the root directory and save the Hygraph endpoint and the bearer token as follows:
NEXT_PUBLIC_HYGRAPH_ENDPOINT=HYGRAPH_PUBLIC_CONTENT_ENDPOINTNEXT_PUBLIC_HYGRAPH_PERMANENTAUTH_TOKEN=HYGRAPH_PERMANENT_AUTH_TOKEN
Replace HYGRAPH_PUBLIC_CONTENT_ENDPOINT
and HYGRAPH_PERMANENT_AUTH_TOKEN
with the tokens from your Hygraph dashboard.
Create a new folder called graphql
and create a file called client.js
inside it. Set up the Hygraph client as follows:
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';import { setContext } from '@apollo/client/link/context';const httpLink = createHttpLink({uri: process.env.NEXT_PUBLIC_HYGRAPH_ENDPOINT,});const authLink = setContext((_, { headers }) => {return {headers: {...headers,authorization: `Bearer ${process.env.NEXT_PUBLIC_HYGRAPH_PERMANENTAUTH_TOKEN}`,},};});export const client = new ApolloClient({link: authLink.concat(httpLink),cache: new InMemoryCache(),});
In the previous code, note that:
httpLink
sets the base URL of the API.authLink
sets the authorization with the bearer token received from the Hygraph backend.- The client is created by passing the
authLink
and thehttpLink
insideApolloClient
.
Open the _app.js
file of your project and replace the existing code with the following:
import '@/styles/globals.css';import { ApolloProvider } from '@apollo/client';import { client } from '@/graphql/client';export default function App({ Component, pageProps }) {return (<ApolloProvider client={client}><Component {...pageProps} /></ApolloProvider>);}
The component is wrapped using the ApolloProvider
, and the client
is passed as a prop. You can access the custom hooks defined at @apollo/client
from anywhere in the app now.
Let’s write a GraphQL query to get all the car details. Open the index.js
file and import the following:
import { useEffect, useState } from 'react';import { gql, useQuery } from '@apollo/client';import CarCard from '@/components/CarCard';
The useQuery
hook allows you to run the queries written using gql
. The useQuery
hook returns three values: loading
, error
, and data
. Until the data or an error is received, the loading
value stays true. The error
throws an error if there’s any, otherwise, it’s undefined
, and the data
holds all the data received from the endpoint.
Replace the content of Home
component with the following:
export default function Home() {const [price, setPrice] = useState('all');const { loading, error, data } = useQuery(gql`query MyQuery {carDetails(where: {${price === 'moreThan4' ? 'price_gt: 400000' : ''}${price === 'lessThan4' ? 'price_lt: 400000' : ''}${price === 'all' ? 'price_gt: 0' : ''}}) {iddescriptiongallery {url}nameOfTheCarpricecondition}}`);const options = [{ value: 'all', label: 'All' },{ value: 'moreThan4', label: '> 400000' },{ value: 'lessThan4', label: '< 400000' },];const onChange = (e) => {setPrice(e.target.value);console.log(e.target.value);};return (<><Head><title>Create Next App</title><meta name='description' content='Generated by create next app' /><meta name='viewport' content='width=device-width, initial-scale=1' /><link rel='icon' href='/favicon.ico' /></Head><main><div><label htmlFor='price'>Filter By Price:</label><select id='price' value={price} onChange={onChange}>{options.map((option) => (<option key={option.value} value={option.value}>{option.label}</option>))}</select></div>{loading ? (<p>Loading...</p>) : (data.carDetails.map((car) => (<CarCardkey={car.id}imageUrl={car.gallery[0].url}description={car.description}name={car.nameOfTheCar}price={car.price}condition={car.condition}id={car.id}/>)))}</main></>);}
The logic is straightforward here. The page has a simple select field to filter the cars. There are three view options inside the folder:
- All cars
- Cars that have a price lower than $400,000
- Cars with a price higher than $400,000
Depending on the data, the CarCard
component is rendered in a loop. For each car, the details are passed as a prop to it. A React state is defined to store the price of the chosen select value to filter cars. By default, the select field will have all
as the value.
Run a query using the useQuery
hook and gql
.
query MyQuery {carDetails(where: {${price === 'moreThan4' ? 'price_gt: 400000' : ''}${price === 'lessThan4' ? 'price_lt: 400000' : ''}${price === 'all' ? 'price_gt: 0' : ''}}) {iddescriptiongallery {url}nameOfTheCarpricecondition}}`);
The MyQuery
has a where condition. Depending on the chosen select value, it shows cars that cost more or less than $400,000. If the select field value is all
, it shows cars that cost more than $0. This query returns the ID, description, gallery image URLs, name, price, and condition of the car.
The options for the select field are hard-coded for now. The onChange
function that sets the price state is defined.
Inside the return statement, the options are mapped to display them inside the select field. If loading is true, it shows a loading message, otherwise, it maps the data
received from the API and passes it inside the prop of the CarCard
component.
The homepage is ready, so let’s move on to the car details page, where you’ll use the data received from the remote source.
Depending on the ID of the route, let’s fetch the data from the API. Write a new GraphQL query and extract the values from there. Paste this code above the return statement:
const query = gql`query carDetail_carInfo($id: ID!) {values: carDetail(where: { id: $id }) {nameOfTheCardescriptiongallery {url}carInfo}}`;const { loading, error, data } = useQuery(query, { variables: { id } });const { values } = loading ? { values: {} } : data;
This is similar to the query used for the homepage. The main difference is that the variable is the id
of the Hygraph content ID here, and another value called carInfo
is fetched from the endpoint. This carInfo
endpoint returns the data received from the remote source.
If you take a look at the code for this page, you can see that the car make, model, engine, number of doors, and a few other data are fetched directly from the remote source using the Hygraph GraphQL endpoint.
Your application is now ready to test. Run the code with the npm run dev
command from your terminal. This starts the Next.js server on http://localhost:3000
. The following GIF demonstrates the application in action.
Your car dealership website is ready for the world!
Editor's Note
#What's next
Hygraph is a headless CMS that allows you to create highly customizable and powerful applications without worrying about the backend stack. You got to explore this functionality just now building a custom website with Hygraph and Next.js specifically for a hypothetical car dealership. This tutorial used the Auto.dev API as a remote data source to fetch information about car models via their VINs; the remote source response was directly accessible through Hygraph’s GraphQL endpoint.
Ready to build your own scalable content experience? Create a free-forever account right here.