graphql-engine/docs/graphql/manual/mutations/insert.rst
2020-07-09 10:42:33 +02:00

742 lines
17 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

.. meta::
:description: Insert an object into the database using a mutation
:keywords: hasura, docs, mutation, insert
.. _insert:
Insert mutation
===============
.. contents:: Table of contents
:backlinks: none
:depth: 1
:local:
Auto-generated insert mutation schema
-------------------------------------
**For example**, the auto-generated schema for the insert mutation field for a table ``article`` looks like this:
.. code-block:: graphql
insert_article (
objects: [article_insert_input!]!
on_conflict: article_on_conflict
): article_mutation_response
# response of any mutation on the table "article"
type article_mutation_response {
# number of affected rows by the mutation
affected_rows: Int!
# data of the affected rows by the mutation
returning: [article!]!
}
# single object insert (supported from v1.2.0)
insert_article_one (
object: article_insert_input!
on_conflict: article_on_conflict
): article
As you can see from the schema:
- ``objects`` argument is necessary and you can pass multiple ``objects`` to the mutation.
- You can pass an ``on_conflict`` argument to convert the mutation to an :ref:`upsert mutation <upsert>`.
- You can return the number of affected rows and the affected objects (with nested objects) in the response.
- You can use the single object insert to get the inserted object directly as the mutation response.
See the :ref:`insert mutation API reference <insert_upsert_syntax>` for the full specifications.
.. note::
If a table is not in the ``public`` Postgres schema, the insert mutation field will be of the format
``insert_<schema_name>_<table_name>``.
Insert a single object
----------------------
**Example:** Insert a new ``article`` object and return the inserted article object in the response:
.. graphiql::
:view_only:
:query:
mutation insert_single_article {
insert_article_one(
object: {
title: "Article 1",
content: "Sample article content",
author_id: 3
}
) {
id
title
}
}
:response:
{
"data": {
"insert_article_one": {
"id": 21,
"title": "Article 1"
}
}
}
Using variables:
.. graphiql::
:view_only:
:query:
mutation insert_single_article($object: article_insert_input! ) {
insert_article_one(object: $object) {
id
title
}
}
:response:
{
"data": {
"insert_article_one": {
"id": 21,
"title": "Article 1"
}
}
}
:variables:
{
"object": {
"title": "Article 1",
"content": "Sample article content",
"author_id": 3
}
}
.. note::
``insert_<object>_one`` will **only** be available if you have select permissions on the table, as it returns the inserted row.
.. admonition:: Supported from
The ``insert_<object>_one`` mutation is supported in versions ``v1.2.0``
and above.
Insert multiple objects of the same type in the same mutation
-------------------------------------------------------------
**Example:** Insert 2 new ``article`` objects and return both the article objects in the response:
.. graphiql::
:view_only:
:query:
mutation insert_multiple_articles {
insert_article(
objects: [
{
title: "Article 2",
content: "Sample article content",
author_id: 4
},
{
title: "Article 3",
content: "Sample article content",
author_id: 5
}
]
) {
returning {
id
title
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 2,
"returning": [
{
"id": 22,
"title": "Article 2"
},
{
"id": 23,
"title": "Article 3"
}
]
}
}
}
Using variables:
.. graphiql::
:view_only:
:query:
mutation insert_multiple_articles($objects: [article_insert_input!]! ) {
insert_article(objects: $objects) {
returning {
id
title
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 2,
"returning": [
{
"id": 22,
"title": "Article 2"
},
{
"id": 23,
"title": "Article 3"
}
]
}
}
}
:variables:
{
"objects": [
{
"title": "Article 2",
"content": "Sample article content",
"author_id": 4
},
{
"title": "Article 3",
"content": "Sample article content",
"author_id": 5
}
]
}
Insert an object and get a nested object in response
----------------------------------------------------
**Example:** Insert a new ``article`` object and return the inserted article object with its author in the response:
.. graphiql::
:view_only:
:query:
mutation insert_article {
insert_article(
objects: [
{
title: "Article 1",
content: "Sample article content",
author_id: 3
}
]
) {
returning {
id
title
author {
id
name
}
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 1,
"returning": [
{
"id": 21,
"title": "Article 1",
"author": {
"id": 3,
"name": "Sidney"
}
}
]
}
}
}
.. _nested_inserts:
Insert an object along with its related objects through relationships
---------------------------------------------------------------------
One-to-one / One-to-many relationships
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Let's say an ``author`` has an ``object relationship`` called ``address`` to the ``addresses`` table and an
``array relationship`` called ``articles`` to the ``articles`` table.
**Example:** Insert an ``author`` along with their ``address`` and a few ``articles``.
.. graphiql::
:view_only:
:query:
mutation insertData {
insert_authors
(objects: [
{
name: "John",
address: {
data: {
location: "San Francisco"
}
},
articles: {
data: [
{
title: "GraphQL Guide",
content: "Let's see what we can do with GraphQL"
},
{
title: "Authentication Guide",
content: "Let's look at best practices for authentication"
}
]
}
}
]
) {
affected_rows
returning {
id
name
address_id
address {
id
location
}
articles {
id
title
author_id
}
}
}
}
:response:
{
"data": {
"insert_authors": {
"affected_rows": 4,
"returning": [
{
"id": 26,
"name": "John",
"address_id": 27,
"address": {
"id": 27,
"location": "San Francisco"
},
"articles": [
{
"id": 28,
"title": "GraphQL Guide",
"author_id": 26
},
{
"id": 29,
"title": "Authentication Guide",
"author_id": 26,
}
]
}
]
}
}
}
**How it works**
A nested insert mutation is processed as follows:
1. The object relationship objects are inserted first, i.e. in this case, the ``address`` is inserted and its ``id`` is
collected in this step.
2. The parent object is inserted next. i.e. in this case, the ``author`` is now inserted with the ``address_id`` being set
to the ``id`` of the address that was inserted. Because of this, it is not allowed to pass ``address_id`` in the
author object if you are also providing data for the address relationship.
The ``id`` of the author is collected in this step.
3. The array relationship objects are inserted at the end. i.e. in this case, the ``articles`` are now inserted with their
``author_id`` set to the author's ``id`` collected in the step 2. Hence, it's not possible to specify ``author_id``
in the data for the articles relationship.
Many-to-many relationships
^^^^^^^^^^^^^^^^^^^^^^^^^^
Let's say the ``articles`` has a :ref:`many-to-many relationship <many_to_many_modelling>` with the ``tags`` table via
a bridge table ``article_tags``.
**Example:** Insert an ``article`` along with a few ``tags``.
.. graphiql::
:view_only:
:query:
mutation insertArticle {
insert_articles(objects: [
{
title: "How to make fajitas",
content: "Guide on making the best fajitas in the world",
author_id: 3,
article_tags: {
data: [
{
tag: {
data: {
label: "Recipes"
},
on_conflict: {
constraint: tags_label_key,
update_columns: [label]
}
}
},
{
tag: {
data: {
label: "Cooking"
},
on_conflict: {
constraint: tags_label_key,
update_columns: [label]
}
}
}
]
}
}
]) {
affected_rows
returning {
id
title
content
author_id
article_tags {
tag {
label
}
}
}
}
}
:response:
{
"data": {
"insert_articles": {
"affected_rows": 5,
"returning": [
{
"author_id": 3,
"article_tags": [
{
"tag": {
"label": "Recipes"
}
},
{
"tag": {
"label": "Cooking"
}
}
],
"content": "Guide on making the best fajitas in the world",
"id": 34,
"title": "How to make fajitas"
}
]
}
}
}
**How it works**
1. The parent object (from the perspective of ``article``) is inserted first i.e. the ``article`` is inserted.
The ``id`` of the article is collected in this step.
2. The array relationship objects (from the perspective of ``article``) are inserted next i.e. the
``article_tags`` are inserted.
1. The object relationship objects (from the perspective of ``article_tags``) are inserted now i.e.
the ``tags`` are now inserted.
The ``ids`` of the tags are collected in this step.
2. The parent object (from the perspective of ``article_tags``) is inserted at the end i.e. the
``article_tags`` are now inserted with their ``article_id`` set to the article's ``id`` collected in step 1.
The ``tag_id`` is set to the tag's ``id`` collected in step 2.1. Hence, its not possible to specify
``article_id`` and ``tag_id`` in the data for the `article_tags` relationship.
**on_conflict**
``on_conflict`` can be passed as an argument in a nested insert statement. In our example, we say that if the unique key (``label``) already
exists for a tag, we update the ``label`` of this respective tag (see :ref:`nested upsert caveats <nested-upsert-caveats>`).
Insert an object with a JSONB field
-----------------------------------
**Example:** Insert a new ``author`` object with a JSONB ``address`` field:
.. graphiql::
:view_only:
:query:
mutation insert_author($address: jsonb) {
insert_author (
objects: [
{
name: "Ash",
address: $address
}
]
) {
affected_rows
returning {
id
name
address
}
}
}
:response:
{
"data": {
"insert_author": {
"affected_rows": 1,
"returning": [
{
"id": 1,
"name": "Ash",
"address": {
"city": "Bengaluru",
"phone": "9090909090",
"state": "Karnataka",
"pincode": 560095,
"street_address": "161, 19th Main Road, Koramangala 6th Block"
}
}
]
}
}
}
:variables:
{
"address": {
"street_address": "161, 19th Main Road, Koramangala 6th Block",
"city": "Bengaluru",
"phone": "9090909090",
"state": "Karnataka",
"pincode": 560095
}
}
Insert an object with an ARRAY field
------------------------------------
To insert fields of array types, you currently have to pass them as a `Postgres array literal <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-INPUT>`__.
**Example:** Insert a new ``author`` with a text array ``emails`` field:
.. graphiql::
:view_only:
:query:
mutation insert_author {
insert_author (
objects: [
{
name: "Ash",
emails: "{ash@ash.com, ash123@ash.com}"
}
]
) {
affected_rows
returning {
id
name
emails
}
}
}
:response:
{
"data": {
"insert_author": {
"affected_rows": 1,
"returning": [
{
"id": 1,
"name": "Ash",
"emails": ["ash@ash.com", "ash123@ash.com"]
}
]
}
}
}
Using variables:
.. graphiql::
:view_only:
:query:
mutation insert_author($emails: _text) {
insert_author (
objects: [
{
name: "Ash",
emails: $emails
}
]
) {
affected_rows
returning {
id
name
emails
}
}
}
:response:
{
"data": {
"insert_author": {
"affected_rows": 1,
"returning": [
{
"id": 1,
"name": "Ash",
"emails": ["ash@ash.com", "ash123@ash.com"]
}
]
}
}
}
:variables:
{
"emails": "{ash@ash.com, ash123@ash.com}"
}
Set a field to its default value during insert
----------------------------------------------
To set a field to its ``default`` value, just omit it from the input object, irrespective of the
:ref:`default value configuration <postgres_defaults>` i.e. via Postgres defaults or using column presets.
**Example:** If the default value of ``id`` is set to auto-incrementing integer, there's no need to pass the ``id`` field to the input object:
.. graphiql::
:view_only:
:query:
mutation insert_article_with_def_id {
insert_article(
objects: [
{
title: "Article 1",
content: "Sample article content",
author_id: 3
}
]
) {
returning {
id
title
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 1,
"returning": [
{
"id": 21,
"title": "Article 1"
}
]
}
}
}
Set a field to NULL during insert
---------------------------------
If a field is ``nullable`` in the database, to set its value to ``null``, either pass its value as ``null`` or
just omit it from the input object.
**Example:** If ``age`` is a nullable field, to set it to ``null``, either don't pass the age field to the input object
or pass it as ``null``:
.. graphiql::
:view_only:
:query:
mutation insert_author_with_null_age {
insert_author(
objects: [
{
name: "Jeff",
}
]
) {
returning {
id
name
age
}
}
}
:response:
{
"data": {
"insert_author": {
"returning": [
{
"id": 11,
"name": "Jeff",
"age": null
}
]
}
}
}
OR
.. graphiql::
:view_only:
:query:
mutation insert_author_with_null_age {
insert_author(
objects: [
{
name: "Jeff",
age: null
}
]
) {
returning {
id
name
age
}
}
}
:response:
{
"data": {
"insert_author": {
"returning": [
{
"id": 11,
"name": "Jeff",
"age": null
}
]
}
}
}