Next.js Integration Guide - Web SDK
On this page
GraphQL is deprecated. Learn More.
The following guide explains how to integrate the Realm Web SDK into a Next.js application. You can use the Realm Web SDK to access data in MongoDB Atlas from web apps, such as those made with Next.js. The Realm Web SDK interfaces with your data in MongoDB Atlas via Atlas App Services. Next.js is a React-based web framework that handles app configuration and structure, and supports client-side, server-side, and static rendering.
The Realm Web SDK supports all of these rendering modes in Next.js:
Client-side rendering: Query MongoDB directly from the browser using the Atlas GraphQL API or MongoDB Data Access.
Server-side rendering: Authenticate users with App Services from the browser and query using the GraphQL API on the server
Static rendering: Fetch data from MondoDB Atlas to generate pages at build time.
Before You Begin
Before using this integration guide, you should:
Be familiar with Next.js. Consider referring to the Next.js documentation.
Create a Next.js app. If creating a Next.js application for the first time, consider referring to the Next.js Tutorial.
Add the Realm Web SDK in your Next.js app. For more information, refer to Realm Web Installation documentation.
Tip
MongoDB Atlas Vercel Integration
If you're using Vercel to host your Next.js app, add the MongoDB Atlas integration to easily connect your Next.js app to Atlas.
Add Authentication
Before you can query MongoDB from your app, you must initialize the App Services client and authenticate a user. You can follow the steps below to connect the Realm Web SDK to client-side Next.js.
In this example, we're going to expose an authenticated Realm.App
instance
throughout the app using Realm.getApp()
in a custom React Hook.
Tip
See also:
For additional ways to expose Realm authentication throughout your application, refer to the Next.js authentication documentation.
Add an App ID environment variable
Use the App ID to connect the Next.js app to Atlas App Services. Expose the App ID throughout the app as an environment variable by doing the following:
Create the file
.env.local
in the project's root directory.Add an environment variable for the App ID. To make the App ID accessible from the browser in addition to the server, preface its name with
NEXT_PUBLIC_
.
NEXT_PUBLIC_APP_ID=<YOUR App Services App ID>
Create a React Hook to get the App
The client uses a React Hook to instantiate and access the Realm.App, which you use to connect to App Services using the App ID. You will use this hook throughout the pages you create in this guide.
Create the file
components/useApp.js
.Add the following code to instantiate and access the
App
instance:
import { useEffect, useState } from "react"; import * as Realm from "realm-web"; export function useApp() { const [app, setApp] = useState(null); // Run in useEffect so that App is not created in server-side environment useEffect(() => { setApp(Realm.getApp(process.env.NEXT_PUBLIC_APP_ID)); }, []); return app; }
Log the user in
Now you can access the app
instance with the useApp()
hook,
and use it to log a user in.
Authenticate anonymously in pages/index.js
when a user arrives
to the app home page.
import { useEffect } from "react"; import * as Realm from "realm-web"; import Link from "next/link"; import { useApp } from "../components/useApp"; export default function Home() { const app = useApp(); // note: useEffect runs in the browser but does not run during server-side rendering useEffect(() => { // If no logged in user, log in if (app && !app.currentUser) { const anonymousUser = Realm.Credentials.anonymous(); app.logIn(anonymousUser); } }, [app, app?.currentUser]); return ( //Your app ); }
In a real application, you would want to have a more complex authentication flow. For more information, refer to the Next.js authentication documentation.
Client-Side Rendering
This section shows how you can integrate Next.js client-side rendering with the Realm Web SDK. Following these steps, you can directly query MongoDB and interact with an Atlas App Services serverless backend through client-side JavaScript in your Next.js application. You can query MongoDB using either MongoDB Data Access or the Atlas GraphQL API.
MongoDB Data Access
Once the App
client is initialized and a user is authenticated,
you can use MongoDB Data Access to query MongoDB directly from
client code in your application.
Access the MongoDB Data Access interface from the app
object
with App.User.mongoClient(),
and then use it to query MongoDB.
import { useEffect, useState } from "react"; import { useApp } from "../components/useApp"; function MongoDbDataAccess({ name }) { const [plant, setPlant] = useState(); const app = useApp(); useEffect(() => { if (app?.currentUser) { const mongo = app?.currentUser?.mongoClient("mongodb-atlas"); const plants = mongo.db("example").collection("plants"); plants.findOne({ name }).then((foundPlant) => { setPlant(foundPlant); }); } }, [app, app?.currentUser, app?.currentUser?.id, name]); return ( <div> <h1>Data from MongoDB Access</h1> {plant ? ( <div> <p>{plant.name}</p> <p>{plant.color}</p> </div> ) : ( "no plant" )} </div> ); } export default function DaffodilInformation() { return <MongoDbDataAccess name="daffodil" />; }
Atlas GraphQL API
Alternatively, you can use the Atlas GraphQL API to query MongoDB via GraphQL from Next.js client-side code.
This example uses the Apollo GraphQL client to execute GraphQL queries. Install the Apollo client with its npm package @apollo/client and its peer dependency graphql.
npm install @apollo/client graphql
Now you can add a page to perform GraphQL queries. The code in the page does the following:
Imports the necessary dependencies
Creates the GraphQL client in a provider component.
Defines the GraphQL query.
Creates a component that consumes the GraphQL provider and runs the query.
Exports the provider component wrapping the consumer component.
All together, the GraphQL page should look as follows:
// 1. Import dependencies import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache, useQuery, gql, } from "@apollo/client"; import { useApp } from "../components/useApp"; // 2. Add GraphQL client provider function GraphQLProvider({ children }) { const app = useApp(); const client = new ApolloClient({ link: new HttpLink({ uri: process.env.NEXT_PUBLIC_GRAPHQL_API_ENDPOINT, // We get the latest access token on each request fetch: async (uri, options) => { const accessToken = app.currentUser?.accessToken; options.headers.Authorization = `Bearer ${accessToken}`; return fetch(uri, options); }, }), cache: new InMemoryCache(), }); return <ApolloProvider client={client}>{children}</ApolloProvider>; } // 3. GraphQL query const GET_PLANT = gql` query Plant($name: String!) { plant(query: { name: $name }) { _id sunlight name color type _partition } } `; // 4. Consumer of provider and query function PlantInformation({ name }) { const { loading, error, data } = useQuery(GET_PLANT, { variables: { name }, }); if (loading || !data) return <p>Loading ...</p>; if (error) console.error("Failed with error:", error); return ( <div> {data.plant ? ( <div> <p>{data.plant.name}</p> <p>{data.plant.color}</p> </div> ) : ( "no plant" )} </div> ); } // 5. Export page with the GraphQL query export default function FullGraphQLPage() { return ( <GraphQLProvider> <PlantInformation name="daffodil" /> </GraphQLProvider> ); }
Tip
For more information on querying Atlas with the GraphQL API with React, refer to the Apollo Client (React) documentation.
Server-Side Rendering
This section shows how you can integrate Next.js server-side rendering with the Realm Web SDK. Using the Realm Web SDK with server-side rendering allows you to access data as a specific user. In doing so, you apply App Services Rules and Permissions to those user queries.
To query MongoDB Atlas directly from the Next.js server, you must set up two different components: Atlas GraphQL API on the Next.js server and the Realm Web SDK in the browser.This section explains the setup of both.
You may want to integrate the Web SDK and Next.js because it allows you to:
Access data stored in Atlas directly on page load.
Apply Atlas App Services rules and permissions to requests to reduce server-side code.
Reduce use of client-side JavaScript.
Perform server-side data manipulation.
On a high level, the process for using the Realm Web SDK with Next.js server-side rendering is as follows:
In the browser, create a instance of your App Services client and log in a user. Save the user's accessToken as a cookie. This examples uses the package nookies, which simplifies cookie management in a Next.js app.
On the server, parse the accessToken cookie and use it to fetch data from MongoDB using the Atlas GraphQL API.
On the server, pre-render the data from MongoDB in your webpage before sending it to the browser.
Note
Do Not Use MongoDB Data Access with Server-side Rendering
While possible to use the MongoDB Data Access in server-side environments to query MongoDB, it is not generally advisable. You would need to persist user credentials in the browser to pass to the server on every request, which is a security vulnerability. Plus, MongoDB Data Access makes requests from a user object, which would need to be re-instantiated and re-authenticated on every request.
The following steps outline using the Realm Web SDK with Next.js server-side rendering.
Create a custom app wrapper to add accessToken to cookies
Create a custom App
page wrapper.
Create the file pages/_app.js
, and get the Realm.App
instance
with the useApp()
hook.
If there is a user currently authenticated, save their accessToken as a cookie.
This transfers the accessToken from the browser to the server on every request.
Set your cookie in a useEffect
hook that runs every time
there's a change in the user object, as shown below.
The setInterval
also resets the credential in cookies before the token expires.
import { useApp } from "../components/useApp"; import { setCookie } from "nookies"; // Import the useEffect hook import { useEffect } from "react"; function MyApp({ Component, pageProps }) { const app = useApp(); // Reset the user access token in cookies on a regular interval useEffect(() => { const user = app?.currentUser; if (user) { setCookie(null, "accessToken", user.accessToken); // Refresh token before session expires const TWENTY_MIN_MS = 1200000; const resetAccessToken = setInterval(async () => { await app?.currentUser?.refreshCustomData(); setCookie(null, "accessToken", user.accessToken); }, TWENTY_MIN_MS); // Clear interval setting access token whenever component unmounts or // there's a change in user. return () => clearInterval(resetAccessToken); } }, [app, app?.currentUser]); return ( <> <Component {...pageProps} app={app} /> </> ); } export default MyApp;
Server-side render the data
Create a new page file to perform the server-side rendering. On the page, add code to perform the following:
Import the relevant dependencies.
Add a function that creates a server-side GraphQL client on every request with the user's current auth token.
Create the GraphQL request which the server uses to fetch data.
Use the Next.js getServerSideProps function to perform the following:
Parse access token from cookies.
Create a GraphQL client with the access token.
Run the GraphQL query.
Return data to be used in the server-side render.
Export the page component to render the data.
All together, the server-side rendering page looks as follows:
// 1. import dependencies import nookies from "nookies"; import { ApolloClient, InMemoryCache, HttpLink, gql } from "@apollo/client"; // 2. Function to create GraphQL client const createClient = (token) => new ApolloClient({ link: new HttpLink({ ssrMode: true, uri: process.env.NEXT_PUBLIC_GRAPHQL_API_ENDPOINT, headers: { Authorization: `Bearer ${token}`, }, }), cache: new InMemoryCache(), }); // 3. GraphQL Query used in SSR const GET_PLANT = gql` query Plant($name: String!) { plant(query: { name: $name }) { _id sunlight name color type _partition } } `; // 4. Server-side logic to parse cookie and run query export async function getServerSideProps(context) { const { accessToken } = nookies.get(context); const client = createClient(accessToken); const { data: { plant: lily }, } = await client.query({ query: GET_PLANT, variables: { name: "daffodil" }, }); return { props: { lily }, }; } // Full page exported that gets the data from SSR export default function Ssr({ lily }) { return ( <div> <h1>Data from Server-Side Rendering</h1> {lily ? ( <div> <p>{lily.name}</p> <p>{lily.color}</p> </div> ) : ( "no plant" )} </div> ); }
Static Rendering
You can use Next.js static rendering with the Realm Web SDK to pull data from MondoDB Atlas and generate page HTML at build time.
You might want to use the Realm Web SDK with static rendering for the following use cases:
Pull in content management system data
Add configuration information
Create internationalized content
Creating and guarding your API key
You need to create a Server API key for authentication. Follow the steps
on the API Key Configuration page to complete this step.
Then, in your Next.js app, add the API key to your .env.local
file.
The variable stored is not accessible from the browser,
as long as you do not prefix it with PUBLIC_NEXT_
.
REALM_API_KEY=secret_api_key
Now you can access the variable in your app, except for in client-side rendering.
const { REALM_API_KEY } = process.env;
Perform static rendering with getStaticProps()
.
You can use the Next.js function getStaticProps() to query MongoDB during static generation.
In order to pre-render your page using getStaticProps()
,
connect your Next.js app to the Realm Web SDK.
Then, you can use getStaticProps()
to fetch data from MongoDB.
The following example shows how to query MongoDB with static rendering.
import * as Realm from "realm-web"; export async function getStaticProps() { const apiKey = process.env.REALM_API_KEY; const app = new Realm.App({ id: process.env.NEXT_PUBLIC_APP_ID }); // Log in user using realm API key const credentials = Realm.Credentials.apiKey(apiKey); const user = await app.logIn(credentials); // Connect to database const mongo = user.mongoClient("mongodb-atlas"); const plants = mongo.db("example").collection("plants"); // Use plants.findOne to query the database const data = await plants.findOne({ name: "daffodil" }); // You must parse data as JSON to use it as a prop const json = JSON.parse(JSON.stringify(data)); return { props: { plant: json, }, }; } export default function Static({ plant }) { return ( <div> <h1>Data from Static Rendering</h1> <div> <div> <p>{plant.name}</p> <p>{plant.color}</p> </div> </div> </div> ); }
Alternate Ways to Query MongoDB from Next.js
In addition to the Realm Web SDK, you can query MongoDB from Next.js in several ways:
Integrate MongoDB with your Next.js application, then execute your MongoDB queries using the MongoDB Node Driver. For more information, see the blog post How to Integrate MongoDB Into Your Next.js App. Using MongoDB drivers is not compatible with App Services user-based Rules and Permissions.
Use the MongoDB Data API to query MongoDB Atlas from the Next.js server. For more information, see Data API Endpoints.