prisma-korea/graphql-schema-generator

Provide optional way to parse prisma data type

hyochan opened this issue · 3 comments

As of version 0.1.0, graphql-schema-generator is very opinionated.

In our parser, it'd be great to officially support the optional way to translate Prisma models.

For example, imagine there is a Prisma model User.

model User {
  id               String    @id @default(cuid()) @db.VarChar(50)
  email         String?   @unique @db.VarChar(255)
  password  String?   @db.VarChar(255)
  post           Post[]
}

In version 0.1.0, above translates to below.

type User {
  id: ID!
  email: String
  password: String
  post: [Post!]!
}

Here, we can think of 2 cases.

Case 1

A developer might not want to expose the field password in graphql schema. Therefore, we need to support the way to remove it.

Case 2

A developer might want the post array to translate to the optional array.

The solution

  • Provide the way to remove a field from translation in a specific model.
  • Provide an optional way to translate Prisma array. -> [!]!, []!, [!] or [].

Idea

Create our own parser file. (Maybe prismaParser.ts)

generator graphql {
   provider = "graphql-schema-generator"
   output   = "../src/schemas"
   parser = "prismaParser.ts"
}

prismaParser.ts might look like

const User = {
  data: {
      posts: 'Post[]',
      // more optional fields being added
      messages: '[Message]!',
  },
  hidden: ['password'],
  parse: {
    array: '[!]!',
  }
}

Actually, that's not a parser because it's not generating AST from source. It similar to config or rule.

And the word rule make think this :

type Rule = {
matcher: (field: DMMF.Field, model: DMMF.Model) => boolean;
transformer: (field: DMMF.Field, type: DMMF.Field['type']) => string;
};

So the solution that I'm thinking is custom rule that can provide a escape hatch to user.
It always applied last, so it overrides all existing changes.

Case 2 can be solved with this right now. And Case 1 can be solved when we modified code to omit field when matcher returns null(or other special value).

@hyochan
This examples might be helpful.

Case 1

beforeAddingTypeModifiers: [
{
matcher: (field) => {
const {name} = field;
if (name === 'password') {
return true;
}
return false;
},
transformer: () => {
throw null;
},
},

Case 2

afterAddingTypeModifiers: [
{
matcher: (field) => {
const {type} = field;
if (/\[(\w+)!]!/gm.exec(type)) {
return true;
}
return false;
},
transformer: (field) => {
const {type} = field;
const match = /\[(\w+)!]!/gm.exec(type);
if (!match) {
return field;
}
const [_, typeWithoutModifiers] = match;
return {...field, type: `[${typeWithoutModifiers}]`};
},
},
],
};