docs/content/blog/nextjs-keystone.md
Keystone is an excellent headless CMS that provides a GraphQL API to access your data. But did you also know that you could use Keystone as a data layer within Next.js applications? Yes, you don't need to host Keystone on a separate server. Keystone's entire data layer is so well abstracted that you could just import the context API in a Next.js application and CRUD your data. In other words, Keystone can be imported and used in serverless environments like Next.js routes, getStaticProps and getServerSideProps.
If you want to browse through the example code to understand how Keystone works within a Next.js application, we have built an example here examples/framework-nextjs-pages-directory.
Live Demo: Keystone + Next.js example in Vercel
To understand how Keystone works within a Next.js application, let's first take a look at how you'd typically use Keystone when you are building end-to-end applications.
This is how a standalone keystone server works:
getStaticProps or getServerSideProps, you will send http requests to Keystone server's GraphQL API to query or mutate your data.With the release of core v3, Keystone can now be used within other servers. Keystone's data layer is well abstracted and exported as node APIs that you could import and use in node based servers like Next.js and deploy to Vercel.
This is how Keystone works within a Next.js application:
getContext in your Next.js application. It is a wrapper around Keystone's data layer.getStaticProps or getServerSideProps using the Keystone context object.Importing Keystone's getContext
/* nextjs-app/src/keystone/context.ts */
import { getContext } from '@keystone-6/core/context';
import config from '../../keystone';
import { Context } from '.keystone/types';
import * as PrismaModule from '.prisma/client';
// Making sure multiple prisma clients are not created during dev hot reloading
export const keystoneContext: Context =
(globalThis as any).keystoneContext || getContext(config, PrismaModule);
if (process.env.NODE_ENV !== 'production') {
(globalThis as any).keystoneContext = keystoneContext;
}
Querying data in getServerSideProps
/* nextjs-app/src/pages/index.tsx */
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
const users = await context.db.User.findMany();
return {
props: { users: users }, // will be passed to the page component as props
};
};
Setting up a Next.js route as a GraphQL server for your client.
/* nextjs-app/src/pages/api/graphql.ts*/
import { createYoga } from 'graphql-yoga';
import type { NextApiRequest, NextApiResponse } from 'next';
import { keystoneContext } from '../../keystone/context';
// An example of how to setup your own yoga graphql server
// using the generated Keystone GraphQL schema.
export const config = {
api: {
bodyParser: false,
},
};
// Use Keystone's context to create GraphQL handler
export default createYoga<{
req: NextApiRequest;
res: NextApiResponse;
}>({
graphqlEndpoint: '/api/graphql',
schema: keystoneContext.graphql.schema,
/*
`keystoneContext` object doesn't have user's session information.
You need an authenticated context to CRUD data behind access control.
keystoneContext.withRequest(req, res) automatically unwraps the session cookie
in the request object and gives you a `context` object with session info
and an elevated sudo context to bypass access control if needed (context.sudo()).
*/
context: ({ req, res }) => keystoneContext.withRequest(req, res),
});
The entire application is just a single Next.js application and can be deployed to Vercel. This reduces the massive overhead of having to maintain the infra of a separate server.
This becomes useful in a handful of situations:
getServerSideProps) using Keystone's context API instead of making a http request, you can send your response faster to the browser.Keystone's Admin UI is served from the Keystone server. Since we don't start the Keystone server and only use the context API within the Next.js server, we won't have access to the Admin UI. This is the only downside now to using Keystone within a Next.js application with the context API. But this might change soon. The Keystone team has already started thinking about a few different approaches to get Keystone's Admin UI to work with external servers so in the near future you might be able to use both the context API and the Admin UI within a single Next.js application.
That said, during development in your local machine, you will have access to the Admin UI since you can easily run two separate servers in your local machine and all you have to do is just start the keystone server and access the Admin UI. But when you deploy your Next.js app to Vercel (or anywhere really), you can't have two servers running and that's why you can't use the Admin UI once you deploy your Next.js application. But if you really need Keystone's Admin UI, you could always just deploy Keystone as a standalone server.
To make it easy to get started, we have built an example showing how you could use Keystone in Next.js applications. Check it out here examples/framework-nextjs-pages-directory. It is setup with auth, server rendering, custom GraphQL server in a Next.js route and everything you need to get started.
Live Demo: Keystone + Next.js example in Vercel
To sum it all up,
context API and Admin UI work within a single Next.js application. Until then, if having an Admin UI is not one of your top priorities, Keystone + Next.js is a great stack for what you're building.That's all folks.
If you like using Keystone, we'd appreciate a shout out in Twitter and a star in GitHub. We also have a friendly slack community, come say hi!
To stay updated on the latest news and announcements about Keystone, subscribe to our RSS feed.