2020-03-11 13:09:25 +03:00
.. meta ::
:description: Creating Hasura actions
2020-05-13 15:33:16 +03:00
:keywords: hasura, docs, actions, create
2020-03-11 13:09:25 +03:00
2020-03-11 22:42:36 +03:00
.. _create_actions:
2020-02-24 19:19:14 +03:00
Creating actions
================
.. contents :: Table of contents
:backlinks: none
2020-05-21 20:27:11 +03:00
:depth: 2
2020-02-24 19:19:14 +03:00
:local:
Introduction
------------
2020-04-16 10:25:19 +03:00
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.
2020-02-24 19:19:14 +03:00
To create an action, you have to:
2020-04-16 10:25:19 +03:00
1. Define the query or mutation
2020-02-24 19:19:14 +03:00
2. Define the required types
3. Create a handler
2020-04-16 10:25:19 +03:00
Let's look at **examples** for mutation and query type actions.
2020-02-24 19:19:14 +03:00
2020-04-16 10:25:19 +03:00
Setup
-----
2020-02-24 19:19:14 +03:00
.. rst-class :: api_tabs
.. tabs ::
2020-03-03 15:02:40 +03:00
.. tab :: Console
There is no setup required for defining actions via the console.
2020-02-24 19:19:14 +03:00
.. tab :: CLI
2020-04-29 10:46:02 +03:00
:ref: `Install <install_hasura_cli>` or :ref: `update to <hasura_update-cli>`
the latest version of Hasura CLI.
2020-02-24 19:19:14 +03:00
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.
2020-04-29 10:46:02 +03:00
If you see `` version: 1 `` (or do not see a version key), upgrade to version
2 by running:
2020-02-24 19:19:14 +03:00
.. code-block :: bash
hasura scripts update-config-v2
Run `` hasura metadata export `` so that you get server's metadata into the
`` metadata/ `` directory.
2020-10-13 14:07:46 +03:00
.. tab :: API
There is no setup required for defining actions via the :ref: `actions metadata API <api_actions>` .
2020-02-24 19:19:14 +03:00
2020-04-16 10:25:19 +03:00
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 `` .
2020-04-29 10:46:02 +03:00
.. contents ::
:backlinks: none
:depth: 1
:local:
2020-02-24 19:19:14 +03:00
Step 1: Define your mutation and associated types
2020-04-16 10:25:19 +03:00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2020-04-29 10:46:02 +03:00
Start with defining the mutation and the required types. These types will reflect
in the GraphQL schema.
2020-02-24 19:19:14 +03:00
.. 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:
2020-08-25 19:21:21 +03:00
.. thumbnail :: /img/graphql/core/actions/mutation-action-create.png
2020-02-24 19:19:14 +03:00
:alt: Console action create
2020-04-29 10:46:02 +03:00
:width: 70%
2020-02-24 19:19:14 +03:00
Define the action as follows in the `` Action Definition `` editor.
.. code-block :: graphql
type Mutation {
2020-04-16 10:25:19 +03:00
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!
}
2020-10-13 14:07:46 +03:00
.. 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"
}
}
}
2020-04-16 10:25:19 +03:00
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
2020-04-29 10:46:02 +03:00
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
2020-04-16 10:25:19 +03:00
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
2020-04-20 09:18:00 +03:00
also have this handler ready at `` https://hasura-actions-demo.glitch.me/login `` .
2020-04-16 10:25:19 +03:00
**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
2020-10-13 14:07:46 +03:00
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.
2020-04-16 10:25:19 +03:00
2020-10-13 14:07:46 +03:00
.. 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>` .
2020-04-16 10:25:19 +03:00
.. 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 ``
2020-08-18 14:45:37 +03:00
.. 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.
2020-04-16 10:25:19 +03:00
Step 3: Finish action creation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Finally, to save the action:
.. rst-class :: api_tabs
.. tabs ::
.. tab :: Console
2020-10-13 14:07:46 +03:00
Hit `` Create `` .
2020-04-16 10:25:19 +03:00
.. tab :: CLI
2020-10-13 14:07:46 +03:00
Run `` hasura metadata apply `` .
.. tab :: API
2020-04-16 10:25:19 +03:00
2020-10-13 14:07:46 +03:00
An action will be created when sending a request to the :ref: `create_action metadata API <create_action>` .
2020-04-16 10:25:19 +03:00
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 `` .
2020-04-29 10:46:02 +03:00
.. contents ::
:backlinks: none
:depth: 1
:local:
2020-04-16 10:25:19 +03:00
Step 1: Define your query and associated types
2020-04-29 10:46:02 +03:00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2020-04-16 10:25:19 +03:00
2020-04-29 10:46:02 +03:00
Start with defining the query and the required types. These types will reflect in
the GraphQL schema.
2020-04-16 10:25:19 +03:00
.. 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:
2020-08-25 19:21:21 +03:00
.. thumbnail :: /img/graphql/core/actions/query-action-create.png
2020-04-16 10:25:19 +03:00
:alt: Console action create
2020-04-29 10:46:02 +03:00
:width: 70%
2020-04-16 10:25:19 +03:00
Define the action as follows in the `` Action Definition `` editor.
.. code-block :: graphql
type Query {
2020-02-24 19:19:14 +03:00
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
2020-04-16 10:25:19 +03:00
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
2020-02-24 19:19:14 +03:00
and save:
.. code-block :: graphql
2020-04-16 10:25:19 +03:00
type Query {
2020-02-24 19:19:14 +03:00
addNumbers (numbers: [Int]): AddResult
}
type AddResult {
sum: Int
}
2020-10-13 14:07:46 +03:00
.. 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"
}
}
}
2020-02-24 19:19:14 +03:00
The above definition means:
2020-04-16 10:25:19 +03:00
* This action will be available in your GraphQL schema as a query called `` addNumbers ``
2020-02-24 19:19:14 +03:00
* 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
2020-04-16 10:25:19 +03:00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2020-02-24 19:19:14 +03:00
A handler is an HTTP webhook where you can perform the custom logic for the
2020-04-29 10:46:02 +03:00
action.
In this case, it is the addition of the numbers. NodeJS/Express code
2020-02-24 19:19:14 +03:00
for this handler would look something like:
.. code-block :: js
const handler = (req, resp) => {
2020-02-25 18:30:00 +03:00
// You can access their arguments input at req.body.input
2020-02-24 19:19:14 +03:00
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'
})
}
};
2020-04-16 10:25:19 +03:00
You can deploy this code somewhere and get the URI. For getting started quickly, we
2020-04-20 09:18:00 +03:00
also have this handler ready at `` https://hasura-actions-demo.glitch.me/addNumbers `` .
2020-02-24 19:19:14 +03:00
2020-04-16 10:25:19 +03:00
**Set the handler**
2020-02-24 19:19:14 +03:00
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.
2020-10-13 14:07:46 +03:00
.. 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>` .
2020-02-25 18:30:00 +03:00
.. admonition :: URL templating
To manage handler endpoints across environments it is possible to template
the endpoints using ENV variables.
2020-03-11 11:54:12 +03:00
e.g. `` https://my-handler-endpoint/addNumbers `` can be templated to `` {{ACTION_BASE_ENDPOINT}}/addNumbers ``
2020-02-25 18:30:00 +03:00
where `` ACTION_BASE_ENDPOINT `` is an ENV variable whose value is set to `` https://my-handler-endpoint ``
2020-02-24 19:19:14 +03:00
Step 3: Finish action creation
2020-04-16 10:25:19 +03:00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Finally, to save the action:
2020-02-24 19:19:14 +03:00
.. rst-class :: api_tabs
.. tabs ::
.. tab :: Console
Hit `` Create `` .
.. tab :: CLI
Run `` hasura metadata apply `` .
2020-10-13 14:07:46 +03:00
.. tab :: API
An action will be created when sending a request to the :ref: `create_action metadata API <create_action>` .
2020-02-24 19:19:14 +03:00
Step 4: Try it out
2020-04-16 10:25:19 +03:00
~~~~~~~~~~~~~~~~~~
2020-02-24 19:19:14 +03:00
In the Hasura console, head to the `` GraphiQL `` tab and try out the new action.
.. graphiql ::
:view_only:
:query:
2020-04-16 10:25:19 +03:00
query {
2020-02-24 19:19:14 +03:00
addNumbers(numbers: [1, 2, 3, 4]) {
sum
}
}
:response:
{
"data": {
"addNumbers": {
"sum": 10
}
}
}
2020-04-16 10:25:19 +03:00
And that's it. You have extended your Hasura schema with a new query.