prisma-nexus-api
TypeScript icon, indicating that this package has built-in type declarations

0.0.24 • Public • Published

prisma-nexus-api

The intention of this project is the following:

  1. Single package with prisma generator and getNexusTypes helper fuction.
  2. Using prisma generator, a ApiConfig type is generated based on db schema. The generated types are to help with auto completion/configuration.
  3. The function getNexusTypes takes a options object that contains this ApiConfig.
  4. getNexusTypes returns all needed nexus types based on the ApiConfig (queries, mutations, inputs, outputs, and models.
  5. Users can use the nexus extendType to extend the types generated at runtime.

See the /packages/usage for an example


Prisma Generator Usage

To use the prisma generator, add it to your prisma schema file.

generator api {
  provider = "prisma-nexus-api"
}

You can then run

npx prisma generate

This will generate all typing needed for your api.

The generator can also have a schemaPath property configured. The default schema path is ./prisma/schema.prisma.

Plugin Usage

To use getNexusTypes, call it with your ApiConfig. Then pass these types to nexus.

Note you must use PrismaSelect from @paljs/plugins. This adds functionality to convert graphql info into a prisma select statement used by our operations.

import { makeSchema } from 'nexus'
import { getNexusTypes, ApiConfig } from 'prisma-nexus-api'
import { applyMiddleware } from 'graphql-middleware'
import { PrismaSelect } from '@paljs/plugins'
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

const apiConfig:ApiConfig = {
  prisma,
  data: {
    all: {}
  }
}

const getSchema = async () => {
  const types = await getNexusTypes({
    apiConfig
  })

  const schema = makeSchema({
    types,
    outputs: {
      schema: path.join(__dirname, './generated/nexus/schema.graphql'),
      typegen: path.join(__dirname, './generated/nexus/nexus.ts')
    }
  })

  const prismaSelect = async (resolve: any, root:any, args:any, ctx: any, info:any) => {
    ctx.select = new PrismaSelect(info).value
    return resolve(root, args, ctx, info)
  }

  return applyMiddleware(schema, prismaSelect)
}

Configruation

You must pass your prismaClient to ApiConfig. You can optionally pass a PubSubEngine for publishing subscription events. See graphql-subscriptions for more on the PubSubEngine.

A ApiConfig can specify a ModelConfiguration for each model in your db. The prisma generator will generate typescript typing to help with auto completion of your apiConfiguration object. There is also a all ModelConfiguration which will be applied to all models.

const apiConfig:ApiConfig = {
    prisma: PrismaClient,
    pubsub?: PubSubEngine,
    data: {
        all: ModelConfiguration,
        User: ModelConfiguration
        Car: ModelConfiguration,
        ...etc
    }
}

A ModelConfiguration can then have configuration for each supported operation type, create, read, update, upsert, delete.

export type ModelConfiguration = {
    removeFromSchema?: boolean
    removedFields?: string[]
    removeNestedCreate?: boolean
    removeNestedUpdate?: boolean
    disableAllOperations?: boolean
    create?: ModelCreateConfiguration
    read?: ModelReadConfiguration
    update?: ModelUpdateConfiguration
    upsert?: ModelUpsertConfiguration
    delete?: ModelDeleteConfiguration
}

export type ModelCreateConfiguration = {
    disableAll?: boolean
    removedFields?: string[]
    createOneOverride?: OperationOverride
}

export type ModelReadConfiguration = {
    disableAll?: boolean
    disableAggregate?: boolean
    disableFindCount?: boolean
    disableFindFirst?: boolean
    disableFindMany?: boolean
    disableFindUnique?: boolean
    removedFields?: string[]
    aggregateOverride?: OperationOverride
    findCountOverride?: OperationOverride
    findFirstOverride?: OperationOverride
    findManyOverride?: OperationOverride
    findUniqueOverride?: OperationOverride
}

export type ModelUpdateConfiguration = {
    disableAll?: boolean
    disableUpdateOne?: boolean
    disableUpdateMany?: boolean
    removedFields?: string[]
    updateOneOverride?: OperationOverride
    updateManyOverride?: OperationOverride
}

export type ModelUpsertConfiguration = {
    disableAll?: boolean
    upsertOneOverride?: OperationOverride
}

export type ModelDeleteConfiguration = {
    disableAll?: boolean
    disableDeleteOne?: boolean
    disableDeleteMany?: boolean
    deleteOneOverride?: OperationOverride
    deleteManyOverride?: OperationOverride
}

Removing fields from the create configuration removes fields from the exposed create input.
Removing fields from the update configuration removes fields from the exposed update input.
Removing fields from the read configuration removes the fields from the graphql outputs.

You can disable any specific operation, disableAggregate, disableFindCount etc

Using disableAll will disable all operations in that grouping. Ex. ModelDeleteConfiguration disableAll will disable both disableDeleteOne and deleteManyOverride. Using the top level disableAllOperations will disable all operations for that model.

removeFromSchema will remove the model from the schema and disableAllOperations. You can disableAllOperations but not remove the schema to allow you to use the model, input, and output types for your own custom operations.

Override

The override functions are a hook you can use for the incoming operation. These allow you to add custom validation, permission checks, or custom logic. If you don't need your validation/authorized checks in this level of scope I suggest using graphql-shield

Note the prisma function will no longer be called by default when using the override hook.


Your override function will recieve the following parameter.

export type OperationOverrideOptions<ParamsType, ContextType> = {
    // car, user, etc
    modelName:string
    // create, update, etc
    prismaOperation:string
    prismaParams: ParamsType
    ctx: ContextType
    apiConfig: ApiConfig
}

The modelName and prismaOperation are passed to allow you to use generic handlers for multiple operations. Ex.

export const operationFilterForOrganizationOverride = (options:OperationOverrideOptions<any, any>) => {
  const {
    modelName,
    prismaOperation,
    prismaParams,
    ctx
  } = options
  const session = ctx.session

  options.prismaParams.where = {
    ...options.prismaParams.where,
    organizationId: session.organizationId
  }

  return (ctx.prisma as any)[modelName][prismaOperation](options.prismaParams)
}

Helper Functions

There are a few helper functions exposed. createAndNotify, updateAndNotify, updateManyAndNotify, etc. These will run their mutations and also publish the event to the subscription pubsub if one is configured. These functions take the override params with an additional option to configure your event.

export interface CreateAndNotifyOptions extends OperationOverrideOptions {
  createEvent?: string
}

By default these events are modelName_CREATED, car_CREATED, user_UPDATED, book_DELETED etc.

Package Sidebar

Install

npm i prisma-nexus-api

Weekly Downloads

27

Version

0.0.24

License

MIT

Unpacked Size

200 kB

Total Files

131

Last publish

Collaborators

  • mitchell.monaghan