prisma-nexus-api
v0.0.26
Published
Tool for creating api's easier using prisma and nexus
Downloads
250
Maintainers
Readme
prisma-nexus-api
The intention of this project is the following:
- Single package with prisma generator and
getNexusTypes
helper fuction. - Using prisma generator, a
ApiConfig
type is generated based on db schema. The generated types are to help with auto completion/configuration. - The function
getNexusTypes
takes a options object that contains thisApiConfig
. getNexusTypes
returns all needed nexus types based on theApiConfig
(queries
,mutations
,inputs
,outputs
, andmodels
.- 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.