Yarn rw g sdl <model> --crud

Sorry if everyone already knew this!

If you’ve used yarn rw g sdl before, you might think that things look a a little empty. In the sdl, there’s no mutations, and the services file only exports the findMany query:

// ./api/src/graphql/users.sdl.js

export const schema = gql`
  type User {
    id: Int!
    email: String!
    name: String
  }

  type Query {
    users: [User!]!
  }

  input CreateUserInput {
    email: String!
    name: String
  }

  input UpdateUserInput {
    email: String
    name: String
  }
`
// ./api/src/services/users/users.js

import { db } from 'src/lib/db'

export const users = () => {
  return db.user.findMany()
}

Turns out you can get mutations + resolvers if you add a flag:

yarn rw g sdl <model> --crud
// ./api/src/graphql/users.sdl.js

export const schema = gql`
  type User {
    id: Int!
    email: String!
    name: String
  }

  type Query {
    users: [User!]!
    user(id: Int!): User!
  }

  input CreateUserInput {
    email: String!
    name: String
  }

  input UpdateUserInput {
    email: String
    name: String
  }

  type Mutation {
    createUser(input: CreateUserInput!): User!
    updateUser(id: Int!, input: UpdateUserInput!): User!
    deleteUser(id: Int!): User!
  }
`
// ./api/src/services/users/users.js

import { db } from 'src/lib/db'

export const users = () => {
  return db.user.findMany()
}

export const user = ({ id }) => {
  return db.user.findOne({
    where: { id },
  })
}

export const createUser = ({ input }) => {
  return db.user.create({
    data: input,
  })
}

export const updateUser = ({ id, input }) => {
  return db.user.update({
    data: input,
    where: { id },
  })
}

export const deleteUser = ({ id }) => {
  return db.user.delete({
    where: { id },
  })
}

This was hard to see because --help was broken for nested commands like db and generate, but that should be fixed soon.

4 Likes

I’m wondering why we don’t generate the crud stuff automatically, do you know @thedavid?

Just saw this – and, no, I don’t and wasn’t apart of the early decisions. Adding to the questions, is there a difference between the --services option and the --crud option? I ran a quick test and the generated files seem to be the same. In this case, --services makes more sense to me as --crud makes me also think components… but that might not be universal. Help text could make it all better either way.

I do think generators that are specific to one thing, e.g. component, service, etc, should default to just that one thing. Perhaps someone is using it just to get the boilerplate and will handle the rest manually? But, there are definitely ways to improve this tooling through better docs and CLI help (thanks, @dom!!) and other things like helpful messages at the end of tasks being run. In this case, if someone runs yarn rw g sdl post, the command could end with the ouput Done. \n Did you know you can also generate the corresponding Service CRUD files with the option --services.

Little details and just thinking out loud as I go. But all of these are the types of things people will :heart:long term.

1 Like

If you purely just want the shell of a service we let you create that and it’s up to you to make sure it ties into a model properly. So, it’s generated with the minimum amount of code needed to get that service to exist—the imports and single “getter” to get you started. It’s just creating the boilerplate for you.

Having an SDL without a service wouldn’t work at all, so generating the SDL also generates the service to go with it.

If you generate a scaffold, the whole point is have the CRUD screens, so we need to generate CRUD actions in the sdl and service to get those to work.

So the idea was that by default a generator creates the minimum amount of code to get you started, then we added functionality like --crud because other generators down the line needed that. But users are free to get the additional functionality of --crud if needed.

My philosophy is that beginners will be creating scaffolds so we create all kinds of code for you. Experienced users will probably just use generators to create the boilerplate stuff, and the less code you include in there means less code you need to rip out later because it doesn’t match what you’re trying to do. So the default usage of the more “advanced” generators (raw SDL and services) are more catered to those experienced users since they’ll be the ones using them.

3 Likes

Makes all the sense, RC.

Question about the following code from sdl.js, are we actually using the services option even though it’s default set to ‘true’? Wondering if this is a remnant from refactoring to Yargs:

export const command = 'sdl <model>'
export const desc = 'Generate a GraphQL schema and service object.'
export const builder = {
  services: { type: 'boolean', default: true },
  crud: { type: 'boolean', default: false },
  force: { type: 'boolean', default: false },
}
// TODO: Add --dry-run command
export const handler = async ({ model, crud, force }) => {
  const tasks = new Listr(
    [
      {
        title: 'Generating SDL files...',
        task: async () => {
          const f = await files({ name: model, crud })
          return writeFilesTask(f, { overwriteExisting: force })
        },
      },
    ].filter(Boolean),
    { collapse: false, exitOnError: true }
  )

  try {
    await tasks.run()
  } catch (e) {
    console.log(c.error(e.message))
  }
}

Yeah it doesn’t seem like the services option is being explicitly referenced here, I would think we can just drop it? If you go to the actual files function I think the service files are built there and included in the list of files that are returned?

1 Like