hasura / graphql-engine

Blazing fast, instant realtime GraphQL APIs on your DB with fine grained access control, also trigger webhooks on database events.
https://hasura.io
Apache License 2.0
31.13k stars 2.76k forks source link

camelCase the responses' fields #6386

Open benams opened 3 years ago

benams commented 3 years ago

Hello guys, I have a postgres schema with snake_case table and field names, but I want to have camelCases keys in my responses' objects. Any known way to achieve that? I can see that apollo server offers fieldResolver where it can be done(https://stackoverflow.com/questions/53881054/convert-snake-case-to-camelcase-field-names-in-apollo-server-express), but I found no similar option for Hasura.

michael-land commented 3 years ago

You can rename columns and tables.

benams commented 3 years ago

As far as I understand I have to rename to columns manually, one by one. No option to have a callback on the data object before it's sent to the client?

michael-land commented 3 years ago

this feature requested at least 1 year ago.I don't think the team gonna implement it or the priority is really low.

You can write a simple script introspect database and rename table/column via metadata API.

benams commented 3 years ago

this feature requested at least 1 year ago.I don't think the team gonna implement it or the priority is really low.

You can write a simple script introspect database and rename table/column via metadata API.

That's interesting, can you elaborate on this "metadata API" and how it can be used to rename the table/column names? (an example would be much appreciated)

michael-land commented 3 years ago

ref: https://hasura.io/docs/1.0/graphql/core/api-reference/schema-metadata-api/index.html#metadata-apis

something like this


export function request(data: Record<string, any>): Promise<any> {
  const API = `${process.env.API}/v1/query`;

  return fetch(API, {
    headers: {
      "Content-Type": "application/json",
      "X-Hasura-Admin-Secret": process.env.HASURA_ADMIN_SECRET as string,
    },
    method: "POST",
    body: JSON.stringify(data),
  }).then(res => res.json());
}

async function generateHasuraApi(tableDefinition: TableDefinition | ViewDefinition): Promise<void> {
  /**
   * generate column names
   */
  await request({
    type: "set_table_custom_fields",
    version: 2,
    args: {
      table: {
        name: tableDefinition.name,
        schema: tableDefinition.schema,
      },
      custom_root_fields: {
        select: camelCase(plural(tableDefinition.name)),
        select_by_pk: camelCase(tableDefinition.name),
        select_aggregate: `${camelCase(plural(tableDefinition.name))}Aggregate`,
        insert: `create${pascalCase(plural(tableDefinition.name))}`,
        insert_one: `create${pascalCase(tableDefinition.name)}`,
        update: `update${pascalCase(plural(tableDefinition.name))}`,
        update_by_pk: `update${pascalCase(tableDefinition.name)}`,
        delete: `delete${pascalCase(plural(tableDefinition.name))}`,
        delete_by_pk: `delete${pascalCase(tableDefinition.name)}`,
      },
      custom_column_names: tableDefinition.columns.reduce<Record<string, string>>((acc, curr) => {
        if (!curr.name.includes("_")) return acc;
        acc[curr.name] = camelCase(curr.name);
        return acc;
      }, {}),
    },
  }).then(console.log);
}

async function generateHasuraUpdatePermission(
  tableDefinition: TableDefinition | ViewDefinition
): Promise<void> {
  /**
   * generate update permission
   */
  await request({
    type: "create_update_permission",
    args: {
      table: {
        name: tableDefinition.name,
        schema: tableDefinition.schema,
      },
      role: "administrator",
      permission: {
        columns: "*",
        check: {},
        filter: {},
      },
    },
  }).then(console.log);
}

async function generateHasuraDeletePermission(
  tableDefinition: TableDefinition | ViewDefinition
): Promise<void> {
  /**
   * generate delete permission
   */
  await request({
    type: "create_delete_permission",
    args: {
      table: {
        name: tableDefinition.name,
        schema: tableDefinition.schema,
      },
      role: "administrator",
      permission: {
        check: {},
        filter: {},
      },
    },
  }).then(console.log);
}

async function generateHasuraInsertPermission(
  tableDefinition: TableDefinition | ViewDefinition
): Promise<void> {
  /**
   * generate create permission
   */
  const createSet: Record<string, any> = {};
  if (tableDefinition.columns.find(column => column.name === "created_by")) {
    createSet.created_by = "x-hasura-user-id";
  }
  if (tableDefinition.columns.find(column => column.name === "operation_center_id")) {
    createSet.operation_center_id = "x-hasura-operation-center-id";
  }
  await request({
    type: "create_insert_permission",
    args: {
      table: {
        name: tableDefinition.name,
        schema: tableDefinition.schema,
      },
      role: "administrator",
      permission: {
        columns: "*",
        check: {},
        set: createSet,
      },
    },
  }).then(console.log);
}

async function generateHasuraSelectPermission(
  tableDefinition: TableDefinition | ViewDefinition
): Promise<void> {
  /**
   * generate select permission
   */
  await request({
    type: "create_select_permission",
    args: {
      table: {
        name: tableDefinition.name,
        schema: tableDefinition.schema,
      },
      role: "administrator",
      permission: {
        columns: "*",
        computed_fields: tableDefinition.name === "user" ? ["defaultWarehouseId"] : undefined,
        limit: 500,
        allow_aggregations: true,
        filter: {},
      },
    },
  }).then(console.log);
}
tirumaraiselvan commented 3 years ago

Hey folks! We do want to give ability to choose the casing (snake, camel, etc) style for the auto-generated components of the graphql schema (like selectByPk instead of select_by_pk) and this is high in our priority: https://github.com/hasura/graphql-engine/issues/3320

Although, the use-case described here is even more "advanced" because you want the ability for Hasura to infer the case of a column name and then convert into camel case in the graphql name. I think for the time being using the new set_table_customization API (v1.3.4 onwards) or set_table_custom_fields API (v1.3.3 and below) is the best way forward. We will definitely consider the current use-case when solving for #3320 .