Skip to content

Apollo server in Next.js API route

Dead-simple Apollo server using Next.js

Haven't worked with a GraphQL server in Next.js for some time now. Can't vouch that this approach is valid anymore.

API route

TypeScript
pages/api/graphql.ts
import {ApolloServer} from 'apollo-server-micro';
import {getSession} from 'next-auth/client';
 
import {schema} from '@/backend/graphql/schema';
import prisma from '@/utils/prisma';
 
const apolloServer = new ApolloServer({
  schema,
  context: async ({req}) => {
    const session = await getSession({req});
 
    const userEmail = session?.user?.email;
 
    // Throw if invalid
    if (!userEmail) {
      throw new Error('Not authenticated');
    }
 
    return {prisma, currentUserEmail: userEmail};
  },
  tracing: process.env.NODE_ENV === 'development',
});
 
export const config = {
  api: {bodyParser: false},
};
 
export default apolloServer.createHandler({
  path: '/api/graphql',
});

Schema

TypeScript
context.ts
import {PrismaClient} from '@prisma/client';
 
export type Context = {prisma: PrismaClient; currentUserEmail: string};
TypeScript
schema.ts
import {GraphQLDateTime} from 'graphql-iso-date';
import {
  asNexusMethod,
  list,
  makeSchema,
  nonNull,
  nullable,
  objectType,
  queryType,
} from 'nexus';
 
export const DateTime = asNexusMethod(GraphQLDateTime, 'date');
 
const User = objectType({
  name: 'User',
  definition(t) {
    t.nonNull.int('id');
    t.string('name');
    t.nonNull.string('email');
    t.field('emailVerified', {type: DateTime});
    t.field('createdAt', {type: nonNull(DateTime)});
    t.field('updatedAt', {type: nonNull(DateTime)});
    t.field('membership', {
      type: Membership,
      resolve: (parent, _, ctx) => {
        return ctx.prisma.user
          .findUnique({where: {id: parent.id}})
          .membership();
      },
    });
  },
});
 
const Membership = objectType({
  name: 'Membership',
  definition(t) {
    t.nonNull.int('id');
    t.nonNull.int('userId');
    t.field('user', {
      type: nonNull(User),
      resolve: (parent, _, ctx) => {
        return ctx.prisma.membership
          .findUnique({where: {id: parent.id}})
          .user();
      },
    });
    t.nonNull.int('orgId');
    t.field('org', {
      type: nonNull(Org),
      resolve: (parent, _, ctx) => {
        return ctx.prisma.membership.findUnique({where: {id: parent.id}}).org();
      },
    });
    t.nonNull.string('roleName');
    t.field('role', {
      type: nonNull(Role),
      resolve: (parent, _, ctx) => {
        return ctx.prisma.membership
          .findUnique({where: {id: parent.id}})
          .role();
      },
    });
  },
});
 
// ... more fields
 
const Query = objectType({
  name: 'Query',
  definition(t) {
    t.field('currentUser', {
      type: User,
      resolve: (root, args, ctx) => {
        return ctx.prisma.user.findUnique({
          where: {email: ctx.currentUserEmail},
        });
      },
    });
  },
});
 
export const schema = makeSchema({
  types: [User, Membership, Query, DateTime],
});