Generating SDL for model fails because the mode's type doesn't exist

Sooo, getting some wierd behaviour on a new project. Has anyone seen this before or know how to fix it?

I downgraded from 6.6.0 to 6.5.1 but still no luck. :frowning:
Sounds similar to Generating web types is broken after adding custom SDL and Service - Get Help and Help Others - RedwoodJS Community but not really sure what to make of it?

Hi @littletuna4 this is known and somewhat expected behavior. It’s a warning that one of your models referred to a relationship but it wasn’t yet my generated. It’s chicken vs egg thing.

See Prisma Relations and Redwood's Generators | RedwoodJS Docs

You just need to generate each of them individually so that eventually all types will exist.

1 Like

Thanks for the response @dthyresson. Sorry I don’t think I’ve explained the behaviour - this is persisting despite having generated fresh sdl for all of my models?

Another point of note is that I can’t generate sdl for user credentials model from the webauthn? Not that I particularly need sdl for it, but its different from my previous project, and I’m not sure why it won’t work?

devlinux@tonysmachine:~/devlinux/monsteraa$ yarn rw g sdl usercredential  -f --no-tests
✖ Error applying template at /home/devlinux/devlinux/monsteraa/node_modules/@redwoodjs/cli/dist/commands/generate/service/templates/test.ts.template for
  userCredential: Cannot read properties of undefined (reading 'replace')
◌ Generating types ...
Error applying template at /home/devlinux/devlinux/monsteraa/node_modules/@redwoodjs/cli/dist/commands/generate/service/templates/test.ts.template for userCredential: Cannot read properties of undefined (reading 'replace')

On a side note, this leads me to another question. Anyone made a script for generating all of the models’ sdl in one hit?

Update:
Made a script for generating all of the sdl in one swoop - error continues to crash api.

// Append api/* to import from api and web/* to import from web
import { db } from 'api/src/lib/db'

export default async ({ args }) => {
  const fs = require('fs')
  const path = require('path')
  const { execSync } = require('child_process')

  function listPrismaModels() {
    const schemaPath = path.join(__dirname, '../api/db/schema.prisma')
    const schemaContent = fs.readFileSync(schemaPath, 'utf8')

    const modelRegex = /^model\s+(\w+)/gm
    let match
    const models = []

    while ((match = modelRegex.exec(schemaContent)) !== null) {
      models.push(match[1])
    }

    return models
  }

  function generateSdlsForModels() {
    const models = listPrismaModels()
    models.forEach((model) => {
      try {
        console.log(`Generating SDL for model: ${model}`)
        execSync(`yarn rw g sdl ${model} --force`, { stdio: 'inherit' })
      } catch (e) {
        console.log(`Failed to generate SDL for model: ${model}`)
      }
    })
  }

  generateSdlsForModels()
}```

I tried on a fresh project and either something in this schema or my environment is breaking something.

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider      = "prisma-client-js"
  binaryTargets = "native"
}

// Define your own datamodels here and run `yarn redwood prisma migrate dev`
// to create migrations for them and apply to your dev DB.
// TODO: Please remove the following example:
model user {
  id                     Int              @id @default(autoincrement())
  email                  String           @unique
  name                   String?
  hashedPassword         String?
  salt                   String?
  googleId               String?
  resetToken             String?
  resetTokenExpiresAt    DateTime?
  webAuthnChallenge      String?
  emailVerified          Boolean          @default(false)
  emailVerificationToken String?
  accountCreatedAt       DateTime         @default(now())
  preferredCurrency      String           @default("USD")
  clubMemberships        clubMembership[]
  wallets                wallet[]
  session                session[]
  credentials            userCredential[]
}

model userCredential {
  id         String  @id
  userId     Int
  user       user    @relation(fields: [userId], references: [id])
  publicKey  Bytes
  transports String?
  counter    BigInt
}

model session {
  id        String         @id @default(uuid())
  createdAt DateTime       @default(now())
  expiresAt DateTime
  handle    String         @unique
  user      user?          @relation(fields: [userId], references: [id])
  userId    Int?
  privacy   sessionPrivacy @default(PUBLIC)
}

enum sessionPrivacy {
  PUBLIC
  FRIENDSONLY
  SECRET
}

model transaction {
  id          Int      @id @default(autoincrement())
  amount      Float
  description String
  createdAt   DateTime @default(now())
  wallet      wallet   @relation(fields: [walletId], references: [id])
  walletId    Int
}

model wallet {
  id             Int           @id @default(autoincrement())
  user           user          @relation(fields: [userId], references: [id])
  userId         Int
  transactions   transaction[]
  club           club?         @relation(fields: [clubId], references: [id])
  organisation   organization? @relation(fields: [organizationId], references: [id])
  organizationId Int?
  clubId         Int?
  currency       String?
}

model club {
  id             Int              @id @default(autoincrement())
  name           String
  description    String
  memberships    clubMembership[]
  organization   organization?    @relation(fields: [organizationId], references: [id])
  organizationId Int?
  privacy        clubPrivacy      @default(PUBLIC)
  wallets        wallet[]
}

enum clubPrivacy {
  PUBLIC // anyone can join
  INVITEONLYVISIBLE // only if an admin has invited you, but anyone can see the club
  INVITEONLYSECRET // only if an admin has invited you, and only members can see the club
  ONREFERRAL // only if a member has referred you
  ONREQUEST // the public may request to join
}

model clubMembership {
  id     Int                  @id @default(autoincrement())
  user   user                 @relation(fields: [userId], references: [id])
  club   club                 @relation(fields: [clubId], references: [id])
  roles  clubMembershipRole[] @default([MEMBER])
  userId Int
  clubId Int
}

enum clubMembershipRole {
  OWNER
  BILLINGADMIN
  EVENTADMIN
  MEMBER
}

model event {
  id            Int          @id @default(autoincrement())
  location      String
  series        eventSeries? @relation(fields: [eventSeriesId], references: [id])
  eventSeriesId Int?
  room          room?        @relation(fields: [roomId], references: [id])
  roomId        Int?
  eventType     eventType    @default(CASH)
}

enum eventType {
  CASH
  TOURNAMENT
  OTHER
}

model organization {
  id     Int      @id @default(autoincrement())
  clubs  club[]
  wallet wallet[]
}

model eventSeries {
  id     Int     @id @default(autoincrement())
  events event[]
}

model room {
  id       Int     @id @default(autoincrement())
  location String
  events   event[]
  status   String  @default("OPEN")
}

Hi, could you elaborate on “what isn’t working or breaking”?

Just an FYI, the sdl generators do the following:

  • look at model and generate types per model
  • it does not look at the model and then determine any relations and then run those; any related models must be run independently
  • it also generates CRUD operations and services if desired (ie, --crud option)
  • after each individual model is processed, it then generates types
  • so if the model refers to a type it doesn’t yet know about since it didn’t yet generate it, the type will throw a warning that it doesn’t know about the type

If you see Error: Unknown type: ... , don’t panic! It’s a known limitation with GraphQL type generation. It happens when you generate the SDL of a Prisma model that has relations before the SDL for the related model exists . Please see Troubleshooting Generators for help.

And see: Prisma Relations and Redwood's Generators | RedwoodJS Docs

What this means is that the generator generated the templates fine – but graphql type generation aka codegen on the api side throws warnings.

Yes, you can process all models, and we talked about that with a force option and an option to skip typegen until the last model so that the typegen knows about all models.

I’ll copy your schema and give it a go.

BTW it is common code convention (but should not be required) to PascalCase models: User, SessionPrivacy, etc.

See: Custom model and field names

If you want your tables to be lowercase or so, you can use map as in: Custom model and field names

model User {
  id                     Int  
@@map("users")

@littletuna4 I was able to generate the schema that you posted:

                  List of relations
 Schema |         Name          |   Type   |  Owner
--------+-----------------------+----------+----------
 public | _prisma_migrations    | table    | postgres
 public | club                  | table    | postgres
 public | clubMembership        | table    | postgres
 public | clubMembership_id_seq | sequence | postgres
 public | club_id_seq           | sequence | postgres
 public | event                 | table    | postgres
 public | eventSeries           | table    | postgres
 public | eventSeries_id_seq    | sequence | postgres
 public | event_id_seq          | sequence | postgres
 public | organization          | table    | postgres
 public | organization_id_seq   | sequence | postgres
 public | room                  | table    | postgres
 public | room_id_seq           | sequence | postgres
 public | session               | table    | postgres
 public | transaction           | table    | postgres
 public | transaction_id_seq    | sequence | postgres
 public | user                  | table    | postgres
 public | userCredential        | table    | postgres
 public | user_id_seq           | sequence | postgres
 public | wallet                | table    | postgres
 public | wallet_id_seq         | sequence | postgres
(21 rows)

for:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider      = "prisma-client-js"
  binaryTargets = "native"
}

// Define your own datamodels here and run `yarn redwood prisma migrate dev`
// to create migrations for them and apply to your dev DB.
// TODO: Please remove the following example:
model user {
  id                     Int              @id @default(autoincrement())
  email                  String           @unique
  name                   String?
  hashedPassword         String?
  salt                   String?
  googleId               String?
  resetToken             String?
  resetTokenExpiresAt    DateTime?
  webAuthnChallenge      String?
  emailVerified          Boolean          @default(false)
  emailVerificationToken String?
  accountCreatedAt       DateTime         @default(now())
  preferredCurrency      String           @default("USD")
  clubMemberships        clubMembership[]
  wallets                wallet[]
  session                session[]
  credentials            userCredential[]
}

model userCredential {
  id         String  @id
  userId     Int
  user       user    @relation(fields: [userId], references: [id])
  publicKey  Bytes
  transports String?
  counter    BigInt
}

model session {
  id        String         @id @default(uuid())
  createdAt DateTime       @default(now())
  expiresAt DateTime
  handle    String         @unique
  user      user?          @relation(fields: [userId], references: [id])
  userId    Int?
  privacy   sessionPrivacy @default(PUBLIC)
}

enum sessionPrivacy {
  PUBLIC
  FRIENDSONLY
  SECRET
}

model transaction {
  id          Int      @id @default(autoincrement())
  amount      Float
  description String
  createdAt   DateTime @default(now())
  wallet      wallet   @relation(fields: [walletId], references: [id])
  walletId    Int
}

model wallet {
  id             Int           @id @default(autoincrement())
  user           user          @relation(fields: [userId], references: [id])
  userId         Int
  transactions   transaction[]
  club           club?         @relation(fields: [clubId], references: [id])
  organisation   organization? @relation(fields: [organizationId], references: [id])
  organizationId Int?
  clubId         Int?
  currency       String?
}

model club {
  id             Int              @id @default(autoincrement())
  name           String
  description    String
  memberships    clubMembership[]
  organization   organization?    @relation(fields: [organizationId], references: [id])
  organizationId Int?
  privacy        clubPrivacy      @default(PUBLIC)
  wallets        wallet[]
}

enum clubPrivacy {
  PUBLIC // anyone can join
  INVITEONLYVISIBLE // only if an admin has invited you, but anyone can see the club
  INVITEONLYSECRET // only if an admin has invited you, and only members can see the club
  ONREFERRAL // only if a member has referred you
  ONREQUEST // the public may request to join
}

model clubMembership {
  id     Int                  @id @default(autoincrement())
  user   user                 @relation(fields: [userId], references: [id])
  club   club                 @relation(fields: [clubId], references: [id])
  roles  clubMembershipRole[] @default([MEMBER])
  userId Int
  clubId Int
}

enum clubMembershipRole {
  OWNER
  BILLINGADMIN
  EVENTADMIN
  MEMBER
}

model event {
  id            Int          @id @default(autoincrement())
  location      String
  series        eventSeries? @relation(fields: [eventSeriesId], references: [id])
  eventSeriesId Int?
  room          room?        @relation(fields: [roomId], references: [id])
  roomId        Int?
  eventType     eventType    @default(CASH)
}

enum eventType {
  CASH
  TOURNAMENT
  OTHER
}

model organization {
  id     Int      @id @default(autoincrement())
  clubs  club[]
  wallet wallet[]
}

model eventSeries {
  id     Int     @id @default(autoincrement())
  events event[]
}

model room {
  id       Int     @id @default(autoincrement())
  location String
  events   event[]
  status   String  @default("OPEN")
}

and yarn rw prisma migrate dev

Could you share the errors?

Note:

? Cannot determine the plural of “eventSeries”.
To continue, the generator requires a unique plural form: â€ș eventSerieses

You do have to provide a plural and singular for the generators. This happens with things like fish, data, series, etc.

Perhaps your multi-script is running into this?

I think are see some issues with generating relations due to the casing of the types:

export const schema = gql`
  type Club {
    id: Int!
    name: String!
    description: String!
    memberships: [clubMembership]!
    organization: organization
    organizationId: Int
    privacy: clubPrivacy!
    wallets: [wallet]!
  }

Error: Unknown type: “clubMembership”.

Because the type is ClubMembership.

export const schema = gql`
  type ClubMembership {
    id: Int!
    user: user!
    club: club!
    roles: [clubMembershipRole]!
    userId: Int!
    clubId: Int!
  }

  enum clubMembershipRole {
    OWNER
    BILLINGADMIN
    EVENTADMIN
    MEMBER
  }

  type Query {
    clubMemberships: [ClubMembership!]! @requireAuth
    clubMembership(id: Int!): ClubMembership @requireAuth
  }

I suggest using uppercase model names and trying again.

Also, take care with names like “transaction” as these can be reserved words in Postgres.

I know sdl generation with lots of relations is cumbersome due to the warnings and I’ve mentioned this just recently to the team.

I’m going to think up a better way.

1 Like

Ok thanks once again for your time.
Error on the api is:

api |   return new _GraphQLError.GraphQLError(`Syntax Error: ${description}`, {
api |          ^
api | 
api | GraphQLError: Syntax Error: Expected Name, found "}".
api |     at syntaxError (/home/devlinux/devlinux/monsteraa/node_modules/graphql/error/syntaxError.js:15:10)
api |     at Parser.expectToken (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:1397:40)
api |     at Parser.parseName (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:108:24)
api |     at Parser.parseInputValueDef (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:869:23)
api |     at Parser.optionalMany (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:1492:28)
api |     at Parser.parseInputFieldsDefinition (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:1038:17)
api |     at Parser.parseInputObjectTypeDefinition (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:1022:25)
api |     at Parser.parseDefinition (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:184:23)
api |     at Parser.many (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:1511:26)
api |     at Parser.parseDocument (/home/devlinux/devlinux/monsteraa/node_modules/graphql/language/parser.js:122:25) {
api |   path: undefined,
api |   locations: [ { line: 20, column: 3 } ],
api |   extensions: [Object: null prototype] {}
api | }
api | 
api | Node.js v20.10.0

I think we’re getting closer to working it out because the schema.graphql file doesn’t get populated on rw dev or rw build for some reason?

Dev OS is Ubuntu (Via wsl) so, admittedly clutching at straws, I tried setting all permissions to 777 in the project folder - not a permission issue.

As per your advice, model names have been changed to CamelCase. Error persists.

Maybe what I should be asking is how to rerun the schema.graphql generation - nothing in the docs
and yarn rw-gen doesn’t do it?

Hmmm this is even wierder again, I can’t generate sdl for UserCredential (not new error):

devlinux@tonysmachine:~/devlinux/monsteraa$ yarn rw g sdl UserCredential  
← Reverted because: Error applying template at
  /home/devlinux/devlinux/monsteraa/node_modules/@redwoodjs/cli/dist/commands/generate/service/templates/test.ts.template for
  UserCredential: Cannot read properties of undefined (reading 'replace')
◌ Generating types ...
Error applying template at /home/devlinux/devlinux/monsteraa/node_modules/@redwoodjs/cli/dist/commands/generate/service/templates/test.ts.template for UserCredential: Cannot read properties of undefined (reading 'replace')
devlinux@tonysmachine:~/devlinux/monsteraa$ 

Thought it might be because of the Bytes or string id or something?
If’s it not to do with my specific problem, there might be issue in this because thats straight from dbAuth setup.

@littletuna4 Could you share your project on GitHub so we can clone and try to reproduce locally?

A repo link and clear step-by-step instructions on how to reproduce would help a lot here. Thanks :pray:

It’s yarn rw g types. This creates the GraphQL Schema file and then types for both api and web sides.

See: Command Line Interface | RedwoodJS Docs

As Tobbe noted, I repo that we can spin up in GitPod will help us diagnose.

Here’s a repo and recording of the setup of another one getting the same error.

littletuna4/temp-new-rw-project (github.com)

recording

Thanks for helping me!

For completeness:
Error on yarn rw g types - noting that SDL has been generated for all models (including Organization):


  It looks like you have a Organization model in your Prisma schema.
  If it's part of a relation, you may have to generate SDL or scaffolding for Organization too.
  So, if you haven't done that yet, ignore this error message and run the SDL or scaffold generator for Organization now.

  See the Troubleshooting Generators section in our docs for more help.

Error: Unknown type: "Organization".
    at getNamedType (/home/devlinux/devlinux/test/node_modules/graphql/utilities/extendSchema.js:427:13)
    at getWrappedType (/home/devlinux/devlinux/test/node_modules/graphql/utilities/extendSchema.js:442:12)
    at buildFieldMap (/home/devlinux/devlinux/test/node_modules/graphql/utilities/extendSchema.js:483:17)
    at fields (/home/devlinux/devlinux/test/node_modules/graphql/utilities/extendSchema.js:672:25)
    at resolveObjMapThunk (/home/devlinux/devlinux/test/node_modules/graphql/type/definition.js:504:40)
    at defineFieldMap (/home/devlinux/devlinux/test/node_modules/graphql/type/definition.js:766:20)
    at GraphQLObjectType._fields (/home/devlinux/devlinux/test/node_modules/graphql/type/definition.js:691:26)
    at GraphQLObjectType.getFields (/home/devlinux/devlinux/test/node_modules/graphql/type/definition.js:710:27)
    at collectReferencedTypes (/home/devlinux/devlinux/test/node_modules/graphql/type/schema.js:387:51)
    at new GraphQLSchema (/home/devlinux/devlinux/test/node_modules/graphql/type/schema.js:174:9)

(node:5691) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)```

Ok, I now see what is happening after you shared your repo.

Organization

The SDL generated for the Organization type is invalid:

export const schema = gql`
  
  type Organization {
    id: Int!
    clubs: [Club]!
    wallet: [Wallet]!
    members: [User]!
  }


  
  type Query {
    organizations: [Organization!]! @requireAuth
    organization(id: Int!): Organization @requireAuth
  }

  
  input CreateOrganizationInput {
    
  }

  
  input UpdateOrganizationInput {
    
  }

  
  type Mutation {
    createOrganization(input: CreateOrganizationInput!): Organization! @requireAuth
    updateOrganization(id: Int!, input: UpdateOrganizationInput!): Organization! @requireAuth
    deleteOrganization(id: Int!): Organization! @requireAuth
  }
`

Note that the

  
  input CreateOrganizationInput {
    
  }

  
  input UpdateOrganizationInput {
    
  }

input ties have no fields because your model:

model Organization {
  id      Int      @id @default(autoincrement())
  clubs   Club[]
  wallet  Wallet[]
  members User[]
}

only has relations, no editable fields.

So, really there are no mutations possible – well easy ones to generate. And because the SDL is invalid then code/type gen fails.

You can create or update, but you’ll have to write those manually if you want to create the Organization and populate the nested wallets, clubs, etc.

UserCredential

The service test generation fails using Bytes for the pubicKey field.

See GraphQL and Scaffolding does not support Prisma Bytes data type · Issue #5841 · redwoodjs/redwood · GitHub

That said:

@rob notes:

You shouldn’t create a scaffold for UserCredential either, you can’t really manually create records for that table, so there’s no need to have a UI for it.

But you’re saying that once you create the UserCredential model and migrate the database, that you can’t create a scaffold for User and it blows up?

That error sounds like the API server stopped running, can you check back further in the console output and see what the error was that crashed it?

See: GraphQL and Scaffolding does not support Prisma Bytes data type · Issue #5841 · redwoodjs/redwood · GitHub

And there are docs Adds docs about removing any UserCredential relationship manually from generated User SDL by cannikin · Pull Request #6672 · redwoodjs/redwood · GitHub and here: Self-hosted Authentication (dbAuth) | RedwoodJS Docs

Thus the recommendation is not to generate that UserCrednetial sdl and remove any use of in the in User SDl type.

@littletuna4 I added a PR to your example repo where type and code gen now works: Updated Schema and Services and SDL to get GraphQL Type and Codegen working by dthyresson · Pull Request #1 · littletuna4/temp-new-rw-project · GitHub

Can see comments for issues:

  1. If no editable fields in a Model, then don’t try to create input types of mutations
  2. Better support of the Bytes field in tests as the sdl service test file for UserCredential that used bytes failed.

Note @littletuna4 one workaround for the Organization generator would be to give the Organization some editable field, like a name:

model Organization {
  id      Int      @id @default(autoincrement())
  name    String
  clubs   Club[]
  wallet  Wallet[]
  members User[]
}

You can then generate:

temp-new-rw-project dt-schema-sdl-updates % yarn rw g sdl organization --force --crud                                     
✔ Generating SDL files...
  ✔ Successfully wrote file `./api/src/graphql/organizations.sdl.ts`
  ✔ Successfully wrote file `./api/src/services/organizations/organizations.scenarios.ts`
  ✔ Successfully wrote file `./api/src/services/organizations/organizations.test.ts`
  ✔ Successfully wrote file `./api/src/services/organizations/organizations.ts`
✔ Generating types ...

You’re my hero!!
Thanks guys!

1 Like