How to Make SelectFields for Prisma enums in RedwoodJS

How to Make SelectFields for Prisma enums in RedwoodJS

:point_right: Demo: https://rw-office-hours-enum-select-list.netlify.app
:point_right: GH Repo: [redwood-office-hours/2022-10-05-enum-select-options at main · redwoodjs/redwood-office-hours · GitHub](redwood-office-hours/2022-10-05-enum-select-options at main · redwoodjs/redwood-office-hours · GitHub}

The Problem

  • Cannot get values from the enum to make a select control because Redwood generates the enum as a type -- and that means you cannot iterate and build options for each entry
  • Web cannot access Prisma client types because the web side doesn't connect to the database
  • Enum values don't have friendly, readable labels, e.g: "EMPIRE_STRIKES_BACK" vs "empire Strikes Back"

Current Scaffolding

Scaffolding will render enums in forms with Radio Options or Checkboxes.

  • But, scaffolding won't pickup new enums .... you would have to re-scaffold and force update the web components.
  • Plus, select fields take up less space in a form.

Code

Let’s make a way to return enum values and labels in a friendly way!

Prisma Schema

enum Episode {
  NEW_HOPE
  EMPIRE_STRIKES_BACK
  RETURN_OF_THE_JEDI
  ROGUE_ONE
}

model Character {
  id        Int       @id @default(autoincrement())
  name      String    @unique
  appearsIn Episode[]
}

GraphQL Schema

Need Postgres for enums …

// 2022-10-05-enum-select-options/api/src/graphql/options.sdl.ts

export const schema = gql`
  interface OptionItem {
    label: String!
  }

  type EpisodeOption implements OptionItem {
    value: Episode!
    label: String!
  }

  type Query {
    episodeOptions: [EpisodeOption!]! @skipAuth
  }
`

Services

// 2022-10-05-enum-select-options/api/src/services/episodeOptions/episodeOptions.ts

import { Episode } from '@prisma/client'
import type { QueryResolvers, EpisodeOption } from 'types/graphql'

import { label } from 'src/lib/helpers'

const getEnumValues = (enumType: Record<string, string>) => {
  return Object.values(enumType).map((value) => {
    return {
      value,
      label: label(value),
    }
  })
}

export const episodeOptions: QueryResolvers['episodeOptions'] = () => {
  return getEnumValues(Episode) as EpisodeOption[]
}

Other Approaches

  • Scripts to construct enums by parsing the Prisma Schema/DMMF

Building a member model with a gender enum. Redwood won’t logs can’t find my input for the mutation.


From the play ground I can save entries with gender: male not as a string

  const [createMember, { loading, error }] = useMutation(
    CREATE_MEMBER_MUTATION,
    {
      onCompleted: () => {
        toast.success('Member created')
        navigate(routes.members())
      },
      onError: (error) => {
        toast.error(error.message)
      },
    }
  )

  const onSave = (input: CreateMemberInput) => {
    const member = {
      id: nanoid(),
      ...input,
    }
    console.log(member)
    createMember({ variables: { ...member } })
  }

  return (
    <div className="rw-segment">
      <header className="rw-segment-header">
        <h2 className="rw-heading rw-heading-secondary">New Member</h2>
      </header>
      <div className="rw-segment-main">
        <MemberForm onSave={onSave} loading={loading} error={error} />
      </div>
    </div>
  )

Had to change my input object to this on the onSave function

    const member = {
      input: {
        id: nanoid(),
        ...data,
      },
    }
    console.log(member)
    createMember({ variables: { ...member } })
  }

Following an earlier post