mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 12:31:52 +03:00
3ad42ed540
Co-authored-by: Rikin Kachhia <54616969+rikinsk@users.noreply.github.com> GitOrigin-RevId: d5b680ce50bfa331bbead32ffe8ad7ba95a6b51f
568 lines
15 KiB
ReStructuredText
568 lines
15 KiB
ReStructuredText
.. meta::
|
|
:description: Creating Hasura actions
|
|
:keywords: hasura, docs, actions, create
|
|
|
|
.. _create_actions:
|
|
|
|
Creating actions
|
|
================
|
|
|
|
.. contents:: Table of contents
|
|
:backlinks: none
|
|
:depth: 2
|
|
:local:
|
|
|
|
Introduction
|
|
------------
|
|
|
|
An action is a GraphQL query or mutation. You have to define the GraphQL type of the
|
|
arguments that the query or mutation accepts and the GraphQL type of its response.
|
|
|
|
To create an action, you have to:
|
|
|
|
1. Define the query or mutation
|
|
2. Define the required types
|
|
3. Create a handler
|
|
|
|
Let's look at **examples** for mutation and query type actions.
|
|
|
|
Setup
|
|
-----
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
There is no setup required for defining actions via the console.
|
|
|
|
.. tab:: CLI
|
|
|
|
:ref:`Install <install_hasura_cli>` or :ref:`update to <hasura_update-cli>`
|
|
the latest version of Hasura CLI.
|
|
|
|
You can either get started with an existing project or create a new project.
|
|
|
|
**For a new project**
|
|
|
|
.. code-block:: bash
|
|
|
|
hasura init
|
|
|
|
This will create a new project. You can set up your GraphQL engine endpoint
|
|
(and admin secret if it exists) in the ``config.yaml``.
|
|
|
|
Run ``hasura metadata export`` so that you get server's metadata into the
|
|
``metadata/`` directory.
|
|
|
|
**For existing projects**
|
|
|
|
Actions are supported only in the v2 config of the CLI. Check the ``config.yaml``
|
|
of your Hasura project for the ``version`` key.
|
|
|
|
If you see ``version: 1`` (or do not see a version key), upgrade to version
|
|
2 by running:
|
|
|
|
.. code-block:: bash
|
|
|
|
hasura scripts update-config-v2
|
|
|
|
Run ``hasura metadata export`` so that you get server's metadata into the
|
|
``metadata/`` directory.
|
|
|
|
.. tab:: API
|
|
|
|
There is no setup required for defining actions via the :ref:`actions metadata API <api_actions>`.
|
|
|
|
Mutation type action
|
|
--------------------
|
|
|
|
Let's start with a mutation that accepts a username and password, and returns
|
|
an access token. We'll call this mutation ``login``.
|
|
|
|
.. contents::
|
|
:backlinks: none
|
|
:depth: 1
|
|
:local:
|
|
|
|
Step 1: Define your mutation and associated types
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Start with defining the mutation and the required types. These types will reflect
|
|
in the GraphQL schema.
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
Go to the ``Actions`` tab on the console and click on ``Create``. This will
|
|
take you to a page like this:
|
|
|
|
.. thumbnail:: /img/graphql/core/actions/mutation-action-create.png
|
|
:alt: Console action create
|
|
:width: 70%
|
|
|
|
Define the action as follows in the ``Action Definition`` editor.
|
|
|
|
.. code-block:: graphql
|
|
|
|
type Mutation {
|
|
login (username: String!, password: String!): LoginResponse
|
|
}
|
|
|
|
In the above action, we called the returning object type to be ``LoginResponse``.
|
|
Define it in the ``New types definition`` as:
|
|
|
|
.. code-block:: graphql
|
|
|
|
type LoginResponse {
|
|
accessToken: String!
|
|
}
|
|
|
|
.. tab:: CLI
|
|
|
|
To create an action, run
|
|
|
|
.. code-block:: bash
|
|
|
|
hasura actions create login
|
|
|
|
This will open up an editor with ``metadata/actions.graphql``. You can enter
|
|
the action's mutation definition and the required types in this file. For your
|
|
``login`` mutation, replace the content of this file with the following
|
|
and save:
|
|
|
|
.. code-block:: graphql
|
|
|
|
type Mutation {
|
|
login (username: String!, password: String!): LoginResponse
|
|
}
|
|
|
|
type LoginResponse {
|
|
accessToken: String!
|
|
}
|
|
|
|
.. tab:: API
|
|
|
|
It is essential that the custom types used in the action are defined *beforehand* via the :ref:`set_custom_types metadata API <set_custom_types>`:
|
|
|
|
.. code-block:: http
|
|
|
|
POST /v1/query HTTP/1.1
|
|
Content-Type: application/json
|
|
X-Hasura-Role: admin
|
|
|
|
{
|
|
"type": "set_custom_types",
|
|
"args": {
|
|
"scalars": [],
|
|
"enums": [],
|
|
"input_objects": [],
|
|
"objects": [
|
|
{
|
|
"name": "LoginResponse",
|
|
"fields": [
|
|
{
|
|
"name": "accessToken",
|
|
"type": "String!"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
Once the custom types are defined, we can create an action via the :ref:`create_action metadata API <create_action>`:
|
|
|
|
.. code-block:: http
|
|
|
|
POST /v1/query HTTP/1.1
|
|
Content-Type: application/json
|
|
X-Hasura-Role: admin
|
|
|
|
{
|
|
"type": "create_action",
|
|
"args": {
|
|
"name": "Login",
|
|
"definition": {
|
|
"kind": "synchronous",
|
|
"type": "mutation",
|
|
"arguments": [
|
|
{
|
|
"name": "username",
|
|
"type": "String!"
|
|
},
|
|
{
|
|
"name": "password",
|
|
"type": "String!"
|
|
}
|
|
],
|
|
"output_type": "LoginResponse",
|
|
"handler": "https://hasura-actions-demo.glitch.me/login"
|
|
}
|
|
}
|
|
}
|
|
|
|
The above definition means:
|
|
|
|
* This action will be available in your GraphQL schema as a mutation called ``login``.
|
|
* It accepts two arguments called ``username`` and ``password`` of type ``String!``.
|
|
* It returns an output type called ``LoginResponse``.
|
|
* ``LoginResponse`` is a simple object type with a field called ``accessToken`` of type ``String!``.
|
|
|
|
Step 2: Create the action handler
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A handler is an HTTP webhook where you can perform the custom logic for the
|
|
action.
|
|
|
|
In this case, we will just return an access token, but typically you would want
|
|
to run all the business logic that the action demands. NodeJS/Express code
|
|
for this handler would look something like:
|
|
|
|
.. code-block:: js
|
|
|
|
const handler = (req, resp) => {
|
|
// You can access their arguments input at req.body.input
|
|
const { username, password } = req.body.input;
|
|
|
|
// perform your custom business logic
|
|
// check if the username and password are valid and login the user
|
|
|
|
// return the response
|
|
return resp.json({
|
|
accessToken: "Ew8jkGCNDGAo7p35RV72e0Lk3RGJoJKB"
|
|
})
|
|
|
|
};
|
|
|
|
You can deploy this code somewhere and get the URI. For getting started quickly, we
|
|
also have this handler ready at ``https://hasura-actions-demo.glitch.me/login``.
|
|
|
|
**Set the handler**
|
|
|
|
Now, set the handler for the action:
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
Set the value of the ``handler`` field to the above endpoint.
|
|
|
|
.. tab:: CLI
|
|
|
|
Go to ``metadata/actions.yaml``. You must see a handler like ``http://localhost:3000`` or ``http://host.docker.internal:3000`` under the action named ``login``. This is a default value taken from ``config.yaml``.
|
|
Update the ``handler`` to the above endpoint.
|
|
|
|
.. tab:: API
|
|
|
|
The action handler must be set when creating an action via the :ref:`create_action metadata API <create_action>`.
|
|
|
|
It can be updated later by using the :ref:`update_action metadata API <update_action>`.
|
|
|
|
.. admonition:: URL templating
|
|
|
|
To manage handler endpoints across environments it is possible to template
|
|
the endpoints using ENV variables.
|
|
|
|
e.g. ``https://my-handler-endpoint/addNumbers`` can be templated to ``{{ACTION_BASE_ENDPOINT}}/addNumbers``
|
|
where ``ACTION_BASE_ENDPOINT`` is an ENV variable whose value is set to ``https://my-handler-endpoint``
|
|
|
|
.. note::
|
|
|
|
If you are running Hasura using Docker, ensure that the Hasura Docker container can reach the handler endpoint.
|
|
See :ref:`this page <docker_networking>` for Docker networking.
|
|
|
|
Step 3: Finish action creation
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Finally, to save the action:
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
Hit ``Create``.
|
|
|
|
.. tab:: CLI
|
|
|
|
Run ``hasura metadata apply``.
|
|
|
|
.. tab:: API
|
|
|
|
An action will be created when sending a request to the :ref:`create_action metadata API <create_action>`.
|
|
|
|
Step 4: Try it out
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
In the Hasura console, head to the ``GraphiQL`` tab and try out the new action.
|
|
|
|
.. graphiql::
|
|
:view_only:
|
|
:query:
|
|
mutation {
|
|
login (username: "jondoe", password: "mysecretpassword") {
|
|
accessToken
|
|
}
|
|
}
|
|
:response:
|
|
{
|
|
"data": {
|
|
"login": {
|
|
"accessToken": "Ew8jkGCNDGAo7p35RV72e0Lk3RGJoJKB"
|
|
}
|
|
}
|
|
}
|
|
|
|
And that's it. You have extended your Hasura schema with a new mutation.
|
|
|
|
Query type action
|
|
-----------------
|
|
|
|
Let's start with a basic query that accepts a list of numbers and returns
|
|
their sum. We'll call this query ``addNumbers``.
|
|
|
|
.. contents::
|
|
:backlinks: none
|
|
:depth: 1
|
|
:local:
|
|
|
|
|
|
Step 1: Define your query and associated types
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Start with defining the query and the required types. These types will reflect in
|
|
the GraphQL schema.
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
Go to the ``Actions`` tab on the console and click on ``Create``. This will
|
|
take you to a page like this:
|
|
|
|
.. thumbnail:: /img/graphql/core/actions/query-action-create.png
|
|
:alt: Console action create
|
|
:width: 70%
|
|
|
|
Define the action as follows in the ``Action Definition`` editor.
|
|
|
|
.. code-block:: graphql
|
|
|
|
type Query {
|
|
addNumbers (numbers: [Int]): AddResult
|
|
}
|
|
|
|
In the above action, we called the returning object type to be ``AddResult``.
|
|
Define it in the ``New types definition`` as:
|
|
|
|
.. code-block:: graphql
|
|
|
|
type AddResult {
|
|
sum: Int
|
|
}
|
|
|
|
.. tab:: CLI
|
|
|
|
To create an action, run
|
|
|
|
.. code-block:: bash
|
|
|
|
hasura actions create addNumbers
|
|
|
|
This will open up an editor with ``metadata/actions.graphql``. You can enter
|
|
the action's query definition and the required types in this file. For your
|
|
``addNumbers`` query, replace the content of this file with the following
|
|
and save:
|
|
|
|
.. code-block:: graphql
|
|
|
|
type Query {
|
|
addNumbers (numbers: [Int]): AddResult
|
|
}
|
|
|
|
type AddResult {
|
|
sum: Int
|
|
}
|
|
|
|
.. tab:: API
|
|
|
|
It is essential that the custom types used in the action are defined *beforehand* via the :ref:`set_custom_types metadata API <set_custom_types>`:
|
|
|
|
.. code-block:: http
|
|
|
|
POST /v1/query HTTP/1.1
|
|
Content-Type: application/json
|
|
X-Hasura-Role: admin
|
|
|
|
{
|
|
"type": "set_custom_types",
|
|
"args": {
|
|
"scalars": [],
|
|
"enums": [],
|
|
"input_objects": [],
|
|
"objects": [
|
|
{
|
|
"name": "AddResult",
|
|
"fields": [
|
|
{
|
|
"name": "sum",
|
|
"type": "Int!"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
Once the custom types are defined, we can create an action via the :ref:`create_action metadata API <create_action>`:
|
|
|
|
.. code-block:: http
|
|
|
|
POST /v1/query HTTP/1.1
|
|
Content-Type: application/json
|
|
X-Hasura-Role: admin
|
|
|
|
{
|
|
"type":"create_action",
|
|
"args": {
|
|
"name":"addNumbers",
|
|
"definition": {
|
|
"kind":"synchronous",
|
|
"type": "query",
|
|
"arguments":[
|
|
{
|
|
"name":"numbers",
|
|
"type":"[Int]!"
|
|
}
|
|
],
|
|
"output_type":"AddResult",
|
|
"handler":"https://hasura-actions-demo.glitch.me/addNumbers"
|
|
}
|
|
}
|
|
}
|
|
|
|
The above definition means:
|
|
|
|
* This action will be available in your GraphQL schema as a query called ``addNumbers``
|
|
* It accepts an argument called ``numbers`` which is a list of integers.
|
|
* It returns an output type called ``AddResult``.
|
|
* ``AddResult`` is a simple object type with a field called ``sum`` of type integer.
|
|
|
|
Step 2: Create the action handler
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A handler is an HTTP webhook where you can perform the custom logic for the
|
|
action.
|
|
|
|
In this case, it is the addition of the numbers. NodeJS/Express code
|
|
for this handler would look something like:
|
|
|
|
.. code-block:: js
|
|
|
|
const handler = (req, resp) => {
|
|
// You can access their arguments input at req.body.input
|
|
const { numbers } = req.body.input;
|
|
|
|
// perform your custom business logic
|
|
// return an error or response
|
|
try {
|
|
return resp.json({
|
|
sum: numbers.reduce((s, n) => s + n, 0)
|
|
});
|
|
} catch(e) {
|
|
console.error(e)
|
|
return resp.status(500).json({
|
|
message: 'unexpected'
|
|
})
|
|
}
|
|
};
|
|
|
|
You can deploy this code somewhere and get the URI. For getting started quickly, we
|
|
also have this handler ready at ``https://hasura-actions-demo.glitch.me/addNumbers``.
|
|
|
|
**Set the handler**
|
|
|
|
Now, set the handler for the action:
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
Set the value of the ``handler`` field to the above endpoint.
|
|
|
|
.. tab:: CLI
|
|
|
|
Go to ``metadata/actions.yaml``. You must see a handler like ``http://localhost:3000``
|
|
or ``http://host.docker.internal:3000`` under the action named ``addNumbers``.
|
|
This is a default value taken from ``config.yaml``.
|
|
|
|
Update the ``handler`` to the above endpoint.
|
|
|
|
.. tab:: API
|
|
|
|
The action handler must be set when creating an action via the Once the custom types are defined, we can create an action via the :ref:`create_action metadata API <create_action>`.
|
|
|
|
It can be updated later by using the :ref:`update_action metadata API <update_action>`.
|
|
|
|
.. admonition:: URL templating
|
|
|
|
To manage handler endpoints across environments it is possible to template
|
|
the endpoints using ENV variables.
|
|
|
|
e.g. ``https://my-handler-endpoint/addNumbers`` can be templated to ``{{ACTION_BASE_ENDPOINT}}/addNumbers``
|
|
where ``ACTION_BASE_ENDPOINT`` is an ENV variable whose value is set to ``https://my-handler-endpoint``
|
|
|
|
Step 3: Finish action creation
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Finally, to save the action:
|
|
|
|
.. rst-class:: api_tabs
|
|
.. tabs::
|
|
|
|
.. tab:: Console
|
|
|
|
Hit ``Create``.
|
|
|
|
.. tab:: CLI
|
|
|
|
Run ``hasura metadata apply``.
|
|
|
|
.. tab:: API
|
|
|
|
An action will be created when sending a request to the :ref:`create_action metadata API <create_action>`.
|
|
|
|
|
|
Step 4: Try it out
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
In the Hasura console, head to the ``GraphiQL`` tab and try out the new action.
|
|
|
|
.. graphiql::
|
|
:view_only:
|
|
:query:
|
|
query {
|
|
addNumbers(numbers: [1, 2, 3, 4]) {
|
|
sum
|
|
}
|
|
}
|
|
:response:
|
|
{
|
|
"data": {
|
|
"addNumbers": {
|
|
"sum": 10
|
|
}
|
|
}
|
|
}
|
|
|
|
And that's it. You have extended your Hasura schema with a new query.
|
|
|
|
.. admonition:: Additional Resources
|
|
|
|
Introduction to Hasura Actions - `View Recording <https://hasura.io/events/webinar/hasura-actions/?pg=docs&plcmt=body&cta=view-recording&tech=>`__. |