typescriptfastifyprisma-graphqlnexus-jsmercurius

Prisma in Mercurius Context Throws Undefined Error


When I try to access prisma I get this error I am using fastify. I am new to both, fastify and GQL

   "type": "GraphQLError",
      "message": "Cannot read properties of undefined (reading 'user')",
      "stack":
          TypeError: Cannot read properties of undefined (reading 'user')

This is how i am passing context


import AltairFastify from "altair-fastify-plugin";
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import mercurius from "mercurius";
import mercuriusCodegen from "mercurius-codegen";
import { makeSchema } from "nexus";
import { schemaTypes } from "./graphql/index";
import path from "path";

const buildContext =
  (fastify: FastifyInstance) =>
  async (req: FastifyRequest, _reply: FastifyReply) => ({
    req,
    prisma: fastify.prisma,
  });

const schema = makeSchema({
  types: schemaTypes,
  outputs: {
    schema: `${__dirname}/generated/schema.graphql`,
    typegen: `${__dirname}/generated/typings.ts`,
  },
  contextType: {
    module: path.join(process.cwd(), "src", "graphql", "context.ts"),
    export: "Context",
  },
});

export async function initGraphql(app: FastifyInstance) {
  try {
    await app.register(mercurius, {
      schema,
      graphiql: false,
      ide: false,
      path: "/graphql",
      allowBatchedQueries: true,
      context: buildContext(app),
    });

    await app.register(AltairFastify, {
      path: "/altair",
      baseURL: "/altair/",
      // 'endpointURL' should be the same as the mercurius 'path'
      endpointURL: "/graphql",
    });

    await mercuriusCodegen(app, {
      // Commonly relative to your root package.json
      targetPath: `${__dirname}/generated/graphql.ts`,
    });
  } catch (err: unknown) {
    app.log.error(err);
  }
}

this is my prisma plugin


import fp from "fastify-plugin";
import { PrismaClient } from "@prisma/client";

/**
 * Prisma plugin for Fastify.
 */
export default fp(async (fastify) => {
  // Initialize Prisma client
  const prisma = new PrismaClient();

  // Decorate Fastify with Prisma client instance
  fastify.decorate("prisma", prisma);

  // Add a hook to close the Prisma client when Fastify is shutting down
  fastify.addHook("onClose", async (instance) => {
    await prisma.$disconnect();
  });

  fastify.log.info("Prisma started...");
});


I am also using Nexus.js


export const UserMutation = mutationType({
  definition(t) {
    t.field("createUser", {
      type: UserType,
      args: {
        firstName: nonNull(stringArg()),
        lastName: nonNull(stringArg()),
        phoneNumber: nonNull(stringArg()),
        otp: nonNull(stringArg()),
      },
      resolve: async (_parent, args, { req, prisma }) => {
        console.log(prisma);
        const { jwt } = req.server;
        const user = await prisma.user.create({
          data: args,
          select: {
            id: true,
            ... // More fields
          },
        });
        const token = jwt.signToken(user.id);
        return { ...user, token };
      },
    });
  },
});

Here prisma is undefined, I tried logging a console and prisma is undefined.

Am i doing something wrong? I tried directly consoling the prisma int the prisma plugin and it works there, without error even if i directly use prisma client in the resolver it is successful.

I tried using the prismaclient directly inside the resolver, passing in different ways for example directly placing the new PrismaClient instead of fastify.prisma in the buildContext nothing works.

NOTE: I am using this template https://github.com/jellydn/fastify-starter


Solution

  • I had been trying to solve an issue for two days, and after waiting for hours, I finally found the solution.

    The problem was that my autoload function was in app.ts, and I was calling it in server.ts. While Prisma was accessible to fastify in app.ts, it was not accessible in server.ts. As a beginner in fastify, I didn't want to waste any more time on this issue, as I was trying to build something quickly rather than learning.

    To resolve the issue, I used 'then' to start the graphql server only after all the plugins had loaded. Here is the code I used:

    // Instantiate Fastify with some config
    const server = Fastify({
      logger: configureLogging(environment),
    });
    
    void server.register(env);
    void server.register(prisma);
    
    // Use fastify-autoload to load all plugins in the 'plugins' folder
    void server
      .register(fastifyAutoload, {
        dir: join(__dirname, "plugins"),
        ignoreFilter(path) {
          return ["/env.ts", "/index.ts", "/prisma.ts"].includes(path);
        },
      })
      .then(() => {
        // Init graphql
        void initGraphql(server);
    
        // Init Swagger
        void initSwagger(server);
      });
    

    I hope that helps!