I think it would be helpful to have a syntax to update multiple tables with a single mutation. I like the approach described here: https://blog.graph.cool/improving-dx-with-nested-mutations-af7711113d6#.em6lzrp2y
Basically transforming this: (where "Author" is a separate table)
mutation {
createPost(
imageUrl: "https://unsplash.it/200/300?image=211"
description: "#boat #beautiful"
authorId: "author-id"
) {
id
}
}
into this:
mutation {
createPost(
imageUrl: "https://unsplash.it/200/300?image=211"
description: "#boat #beautiful"
authorId: "author-id"
author: {
email: "[email protected]"
name: "Nilan"
password: "1234"
}
) {
id
}
}
would probably want to expand to include list manipulation as well:
into this:
mutation {
createPost(
imageUrl: "https://unsplash.it/200/300?image=211"
description: "#boat #beautiful"
authors: [
{
email: "[email protected]"
name: "Nilan"
password: "1234"
},
{
email: "[email protected]"
name: "bob"
password: "1234"
}
]
) {
id
}
}
Any thoughts on this? Not sure of the downstream implications of something like this.
I’m interested to know what kind of mutation you are envisioning here. Would:
mutation {
createPost(
imageUrl: "https://unsplash.it/200/300?image=211"
description: "#boat #beautiful"
authorId: "author-id"
author: {
email: "[email protected]"
name: "Nilan"
password: "1234"
}
) {
id
}
}
Update or insert author?
I’d love to see a feature like this, but I would want to make sure we have a clear proposal for how we would implement nested mutations 😊
I think ideally I'd like to see it "replace" whatever is currently in the database. Using the primary key on the other table to determine if it exists or not. It's probably most useful in cases of one-to-many relationships.
To illustrate, say your tables are set up like this
Table "Posts":
ID | Title | Description
--- | --- | ---
1 | Really Interesting Post | The description of the really interesting post
Table Authors:
ID|PostID|Name|Email
---|---|---|---
1|1|John Cleese|[email protected]
2|1|Michael Palin|[email protected]
If I execute the mutation below, I'd like to see:
mutation {
updatePost(
authorsByPostId: [
{
authorId: 1
email: "[email protected]"
},
{
email: "[email protected]"
name: "Eric Idle"
password: "1234"
}
]
) {
id
}
I realize I'm missing some info in the mutation, just trying to show the relevant pieces.
Why would a nested mutation be better then:
mutation {
createAuthor(...) { ... }
updateAuthor(...) { ... }
updatePost(...) { ... }
}
Does this provide any functionality past simple syntax sugar?
I think it provides additional capability in the case of an insert with an auto-incrementing ID.
Let's say I'm inserting a new post and a new author associated with that post. First, I need to run the createPost mutation to create the post (so I can get the new ID) then I need to run a second mutation (with the newly created ID) on a second network request to create the associated author.
Allowing you to create the whole shebang in a single network request gives you the benefit of less overhead and ideally the benefit of a single transaction, so when things go wrong the whole thing fails together.
Yep. I can see how that can be useful. What about introducing a new feature to our GraphQL runtime that allowed mutation fields to become variables? For example:
mutation {
createAuthor(...) {
author {
id @asVariable(name: "newAuthorId")
}
}
updatePost(input: { authorId: $newAuthorId, ... }) {
...
}
}
I’ve seen the need for a GraphQL feature like this in a few different places. Would something like this adequately solve the problem?
Hi, It seems than now the graphql specs allow to do nested mutations using a @relation new directive in a schema like this:
type Author {
id: ID!
contactDetails: ContactDetails @relation(name: "AuthorContactDetails")
posts: [Post!]! @relation(name: "AuthorPosts")
description: String!
}
type ContactDetails {
id: ID!
author: Author @relation(name: "AuthorContactDetails")
email: String!
}
Nested mutation example:
mutation createAuthorAndContactDetails {
createAuthor(
description: "I am a good author!"
contactDetails: {
email: "[email protected]"
}
) {
id
contactDetails {
id
}
}
}
More info:
Is it interesting and possible to support this Graphql feature?
I would say any spec-compliant feature is interesting to graphile-build (and Postgraphile / PostgraphQL by extension)
That said, and priority aside, @benjie will have to chime in on the possibility.
I believe this can be achieved via a plugin, though we might not have all the hooks in place to enable it currently. I can't see myself looking at this in the next 6 months though; so if anyone else wants to take it on feel free!
I am going to revive this issue as I had a chat on the topic with @benjie today on Gitter. I'll add a longer comment tomorrow (it's getting late here), but for now, to reply to @calebmer 's comment:
Does this provide any functionality past simple syntax sugar?
It does. I think the main benefit (at least to me) would be to create nested entities within the same transaction. For example, let's say I have a database of users and addresses, with a relation user has one address. I may want to display a form in my application to create a new user and set its address. In this context, it would be (as far as I can see) much better if the address and the user were created within a single transaction. Otherwise, if the address creation succeeds but the user creation failed, I would have to reason on the client-side that I already have an address record and that I do not need to re-create it. Furthermore, if the user were to close the page at this point I would have a "dangling" address record in the database. I hope this makes sense!
Your suggestion of allowing mutation payload fields to become variable seems like it would do the trick, but is it a feature that can be easily implement / is it a "good approach"?
@mlipscombe is also working on this.
I've published a very alpha version of postgraphile-plugin-nested-mutations to npm. Github project here: https://github.com/mlipscombe/postgraphile-plugin-nested-mutations. Feedback is very much welcome!
@mlipscombe THANK YOU!
Closing this for now since a plugin is available 👍
@benjie or @mlipscombe : Nested mutation is not working with PostGraphileNestedMutations.
const options = {
watchPg: false,
graphiql: true,
appendPlugins: [
PostGraphileNestedMutations,
],
skipPlugins: [NodePlugin]
};
app.use(postgraphile(database, SchemaName, options));
create table t1.discussion
(
discussion_id uuid primary key default uuid_generate_v1mc(),
trail_order integer,
message_text text,
message_date timestamp with time zone not null default now(),
status varchar (5) not null default 'ACTV',
status_change_date timestamp with time zone not null default now()
);
CREATE TABLE t1.discussion_question
(
discussion_question_id uuid NOT NULL DEFAULT uuid_generate_v1mc(),
discussion_id uuid,
question_result character varying(5) COLLATE pg_catalog."default" NOT NULL,
question_other_result text COLLATE pg_catalog."default",
CONSTRAINT discussion_question_pkey PRIMARY KEY (discussion_question_id),
CONSTRAINT discussion_question_discussion_id_fkey FOREIGN KEY (discussion_id)
REFERENCES t1.discussion (discussion_id)
);
Do I need to change any other thing in database for smart comments?
anything else need to be added for plugin options.
@sspsinha Please redirect your issue to https://github.com/mlipscombe/postgraphile-plugin-nested-mutations (be sure to read the README). I've edited your comment for formatting reasons.
Thanks Benjie. Will post it there. I went through Read me, but its not working as expected. Thanks for formatting.
Most helpful comment
Yep. I can see how that can be useful. What about introducing a new feature to our GraphQL runtime that allowed mutation fields to become variables? For example:
I’ve seen the need for a GraphQL feature like this in a few different places. Would something like this adequately solve the problem?