PJPalJS

Nexus GraphQL Integration

Introduction

A Nexus plugin that provides Prisma integration with automatic field selection, admin schema generation, and GraphQL scalar types. This package bridges Prisma and Nexus GraphQL to create type-safe, efficient GraphQL APIs.

Installation

Peer Dependencies

This package requires the following peer dependencies:

  • @prisma/client ^6
  • graphql ^15 || ^16
  • nexus ^1

Main Exports

paljs Plugin

The main Nexus plugin that provides Prisma integration and field selection.

typescript
import { makeSchema } from "nexus";
import { paljs } from "@paljs/nexus";
 
const schema = makeSchema({
  types: [
    // Your types here
  ],
  plugins: [
    paljs({
      // Plugin configuration
      includeAdmin: true,
      adminSchemaPath: "./prisma/schema.prisma",
      excludeScalar: ["Upload"],
      prismaSelectOptions: {
        defaultFields: {
          User: { id: true, email: true },
        },
        excludeFields: {
          User: ["password"],
        },
      },
    }),
  ],
});

Settings Interface

Configuration interface for the paljs plugin.

typescript
interface Settings<
  ModelName extends string = "",
  ModelsObject extends Record<ModelName, Record<string, any>> = Record<
    ModelName,
    Record<string, any>
  >,
> {
  // Include admin queries and mutations
  includeAdmin?: boolean;
 
  // Path to Prisma schema file for admin generation
  adminSchemaPath?: string;
 
  // Scalar types to exclude from generation
  excludeScalar?: string[];
 
  // PrismaSelect configuration options
  prismaSelectOptions?: PrismaSelectOptions<ModelName, ModelsObject>;
}

Core Functionality

Automatic Field Selection

The plugin automatically adds a select object to your GraphQL context based on the fields requested in the query.

typescript
import { queryType } from "nexus";
 
export const Query = queryType({
  definition(t) {
    t.list.field("users", {
      type: "User",
      resolve: async (_, args, { prisma, select }) => {
        // select is automatically generated based on GraphQL query
        return prisma.user.findMany({
          ...args,
          ...select, // Optimized field selection
        });
      },
    });
  },
});

Admin Schema Generation

When includeAdmin is enabled, the plugin automatically generates admin queries and mutations:

typescript
// Auto-generated admin queries
query {
  findFirstUser(where: { ... }, orderBy: { ... })
  findManyUser(where: { ... }, orderBy: { ... }, skip: 10, take: 20)
  findUniqueUser(where: { id: 1 })
  aggregateUser(_count: true, _avg: { age: true })
}
 
// Auto-generated admin mutations
mutation {
  createOneUser(data: { ... })
  updateOneUser(where: { id: 1 }, data: { ... })
  deleteOneUser(where: { id: 1 })
  upsertOneUser(where: { id: 1 }, create: { ... }, update: { ... })
}

Built-in Scalar Types

The plugin includes common GraphQL scalar types for Prisma:

typescript
// Available scalar types:
DateTime;
Json;
Decimal;
BigInt;
Bytes;

Usage Examples

Basic Setup

typescript
import { makeSchema, objectType, queryType } from "nexus";
import { paljs } from "@paljs/nexus";
 
// Define your types
const User = objectType({
  name: "User",
  definition(t) {
    t.nonNull.int("id");
    t.nonNull.string("email");
    t.string("name");
    t.list.field("posts", {
      type: "Post",
      resolve: (parent, _, { prisma, select }) =>
        prisma.user.findUnique({ where: { id: parent.id } }).posts(select),
    });
  },
});
 
const Post = objectType({
  name: "Post",
  definition(t) {
    t.nonNull.int("id");
    t.nonNull.string("title");
    t.string("content");
    t.field("author", {
      type: "User",
      resolve: (parent, _, { prisma, select }) =>
        prisma.post.findUnique({ where: { id: parent.id } }).author(select),
    });
  },
});
 
// Define queries
const Query = queryType({
  definition(t) {
    t.list.field("users", {
      type: "User",
      resolve: (_, __, { prisma, select }) => prisma.user.findMany(select),
    });
 
    t.field("user", {
      type: "User",
      args: { id: nonNull(intArg()) },
      resolve: (_, { id }, { prisma, select }) =>
        prisma.user.findUnique({ where: { id }, ...select }),
    });
  },
});
 
// Create schema with paljs plugin
const schema = makeSchema({
  types: [User, Post, Query],
  plugins: [
    paljs({
      includeAdmin: true,
      adminSchemaPath: "./prisma/schema.prisma",
    }),
  ],
  outputs: {
    schema: "./generated/schema.graphql",
    typegen: "./generated/nexus.ts",
  },
});

Advanced Configuration

typescript
import { paljs } from "@paljs/nexus";
 
const schema = makeSchema({
  types: [
    /* your types */
  ],
  plugins: [
    paljs({
      includeAdmin: true,
      adminSchemaPath: "./prisma/schema.prisma",
      excludeScalar: ["Upload", "JSON"],
      prismaSelectOptions: {
        defaultFields: {
          User: { id: true, email: true, createdAt: true },
          Post: { id: true, title: true, published: true },
          Comment: (select) => {
            // Dynamic default fields based on selection
            const base = { id: true, content: true };
            if (select.author) {
              return { ...base, authorId: true };
            }
            return base;
          },
        },
        excludeFields: {
          User: ["password", "hash", "salt"],
          Post: ["internalNotes"],
          Session: (select) => {
            // Dynamic field exclusion
            const excluded = ["token"];
            if (!select.user) {
              excluded.push("userId");
            }
            return excluded;
          },
        },
      },
    }),
  ],
});

Multi-Schema Support

typescript
import { Prisma as UserPrisma } from "./generated/user-client";
import { Prisma as BlogPrisma } from "./generated/blog-client";
 
const schema = makeSchema({
  types: [
    /* your types */
  ],
  plugins: [
    paljs({
      includeAdmin: true,
      prismaSelectOptions: {
        dmmf: [UserPrisma.dmmf, BlogPrisma.dmmf],
        defaultFields: {
          User: { id: true, email: true },
          Post: { id: true, title: true },
        },
      },
    }),
  ],
});

Custom Scalar Configuration

typescript
import { scalarType } from "nexus";
import { paljs } from "@paljs/nexus";
 
// Define custom scalars
const Upload = scalarType({
  name: "Upload",
  // ... scalar implementation
});
 
const schema = makeSchema({
  types: [Upload /* other types */],
  plugins: [
    paljs({
      includeAdmin: true,
      // Exclude custom scalars from auto-generation
      excludeScalar: ["Upload"],
    }),
  ],
});

Context Integration

Setting up Context

typescript
import { PrismaClient } from "@prisma/client";
import { PrismaSelect } from "@paljs/plugins";
 
const prisma = new PrismaClient();
 
export interface Context {
  prisma: PrismaClient;
  select: any;
}
 
export function createContext(): Context {
  return {
    prisma,
    select: {}, // Will be populated by paljs plugin
  };
}

Using in Resolvers

typescript
import { queryType, mutationType } from "nexus";
 
export const Query = queryType({
  definition(t) {
    t.list.field("posts", {
      type: "Post",
      args: {
        where: "PostWhereInput",
        orderBy: list("PostOrderByWithRelationInput"),
        take: "Int",
        skip: "Int",
      },
      resolve: async (_, args, { prisma, select }) => {
        // select automatically includes only requested fields
        return prisma.post.findMany({
          ...args,
          ...select,
        });
      },
    });
 
    t.field("post", {
      type: "Post",
      args: { where: nonNull("PostWhereUniqueInput") },
      resolve: async (_, { where }, { prisma, select }) => {
        return prisma.post.findUnique({
          where,
          ...select,
        });
      },
    });
  },
});
 
export const Mutation = mutationType({
  definition(t) {
    t.field("createPost", {
      type: "Post",
      args: { data: nonNull("PostCreateInput") },
      resolve: async (_, { data }, { prisma, select }) => {
        return prisma.post.create({
          data,
          ...select,
        });
      },
    });
  },
});

Performance Optimization

Query Optimization

typescript
// Before: Without field selection
const users = await prisma.user.findMany({
  include: {
    posts: {
      include: {
        comments: {
          include: {
            author: true,
          },
        },
      },
    },
  },
});
 
// After: With automatic field selection
const users = await prisma.user.findMany(select);
// Only includes fields actually requested in GraphQL query

Nested Relation Optimization

typescript
const User = objectType({
  name: "User",
  definition(t) {
    t.nonNull.int("id");
    t.nonNull.string("email");
 
    // Optimized relation resolution
    t.list.field("posts", {
      type: "Post",
      resolve: (parent, _, { prisma, select }) => {
        // select automatically handles nested field selection
        return prisma.user
          .findUnique({
            where: { id: parent.id },
          })
          .posts(select);
      },
    });
  },
});

Type Safety Features

Generated Types

The plugin automatically generates TypeScript types for better type safety:

typescript
// Auto-generated input types
UserCreateInput;
UserUpdateInput;
UserWhereInput;
UserWhereUniqueInput;
UserOrderByWithRelationInput;
 
// Auto-generated query types
FindFirstUserArgs;
FindManyUserArgs;
FindUniqueUserArgs;
AggregateUserArgs;
 
// Auto-generated mutation types
CreateOneUserArgs;
UpdateOneUserArgs;
DeleteOneUserArgs;
UpsertOneUserArgs;

Type-Safe Resolvers

typescript
import { User, Post } from "./generated/types";
 
const Query = queryType({
  definition(t) {
    t.field("user", {
      type: "User",
      args: { id: nonNull(intArg()) },
      resolve: async (_, { id }, { prisma, select }): Promise<User | null> => {
        return prisma.user.findUnique({
          where: { id },
          ...select,
        });
      },
    });
  },
});

Features

  • Automatic Field Selection: Optimizes database queries based on GraphQL field selection
  • Admin Schema Generation: Automatically generates admin queries and mutations
  • Built-in Scalars: Includes common Prisma scalar types for GraphQL
  • Type Safety: Full TypeScript support with generated types
  • Performance Optimization: Reduces database load through intelligent field selection
  • Multi-Schema Support: Works with multiple Prisma schemas
  • Flexible Configuration: Extensive customization options
  • Nexus Integration: Seamless integration with Nexus GraphQL framework
  • Production Ready: Battle-tested in production environments

Command Palette

Search for a command to run...