Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE REQUEST] Global naming convention #3320

Closed
go4cas opened this issue Nov 8, 2019 · 29 comments
Closed

[FEATURE REQUEST] Global naming convention #3320

go4cas opened this issue Nov 8, 2019 · 29 comments
Assignees
Labels
a/cloud c/server Related to server iteration k/enhancement New feature or improve an existing feature

Comments

@go4cas
Copy link

go4cas commented Nov 8, 2019

With the introduction of the Custom GraphQL Root Fields and GraphQL Field Name features, one can specify the name used in the schema generation, for every field and table. This is very useful, especially, when referencing from client apps. But, this might become overhead for very large schemas, spanning across multiple Postgres schemas.

I would like to see a feature where one can specify the naming convention globally (perhaps at a schema level?), e.g. camelCase, PascalCase, etc.

This should be available for table names, field names, mutation names and relationship names.

Bonus Request:

  • Would be even more amazing if we could define additional text to the naming convention, e.g. insert mutations: 'add'${PascalCase table name}
  • Also be able to specify singular or plural in the convention, e.g. by_pk query name: ${singular camelCase table name} and normal select query: ${plural camelCase table name}
@marionschleifer marionschleifer added the c/server Related to server label Nov 8, 2019
@lexi-lambda lexi-lambda added help wanted Good candidate for contribution. Community help wanted! k/enhancement New feature or improve an existing feature p/longterm Low priority issues that will be picked up based on user feedback or bandwidth labels Nov 21, 2019
@VienDinhCom
Copy link

As a JavaScript developer, I really want this feature.

@vhiairrassary
Copy link

👍We are interested as well. Currently we rely on the Custom GraphQL Root Fields feature but it involves error-prone work. Also, some parts cannot be changed such as on_conflict & update_columns fields.

In the meantime we should soon have missing field available in console: #4203.

@michael-land
Copy link

I wrote a utility function for internal use to do the naming conversion. Fork it if you need more customization, such as generate permission/interfaces etc. (I remove permission part since it's business related)

https://github.com/xiaoyu-tamu/hasura-utilities

@beepsoft
Copy link

beepsoft commented Apr 1, 2020

@xiaoyu-tamu I do something similar but based on Java JPA annotations + specific Hasura annotations (for permissions, for example)

https://github.com/beepsoft/hasuraconf

Just added support for the new Hasura 1.2 root fields insert_one, update_by_pk, delete_by_pk.

@jgoux
Copy link

jgoux commented Jul 2, 2020

Please take inspiration from Postgraphile naming convention. It has the best defaults IMO.

Here is the doc of its inflection system : https://www.graphile.org/postgraphile/inflection/

And the simplified naming plugin : https://github.com/graphile/pg-simplify-inflector

@sbussard
Copy link

sbussard commented Jul 8, 2020

I'd really like to have this feature. After going through painstaking effort to convert each root query and relationship name to camelCase I was shocked to find out that aggregates of relationships still use snake_case and apparently there's no way to change it.

This is a clash of conventions between the front end and the data layer

@abdullah2993
Copy link
Contributor

abdullah2993 commented Jul 9, 2020

+1 for this, it is very tedious to change them one by one

@sbussard
Copy link

sbussard commented Jul 10, 2020

Aliases might be a temporary workaround
https://atheros.ai/blog/how-to-use-graphql-aliases

@tirumaraiselvan tirumaraiselvan removed the help wanted Good candidate for contribution. Community help wanted! label Aug 24, 2020
@danielkcz
Copy link

danielkcz commented Oct 10, 2020

When I started with Hasura a couple of months ago, being used to camelCase I did not understand why all examples bother with ugly snake_case. So I went straight for camelCase for all my DB entities.

Today after several frustrating moments trying to write custom SQLs for either views or computed fields I've given up and renamed columns/tables/views to snake_case. Arguably arbitrary SQL is not needed that often with Hasura, but when things get complicated it's kinda hard to find the error in syntax, especially for a frontend developer.

I think Hasura should explain the pros/cons of each naming convention in docs.

To keep my Javascript OCD happy I had to painfully specify custom GraphQL names for all columns. Only to later realize to do the same with table names, I would have to specify all 9 names for each table to keep it consistent. Why there can't be a single name similar to columns that gets suffix?

Then I also realized that my actions use camelCase as well and I would have a total mess in naming conventions. On top of that, my backend uses the action name from the payload to identify action instead of having it in the URL. That would cascade into changes I am not comfortable with anymore.

So after weighing the pros/cons I've reverted everything back and I will rather deal with SQL from time to time than this craziness :/

@daniel-xyz
Copy link

For most users this customisation probably wouldn't be needed if Hasura would transform all table/column/relation names to GraphQL conventions automatically the same way that Postgraphile does it: https://www.graphile.org/postgraphile/inflection/

On one hand it's nice that you can rename all fields by yourself, but on the other it is frustrating that you're still stuck to the GraphQL type names that Hasura generates from tacked tables. For example, I now have camelCase fields but snake_case types and can't find any option to rename those types apart from defining new custom types.

The whole customization shouldn't even be necessary in the first place. I think it's a "nice to have", but more importantly, Hasura should apply common GraphQL conventions automatically or via a checkbox when you're about to track new tables. This is something that I, as a new user, miss by far the most after a few days playing around with Hasura.

@tirumaraiselvan tirumaraiselvan added p/high candidate for being included in the upcoming sprint and removed p/longterm Low priority issues that will be picked up based on user feedback or bandwidth labels Dec 24, 2020
@samuelcastro
Copy link

+1000 I'd love to see this.

@samuelcastro
Copy link

Any update on this @tirumaraiselvan ?

@owlinthestars
Copy link

+1000000 for this

@jangocoo
Copy link

+1 would be great to have it

@Aarbel
Copy link

Aarbel commented Mar 5, 2021

+100000, standard and easy to read naming is really something, @tirumaraiselvan any news ?

@arachnidiskandar
Copy link

+1 would be really convinent :)

@beepsoft
Copy link

A couple of days ago I sent my questions regarding some of the most popular pending issues via the hasura.io chat facility and received this answer from Allison:

I believe the naming conventions RFC is highest priority right now from that list, but other issues are lower priority than the 2.0-related core improvements. Many of these core improvements will help make these issues easier to resolve in the future, and we're looking forward to faster velocity as our team grows and the product matures.

My hope is that this is not a general answer but that team is actually actively work on it. 🤞

By the way, these are the long pending issues I am most interested in beside global naming conventions that I asked about via chat:

update nested object
#1573

[RFC] nested updates
#3852

One to One relationship causing Not-Null Constraint Violation on Nested Data Insert
#2576

allow nested inserts for one-to-one relationships (fix #2576)
#2852

How to mark certain columns as required fields in the generated GraphQL schema
#1433

Feature Request: Rate limiting and Scoring
#2151

Performance gets affected when using 'limit' as it applies limit after the table join
#5745

@quixotik
Copy link

Very much anticipating this feature. Will it also:

  • be able to take into account any column prefixes? All my columns are prefixed, so being able to specify to strip that off would be great
  • allow renaming of functions

@hiroingk
Copy link

I have manually changed the GraphQL field to camelCase.
But, the following two things cannot be changed even manually now.

  • query arg name: distinct_on order_by
  • array relationships field name: _aggregate

I want to change the above manually.

@jjangga0214
Copy link
Contributor

jjangga0214 commented May 25, 2021

For a workaround, we can place an additional transforming layer in front of Hasura.

This has benefits like

  • Literally every naming (without exception) can be modified. (For example, query/mutation/subscription arg name can be changed as well. This solves a problem @hiroingk mentioned above (arg name and array relationships cannot be changed on Hasura console or through API.). )
  • This can be automatic.

And downsides are

  • You have to operate another server application (or it's also possible to modify schema on the client-side.)
  • Performance may be slightly reduced. (But this may not be a serious issue for many services.)

Code snippet for nodejs (typescript) example:
change-case and @graphql-tools/wrap carry out the main job. Refer to their official docs for more detail.

import {
  RenameTypes,
  RenameRootTypes,
  RenameRootFields,
  RenameObjectFields,
  RenameInterfaceFields,
  RenameInputObjectFields,
  TransformEnumValues,
  wrapSchema,
} from '@graphql-tools/wrap'
import { isUpperCase } from 'is-upper-case'
import { GraphQLFieldConfig } from 'graphql'
import { camelCase, pascalCase } from 'change-case'

function changeCase(
  target: string,
  change: (arg: string) => string = camelCase,
) {
  // Checking regex for preserving prefix underscore(s), which Hasura also generates.
  // If you want to remove those prefix underscores,
  // just use plain `camelCase` or `pascalCase` functions, instead of this function.
  return `${target.match(/^_+/g) || ''}${change(target)}`
}

function transformFieldConfig(fieldConfig: GraphQLFieldConfig<any, any>) {
  const newArgs: { [key: string]: any } = {}
  for (const argName in fieldConfig.args) {
    if (Object.prototype.hasOwnProperty.call(fieldConfig.args, argName)) {
      newArgs[changeCase(argName)] = fieldConfig.args[argName]
    }
  }
  fieldConfig.args = newArgs
}

// Use this schema object as you want. For example, you may run apollo-server by providing this.
const schema = wrapSchema({
  // Other options are omitted for brevity. Refer to the official docs to connect with Hasura.
  transforms: [
    new RenameTypes((name) => changeCase(name, pascalCase)), // By convention, type name is PascalCase.
    new RenameRootTypes((name) => {
      switch (name) {
        case 'query_root':
          return 'Query'
        case 'mutation_root':
          return 'Mutation'
        case 'subscription_root':
          return 'Subscription'
        default:
          throw new Error('Root type name is not expected.')
      }
    }),
    new RenameRootFields((operationName, fieldName, fieldConfig) => {
      transformFieldConfig(fieldConfig)
      return changeCase(fieldName)
    }),
    new RenameObjectFields((typeName, fieldName, fieldConfig) => {
      transformFieldConfig(fieldConfig)
      return changeCase(fieldName)
    }),
    new RenameInterfaceFields((typeName, fieldName, fieldConfig) => {
      transformFieldConfig(fieldConfig)
      return changeCase(fieldName)
    }),
    new RenameInputObjectFields((typeName, fieldName, inputFieldConfig) => {
      return changeCase(fieldName)
    }),
    // @ts-ignore // Type issue. REF: https://github.com/ardatan/graphql-tools/issues/2994
    new TransformEnumValues((typeName, enumValue, enumValueConfig) => {
      // According to the official specification, all-caps case is recommended for enums values.
      // REF: http://spec.graphql.org/June2018/#EnumValue
      // However, hasura generates enums that represents columns 
      // (ex: `user_update_column`), whose values are same as columns.
      // Thus, hereby leaving an exception. 
      // If enum Value is already upper case, leave it as is. 
      // If not, then change it to camel case (as column names are already modified 
      // to camel case from `RenameObjectFields` above) 
      const newEnumValue = isUpperCase(enumValue)
        ? enumValue
        : changeCase(enumValue)
      return [newEnumValue, enumValueConfig]
    }),
  ],
})

@Aarbel
Copy link

Aarbel commented May 25, 2021

@jgoux

@m-rgba
Copy link
Contributor

m-rgba commented May 29, 2021

I've built a small script to automate the process of renaming the custom_root_fields to camelCase using a method similar to the Postgraphile plugins mentioned above (using inflect / plurals, etc.) over the Hasura metadata API.

https://github.com/m-rgba/hasura-snake-to-camel

Should be pretty easy to customize for most naming use-cases.

Output:

Example for => Table Name : active_user
---------------------------------
Before                   : After
active_user              : activeUsers
active_user_by_pk        : activeUser
active_user_aggregagte   : activeUsersAggregate
active_user_insert       : activeUsersInsert
active_user_insert_one   : activeUserInsert
active_user_update       : activeUsersUpdate
active_user_update_by_pk : activeUserUpdate
active_user_delete       : activeUsersDelete
active_user_delete_by_pk : activeUserDelete

@jaggad
Copy link

jaggad commented Jun 14, 2021

+1 would be an awesome feature. Would mean we can avoid heaps of aliases which literally just convert the snake_case to camelCase and also in our typescript types inferred from hasura they would be naming convention compliant.

@svarlamov
Copy link

@m-rgba I found your script very useful and easy to customize! A cleaner Python script with docs would be great, I'll see if I can create a PR on that (I don't use Jupyter Notebooks and putting credentials into Collab isn't great)

@svarlamov
Copy link

Following up on @m-rgba's point, I've created an updated script that can be run continuously to keep your GQL schema naming conventions up to date as you work on your data model: https://github.com/exlinc/hasura-enforce-camel-case

@bkniffler
Copy link
Contributor

I've created a nodejs cli package over at https://github.com/bkniffler/hasura-camelize, based on the work of @svarlamov and @m-rgba. Just a matter of:

npm i -g hasura-camelize
hasura-camelize --host https://some.domain --secret some-secret --dry

@tirumaraiselvan tirumaraiselvan changed the title [FEATURE REQUEST] Global naming convention [FEATURE REQUEST] Global naming convention (needs spec) Sep 14, 2021
@gilligan gilligan added iteration and removed p/high candidate for being included in the upcoming sprint labels Feb 2, 2022
@tirumaraiselvan
Copy link
Contributor

Hey folks,

We have a RFC published for this feature here: #8290

Request you to give your comments so we can satisfy your requirements as much as possible. Very eager, as all of you, to begin working on this!

@tirumaraiselvan tirumaraiselvan changed the title [FEATURE REQUEST] Global naming convention (needs spec) [FEATURE REQUEST] Global naming convention Mar 9, 2022
@iki
Copy link

iki commented Apr 2, 2022

Awesome @bkniffler, thank you!

Using endpoint/secret set in environment, .env, or Hasura config.yaml (in that order, as used by Hasura CLI):

npm i -d hasura-camelize dotenv-cli
npm run schema:coerce

with package.json scripts:

  {
    "schema:coerce": "npm run -- withenv schema:coerce:run",
    "schema:coerce:run": "hasura-camelize --relations --pgMaterializedViews --host \"$HASURA_GRAPHQL_ENDPOINT\" --secret \"$HASURA_GRAPHQL_ADMIN_SECRET\"",
    "withenv": "dotenv -- npm run yamlenv npm run --",
    "yamlenv": "yamlenv(){ set HASURA_GRAPHQL_ENDPOINT endpoint; set HASURA_GRAPHQL_ADMIN_SECRET admin_secret; $@; }; set(){ [ -n \"${!1}\" ] || export $1=\"$(sed -n \"/^$2:/s/$2: *//p\" config.yaml)\"; }; yamlenv"
  }

I've created a nodejs cli package over at https://github.com/bkniffler/hasura-camelize, based on the work of @svarlamov and @m-rgba. Just a matter of:

npm i -g hasura-camelize
hasura-camelize --host https://some.domain --secret some-secret --dry

@tirumaraiselvan
Copy link
Contributor

Hey folks

Experimental support for "global naming conventions" has been added in v2.8.0-beta.1 . Pls see the docs here for usage.

Note that this is only available for Postgres database for now. Console support (where ever needed e.g. #8573) is still pending.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a/cloud c/server Related to server iteration k/enhancement New feature or improve an existing feature
Projects
None yet
Development

No branches or pull requests