graphql-nexus / nexus-plugin-prisma

Deprecated
MIT License
828 stars 118 forks source link

Automated db/api enum mapping #784

Open jasonkuhrt opened 4 years ago

jasonkuhrt commented 4 years ago

Perceived Problem

Instances

Ideas / Proposed Solution(s)

Different ideas, no obvious winner yet

  1. Select + automatically project

    User would see all Prisma enums appear in type selection of an arg builder, like so:

    schema.extendType({
      type: 'Mutation',
      definition(t) {
        t.boolean('upsertCalendar', {
          args: {
            toto: schema.arg({ type: 'Foo' }), // <-- here, Foo is a prisma enum
          },
          resolve(_parent, args, ctx) {
            // ...
          },
        })
      },
    })

    The first time a prisma enum is selected, Nexus would auto-project it into the schema.

    Pros:

    • Very succinct write experience

    Cons:

    • What if user has many prisma enums and doesn't want to project any of them? Then their autocomplete is filled with noise.
    • What if the schema is large with enums at both the prisma and api layers, keeping track of which layer they come from could become impossible? Matters?
    • Read experience seems pretty bad here. Very easy to miss what's going on.
  2. Allow mapping enums like e.g. objects

    User would get a new way to work with enums in Nexus

    schema.enumType({
      name: 'Foo',
      case?: 'lower' | 'upper' | 'passthrough' // <-- type level config
      definition(t) {
        t.model.A({ case: 'lower' | 'upper' | 'passthrough' }) // <-- member level config
        t.model.B()
        t.model.C({ alias: 'Z' )
      }
    })

    Then use it (looks just like above):

    schema.extendType({
      type: 'Mutation',
      definition(t) {
        t.boolean('upsertCalendar', {
          args: {
            toto: schema.arg({ type: 'Foo' }),
          },
          resolve(_parent, args, ctx) {
            // ...
          },
        })
      },
    })

    Pros:

    • consistent api pattern with other nexus prisma mapping apis
    • alias makes it possible to automate reverse aliasing when sending graphql args to prisma client. For example above C -> Z thus:

      schema.extendType({
        type: 'Mutation',
        definition(t) {
          t.boolean('upsertCalendar', {
            args: {
              toto: schema.arg({ type: 'Foo' }),
            },
            resolve(_parent, args, ctx) {
              return ctx.db.qux.getMany({
                where: {
                  foo: mapBack.Foo(args.toto) // automate `Z -> C`
                }
              })
            },
          })
        },
      })

    Cons:

    • verbose
  3. A db projecting API

    schema.extendType({
      type: 'Mutation',
      definition(t) {
        t.boolean('upsertCalendar', {
          args: {
            // The following things are happening here:
            // db is a static import
            // db.Foo is a representation of the db layer enum called Foo
            // db.Foo() projects the db layer Foo onto the api
            // db.foo() returns a reference to the nexus graphql type
            toto: schema.arg({ type: db.Foo() }),
          },
          resolve(_parent, args, ctx) {
            // ...
          },
        })
      },
    })

    The above achieves the succinctness of idea (1). But It can also capture the flexibility of idea (2):

    db.Foo({
      $alias?: string // name
      $case?: 'lower' | 'upper' | 'passthrough'
      // whitelist/blacklist, automate projection
      $pick?: ('A' | 'B' | ...)[]
      $omit?: ('A' | 'B' | ...)[] // $omit: [] implies all
      // key names matching the enum member names
      // granular configuration
      A?: boolean | { alias?: string, case?: ... },
      B?: boolean | { alias?: string, case?: ... },
      C?: boolean | { alias?: 'Z', case?: ... }, // example for below
      // ...
    })
    schema.extendType({
      type: 'Mutation',
      definition(t) {
        t.boolean('upsertCalendar', {
          args: {
            toto: schema.arg({ type: 'Foo' }),
          },
          resolve(_parent, args, ctx) {
            return ctx.db.qux.getMany({
              where: {
                foo: db.Foo.$mapBack(args.toto) // automate `Z -> C`
              }
            })
          },
        })
      },
    })

    Could also easily be integrated into composites

    schema.enumType({
      name: 'Qux',
      members: [...db.Foo.$omit('C'), ...db.Bar.$pick('D'), 'R', db.ToTo.E]
    })

    pros:

    • powerful AND succinct
    • gradual API
    • provides a natural home for mapBack feature
    • intuitive (e.g. composite enums)

    cons:

    • new API pattern...
jasonkuhrt commented 4 years ago

I'm really liking the gist of idea 3 so far... @Weakky looking forward to your thoughts! @chrisdrackett @shoaibsharif too as original issue creators!