2020-01-14 15:57:45 +03:00
|
|
|
|
.. meta::
|
|
|
|
|
:description: Use enums in Hasura
|
|
|
|
|
:keywords: hasura, docs, schema, enum
|
|
|
|
|
|
2020-03-11 22:42:36 +03:00
|
|
|
|
.. _enums:
|
|
|
|
|
|
2018-09-11 14:11:24 +03:00
|
|
|
|
Enum type fields
|
|
|
|
|
================
|
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. contents:: Table of contents
|
|
|
|
|
:backlinks: none
|
|
|
|
|
:depth: 2
|
|
|
|
|
:local:
|
2018-12-03 15:12:24 +03:00
|
|
|
|
|
2020-05-21 12:41:45 +03:00
|
|
|
|
Introduction
|
|
|
|
|
------------
|
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Enum type fields are restricted to a fixed set of allowed values.
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Enums in a database
|
|
|
|
|
-------------------
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
In a relational database such as Postgres, an enum type field in a table can be defined in two ways:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. _native_pg_enum:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Using `native Postgres enum types <https://www.postgresql.org/docs/current/datatype-enum.html>`__
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
While the most obvious solution, native enum types have significant drawbacks: they are not easily mutable.
|
|
|
|
|
New values cannot be added to an enum inside a transaction (that is, ``ALTER TYPE ... ADD VALUE`` is not
|
|
|
|
|
supported by transactional DDL), and values cannot be removed from an enum at all without completely dropping
|
|
|
|
|
and recreating it (which cannot be done if the enum is in use by *any* tables, views, or functions). Therefore,
|
|
|
|
|
native enum types should only be used for enums that are guaranteed to *never* change, such as days of the
|
|
|
|
|
week.
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. _reference_table_enum:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Using `foreign-key references <https://www.postgresql.org/docs/current/tutorial-fk.html>`__ to a single-column table
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
This approach represents an enum using ordinary relational database concepts. The enum type is represented by a
|
|
|
|
|
table, and the values of the enum are rows in the table. Columns in other tables that use the enum are ordinary
|
|
|
|
|
foreign-key references to the enum table.
|
|
|
|
|
|
|
|
|
|
For enums with values that are dynamic and may require updates, such as a list of tags or user roles, this
|
|
|
|
|
approach is strongly recommended. Modifying an enum defined this way is easy: simply insert, update, or delete
|
|
|
|
|
rows in the enum table (and updates or deletes can even be cascaded to references, and they may be done within
|
|
|
|
|
a transaction).
|
|
|
|
|
|
|
|
|
|
Enums in the Hasura GraphQL engine
|
|
|
|
|
----------------------------------
|
|
|
|
|
|
|
|
|
|
Given the limitations of native Postgres enum types (as described :ref:`above <native_pg_enum>`), Hasura
|
|
|
|
|
currently only generates GraphQL enum types for enums defined using the
|
|
|
|
|
:ref:`referenced tables <reference_table_enum>` approach.
|
|
|
|
|
|
|
|
|
|
You may use native Postgres enum types in your database schema, but they will essentially be treated like text
|
|
|
|
|
fields in the generated GraphQL schema. Therefore, this guide focuses primarily on modeling an enum using a
|
|
|
|
|
reference table, but you may still use native Postgres enum types to help maintain data consistency in your
|
|
|
|
|
database. You can always choose to create a table with the values of a Postgres enum as shown in the
|
|
|
|
|
:ref:`section below <create_enum_table_from_pg_enum>`.
|
|
|
|
|
|
|
|
|
|
**Example:** Let’s say we have a database that tracks user information, and users may only have one of three specific
|
|
|
|
|
roles: user, moderator, or administrator. To represent that, we might have a ``users`` table with the following schema:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
|
|
|
|
.. code-block:: sql
|
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
CREATE TABLE users (
|
|
|
|
|
id serial PRIMARY KEY,
|
|
|
|
|
name text NOT NULL,
|
|
|
|
|
role text NOT NULL
|
|
|
|
|
);
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
Now we can insert some users into our database:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
|
|
|
|
.. code-block:: sql
|
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
INSERT INTO users (name, role) VALUES
|
|
|
|
|
('Alyssa', 'administrator'),
|
|
|
|
|
('Ben', 'moderator'),
|
|
|
|
|
('Gerald', 'user');
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
This works alright, but it doesn’t prevent us from inserting nonsensical values for ``role``, such as
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
|
|
|
|
.. code-block:: sql
|
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
INSERT INTO users (name, role) VALUES
|
|
|
|
|
('Hal', 'spaghetti');
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
which we certainly don’t want. Hence we should create an enum to restrict the allowed values.
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. _create_enum_table:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Creating an enum compatible table
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
To represent an enum, we’re going to create an _`enum table`, which for Hasura’s purposes is any table that meets
|
2019-08-16 00:19:47 +03:00
|
|
|
|
the following restrictions:
|
2019-03-13 13:04:40 +03:00
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
1. The table must have a single-column primary key of type ``text``. The values of this column are the legal values
|
|
|
|
|
of the enum, and they must all be `valid GraphQL enum value names
|
|
|
|
|
<https://graphql.github.io/graphql-spec/June2018/#EnumValue>`__.
|
|
|
|
|
2. Optionally, the table may have a second column, also of type ``text``, which will be used as a description of each
|
|
|
|
|
value in the generated GraphQL schema.
|
2020-05-21 12:41:45 +03:00
|
|
|
|
3. The table must not contain any other columns.
|
2019-09-03 17:17:36 +03:00
|
|
|
|
4. The table must contain at least 1 row.
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
**For example**, to create an enum that represents our user roles, we would create the following table:
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
.. code-block:: sql
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
CREATE TABLE user_role (
|
|
|
|
|
value text PRIMARY KEY,
|
|
|
|
|
comment text
|
|
|
|
|
);
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-08-16 00:19:47 +03:00
|
|
|
|
INSERT INTO user_role (value, comment) VALUES
|
|
|
|
|
('user', 'Ordinary users'),
|
|
|
|
|
('moderator', 'Users with the privilege to ban users'),
|
|
|
|
|
('administrator', 'Users with the privilege to set users’ roles');
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. _create_enum_table_from_pg_enum:
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. admonition:: Creating an enum table from a native PG enum
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
You can create a table containing the values of a PG enum by executing the following SQL:
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
.. code-block:: sql
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
CREATE TABLE "<my_enum_table>" (value TEXT PRIMARY KEY);
|
|
|
|
|
INSERT INTO "<my_enum_table>" (value) (SELECT unnest(enum_range(NULL::"<my_enum>")))::text);
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Next, we need to tell Hasura that this table represents an enum.
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Setting a table as an enum table
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Once we have a table which satisfies the conditions for an enum table as described :ref:`above <create_enum_table>`,
|
|
|
|
|
we need to tell Hasura that this table represents an enum.
|
|
|
|
|
|
|
|
|
|
.. rst-class:: api_tabs
|
|
|
|
|
.. tabs::
|
|
|
|
|
|
|
|
|
|
.. tab:: Console
|
|
|
|
|
|
|
|
|
|
Head to the ``Modify`` tab of the table and toggle the switch in the
|
|
|
|
|
``Set table as enum`` section:
|
|
|
|
|
|
2020-05-05 06:52:08 +03:00
|
|
|
|
.. thumbnail:: /img/graphql/manual/schema/enum-set.png
|
2020-01-08 16:20:18 +03:00
|
|
|
|
:alt: Set table as enum
|
2019-09-03 17:17:36 +03:00
|
|
|
|
|
|
|
|
|
.. tab:: API
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
A table can be set as an enum via the following 2 methods:
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
- passing ``true`` for the ``is_enum`` option of the :ref:`track_table` API while tracking a table
|
|
|
|
|
- using the :ref:`set_table_is_enum` API to change whether or not an already-tracked table should be used as
|
|
|
|
|
an enum
|
|
|
|
|
|
|
|
|
|
Using an enum table
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
To set a field of a table as an enum in the GraphQL schema, we need to set a reference from it to the enum table
|
|
|
|
|
via a foreign key.
|
|
|
|
|
|
|
|
|
|
**For example**, to update our ``users`` table to reference the ``user_role`` enum table:
|
|
|
|
|
|
|
|
|
|
.. code-block:: sql
|
|
|
|
|
|
|
|
|
|
ALTER TABLE users ADD CONSTRAINT
|
|
|
|
|
users_role_fkey FOREIGN KEY (role) REFERENCES user_role;
|
|
|
|
|
|
|
|
|
|
Making queries using enum values
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
Once a table has been tracked as an enum, the GraphQL schema will be updated to expose the values of the
|
|
|
|
|
table as GraphQL enum values i.e. only the exposed values will be permitted for all fields referencing to it.
|
|
|
|
|
|
|
|
|
|
**For example**, the ``role`` column of the ``users`` table only permits the values in the ``user_role`` table:
|
2019-08-16 00:19:47 +03:00
|
|
|
|
|
|
|
|
|
.. code-block:: graphql
|
|
|
|
|
|
|
|
|
|
type users {
|
|
|
|
|
id: Int!
|
|
|
|
|
name: String!
|
|
|
|
|
role: user_role_enum!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum user_role_enum {
|
|
|
|
|
"Users with the privilege to set users’ roles"
|
|
|
|
|
administrator
|
|
|
|
|
|
|
|
|
|
"Users with the privilege to ban users"
|
|
|
|
|
moderator
|
|
|
|
|
|
|
|
|
|
"Ordinary users"
|
|
|
|
|
user
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
When making queries that filter on the ``role`` column, use the name of the enum value directly rather than providing
|
|
|
|
|
a string:
|
|
|
|
|
|
|
|
|
|
.. graphiql::
|
|
|
|
|
:view_only:
|
|
|
|
|
:query:
|
|
|
|
|
{
|
2019-09-03 17:17:36 +03:00
|
|
|
|
users(
|
|
|
|
|
where: {
|
|
|
|
|
role: {_eq: administrator}
|
|
|
|
|
}
|
|
|
|
|
) {
|
2019-08-16 00:19:47 +03:00
|
|
|
|
id
|
|
|
|
|
name
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
:response:
|
|
|
|
|
{
|
|
|
|
|
"data": {
|
|
|
|
|
"users": [
|
|
|
|
|
{
|
|
|
|
|
"id": 1,
|
|
|
|
|
"name": "Alyssa"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Enums and migrations
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
As enum tables have a requirement to contain at least 1 row, it is necessary to have a migration which inserts
|
|
|
|
|
values into an enum table. Otherwise while applying migrations an error would be thrown while trying to set the
|
|
|
|
|
table as an enum.
|
|
|
|
|
|
|
|
|
|
The migration which inserts values into an enum table needs to be between the migration creating the table
|
|
|
|
|
and the migration setting it as an enum.
|
|
|
|
|
|
|
|
|
|
This can be achieved via the console by performing the following steps while setting up an enum table:
|
|
|
|
|
|
|
|
|
|
1. Create the enum table
|
|
|
|
|
2. Use the ``RawSQL`` tab of the console to insert the enum values into the table and mark the insert as a migration
|
|
|
|
|
3. Set the table as an enum
|
|
|
|
|
|
2020-03-11 22:42:36 +03:00
|
|
|
|
You can also :ref:`manually create migration files <manual_migrations>` to achieve
|
2019-09-03 17:17:36 +03:00
|
|
|
|
this.
|
|
|
|
|
|
|
|
|
|
Current limitations
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^
|
2018-09-11 14:11:24 +03:00
|
|
|
|
|
2019-09-03 17:17:36 +03:00
|
|
|
|
Currently, Hasura does not automatically detect changes to the contents of enum tables, so the GraphQL schema will
|
2019-11-12 16:09:00 +03:00
|
|
|
|
only be updated after :ref:`manually reloading metadata <reload_metadata_manual>` after inserting, updating, or deleting rows from an enum table.
|