Postgraphile: Is there a way to remove id from creation mutations?

Created on 21 Mar 2017  路  18Comments  路  Source: graphile/postgraphile

I currently use an id generator (default value of primary key), but the request has to supply a client generated id field.

Is there some convention which removes the ID from the creation (and update) inputs?

馃檹 help-wanted

Most helpful comment

@benjie Awesome. Great implementation. Thanks!

All 18 comments

Not currently, but exposing some hooks so these fields could be skipped would be very (VERY) welcome.

Is this just a style thing so a nullable id field would not be available in the create mutation, or is this functionality blocking something you are trying to do? If it鈥檚 just a style thing I like the idea of just creating a procedure for creates and updates and disabling the default ones by not granting those permissions.

For me, I have some not-null fields I'd like to remove from the schema (they're set in triggers using the same permissions so making them available to the user is unnecessary and, since they're not-null and thus must be specified, a little annoying)

Can we grant creates and updates on specific columns? If so could we use that information to hide fields from the schema?

My issue is that the trigger that sets those fields does so by looking up other fields using the same role's RLS protections. So those columns should be writable by that role, they just shouldn't be exposed to the end-user. Though I could create another role that inherits the one role and extends its privileges to these two columns I'd rather Keep It Simple.

A generic approach could be to provide several callback functions which can manipulate the introspection, GraphQL schema generation, queries, and payloads.
This could be extremely powerful for advanced use cases.

I am aware that this is going a bit against the "everything is inferred by Postgres" stream, but that would be something that could prove very versatile.
This is especially relevant for things that Postgres might not be perfectly able to provide or missing features of PostGraphQL might be simulated with such hooks.

For example, we use routable GUID that contains an identifier for the table, which makes the generated nodeId obsolete. With such hooks, we could just implement the id to table mapping manually.

Edit: Oh there is #300

As I mentioned in https://github.com/postgraphql/postgraphql/issues/300#issuecomment-284202022 there are already some rudimentary hooks which I鈥檇 be comfortable exposing experimentally as people determine what degree of extension they want/need.

@benjie this is now 100% possible in v4 right? You'd just hook "GraphQLObject:fields" (or whatever it's called) and pop off the ones you don't want from the CreateThingInput yes?

https://www.graphile.org/postgraphile/extending/#removing-things-from-the-schema

It's currently possible but the DX around it is non-optimal so I'm going to keep this issue open as a reminder to make it easier.

This is going to be an easier way of doing this kind of thing, but I'm not sure if this exact use case is covered yet:

https://github.com/graphile/graphile-build/pull/73

Thanks for this nice project,

For example, for adding a id field, for any model/table, how would we proceed? From reading https://www.graphile.org/postgraphile/extending/#adding-root-querymutation-fields I'm not sure a plugin can do that, so it's more this kind of hook? What would it look like for simply auto-generating id as someUid() for example?

Hi @caub, could you expand on your requirements a little, perhaps with some pseudo-code to suggest the shape of the solution you'd like to see?

My current understanding is that you have database tables that do not have an "id" field, and you want to add an "id" field to their types in GraphQL (without modifying the database)? Where would the data returned from the "id" field come from - is it a combination of existing database field attributes?

Yes, basically, I'd have a users, posts tables, each of them have a TEXT PRIMARY KEY, and I'd use a library in server to generate it, example http://npm.im/uid-safe.

pseudo-code: (based on https://www.graphile.org/postgraphile/usage-library/)

const uid = require('uid-safe').sync
postgraphql(process.env.DATABASE_URL, schemaName, {
  prependPlugins:  [ // because id generation should go as early as possible
    generateIds
  ],
})
// generateIds plugin should be simple, the id generation doesn't depend on data
// it should be active only for mutations, of course
function generateIds(builder) {
  builder.hook("GraphQLObjectType:fields", (
    fields, // input object
    { extend, graphql: { GraphQLString } }, // Build
    context // Context
  ) => { // how to know if we are dealing with a mutation or query?
    for (const [field, val] of Object.entries(fields)) {
      if (field === 'user' && !val.id) {
        val.id = {
          type: GraphQLString,
          resolve: () => uid(24)
        }
      }
      // same for field.post
    }
    return fields
  }
}

so that I can do:

mutation ($user: UserInput!) {
  createUser(input: {user: $user}) {
    clientMutationId
    user {
      id
      email
    }
  }
}
{"user": {"email": "[email protected]", "name": "oki"}}

without needing id

example table:

CREATE TABLE users (
  id             TEXT PRIMARY KEY,
  name   TEXT,
  ...
)

@benjie does it make sense? I'm not sure how to select a query or a mutation in a plugin

Ah, so there is already an id field within your database and exposed by PostGraphile, and you want to remove that field from your generated schema specifically only for create mutations, and instead have PostGraphile generate a default value for it which will be written to the database's id field.

If I had these needs, I would:

  1. Use pgColumnFilter to filter the id column from create mutations
  2. Create PostgreSQL function to generate the UID and set this as the default value for the column (you could implement this in plv8 if you want to use JavaScript code, but personally I'd use plpgsql and the pgcrypto features)

Is there a reason it needs to be done as a plugin?

If I'm still not following, please could you post an SQL definition of one of your tables and a GraphQL schema definition showing what you want?

@benjie thanks, yes doing it as a function is indeed a good possibility for this case, I was thinking of a plugin because sometimes there are other custom validation/manipulation that I could do at the same time, but yes a DEFAULT for id is quite cleaner, and simpler

I'll try what you proposed

@benjie I have a similar use case to the one described here. I generate IDs internally (in PG) and don't want them required on mutations for certain types / tables. I've reviewed the tests for pgColumnFilter but don't see how I can limit the filter to just create mutations. Is there somewhere I can look to get the shape of the attr build and context args?

Use smart comments:

comment on column my_schema.my_table.my_column is E'@omit create';

I don't recommend using pgColumnFilter for this any more, it's hard to do well unless you understand the codebase. Hopefully this will improve, but I've not had time to document it fully.

Closing this issue as it's now solved by Smart Comments.

@benjie Awesome. Great implementation. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

giacomorebonato picture giacomorebonato  路  3Comments

ssomnoremac picture ssomnoremac  路  5Comments

srghma picture srghma  路  3Comments

WestleyArgentum picture WestleyArgentum  路  3Comments

mrbarletta picture mrbarletta  路  5Comments