Skip to content

Idea: input types with built-in transform #1290

@macrozone

Description

@macrozone

Sometimes you want to create multiple queries and mutations that all use the same input object and want to pass that to your orm, like prisma:

builder.queryField("project", (t) =>
  t.prismaField({
    type: "Project",
    args: {
      where: t.arg({
        required: true,
        type: ProjectWhereUniqeInputRef,
      }),
    },
    nullable: true,
    resolve: async (query, parent, { where }) => {
      return prisma.organisation.findUnique({
        ...query,
        where
      });
    },
  }),
);

builder.mutationField("updateProject", (t) =>
  t.prismaField({
    type: "Project",
    args: {
      data: ....
      where: t.arg({
        type: ProjectWhereUniqeInputRef,
        required: true,
      }),
    },
    resolve: async (query, parent, { data, where }) => {
      return prisma.project.update({
        data,
        where,
        ...query,
      });
    },
  }),
);

however, this requires that the backing type of ProjectWhereUniqeInputRef has the same shape as prisma requires as were.

If its not the same, you have to transform it, but you have to do that on each query:

builder.queryField("project", (t) =>
  t.prismaField({
    type: "Project",
    args: {
      where: t.arg({
        required: true,
        type: ProjectWhereUniqeInputRef,
      }),
    },
    nullable: true,
    resolve: async (query, parent, { where }) => {
      return prisma.organisation.findUnique({
        ...query,
        where: await transformProjectWhere(where) // transform
      });
    },
  }),
);

builder.mutationField("updateProject", (t) =>
  t.prismaField({
    type: "Project",
    args: {
      data: ....
      where: t.arg({
        type: ProjectWhereUniqeInputRef,
        required: true,
      }),
    },
    resolve: async (query, parent, { data, where }) => {
      return prisma.project.update({
        data,
        where: await transformProjectWhere(where),
        ...query,
      });
    },
  }),
);

this is of course doable, but can be a bit awkward.

An elegant solution would be that ProjectWhereUniqeInputRef would already do this transformation:

type InputShape = {
  tenantId: string,
  slug: string
}

type TransformedInputShape = {
   tenantId_slug: { // bit a trivial example, but e.g. to "hide" those combined id keys
     tenantId: string,
     slug: string
   }
}
export const ProjectWhereUniqueInputRef = builder
  .inputRef<InputShape, TransformedInputShape>("ProjectWhereUniqueInput")
  .implement({
    fields: (t) => ({
      tenantId: t.string({
        required: true,
      }),
      slug: t.string({
        required: true,

      }),
    }),
transform: (input /* has InputShape */) => { // can be async function as well
     return { // has to return TransformedInputShape
         tenantId_slug: {slug: input.slug, tenantId: input.tenantId}
      }
   }
  });

so wherever you use ProjectWhereUniqueInputRef in an argument, the object has the TransformedInputShape in the resolver instead of InputShape

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions