Hasura DDN streamlines the development and operation of modern federated data APIs, solving key challenges in the process.
With powerful tooling and a code-driven workflow, Hasura DDN is not just an improvement in the data access
portion of the product development lifecycle, but a complete game-changer in the way you create, run, and manage APIs.
For information on upgrading from Hasura v2, see the [upgrade guide](/upgrade/overview.mdx).
Quickstart. Free. Easy. Fast.
==============================
# quickstart.mdx
URL: https://hasura.io/docs/3.0/docs/quickstart
# Quickstart with Hasura DDN
In less than a minute and without needing a data source connection string, you can have a supergraph API
running locally and deployed on Hasura DDN. Check out the video [here](https://www.youtube.com/watch?v=OsO6TzwFb30).
## Prerequisites
1. **Install the DDN CLI**
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
2. **Install [Docker](https://docs.docker.com/engine/install/)** (needed for local development)
The Docker based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making
the development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
3. **Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
{`ddn doctor`}
After you log in, the CLI will acknowledge your login and give you access to Hasura Cloud resources.
Once you move into this new directory, you'll see your project's files scaffolded out for you by running `ls`.
From the dropdown, choose the `hasura/postgres` data connector and connect to our sample PostgreSQL database using this
URL: `postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app`
This will create Hasura metadata describing the schema of your data source. After running this, you should see tables
present in your `app/connector/my_connector/configuration.json` file, which you can open in your preferred editor.
Create metadata for [models](/reference/metadata-reference/models.mdx),
[commands](/reference/metadata-reference/commands.mdx), and
[relationships](/reference/metadata-reference/relationships.mdx) in your supergraph. These define the structure,
operations, and connections within your supergraph and are generated as Hasura Metadata Language (HML) present in your
`app/metadata` directory.
Create an immutable build of your supergraph and the local assets to run the engine. The build is stored as a set of
JSON files in `engine/build`.
Start your engine, connector, and other services. You can see a list of all running services using `docker ps`.
In a new terminal tab open to the project's directory, open the console β Hasura DDN's GUI β so you can test out your
API.
Execute this query via your console. You'll see a list of all users and their orders returned!
Provision a new project on Hasura DDN, which serves as the deployment environment for your supergraph. Once complete,
the CLI will return the project's name.
When this process is complete, the CLI will return a link to the hosted API where you can run the same query as before.
**That's it! you've created your very first, fully-functioning supergraph and deployed it to Hasura DDN π**
## Next steps
What we've laid out above is the quickest way to get started with Hasura DDN. However, you have complete, granular
control over each step in the process and can extend and customize your supergraph to fit your teams' needs. After
deploying your first supergraph, take your next steps with Hasura DDN π
- [Connect to your own PostgreSQL](/how-to-build-with-ddn/with-postgresql.mdx)
==============================
# overview.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/overview
# Basics
## Introduction
Hasura DDN enables a powerful, yet simple-to-create API on all your data. Each tutorial in this section will guide you
through creating your first Hasura DDN API on top of your preferred data source. First, let's go over the broad stokes
of how it works.
### You have data
Your data can live in a variety of places - relational or non-relational databases, OLAP or OLTP databases, vector
stores, third-party services, or output by your code. With Hasura you can access all of it from a single API endpoint.
### You connect it to Hasura
Regardless of where your data lives, you'll connect it to your API using a **native data connector**. These connectors
will introspect your data sources and generate metadata that Hasura DDN will use to build your API.
### You configure your API in metadata
This **semantic metadata layer** is written as Hasura Metadata Language (HML), which is an extension of YAML. You can
use the DDN CLI and your text editor to fine-tune and configure the API to your liking; this includes easily creating
metadata objects for permissions, relationships, configuring authentication, etc.
### You serve the API
To convert your metadata into something that you can query, you'll create a build - an immutable snapshot of your API at
that point in time which the Hasura Engine can run and serve. You can then use the **console** - Hasura's GUI - to query
and analyze your GraphQL API, or integrate it with your client apps.
You can serve this API locally during development for testing and iteration, on Hasura DDN for scalability and ease of
use, or on your own infrastructure if you need complete control and customization.
## Get started with your favorite source
- [PostgreSQL](/how-to-build-with-ddn/with-postgresql.mdx)
- [MongoDB](/how-to-build-with-ddn/with-mongodb.mdx)
- [ClickHouse](/how-to-build-with-ddn/with-clickhouse.mdx)
- [Oracle](/how-to-build-with-ddn/with-oracle.mdx)
- [Others](/how-to-build-with-ddn/with-others.mdx)
==============================
# with-bigquery.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-bigquery
# Get Started with Hasura DDN and BigQuery
## Overview
This tutorial will guide you through setting up a Hasura DDN project with Google BigQuery. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a BigQuery database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
- Mutate data
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
You'll also need:
- A Google Cloud account and project
- A Google Cloud service account with appropriate permissions for the project and BigQuery dataset you'll use.
- The service account JSON key file downloaded from Google Cloud Console
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Create a new BigQuery dataset
In the Google Cloud console, navigate to the [BigQuery page](https://console.cloud.google.com/bigquery) and create a new
dataset called `hasura_demo`. You can do so by clicking "Studio" in the left sidebar, then with the three dots button
next to the project name in the "Explorer", selecting "Create data set". Give it an id of `hasura_demo` and click
"Create data set".
Make sure that in "IAM and admin", in "Service accounts", you have a service account with at least the "BigQuery User"
role. Using the "Owner" or "BigQuery Admin" role will cover this role too.
You should also create and download a JSON key file for the service account by clicking on its name and selecting "Keys"
in the navigation menu.
The JSON key file should look like this example:
```json
{
"type": "service_account",
"project_id": "project-id",
"private_key_id": "private-key-id",
"private_key": "-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n",
"client_email": "service-account-email",
"client_id": "client-id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
}
```
### Step 4. Seed your BigQuery database
Create a table in your data set with name `users` by clicking on the `hasura_demo` dataset in the BigQuery console and
then clicking on the `+ Create table` button in the top right. Select `Empty table` and give it a name.
You can use the following schema text definition to create the table in the BigQuery console:
```json
[
{
"name": "user_id",
"type": "INTEGER",
"mode": "REQUIRED"
},
{
"name": "name",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "age",
"type": "INTEGER",
"mode": "NULLABLE"
}
]
```
Once created, you can click on the table and the "Query" button to seed the table with some data:
```SQL title="Then, seed the table:"
INSERT INTO hasura_demo.users (user_id, name, age) VALUES (1, 'Alice', 25), (2, 'Bob', 30), (3, 'Charlie', 35);
```
### Step 5. Configure a service account key file
#### Step 5.1. Move your downloaded service account key file to your connector folder
```sh title="Copy your service account key file to the connector directory:"
mv /path/to/your/key.json app/connector/my_bigquery/key.json
```
#### Step 5.2. Configure your JDBC connection string in the connector's environment variable `.env` file. The connection string
should follow this format:
```plaintext
APP_MY_BIGQUERY_JDBC_URL=jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=your-project-id;DefaultDataset=your-dataset;OAuthType=0;OAuthServiceAcctEmail=your-service-account-email;OAuthPvtKey=/etc/connector/your-key.json
```
In the connection string make sure to replace:
- `your-project-id` with your Google Cloud project ID
- `your-dataset` with your default BigQuery dataset
- `your-service-account-email` with your service account email
- `your-key.json` must match the name of the file you placed in the connector folder
### Step 6. Initialize your BigQuery connector
```sh title="In your project directory, run:"
ddn connector init my_bigquery -i
```
From the dropdown, select `hasura/bigquery-jdbc` (you can type to filter the list), and enter the value of the
connection string you just created for the `JDBC_URL` environment variable.
### Step 7. Introspect your BigQuery database
```sh title="Use the CLI to introspect your BigQuery database:"
ddn connector introspect my_bigquery
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_bigquery/configuration.json` file.
```sh title="You can check which resources are available β and their status β at any point using the CLI:"
ddn connector show-resources my_bigquery
```
### Step 8. Add your model
```sh title="Track your BigQuery tables as models in your DDN metadata:"
ddn model add my_bigquery users
```
:::tip Add all resources at once
You can add all of your models at once by running:
```sh
ddn model add my_bigquery "*"
```
You can also do the same for commands and relationships.
:::
Open the `app/metadata` directory and you'll find newly-generated files for each table you track. The DDN CLI uses these
Hasura Metadata Language files to represent your BigQuery tables in your API as
[models](/reference/metadata-reference/models.mdx).
### Step 9. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 10. Start your local services
```sh title="Start your local Hasura DDN Engine and BigQuery connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 11. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write a query for your table:"
query GetUsers {
users {
userId
name
age
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"users": [
{
"userId": "1",
"name": "Alice",
"age": "25"
},
{
"userId": "3",
"name": "Charlie",
"age": "35"
},
{
"userId": "2",
"name": "Bob",
"age": "30"
}
]
}
}
```
### Step 12. Iterate on your BigQuery schema
Add a new table to your BigQuery dataset with the name `posts`. You can use the following schema text definition to
create the table in the console:
```json title="Add a new posts table to your BigQuery dataset:"
[
{
"name": "user_id",
"type": "INTEGER",
"mode": "REQUIRED"
},
{
"name": "post_id",
"type": "INTEGER",
"mode": "REQUIRED"
},
{
"name": "title",
"type": "STRING",
"mode": "NULLABLE",
"maxLength": "255"
},
{
"name": "content",
"type": "STRING",
"mode": "NULLABLE"
}
]
```
Once created, you can click on the table and the "Query" button to seed the table with some data:
```SQL title="Seed the new table:"
INSERT INTO hasura_demo.posts (user_id, post_id, title, content) VALUES
(1, 1, 'My First Post', 'This is the first post for Alice.'),
(1, 2, 'Another Post', 'Alice writes again!'),
(2, 3, 'Bobs Post', 'Bob shares his thoughts.'),
(3, 4, 'Hello World', 'Charlie joins the conversation.');
```
### Step 13. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 13.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_bigquery
```
In `app/connector/my_bigquery/configuration.json`, you'll see schema updated to include operations for the `posts`
table.
#### Step 13.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_bigquery posts
```
In `app/metadata/my_bigquery.hml`, you'll see `posts` present in the metadata.
### Step 14. Create a Relationship in your Hasura metadata
Let's also now create a Relationship in our Hasura metdata for between the `users` and `posts` tables.
[Hasura VS Code Extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) can help you with this.
```yaml title="Add a new relationship to your Hasura metadata:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: Posts
target:
model:
name: Users
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: userId
```
Since this is added directly to the metadata, you don't need to run any commands to add it from the introspection of the
connector. By building your supergraph you will make it available in your API.
### Step 15. Rebuild your project and restart your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring everything back up:"
ddn run docker-start
```
```sh title="Run the console again:"
ddn console --local
```
### Step 16. Run your first query utilizing the relationship
```graphql title="Run your first query with a relationship in the GraphiQL console:"
query GetPostsWithAuthors {
posts {
postId
title
content
user {
age
name
userId
}
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"postId": "1",
"title": "My First Post",
"content": "This is the first post for Alice.",
"user": {
"age": "25",
"name": "Alice",
"userId": "1"
}
},
{
"postId": "2",
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"age": "25",
"name": "Alice",
"userId": "1"
}
},
{
"postId": "3",
"title": "Bobs Post",
"content": "Bob shares his thoughts.",
"user": {
"age": "30",
"name": "Bob",
"userId": "2"
}
},
{
"postId": "4",
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"age": "35",
"name": "Charlie",
"userId": "3"
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with BigQuery! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local BigQuery database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- You learned how to iterate on your schema and refresh your metadata to reflect changes.
- You added a relationship between your `users` and `posts` tables.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our [BigQuery](/reference/connectors/bigquery/index.mdx) docs to learn more about how to use Hasura DDN
with BigQuery.
==============================
# with-clickhouse.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-clickhouse
# Get Started with Hasura DDN and ClickHouse
## Overview
This tutorial takes about fifteen minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a ClickHouse instance
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch. We'll use a locally running ClickHouse instance via Docker and
connect it to Hasura, but you can easily follow the steps if you already have data seeded or are using ClickHouse's
hosted service; Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your ClickHouse connector
```sh title="In your project directory, run:"
ddn connector init my_ch -i
```
From the dropdown, start typing `clickhouse` and hit enter to accept the default port. Then, provide the following
values:
**Connection string**
```plaintext
http://local.hasura.dev:8123
```
**Username**
```plaintext
default_user
```
**Password**
```plaintext
default_password
```
### Step 4. Start the local ClickHouse container
```sh title="Begin by creating a compose file for the ClickHouse service:"
touch app/connector/my_ch/compose.clickhouse.yaml
```
```yaml title="Then, open the file and add the following:"
services:
clickhouse:
image: clickhouse/clickhouse-server
container_name: clickhouse-server
ports:
- "8123:8123"
- "9000:9000"
volumes:
- ./clickhouse-data:/var/lib/clickhouse
environment:
CLICKHOUSE_USER: "default_user"
CLICKHOUSE_PASSWORD: "default_password"
CLICKHOUSE_DB: "default"
```
```sh title="Run the container:"
docker compose -f app/connector/my_ch/compose.clickhouse.yaml up -d
```
```sh title="Send a curl request to create the table in the database:"
curl -u default_user:default_password -X POST \
--data "CREATE TABLE users (user_id UInt32, name String, age UInt8) ENGINE = MergeTree() ORDER BY user_id;" \
http://localhost:8123
```
```sh title="Then, seed the table:"
curl -u default_user:default_password -X POST \
--data "INSERT INTO users (user_id, name, age) VALUES (1, 'Alice', 25), (2, 'Bob', 30), (3, 'Charlie', 35);" \
http://localhost:8123
```
```sh title="You can verify this by running:"
curl -u default_user:default_password -X POST \
--data "SELECT * FROM users;" \
http://localhost:8123
```
You should see a list of users returned.
### Step 5. Introspect your ClickHouse database
```sh title="Next, use the CLI to introspect your ClickHouse database:"
ddn connector introspect my_ch
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_ch/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_ch
```
### Step 6. Add your model
```sh title="Now, track the table from your ClickHouse database as a model in your DDN metadata:"
ddn model add my_ch users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from ClickHouse in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and ClickHouse connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
userId
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"userId": 1,
"name": "Alice",
"age": 25
},
{
"userId": 2,
"name": "Bob",
"age": 30
},
{
"userId": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your ClickHouse schema
```sh title="Let's add a new table for posts:"
curl -u default_user:default_password -X POST \
--data "CREATE TABLE posts (
user_id UInt32,
post_id UInt32,
title String,
content String
) ENGINE = MergeTree()
ORDER BY user_id;" \
http://localhost:8123
```
```sh title="Then, seed it:"
curl -u default_user:default_password -X POST \
--data "INSERT INTO posts (user_id, post_id, title, content) VALUES
(1, 1, 'My First Post', 'This is Alice''s first post.'),
(1, 2, 'Another Post', 'Alice writes again!'),
(2, 3, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 4, 'Hello World', 'Charlie joins the conversation.');" \
http://localhost:8123
```
```sh title="Finally, we can check the posts were generated:"
curl -u default_user:default_password -X POST \
--data "SELECT * FROM posts;" \
http://localhost:8123
```
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_ch
```
In `app/connector/my_ch/configuration.json`, you'll see schema updated to include operations for the `posts` table. In
`app/metadata/my_ch.hml`, you'll see `posts` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_ch posts
```
#### Step 11.3. Kill your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
#### Step 11.4. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.5 Restart your services
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
userId
postId
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"userId": 1,
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"userId": 1,
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"userId": 2,
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"userId": 3,
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 13. Create a relationship
```yaml title="Open the Posts.hml file and add the following to the end:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: Posts
target:
model:
name: Users
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: userId
```
:::tip LSP-Assisted authoring is available
We've created an extension for VS Code that leverages LSP to make authoring these metadata objects easier. Check it out
[here](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura).
:::
### Step 14. Rebuild your project
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 15. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
postId
title
content
user {
userId
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"userId": 2,
"name": "Bob",
"age": 30
}
},
{
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"userId": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with ClickHouse! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local ClickHouse database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our ClickHouse docs to learn more about how to use Hasura DDN with ClickHouse. Or, if you're ready, get
started with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-databricks.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-databricks
# Get Started with Hasura DDN and Databricks
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a hosted Databricks instance
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; you'll connect a hosted Databricks instance to Hasura, but you can
easily follow the steps if you already have data seeded. Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Create and seed a new Databricks database
Head to [Databricks](https://databricks.com) and create an account if you don't already have one. Then, create a new
instance.
From your instance's dashboard, choose `SQL Editor` and create a new query. At the top of the query editor, there will
be a breadcrumb letting you know which **catalog** and **schema** you're currently utilizing. **Before proceeding,
ensure you've selected `main` and `default`**.
```sql title="Then, paste the SQL below to set up your schema:"
CREATE TABLE default.users (
id BIGINT GENERATED ALWAYS AS IDENTITY, name STRING NOT NULL, age INT NOT NULL
);
COMMENT ON TABLE default.users IS 'The users table contains information about application users';
INSERT INTO default.users (name, age)
VALUES
('Alice', 25),
('Bob', 30),
('Charlie', 35);
```
Choose `Run all statements` to create the table, add the comment, and insert the users.
```sql title="You can verify this worked by running the following:"
SELECT * FROM users;
```
### Step 4. Initialize your Databricks connector
```sh title="In your project directory, run:"
ddn connector init my_databricks -i
```
From the dropdown, select `hasura/databricks-jdbc` (you can type to filter the list).
#### `JDBC_URL`
You'll be prompted for your JDBC URL; you can construct the base of this using your Databricks UI under `SQL Warehouses`
Β» `` Β» `Connection details`.
```plaintext title="An example JDBC URL for Databricks using the main catalog:"
jdbc:databricks://:/default;transportMode=http;ssl=1;AuthMech=3;httpPath=/sql/1.0/warehouses/;UID=token;PWD=;ConnCatalog=main;
```
:::info Constructing your JDBC URL
The Databricks connector utilizes a JDBC URL that includes:
- An access token β which you can generate with the `Create a personal access token` button in the top right in the same
UI as where you found the base for your connection string.
- A `ConnCatalog` parameter β which references the catalog to connect to and use for SQL queries during introspection.
:::
#### `JDBC_SCHEMAS`
This comma-separated list of schemas is **case-sensitive** and should not include any spaces. For our tutorial, we'll
simply enter `default` for this value.
### Step 5. Introspect your Databricks instance
```sh title="Next, use the CLI to introspect your Databricks instance:"
ddn connector introspect my_databricks
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_databricks/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_databricks
```
### Step 6. Add your model
```sh title="Now, track the table from your Databricks instance as a model in your DDN metadata:"
ddn model add my_databricks main.default.users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `MainDefaultUsers.hml`. The DDN CLI will use
this Hasura Metadata Language file to represent the `users` table from Databricks in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Databricks connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GetUsers {
mainDefaultUsers {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"mainDefaultUsers": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your Databricks schema
```sql title="Let's add a new table for posts:"
CREATE TABLE posts (
id BIGINT GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
user_id INT NOT NULL,
title STRING NOT NULL,
content STRING NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
USING DELTA
TBLPROPERTIES (
'delta.feature.allowColumnDefaults' = 'supported'
);
COMMENT ON TABLE default.posts IS 'Posts are written by users and mapped to them using their id column';
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice''s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
```
Choose `Run all statements` to create the table, add the comment, and insert the posts.
```sql title="Verify this by running the following query:"
-- Fetch all posts with user information
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_databricks
```
In `app/connector/my_databricks/configuration.json`, you'll see schema updated to include operations for the `posts`
table. In `app/metadata/my_databricks.hml`, you'll see `posts` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_databricks main.default.posts
```
#### Step 11.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
mainDefaultPosts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"mainDefaultPosts": [
{
"id": "1",
"title": "My First Post",
"content": "This is Alices first post."
},
{
"id": "2",
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": "3",
"title": "Bobs Post",
"content": "Bob shares his thoughts."
},
{
"id": "4",
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 13. Create a relationship
```yaml title="Find the MainDefaultPosts.hml file in your connector's metadata directory and add the following relationship object to the bottom:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: MainDefaultPosts
target:
model:
name: MainDefaultUsers
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: id
```
This will create a relationship that maps the `userId` for any post to the `id` of a user, allowing for nested queries.
### Step 14. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 15. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
mainDefaultPosts {
id
title
content
user {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"mainDefaultPosts": [
{
"id": "1",
"title": "My First Post",
"content": "This is Alices first post.",
"user": {
"id": "1",
"name": "Alice",
"age": 25
}
},
{
"id": "2",
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"id": "1",
"name": "Alice",
"age": 25
}
},
{
"id": "3",
"title": "Bobs Post",
"content": "Bob shares his thoughts.",
"user": {
"id": "2",
"name": "Bob",
"age": 30
}
},
{
"id": "4",
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"id": "3",
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with Databricks! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a hosted Databricks instance.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our [Databricks docs](/reference/connectors/databricks/index.mdx) to learn more about how to use Hasura
DDN with Databricks. Or, if you're ready, get started with adding [permissions](/auth/permissions/index.mdx) to control
access to your API.
==============================
# with-duckdb.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-duckdb
# Get Started with Hasura DDN and DuckDB
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a local DuckDB database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; you'll connect a local DuckDB instance to Hasura, but you can easily
follow the steps if you already have data seeded. Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your DuckDB connector
```sh title="In your project directory, run:"
ddn connector init my_duckdb -i
```
From the dropdown, select `/hasura/duckdb` (you can type to filter the list). Then, enter the following file path:
```plaintext
/etc/connector/data.duckdb
```
:::info Why this path?
When your DuckDB connector starts as a container, its directory in your project gets mounted. This will make the
`data.duckdb` file accessible to the container and the connector. We'll create this file in the next step.
:::
### Step 4. Prepare an initial DuckDB file
:::info Install the DuckDB CLI
You'll need the DuckDB CLI installed on your machine to prepare the database file. You can install it
[here](https://duckdb.org/docs/installation/?version=stable&environment=cli&platform=macos&download_method=direct).
:::
```sh itle="Next, from your project's root directory, using the DuckDB CLI run the following:"
echo "
-- Create a sequence since DuckDB doesn't support auto-incrementing
CREATE SEQUENCE users_id_seq;
-- Create the table using the sequence
CREATE TABLE users (
id INTEGER PRIMARY KEY DEFAULT nextval('users_id_seq'),
name VARCHAR(255) NOT NULL,
age INTEGER NOT NULL
);
-- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
" | duckdb app/connector/my_duckdb/data.duckdb
```
You can verify this worked by running the following command from the project's root to query all records from the
`users` table:
```sh
duckdb app/connector/my_duckdb/data.duckdb "SELECT * FROM users;"
```
### Step 5. Introspect your DuckDB database
```sh title="Next, use the CLI to introspect your DuckDB database:"
ddn connector introspect my_duckdb
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_duckdb/config.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_duckdb
```
### Step 6. Add your model
```sh title="Now, track the table from your DuckDB database as a model in your DDN metadata:"
ddn model add my_duckdb users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from DuckDB in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and DuckDB connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your DuckDB schema
```sh title="From the root of the project, use the DuckDB CLI to add a new table and insert some data to your DuckDB database:"
echo "
-- Create a sequence for posts
CREATE SEQUENCE posts_id_seq;
-- Create the posts table
CREATE TABLE posts (
id INTEGER PRIMARY KEY DEFAULT nextval('posts_id_seq'),
user_id INTEGER NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Insert some seed data
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice''s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
" | duckdb app/connector/my_duckdb/data.duckdb
```
```sh title="Verify this by running the following command:"
echo "
-- Fetch all posts with user information
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
" | duckdb app/connector/my_duckdb/data.duckdb
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```plaintext title="First, bring down your running services:"
CTRL + C
```
```sh title="Run the introspection command again:"
ddn connector introspect my_duckdb
```
In `app/connector/my_duckdb/config.json`, you'll see schema updated to include operations for the `posts` table. In
`app/metadata/my_duckdb.hml`, you'll see `posts` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_duckdb posts
```
#### Step 11.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 13. Create a relationship
```yaml title="Find the Posts.hml file in your connector's metadata directory and add the following relationship object to the bottom:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: Posts
target:
model:
name: Users
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: id
```
This will create a relationship that maps the `userId` for any post to the `id` of a user, allowing for nested queries.
### Step 14. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 15. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
id
title
content
user {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"id": 2,
"name": "Bob",
"age": 30
}
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"id": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with DuckDB! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local DuckDB database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our DuckDB docs to learn more about how to use Hasura DDN with DuckDB. Or, if you're ready, get started
with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-elasticsearch.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-elasticsearch
# Get Started with Hasura DDN and Elasticsearch
## Overview
This tutorial takes about fifteen minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to an Elasticsearch instance
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch. We'll use a locally running Elasticsearch instance via Docker and
connect it to Hasura, but you can easily follow the steps if you already have data seeded or are using Elasticsearch's
hosted service; Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Elasticsearch connector
```sh title="In your project directory, run:"
ddn connector init my_es -i
```
From the dropdown, start typing `elasticsearch` and hit enter to accept the default port. Then, provide the following
values:
**ELASTICSEARCH_URL**
```plaintext
http://local.hasura.dev:9200/
```
**ELASTICSEARCH_USERNAME**
```plaintext
elastic
```
**ELASTICSEARCH_PASSWORD**
```plaintext
elastic
```
For now, you can leave the rest of the values as blanks (or their defaults, if present).
### Step 4. Start the local Elasticsearch container
```sh title="Begin by starting a local Elasticsearch container on port 9200:"
docker run --rm -p 127.0.0.1:9200:9200 -d --name elasticsearch \
-e ELASTIC_PASSWORD="elastic" \
-e "discovery.type=single-node" \
-e "xpack.security.http.ssl.enabled=false" \
-e "xpack.license.self_generated.type=trial" \
docker.elastic.co/elasticsearch/elasticsearch:8.17.1
```
```sh title="Then, add a mapping to the database:"
curl -X PUT "http://localhost:9200/customers/" -u elastic:elastic -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"customer_id": {
"type": "keyword"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"email": {
"type": "keyword",
"index": true
},
"location": {
"type": "geo_point"
}
}
}
}
'
```
```sh title="Finally, add some data to the database:"
curl -X POST "http://localhost:9200/_bulk" -u elastic:elastic -H 'Content-Type: application/json' -d'
{ "index": { "_index": "customers", "_id": "1" } }
{ "customer_id": "CUST001", "name": "John Doe", "email": "john.doe@example.com", "location": { "lat": 40.7128, "lon": -74.0060 } }
{ "index": { "_index": "customers", "_id": "2" } }
{ "customer_id": "CUST002", "name": "Jane Smith", "email": "jane.smith@example.com", "location": { "lat": 34.0522, "lon": -118.2437 } }
{ "index": { "_index": "customers", "_id": "3" } }
{ "customer_id": "CUST003", "name": "Alice Johnson", "email": "alice.j@example.com", "location": { "lat": 51.5074, "lon": -0.1278 } }
{ "index": { "_index": "customers", "_id": "4" } }
{ "customer_id": "CUST004", "name": "Bob Brown", "email": "bob.brown@example.com", "location": { "lat": 48.8566, "lon": 2.3522 } }
{ "index": { "_index": "customers", "_id": "5" } }
{ "customer_id": "CUST005", "name": "Charlie Davis", "email": "charlie.d@example.com", "location": { "lat": 35.6895, "lon": 139.6917 } }
'
```
```sh title="You can verify the data by running:"
curl --location 'http://localhost:9200/customers/_search' \
-H 'Content-Type: application/json' \
-u elastic:elastic \
--data '{
"_source": [
"_id",
"name"
]
}'
```
### Step 5. Introspect your Elasticsearch database
```sh title="Next, use the CLI to introspect your Elasticsearch database:"
ddn connector introspect my_es
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_es/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_es
```
### Step 6. Add your model
```sh title="Now, track the index from your Elasticsearch database as a model in your DDN metadata:"
ddn model add my_es customers
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Customers.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `customers` index from Elasticsearch in your API as a
[model](/reference/metadata-reference/models.mdx).
:::tip
You can import all the indexes from your Elasticsearch database as models by running `ddn model add my_es "*"`.
:::
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Elasticsearch connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query MyQuery {
customers {
email
name
}
}
```
```json title="You'll get the following response:"
{
"data": {
"customers": [
{
"email": "john.doe@example.com",
"name": "John Doe"
},
{
"email": "jane.smith@example.com",
"name": "Jane Smith"
},
{
"email": "alice.j@example.com",
"name": "Alice Johnson"
},
{
"email": "bob.brown@example.com",
"name": "Bob Brown"
},
{
"email": "charlie.d@example.com",
"name": "Charlie Davis"
}
]
}
}
```
### Step 10. Iterate on your Elasticsearch schema
```sh title="Let's add a new index for transactions:"
curl -X PUT "http://localhost:9200/transactions/" -u elastic:elastic -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"transaction_id": {
"type": "keyword"
},
"timestamp": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
},
"customer_id": {
"type": "keyword"
},
"transaction_details": {
"properties": {
"item_id": {
"type": "keyword"
},
"item_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"price": {
"type": "float"
},
"quantity": {
"type": "integer"
},
"currency": {
"type": "keyword"
}
}
}
}
}
}
'
```
```sh title="Add some data to the new index:"
curl -X POST "http://localhost:9200/_bulk" -u elastic:elastic -H 'Content-Type: application/json' -d'
{ "index": { "_index": "transactions", "_id": "1" } }
{ "transaction_id": "TXN001", "timestamp": "2024-09-01T12:00:00", "customer_id": "CUST001", "transaction_details": { "item_id": "ITEM001", "item_name": "Laptop", "price": 999.99, "quantity": 1, "currency": "USD" } }
{ "index": { "_index": "transactions", "_id": "2" } }
{ "transaction_id": "TXN002", "timestamp": "2024-09-02T14:30:00", "customer_id": "CUST002", "transaction_details": { "item_id": "ITEM002", "item_name": "Smartphone", "price": 599.99, "quantity": 2, "currency": "USD" } }
{ "index": { "_index": "transactions", "_id": "3" } }
{ "transaction_id": "TXN003", "timestamp": "2024-09-03T09:45:00", "customer_id": "CUST003", "transaction_details": { "item_id": "ITEM003", "item_name": "Tablet", "price": 299.99, "quantity": 1, "currency": "USD" } }
{ "index": { "_index": "transactions", "_id": "4" } }
{ "transaction_id": "TXN004", "timestamp": "2024-09-04T16:15:00", "customer_id": "CUST004", "transaction_details": { "item_id": "ITEM004", "item_name": "Headphones", "price": 199.99, "quantity": 1, "currency": "USD" } }
{ "index": { "_index": "transactions", "_id": "5" } }
{ "transaction_id": "TXN005", "timestamp": "2024-09-05T11:30:00", "customer_id": "CUST005", "transaction_details": { "item_id": "ITEM005", "item_name": "Monitor", "price": 149.99, "quantity": 2, "currency": "USD" } }
'
```
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes adding, modifying,
or dropping tables.
:::
#### Step 11.1. Kill your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
#### Step 11.2. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_es
```
In `app/connector/my_es/configuration.json`, you'll see schema updated to include the `transactions` index. In
`app/metadata/my_es.hml`, you'll see `transactions` present in the metadata as well.
#### Step 11.3. Update your metadata
```sh title="Add the transactions index as a model:"
ddn model add my_es transactions
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Transactions.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `transactions` index from Elasticsearch in your API as a
[model](/reference/metadata-reference/models.mdx).
:::tip
You can import all the indexes from your Elasticsearch database as models by running `ddn model add my_es "*"`.
:::
#### Step 11.4. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.5 Restart your services
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the transactions model:"
query MyQuery {
transactions {
customerId
transactionId
transactionDetails {
currency
price
}
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"transactions": [
{
"customerId": "CUST001",
"transactionId": "TXN001",
"transactionDetails": [
{
"currency": "USD",
"price": 999.99
}
]
},
{
"customerId": "CUST002",
"transactionId": "TXN002",
"transactionDetails": [
{
"currency": "USD",
"price": 599.99
}
]
},
{
"customerId": "CUST003",
"transactionId": "TXN003",
"transactionDetails": [
{
"currency": "USD",
"price": 299.99
}
]
},
{
"customerId": "CUST004",
"transactionId": "TXN004",
"transactionDetails": [
{
"currency": "USD",
"price": 199.99
}
]
},
{
"customerId": "CUST005",
"transactionId": "TXN005",
"transactionDetails": [
{
"currency": "USD",
"price": 149.99
}
]
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with Elasticsearch! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Elasticsearch database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Elasticsearch docs to learn more about how to use Hasura DDN with Elasticsearch. Or, if you're ready,
get started with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-graphql.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-graphql
# Get Started with Hasura DDN and an existing GraphQL API
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to an existing GraphQL API
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting with an existing GraphQL API to and connecting it to Hasura; for ease, we'll use
the [SpaceX API](https://studio.apollographql.com/public/SpaceX-pxxbxen/variant/current/home). Hasura will never modify
your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your GraphQL connector
```sh title="In your project directory, run:"
ddn connector init my_graphql -i
```
From the dropdown, select `hasura/graphql` (you can type to filter the list), then hit enter to accept the default of
all the options.
You'll be prompted for the API's endpoint:
| Variable | Description | Example |
| ------------------ | ------------------------------------------ | ------------------------------------------- |
| `GRAPHQL_ENDPOINT` | The GraphQL endpoint for the existing API. | `https://spacex-production.up.railway.app/` |
Enter the example endpoint above.
:::info You can use different configurations for introspection and query execution
This environment variable will be written to the connector's `configuration.json` file. If you'd like to use a different
configuration for introspection vs. executing requests, see the configuration docs
[here](/reference/connectors/graphql/configuration.mdx).
:::
### Step 4. Introspect your GraphQL endpoint
```sh title="Next, use the CLI to introspect your GraphQL endpoint:"
ddn connector introspect my_graphql
```
After running this, you should see a representation of your GraphQL API's schema in the
`app/connector/my_graphql/schema.graphql` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_graphql
```
### Step 5. Add your first command
The GraphQL connector exposes various top-level fields in an existing GraphQL API as
[commands](/reference/metadata-reference/commands.mdx).
```sh title="Let's track single command from the existing API:"
ddn command add my_graphql launches
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Launches.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `launches` type from your GraphQL API in your supergraph as a command.
### Step 6. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 7. Start your local services
```sh title="Start your local Hasura DDN Engine and GraphQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 8. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_LAUNCH_DATA {
launches {
id
missionName
}
}
```
```json title="You'll get the following response:"
{
"data": {
"launches": [
{
"id": "5eb87cd9ffd86e000604b32a",
"missionName": "FalconSat"
},
{
"id": "5eb87cdaffd86e000604b32b",
"missionName": "DemoSat"
},
{
"id": "5eb87cdbffd86e000604b32c",
"missionName": "Trailblazer"
},
...
]
}
}
```
### Step 9. Add another command
```sh title="Let's add the command to query for rockets:"
ddn command add my_graphql rockets
```
In `app/connector/my_graphql/configuration.json`, you'll see schema updated to include operations for the `rockets`
GraphQL endpoint. In `app/metadata/my_graphql.hml`, you'll see `rockets` present in the metadata as well.
### Step 10. Rebuild your project
```sh title="Next, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 11. Query your new build
```graphql title="Head back to your console and query the rockets:"
query GET_ROCKETS_DATA {
rockets {
name
costPerLaunch
description
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"rockets": [
{
"name": "Falcon 1",
"costPerLaunch": 6700000,
"description": "The Falcon 1 was an expendable launch system privately developed and manufactured by SpaceX during 2006-2009. On 28 September 2008, Falcon 1 became the first privately-developed liquid-fuel launch vehicle to go into orbit around the Earth."
},
{
"name": "Falcon 9",
"costPerLaunch": 50000000,
"description": "Falcon 9 is a two-stage rocket designed and manufactured by SpaceX for the reliable and safe transport of satellites and the Dragon spacecraft into orbit."
}
...
]
}
}
```
:::info create relationships to your own data
You can create relationships between resources in your connected GraphQL endpoint and data living in other data sources.
As an example, you may have a PostgreSQL database with a `booster` [model](/reference/metadata-reference/models.mdx)
exposed and which contains a unique `id` field. This field can be used to create a
[relationship](/reference/metadata-reference/relationships.mdx) between the `rockets` command and a `booster` in your
database (or any other resource that utilizes a unique `id`) to return information across your sources in a single
query.
:::
## Next steps
Congratulations on completing your first Hasura DDN project with an external GraphQL API! π
Here's what you just accomplished:
- You started with a fresh project and connected it to an existing GraphQL API.
- You set up metadata to represent your existing GraphQL schema and how it integrates with your local Hasura project,
which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
The GraphQL connector also supports mutations; you can create, update, or delete resources using your API.
Take a look at our [GraphQL connector docs](/reference/connectors/graphql/index.mdx) to learn more about how to use
Hasura DDN with existing GraphQL APIs. Or, if you're ready, get started with adding
[permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-http.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-http
# Get Started with Hasura DDN and HTTP APIs
## Overview
This tutorial takes about fifteen minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to an existing HTTP API
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch. We'll connect to the
[`{JSON} Placeholder` API](https://jsonplaceholder.typicode.com/); Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your HTTP connector
```sh title="In your project directory, run:"
ddn connector init my_http -i
```
From the dropdown, select `hasura/http` (you can type to filter the list). The HTTP connector will not ask for any
environment variables. Instead, you'll find a configuration file at `app/connector/my_http/config.yaml`.
This configuration file allows you to adjust the settings of your connector and determine which APIs are included; the
`files` array expects an array of configuration files in either YAML or JSON format.
:::info What types of configuration files can I use?
By default, the connector ships with the `{JSON} Placeholder` API's schema included. This is what we'll use in this
guide.
However, you can use any OpenAPI 2 or 3 specification files. A list of common schemas can be found
[here](https://github.com/hasura/ndc-http-recipes/tree/main/recipes).
:::
### Step 4. Introspect your API's schema
```sh title="Next, use the CLI to introspect your API's schema:"
ddn connector introspect my_http
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_http/schema.output.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_http
```
### Step 5. Add your first command
The HTTP connector exposes each resource in the API's schema as a [command](/reference/metadata-reference/commands.mdx).
```sh title="Let's track a single command from the existing API:"
ddn command add my_http getUsers
```
### Step 6. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 7. Start your local services
```sh title="Start your local Hasura DDN Engine and HTTP connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 8. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_USERS {
getUsers {
id
name
}
}
```
```json title="You'll get the following response:"
{
"data": {
"getUsers": [
{
"id": 1,
"name": "Leanne Graham"
},
{
"id": 2,
"name": "Ervin Howell"
},
{
"id": 3,
"name": "Clementine Bauch"
},
...
]
}
}
```
### Step 9. Add another command
```sh title="Let's add the command to query for posts:"
ddn command add my_http getPosts
```
### Step 10. Rebuild your project
```sh title="Next, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 11. Query your new build
```graphql title="Head back to your console and query the posts:"
query GET_POSTS {
getPosts {
id
userId
title
body
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"getPosts": [
{
"id": 1,
"userId": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"id": 2,
"userId": 1,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{
"id": 3,
"userId": 1,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
},
...
]
}
}
```
### Step 12. Create a relationship
In your DDN metadata, you can create [relationships](/reference/metadata-reference/relationships.mdx) between resources
in your API. Below, we'll define a relationship β with the assistance of the
[VS Code Extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.Hasura) β between `users` and `posts`.
```yaml title="Add the following to the end of the app/connector/my_http/metadata/getUsers.hml file:"
---
kind: Relationship
version: v1
definition:
name: posts
sourceType: User
target:
command:
name: GetPosts
mapping:
- source:
fieldPath:
- fieldName: id
target:
argument:
argumentName: userId
```
```sh title="Next, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
```graphql title="Run the following query to return nested data about posts for each user:"
query GET_USERS_AND_POSTS {
getUsers {
id
name
posts {
id
title
body
}
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"getUsers": [
{
"id": 1,
"name": "Leanne Graham",
"posts": [
{
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
...
]
},
...
]
}
}
```
:::info create relationships to your own data
You can create relationships between resources in your connected HTTP API and data living in other data sources.
:::
### Step 13. Insert data
We can also track existing insert, update, and delete operations available via the API.
```sh title="Let's track this createPost command:"
ddn command add my_http createPost
```
```sh title="Next, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
```graphql title="Then, run the following mutation:"
mutation INSERT_SINGLE_POST {
createPost(body: { body: "This is a new post!", title: "New Post" }) {
id
title
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"createPost": {
"id": 101,
"title": "New Post"
}
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with the HTTP connector! π
Here's what you just accomplished:
- You started with a fresh project and connected it to the `{JSON} Placeholder` HTTP API.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
- Finally, we looked at how to enable mutations and insert data using your new API.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our [HTTP connector docs](/reference/connectors/http/index.mdx) to learn more about how to use Hasura DDN
with existing HTTP APIs. Or, if you're ready, get started with adding [permissions](/auth/permissions/index.mdx) to
control access to your API.
==============================
# with-mongodb.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-mongodb
# Get Started with Hasura DDN and MongoDB
## Overview
This tutorial takes about fifteen minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a MongoDB database
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch. We'll use a locally running MongoDB instance via Docker and connect
it to Hasura, but you can easily follow the steps if you already have data seeded; Hasura will never modify your source
schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Install mongosh
This tutorial uses mongosh β the Mongo shell β to interact with the local MongoDB instance. You can download it
[here](https://www.mongodb.com/try/download/shell).
### Step 2. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 3. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 4. Initialize your MongoDB connector
```sh title="In your project directory, run:"
ddn connector init my_mongo -i
```
From the dropdown, start typing `mongo` and hit enter to accept the default port. Then, provide the following connection
string:
```plaintext
mongodb://local.hasura.dev:27017/my_database
```
### Step 5. Start the local MongoDB container
```sh title="Begin by creating a compose file for the Mongo service:"
touch app/connector/my_mongo/compose.mongo.yaml
```
```yaml title="Then, open the file and add the following:"
services:
mongodb:
image: mongo:latest
container_name: mongodb
ports:
- "27017:27017"
```
```sh title="Run the container:"
docker compose -f app/connector/my_mongo/compose.mongo.yaml up -d
```
```sh title="Use the mongosh shell to seed the database:"
docker exec -it mongodb mongosh my_database --eval "
db.users.insertMany([
{ user_id: 1, name: 'Alice', age: 25 },
{ user_id: 2, name: 'Bob', age: 30 },
{ user_id: 3, name: 'Charlie', age: 35 }
]);
"
```
The shell will return information about the newly-inserted `users` records.
### Step 6. Introspect your MongoDB database
```sh title="Next, use the CLI to introspect your MongoDB database:"
ddn connector introspect my_mongo
```
After running this, you should see a representation of your collection's schema in
`app/connector/my_mongo/schema/users.json`; you can view this using `cat` or open the file in your editor.
For each collection in your database, the MongoDB connector will generate a separate JSON file representing it.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_mongo
```
### Step 7. Add your model
```sh title="Now, track the collection from your MongoDB database as a model in your DDN metadata:"
ddn model add my_mongo users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` collections from MongoDB in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 8. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 9. Start your local services
```sh title="Start your local Hasura DDN Engine and MongoDB connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 10. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
userId
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"userId": 1,
"name": "Alice",
"age": 25
},
{
"userId": 2,
"name": "Bob",
"age": 30
},
{
"userId": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 11. Iterate on your MongoDB schema
```sh title="Let's add a new collection for posts:"
docker exec -it mongodb mongosh my_database --eval "
db.posts.insertMany([
{ user_id: 1, post_id: 1, title: 'My First Post', content: 'This is Alice\'s first post.' },
{ user_id: 1, post_id: 2, title: 'Another Post', content: 'Alice writes again!' },
{ user_id: 2, post_id: 3, title: 'Bob\'s Post', content: 'Bob shares his thoughts.' },
{ user_id: 3, post_id: 4, title: 'Hello World', content: 'Charlie joins the conversation.' }
]);
"
```
### Step 12. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping collections.
:::
#### Step 12.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_mongo
```
In `app/connector/my_mongo/configuration.json`, you'll see schema updated to include operations for the `posts`
collection. You'll also see a `posts.json` file in the `schema` directory.
#### Step 12.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_mongo posts
```
#### Step 12.3. Kill your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
#### Step 12.4. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 12.5 Restart your services
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 13. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
userId
postId
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"userId": 1,
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"userId": 1,
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"userId": 2,
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"userId": 3,
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 14. Create a relationship
```yaml title="Open the Posts.hml file and add the following to the end:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: Posts
target:
model:
name: Users
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: userId
```
:::tip LSP-Assisted authoring is available
We've created an extension for VS Code that leverages LSP to make authoring these metadata objects easier. Check it out
[here](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura).
:::
### Step 15. Rebuild your project
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 16. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
postId
title
content
user {
userId
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"userId": 2,
"name": "Bob",
"age": 30
}
},
{
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"userId": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with MongoDB! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local MongoDB database.
- You set up metadata to represent your collections and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our MongoDB docs to learn more about how to use Hasura DDN with MongoDB. Or, if you're ready, get started
with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-mysql.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-mysql
# Get Started with Hasura DDN and MySQL
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a MySQL database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; you'll connect a locally-running MySQL instance to Hasura, but you
can easily follow the steps if you already have data seeded. Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your MySQL connector
```sh title="In your project directory, run:"
ddn connector init my_mysql -i
```
From the dropdown, select `/hasura/mysql` (you can type to filter the list). Then, enter the following JDBC URL:
```plaintext
jdbc:mysql://user:password@local.hasura.dev:3306/mydb
```
### Step 4. Start the local MySQL container and Adminer
```sh title="Begin by creating a compose file for the MySQL service:"
touch app/connector/my_mysql/compose.mysql.yaml
```
```yaml title="Then, open the file and add the following:"
services:
mysql:
image: mysql:8.0
container_name: mysql-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: mydb
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --default-authentication-plugin=mysql_native_password
adminer:
image: adminer:latest
container_name: adminer
restart: always
ports:
- "8080:8080"
environment:
ADMINER_DEFAULT_SERVER: mysql
volumes:
mysql-data:
```
```sh title="Run the container:"
docker compose -f app/connector/my_mysql/compose.mysql.yaml up -d
```
You can open Adminer by visiting: [`http://localhost:8080`](http://localhost:8080)
You'll be prompted for the username, password, and database name. Use the values from the `compose.mysql.yaml` above.
### Step 5. Create a table in your MySQL database
```sql title="Next, via Adminer select SQL command from the left-hand nav, then enter the following:"
-- Create the table
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
age INT NOT NULL
);
-- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
```
You can verify this worked by using Adminer to query all records from the `users` table:
```sql
SELECT * FROM users;
```
### Step 6. Introspect your MySQL database
```sh title="Next, use the CLI to introspect your MySQL database:"
ddn connector introspect my_mysql
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_mysql/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_mysql
```
### Step 7. Add your model
```sh title="Now, track the table from your MySQL database as a model in your DDN metadata:"
ddn model add my_mysql "mydb.users"
```
Open the `app/metadata` directory and you'll find a newly-generated file: `MydbUsers.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `users` table from MySQL in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 8. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 9. Start your local services
```sh title="Start your local Hasura DDN Engine and MySQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 10. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
mydbUsers {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"mydbUsers": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 11. Iterate on your MySQL schema
```sql title="Via Adminer, add a new table and insert some data to your MySQL database:"
-- Create the posts table
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Insert some seed data
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice\'s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob\'s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
```
```sql title="Using Adminer, verify this by running the following query:"
-- Fetch all posts with user information
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 12. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 12.1. Re-introspect your data source
```plaintext title="First, bring down your running services:"
CTRL + C
```
```sh title="Run the introspection command again:"
ddn connector introspect my_mysql
```
In `app/connector/my_mysql/configuration.json`, you'll see schema updated to include operations for the `posts` table.
In `app/metadata/my_mysql.hml`, you'll see `mydb.posts` present in the metadata as well.
#### Step 12.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_mysql "mydb.posts"
```
#### Step 12.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 12.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 13. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
mydbPosts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"mydbPosts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 14. Create a relationship
```sh title="Since there's already a foreign key on the posts table in MySQL, we can easily add the relationship:"
ddn relationship add my_mysql "mydb.posts"
```
You'll see a new metadata object added to the `app/metadata/MydbPosts.hml` file of kind `Relationship` explaining the
relationship between `posts` and `users`.
### Step 15. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 16. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
mydbPosts {
id
title
content
mydbUser {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"mydbPosts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"mydbUser": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!",
"mydbUser": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"mydbUser": {
"id": 2,
"name": "Bob",
"age": 30
}
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"mydbUser": {
"id": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with MySQL! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local MySQL database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our MySQL docs to learn more about how to use Hasura DDN with MySQL. Or, if you're ready, get started
with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-oracle.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-oracle
# Get Started with Hasura DDN and Oracle
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to an Oracle database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch. We'll use an Docker container of Oracle and connect it to Hasura,
but you can easily follow the steps if you already have data seeded in an existing Oracle database; Hasura will never
modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Oracle connector
```sh title="In your project directory, run:"
ddn connector init my_oracle -i
```
From the dropdown, start typing `oracle` and hit enter to accept the default port. Then, provide the following values:
**JDBC URL**
```plaintext
jdbc:oracle:thin:@local.hasura.dev:1521/XEPDB1?user=example&password=mypassword
```
For Schemas, hit enter to use the database provided in the JDBC URL.
### Step 4. Start the local Oracle container
Run the following command to start the Oracle container:
```sh
docker run -d \
--name oracle-server \
--restart always \
-p 1521:1521 \
-e ORACLE_PASSWORD=oraclepassword \
-e APP_USER=example \
-e APP_USER_PASSWORD=mypassword \
-e TARGET_PDB=XEPDB1 \
gvenzl/oracle-xe:21.3.0-slim
```
:::tip Startup time
The Oracle container may take a minute or so to start up. You can check on its progress by running
`docker logs oracle-server`. You should see something like the following:
```plaintext
DATABASE IS READY TO USE!
```
:::
```sh title="Use the SQL*PLUS shell to start the SQL prompt:"
docker exec -it oracle-server sqlplus example/mypassword@local.hasura.dev:1521/XEPDB1
```
```SQL title="Create a table in the database:"
CREATE TABLE users (user_id Number NOT NULL, name Varchar2(45) NOT NULL, age Number NOT NULL);
```
```SQL title="Then, seed the table:"
INSERT ALL
INTO users (user_id, name, age) VALUES (1, 'Alice', 25)
INTO users (user_id, name, age) VALUES (2, 'Bob', 30)
INTO users (user_id, name, age) VALUES (3, 'Charlie', 35)
SELECT 1 FROM DUAL;
```
```SQL title="You can verify this by running:"
SELECT * FROM users;
```
You should see a list of users returned.
### Step 5. Introspect your Oracle database
```sh title="Next, use the CLI to introspect your Oracle database:"
ddn connector introspect my_oracle
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_oracle/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available β and their status β at any point using the CLI:"
ddn connector show-resources my_oracle
```
### Step 6. Add your model
```sh title="Now, track the table from your Oracle database as a model in your DDN metadata:"
ddn models add my_oracle "EXAMPLE.USERS"
```
Open the `app/metadata` directory and you'll find a newly-generated file: `ExampleUsers.hml`. The DDN CLI will use this
Hasura Hasura Metadata Language file to represent the `users` table from Oracle in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Oracle connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
exampleUsers {
userId
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"exampleUsers": [
{
"userId": 1,
"name": "Alice",
"age": 25
},
{
"userId": 2,
"name": "Bob",
"age": 30
},
{
"userId": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your Oracle schema
```SQL title="Let's add a new table for posts:"
CREATE TABLE posts (
user_id Number,
post_id Number,
title Varchar2(45),
content Varchar2(45)
);
```
```SQL title="Then, seed it:"
INSERT ALL
INTO posts (user_id, post_id, title, content) VALUES (1, 1, 'My First Post', 'This is Alice''s first post.')
INTO posts (user_id, post_id, title, content) VALUES (1, 2, 'Another Post', 'Alice writes again!')
INTO posts (user_id, post_id, title, content) VALUES (2, 3, 'Bob''s Post', 'Bob shares his thoughts.')
INTO posts (user_id, post_id, title, content) VALUES (3, 4, 'Hello World', 'Charlie joins the conversation.')
SELECT 1 FROM DUAL;
```
```SQL title="Finally, we can check the posts were generated:"
SELECT * FROM posts;
```
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_oracle
```
In `app/connector/my_oracle/configuration.json`, you'll see schema updated to include operations for the `posts` table.
In `app/metadata/my_oracle.hml`, you'll see `posts` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_oracle "EXAMPLE.POSTS"
```
#### Step 11.3. Kill your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
#### Step 11.4. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.5 Restart your services
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
examplePosts {
userId
postId
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"examplePosts": [
{
"userId": 1,
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"userId": 1,
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"userId": 2,
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"userId": 3,
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 13. Create a relationship
```yaml title="Open the ExamplePosts.hml file and add the following to the end:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: ExamplePosts
target:
model:
name: ExampleUsers
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: userId
```
:::tip LSP-Assisted authoring is available
We've created an extension for VS Code that leverages LSP to make authoring these metadata objects easier. Check it out
[here](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura).
:::
### Step 14. Rebuild your project
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 15. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
examplePosts {
postId
title
content
user {
userId
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"examplePosts": [
{
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"userId": 2,
"name": "Bob",
"age": 30
}
},
{
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"userId": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with Oracle! π
Here's what you just accomplished:
- You started with a fresh project and connected it to an Oracle database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our [Oracle docs](/reference/connectors/oracle/index.mdx) to learn more about how to use Hasura DDN with
Oracle. Or, if you're ready, get started with adding [permissions](/reference/metadata-reference/permissions.mdx) to
control access to your API.
==============================
# with-openapi.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-openapi
# Get Started with Hasura DDN and OpenAPI
## Overview
The Hasura OpenAPI connector allows you to seamlessly integrate HTTP APIs defined in OpenAPI (formerly Swagger)
specifications into your Hasura GraphQL API. This connector works by:
1. Reading your OpenAPI Document (either from a URL or local file)
2. Generating TypeScript code that represents your API endpoints and types
3. Converting these HTTP endpoints into GraphQL queries and mutations
4. Exposing them through Hasura's DDN's GraphQL API
This means you can:
- Connect to any REST API that provides an OpenAPI specification
- Transform REST endpoints into GraphQL operations automatically
- Combine these APIs with other data sources in your Hasura project
- Apply Hasura's permission system to control access to these endpoints
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch. We will use the [Petstore API](https://petstore3.swagger.io/) in
this tutorial, but you can use any OpenAPI document.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your OpenAPI connector
```sh title="In your project directory, run:"
ddn connector init my_openapi -i
```
From the dropdown, start typing `openapi` and hit enter. Then, provide the following values:
**NDC_OAS_DOCUMENT_URI**
```plaintext
https://petstore3.swagger.io/api/v3/openapi.json
```
**NDC_OAS_BASE_URL**
```plaintext
https://petstore3.swagger.io/api/v3
```
For now, you can leave the rest of the values as blanks (or their defaults, if present).
:::info Environment Variables
You can configure the OpenAPI Connector using
[supported environment variables](/reference/connectors/openapi-lambda/env.mdx).
:::
### Step 4. Introspect your OpenAPI Document
```sh title="Next, use the CLI to introspect your OpenAPI Docuemnt:"
ddn connector introspect my_openapi
```
Running this command results in the creation of a few files in `app/connector/my_openapi`. Let's break it down:
`api.ts` - This file is generated using the OpenAPI document and is a direct represntation of it in Typescript. It
contains the generated TypeScript client for the OpenAPI document. It also contains all the types (schemas) that are
defined in the OpenAPI document, alongwith all the API calls.
`functions.ts` - This file contains simple wrapper functions over the API calls that are defined in the `api.ts` file.
These functions will be exposed as queries and mutations in your DDN GraphQL API.
`tsconfig.json` - This file is the TypeScript configuration file for the generated code.
`package.json`, `package-lock.json` - These files are generated to manage the dependencies of the generated code.
You will also the a representation of the functions defined in `functions.ts` file in the `definition.schema` field of
the `app/connector/metadata/my_openapi.hml` file.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_openapi
```
:::info Under the hood
The OpenAPI Connector internally makes use of the
[TypeScript Lambda Connector](/business-logic/add-a-lambda-connector.mdx) to expose the generated code as a GraphQL API.
:::
### Step 5. Add your API
```sh title="Now, track an API from your OpenAPI Document as a command in your DDN metadata:"
ddn command add my_openapi getStoreGetOrderById
```
Open the `app/metadata` directory and you'll find a newly-generated file: `GetStoreGetOrderById.hml`. The DDN CLI will
use this Hasura Metadata Language file to represent the `getStoreGetOrderById` function from `functions.ts` file as
[command](/reference/metadata-reference/commands.mdx). This function in turn represents the
`GET :/store/order/{orderId}` API in your OpenAPI Document.
:::tip Import all at once
You can import all the APIs from your OpenAPI Document as commands by running `ddn command add my_openapi "*"`.
:::
### Step 6. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 7. Start your local services
```sh title="Start your local Hasura DDN Engine and OpenAPI connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 8. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query MyQuery {
getStoreGetOrderById(orderId: 10) {
complete
id
petId
quantity
shipDate
status
}
}
```
```json title="You'll get a similar response to the following:"
{
"data": {
"getStoreGetOrderById": {
"complete": true,
"id": 10,
"petId": 198772,
"quantity": 7,
"shipDate": "2025-03-04T08:23:30.139+00:00",
"status": "approved"
}
}
}
```
:::note Petstore API considerations
The Petstore API is a public API and may have different data than what is shown here. Also, sometimes the API may not
return data for certain IDs, or, the server may not be accepting requests at all. These will show as errors in the
console.
:::
### Step 9. Iterate on your OpenAPI Document
If you have added new APIs to your OpenAPI Document, you can introspect it again to update your metadata. The OpenAPI
connector looks at the `NDC_OAS_FILE_OVERWRITE` to determine whether it should overwrite the existing `api.ts` and
`functions.ts` file or not. You'll need to set this to `true` in your project's `.env` file if you wish to get the new
changes from your OpenAPI Document into the generated TypeScript files.
#### Step 9.1 Set the NDC_OAS_FILE_OVERWRITE env var to true
In your projects `.env` file, make sure the the `APP_MY_OPENAPI_NDC_OAS_FILE_OVERWRITE` variable is set to `true`.
### Step 10. Refresh your metadata and rebuild your project
:::tip Making changes
The following steps are necessary each time you make changes to your `functions.ts` file. This includes adding,
modifying, or deleting functions.
:::
#### Step 10.1. Kill your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
#### Step 10.2. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_openapi
```
You'll notice that the `api.ts` and `functions.ts` files have been updated with the new APIs.
#### Step 10.3. Update your metadata
```sh title="Add the new APIs as commands:"
ddn command add my_openapi "*"
```
Open the `app/metadata` directory and you'll find a newly-generated files for the new APIs.
#### Step 10.4. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 10.5 Restart your services
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 11. Query your new build
Your GraphQL API should now have the new APIs from your OpenAPI Document. You can query them in the console.
## Next steps
Congratulations on completing your first Hasura DDN project with HTTP APIs using OpenAPI! π
Here's what you just accomplished:
- You started with a fresh project and connected it to HTTP APIs using an OpenAPI Document.
- You set up metadata to represent your HTTP APIs, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose any data with an OpenAPI spec, empowering you to iterate and scale with
confidence. Great work!
Take a look at our [OpenAPI docs](https://github.com/hasura/ndc-open-api-lambda/blob/main/docs/documentation.md) to
learn more about how to use Hasura DDN with OpenAPI. Or, if you're ready, get started with adding
[permissions](/reference/metadata-reference/permissions.mdx) to control access to your API.
==============================
# with-postgresql.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-postgresql
# Get Started with Hasura DDN and PostgreSQL
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a PostgreSQL database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
- Mutate data
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; a PostgreSQL docker image ships with the data connector you'll use
in just a bit to connect a locally-running database to Hasura, but you can easily follow the steps if you already have
data seeded; Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your PostgreSQL connector
```sh title="In your project directory, run:"
ddn connector init my_pg -i
```
From the dropdown, select `/hasura/postgres` (you can type to filter the list), then hit enter to accept the default of
all the options.
The CLI will output something similar to this:
```plaintext
HINT To access the local Postgres database:
- Run: docker compose -f app/connector/my_pg/compose.postgres-adminer.yaml up -d
- Open Adminer in your browser at http://localhost:5143 and create tables
- To connect to the database using other clients use postgresql://user:password@local.hasura.dev:8105/dev
```
### Step 4. Start the local PostgreSQL container and Adminer
```sh title="Use the hint from the CLI output:"
docker compose -f app/connector/my_pg/compose.postgres-adminer.yaml up -d
```
Run `docker ps` to see on which port Adminer is running. Then, you can then navigate to the address below to access it:
```plaintext
http://localhost:
```
### Step 5. Create a table in your PostgreSQL database
```sql title="Next, via Adminer select SQL command from the left-hand nav, then enter the following:"
--- Create the table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
--- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
```
You can verify this worked by using Adminer to query all records from the `users` table:
```sql
SELECT * FROM users;
```
### Step 6. Introspect your PostgreSQL database
```sh title="Next, use the CLI to introspect your PostgreSQL database:"
ddn connector introspect my_pg
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_pg/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_pg
```
### Step 7. Add your model
```sh title="Now, track the table from your PostgreSQL database as a model in your DDN metadata:"
ddn model add my_pg users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 8. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 9. Start your local services
```sh title="Start your local Hasura DDN Engine and PostgreSQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 10. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 11. Iterate on your PostgreSQL schema
```sql title="Via Adminer, add a new table and insert some data to your PostgreSQL database:"
-- Create the posts table
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert some seed data
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice''s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
```
```sql title="Using Adminer, verify this by running the following query:"
-- Fetch all posts with user information
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 12. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 12.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_pg
```
In `app/connector/my_pg/configuration.json`, you'll see schema updated to include operations for the `posts` table. In
`app/metadata/my_pg.hml`, you'll see `posts` present in the metadata as well.
#### Step 12.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_pg "posts"
```
#### Step 12.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 12.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 13. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 14. Create a relationship
```sh title="Since there's already a foreign key on the posts table in PostgreSQL, we can easily add the relationship:"
ddn relationship add my_pg "posts"
```
You'll see a new metadata object added to the `app/metadata/posts.hml` file of kind `Relationship` explaining the
relationship between `posts` and `users`.
### Step 15. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 16. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
id
title
content
user {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"id": 2,
"name": "Bob",
"age": 30
}
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"id": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
### Step 17. Add all commands
We'll track the available operations β for inserting, updating, and deleting β on our `users` and `posts` tables as
commands.
```sh title="Add all available commands:"
ddn command add my_pg "*"
```
You'll see newly-generated metadata files in the `metadata` directory for your connector that represent insert, update,
and delete operations.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 18. Insert new data
```graphql title="Create a new post for Charlie:"
mutation InsertSinglePost {
insertPosts(
objects: {
content: "I am an expert in Bird Law and I demand satisfaction."
title: "Charlie has more to say"
userId: "3"
}
) {
returning {
id
title
content
user {
id
name
}
}
}
}
```
You should see a response that returns your inserted data along with the `id` and `name` fields for the author.
## Next steps
Congratulations on completing your first Hasura DDN project with PostgreSQL! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local PostgreSQL database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
- Finally, we looked at how to enable mutations and insert data using your new API.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our PostgreSQL docs to learn more about how to use Hasura DDN with PostgreSQL. Or, if you're ready, get
started with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-prometheus.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-prometheus
# Get Started with Hasura DDN and Prometheus
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Prometheus instance
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; you'll connect a locally-running Prometheus instance β set up to
scrape metrics from itself β to Hasura, but you can easily follow the steps if you already have an existing Prometheus
server. Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Prometheus connector
```sh title="In your project directory, run:"
ddn connector init my_prometheus -i
```
From the dropdown, select `hasura/prometheus` (you can type to filter the list). Then, enter the following connection
string when prompted:
```plaintext
http://local.hasura.dev:9090
```
### Step 4. Start the local Prometheus instance
```sh title="Begin by creating a compose file for the Prometheus server:"
touch app/connector/my_prometheus/compose.prometheus.yaml
```
```yaml title="Then, open the file and add the following:"
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- "--config.file=/etc/prometheus/prometheus.yml"
```
```sh title="In the same directory, create a Prometheus configuration file:"
touch app/connector/my_prometheus/prometheus.yml
```
```yaml title="Then, open the file and add the following:"
global:
scrape_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["local.hasura.dev:9090"]
```
```sh title="Run the container:"
docker compose -f app/connector/my_prometheus/compose.prometheus.yaml up -d
```
You can open Prometheus by visiting: [`http://localhost:9090`](http://localhost:9090). Go ahead and navigate here and
poke around...we'll need some requests against the server for our queries later!
### Step 5. Introspect your Prometheus server
```sh title="Next, use the CLI to introspect your Prometheus server:"
ddn connector introspect my_prometheus
```
After running this, you should see a representation of your Prometheus server's schema in the
`app/connector/my_prometheus/configuration.yaml` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_prometheus
```
### Step 6. Add your model
```sh title="Now, let's track a common counter metric as a model in your DDN metadata:"
ddn model add my_prometheus prometheus_http_requests_total
```
Open the `app/metadata` directory and you'll find a newly-generated file: `PrometheusHttpRequestsTotal.hml`. The DDN CLI
will use this Hasura Metadata Language file to represent the `prometheus_http_requests_total` metric from Prometheus in
your API as a [model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Prometheus connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
We'll write a GraphQL query that's the equivalent of this PromQL query:
```plaintext
sum(rate(prometheus_http_requests_total{job="prometheus"}[1m]))
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_REQUESTS_PER_SECOND {
prometheusHttpRequestsTotal(
args: { fn: [{ rate: "1m" }, { sum: [] }] }
where: { timestamp: { _gt: "2025-03-26" }, job: { _eq: "prometheus" } }
) {
timestamp
value
}
}
```
:::tip Change the `_gt` value
For the most accurate results, change the date value to a recent date.
:::
```json title="You'll get a response that looks like this:"
{
"data": {
"prometheusHttpRequestsTotal": [
{
"job": "prometheus",
"timestamp": 1743004075,
"value": 0.08889283968176363
}
]
}
}
```
### Step 10. Add another model
```sh title="Add the process_cpu_seconds_total counter metric:"
ddn model add my_prometheus process_cpu_seconds_total
```
In `app/metadata` you'll find a newly-generated file: `ProcessCpuSecondsTotal.hml`. This will be used to expose the
counter metric via your supergraph API.
### Step 11. Rebuild your project
```sh title="Next, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the new model:"
query GET_CPU_PROCESS_TOTAL {
processCpuSecondsTotal(
args: { fn: [{ rate: "1m" }, { sum: [] }] }
where: { timestamp: { _gt: "2025-03-26" }, job: { _eq: "prometheus" } }
) {
timestamp
value
}
}
```
```json title="You'll get a response that looks like this:"
{
"data": {
"processCpuSecondsTotal": [
{
"timestamp": 1743010998,
"value": 0.002222123461179495
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with Prometheus! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Prometheus server.
- You set up metadata to represent your counter metrics, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our [Prometheus docs](/reference/connectors/prometheus/index.mdx) to learn more about how to use Hasura
DDN with Prometheus. Or, if you're ready, get started with adding [permissions](/auth/permissions/index.mdx) to control
access to your API.
==============================
# with-qdrant.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-qdrant
# Get Started with Hasura DDN and Qdrant
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Qdrant-hosted vector database
- Generate Hasura metadata
- Create a build
- Run your first query
- Mutate data
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; you'll connect a hosted Qdrant instance to Hasura, but you can
easily follow the steps if you already have data seeded. Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Create and seed a new Qdrant database
Head to [Qdrant](https://cloud.qdrant.io) and create an account if you don't already have one. Then, create a new
cluster.
Qdrant will share an API key immediately after the cluster is provisioned; go ahead and copy this down as you'll need it
in the following steps.
From your cluster's dashboard, choose the `Load Sample Data` option. Follow the steps outlined by Qdrant which allow you
to execute and insert data directly via their UI. Youβll finish setup when you've gotten through the instructions for
implementing multitenancy. This will give you upwards of eight collections to play with.
### Step 4. Initialize your Qdrant connector
```sh title="In your project directory, run:"
ddn connector init my_qdrant -i
```
From the dropdown, select `hasura/qdrant` (you can type to filter the list), then hit enter to accept the default of all
the options.
You'll be prompted for two environment variables:
| Variable | Description | Example |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `QDRANT_URL` | The connection string for the Qdrant database, including the port. You can generate this by selecting `Connect` under the Cluster in your dashboard. | `https://..:` |
| `QDRANT_API_KEY` | The Qdrant API key presented to you when the cluster was provisioned. | `eyJ...` |
### Step 5. Introspect your Qdrant database
```sh title="Next, use the CLI to introspect your Qdrant database:"
ddn connector introspect my_qdrant
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_qdrant/config.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_qdrant
```
If you run this command, you'll see there are several models from the sample data available; additionally, we'll see a
set of commands.
### Step 6. Add your first model
```sh title="Let's track the terraforming collection as a model:"
ddn model add my_qdrant terraforming
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Terraforming.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `terraforming` collection from Qdrant in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Qdrant connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_TERRAFORMING_DATA {
terraforming(args: {}) {
id
land
color
humidity
life
vector
}
}
```
```json title="You'll get the following response:"
{
"data": {
"terraforming": [
{
"id": 1,
"land": "forest",
"color": "green",
"humidity": 40,
"life": true,
"vector": [0.1, 0.2, 0.3, 0.4]
},
{
"id": 2,
"land": "lake",
"color": "blue",
"humidity": 100,
"life": true,
"vector": [0.2, 0.3, 0.4, 0.5]
},
{
"id": 3,
"land": "steppe",
"color": "green",
"humidity": 25,
"life": false,
"vector": [0.3, 0.4, 0.5, 0.6]
},
{
"id": 4,
"land": "desert",
"color": "red",
"humidity": 5,
"life": false,
"vector": [0.4, 0.5, 0.6, 0.7]
},
{
"id": 5,
"land": "marsh",
"color": "black",
"humidity": 90,
"life": true,
"vector": [0.5, 0.6, 0.7, 0.8]
},
{
"id": 6,
"land": "cavern",
"color": "black",
"humidity": 15,
"life": false,
"vector": [0.6, 0.7, 0.8, 0.9]
}
]
}
}
```
You can also utilize vectors to perform similarity searches and even limit the number of results.
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_TERRAFORMING_DATA_BASED_ON_VECTORS {
terraforming(args: { search: { vector: [0.5, 0.6, 0.7, 0.8], scoreThreshold: 0.1 } }, limit: 2) {
id
land
color
humidity
life
vector
}
}
```
```json title="You'll get the following response:"
{
"data": {
"terraforming": [
{
"id": 6,
"land": "cavern",
"color": "black",
"humidity": 15,
"life": false,
"vector": [0.6, 0.7, 0.8, 0.9]
},
{
"id": 5,
"land": "marsh",
"color": "black",
"humidity": 90,
"life": true,
"vector": [0.5, 0.6, 0.7, 0.8]
}
]
}
}
```
### Step 10. Iterate on your API
#### Step 10.1. Add more resources
Our sample data set contains a number of different collections; let's add them all.
```sh title="Add the remaining collections as models:"
ddn model add my_qdrant "*"
```
#### Step 10.2. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 10.3. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 11. Query your new build
```graphql title="Head back to your console and query the dinosaurs model:"
query GET_JOHN_HAMMOND {
dinosaurs(args: {}) {
id
dinosaur
diet
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"dinosaurs": [
{
"id": 1,
"dinosaur": "t-rex",
"diet": [
{
"food": "leaves",
"likes": false
},
{
"food": "meat",
"likes": true
}
]
},
{
"id": 2,
"dinosaur": "diplodocus",
"diet": [
{
"food": "leaves",
"likes": true
},
{
"food": "meat",
"likes": false
}
]
}
]
}
}
```
### Step 12. Add all commands
We'll track the available operations β for inserting, updating, and deleting β on our collections as commands.
```sh title="Add all available commands:"
ddn command add my_qdrant "*"
```
You'll see newly-generated metadata files in the `metadata` directory for your connector that represent insert, update,
and delete operations.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 13. Update existing data
```graphql title="Let's update the t-rex listing:"
mutation MAKE_HIM_KING {
updateDinosaursOne(object: { id: 1, vector: [0.1, 0.2, 0.3, 0.4], dinosaur: "T-Rex" })
}
```
You should see a response that alerts you to the operation being completed successfully.
## Next steps
Congratulations on completing your first Hasura DDN project with Qdrant! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Qdrant database.
- You set up metadata to represent your collections, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your API and refresh your metadata to reflect changes.
- Finally, we looked at how to enable mutations and modify data using your new API.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Qdrant docs to learn more about how to use Hasura DDN with
[Qdrant](https://qdrant.tech/documentation/). Or, if you're ready, get started with adding
[permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-redshift.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-redshift
# Get Started with Hasura DDN and Amazon Redshift
## Overview
This tutorial will guide you through setting up a Hasura DDN project with Amazon Redshift. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Redshift database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
- Mutate data
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
You'll also need:
- An AWS account
- A Redshift database with namespace
- An IAM user with access to the Redshift database
- The IAM user's credentials
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Create a new Redshift database
In the AWS console, navigate to the [Redshift page](https://console.aws.amazon.com/redshift) and create a new database
called `hasura_demo` in your namespace. You can do so by clicking "Query data" in the top right. Then click the "+
Create" button and select database. Select the appropriate cluster / workgroup, name the database `hasura_demo` and
click "Create database".
### Step 4. Create a table in your Redshift database
```sql title="Connect to your Redshift database and create the users table:"
--- Create the table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
--- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
```
You can verify this worked by querying all records from the `users` table:
```sql
SELECT * FROM users;
```
### Step 5. Initialize your Redshift connector
```sh title="In your project directory, run:"
ddn connector init my_redshift -i
```
From the dropdown, select `hasura/redshift` (you can type to filter the list), and enter the value of the connection
string.
| ENV | Example | Description |
| -------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `JDBC_URL` | `jdbc:redshift://:/?user=&password=` | The JDBC URL to connect to the Amazon Redshift database. |
| `JDBC_SCHEMAS` | `public,app` | The schemas to use for the database. Optional. This can also be included in the connection string. |
Hasura will never modify your source schema.
### Step 6. Introspect your Redshift database
```sh title="Next, use the CLI to introspect your Redshift database:"
ddn connector introspect my_redshift
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_redshift/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available β and their status β at any point using the CLI:"
ddn connector show-resources my_redshift
```
### Step 7. Add your model
```sh title="Now, track the table from your Redshift database as a model in your DDN metadata:"
ddn model add my_redshift users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from Redshift in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 8. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 9. Start your local services
```sh title="Start your local Hasura DDN Engine and Redshift connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 10. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 11. Iterate on your Redshift schema
```sql title="Connect to your Redshift database and add a new table with some data:"
-- Create the posts table
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert some seed data
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice''s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
```
```sql title="Verify the data by running the following query:"
-- Fetch all posts with user information
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 12. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 12.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_redshift
```
In `app/connector/my_redshift/configuration.json`, you'll see schema updated to include operations for the `posts`
table. In `app/metadata/my_redshift.hml`, you'll see `posts` present in the metadata as well.
#### Step 12.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_redshift "posts"
```
#### Step 12.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 12.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 13. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 14. Create a relationship
```sh title="Since there's already a foreign key on the posts table in Redshift, we can easily add the relationship:"
ddn relationship add my_redshift "posts"
```
You'll see a new metadata object added to the `app/metadata/posts.hml` file of kind `Relationship` explaining the
relationship between `posts` and `users`.
### Step 15. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 16. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
id
title
content
user {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"id": 2,
"name": "Bob",
"age": 30
}
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"id": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
### Step 17. Add all commands
We'll track the available operations β for inserting, updating, and deleting β on our `users` and `posts` tables as
commands.
```sh title="Add all available commands:"
ddn command add my_redshift "*"
```
You'll see newly-generated metadata files in the `metadata` directory for your connector that represent insert, update,
and delete operations.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 18. Insert new data
```graphql title="Create a new post for Charlie:"
mutation InsertSinglePost {
insertPosts(
objects: {
content: "I am an expert in Bird Law and I demand satisfaction."
title: "Charlie has more to say"
userId: "3"
}
) {
returning {
id
title
content
user {
id
name
}
}
}
}
```
You should see a response that returns your inserted data along with the `id` and `name` fields for the author.
## Next steps
Congratulations on completing your first Hasura DDN project with Redshift! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Redshift database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
- Finally, we looked at how to enable mutations and insert data using your new API.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Redshift docs to learn more about how to use Hasura DDN with Redshift. Or, if you're ready, get
started with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-snowflake.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-snowflake
# Get Started with Hasura DDN and Snowflake
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Snowflake instance
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch, but you can easily follow the steps if you already have data seeded;
Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Snowflake connector
```sh title="In your project directory, run:"
ddn connector init my_snowflake -i
```
Select `hasura/snowflake` from the list of connectors.
```plaintext title="The CLI will ask you for a connection string in the JDBC URL format, e.g.:"
jdbc:snowflake://.snowflakecomputing.com?user=YOUR_USERNAME&password=YOUR_PASSWORD&db=YOUR_DATABASE&warehouse=YOUR_WAREHOUSE&schema=YOUR_SCHEMA&role=YOUR_ROLE
```
:::info Generating Snowflake JDBC URLs
From Snowflake's UI, you can click on the account menu in the Snowflake control panel and then select
`Connect a tool to Snowflake` to get a generated JDBC string from the `Connector/Drivers` tab.
To learn more about Snowflake's conventions for JDBC URLs, see their
[docs](https://docs.snowflake.com/developer-guide/jdbc/jdbc-configure#jdbc-driver-connection-string).
:::
### Step 4. Create and seed a new Snowflake database
In a warehouse, create a new database called `DOCS`. Then, on the `PUBLIC` schema for the `DOCS` database, create a new
table using the standard format via the Snowflake UI:
```sql
CREATE TABLE users (
id INT AUTOINCREMENT START 1 INCREMENT 1 PRIMARY KEY,
name STRING NOT NULL,
age INT NOT NULL
);
```
Next, open a new worksheet on your database's `PUBLIC` schema and insert some seed data:
```sql
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
```
You can verify this worked by using the worksheet to query all records from the `users` table:
```sql
SELECT * FROM users;
```
### Step 5. Introspect your Snowflake database
```sh title="Next, use the CLI to introspect your Snowflake database:"
ddn connector introspect my_snowflake
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_snowflake/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_snowflake
```
### Step 6. Add your model
```sh title="Now, track the table from your Snowflake database as a model in your DDN metadata:"
ddn model add my_snowflake DOCS.PUBLIC.USERS
```
Open the `app/metadata` directory and you'll find a newly-generated file: `DocsPublicUsers.hml`. The DDN CLI will use
this Hasura Metadata Language file to represent the `users` table from Snowflake in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Snowflake connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
docsPublicUsers {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"docsPublicUsers": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your Snowflake schema
```sql title="Add a new table for posts:"
CREATE TABLE posts (
id INT AUTOINCREMENT PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice''s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
```
```sql title="Using the worksheet, query the joined data:"
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_snowflake
```
In `app/connector/my_snowflake/configuration.json`, you'll see schema updated to include operations for the `posts`
table. In `app/metadata/my_snowflake.hml`, you'll see `DOCS.PUBLIC.POSTS` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_snowflake "DOCS.PUBLIC.POSTS"
```
#### Step 11.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
docsPublicPosts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"docsPublicPosts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with Snowflake! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Snowflake database.
- You set up metadata to represent your tables, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Snowflake docs to learn more about how to use Hasura DDN with Snowflake. Or, if you're ready, get
started with adding [permissions](/reference/metadata-reference/permissions.mdx) to control access to your API.
==============================
# with-sqlserver.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-sqlserver
# Get Started with Hasura DDN and SQL Server
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a SQL Server database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch but you can easily follow the steps if you already have data seeded.
Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Start a local SQL Server container
```sh title="Start a SQL Server instance"
docker pull mcr.microsoft.com/mssql/server:2022-latest
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Password123" -p 1433:1433 --name sqlserver -d mcr.microsoft.com/mssql/server:2022-latest
```
### Step 4. Connect and create a table
```sh
docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P Password123 -C -N
```
```SQL title="Create a table in the database:"
CREATE TABLE users (user_id int primary key, name varchar(255), age int);
GO
```
```SQL title="Then, seed the table:"
INSERT INTO users (user_id, name, age) VALUES (1, 'Alice', 25), (2, 'Bob', 30), (3, 'Charlie', 35);
GO
```
```SQL title="You can verify this by running:"
SELECT * FROM users;
GO
```
You should see a list of users returned.
### Step 4. Initialize your SQL Server connector
```sh title="In your project directory, run:"
ddn connector init my_sqlserver -i
```
From the dropdown, start typing `sqlserver` and hit enter to accept the default port. Then, provide the following
values:
**Connection string**
The connection string format will be in the format
`Server=,;Database=;Uid=;Password=` and so is as follows:
```plaintext
Server=local.hasura.dev,1433;Database=master;Uid=sa;Password=Password123;TrustServerCertificate=true
```
:::warning Certificate security
`TrustServerCertificate=true` should only be added for local databases.
:::
### Step 5. Introspect your SQL Server database
```sh title="Next, use the CLI to introspect your SQL Server database:"
ddn connector introspect my_sqlserver
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_sqlserver/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available, and their status, at any point using the CLI:"
ddn connector show-resources my_sqlserver
```
### Step 6. Add your model
```sh title="Now, track the table from your SQL Server database as a model in your DDN metadata:"
ddn model add my_sqlserver users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from SQL Server in your API as a
[model](/reference/metadata-reference/models.mdx).
:::tip
To track all available models in the dababase, run:
```sh
ddn model add my_sqlserver "*"
```
:::
### Step 7. Create a new supergraph build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
Use the `docker-start` script in the Hasura context to start your local services.
```sh title="Start your local Hasura DDN Engine and SQL Server connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
userId
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"userId": 1,
"name": "Alice",
"age": 25
},
{
"userId": 2,
"name": "Bob",
"age": 30
},
{
"userId": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your SQL Server schema
```sh title="Let's add a new table for posts:"
CREATE TABLE posts (user_id int, post_id int primary key, title varchar(255), content varchar(255), FOREIGN KEY (user_id) REFERENCES users(user_id));
GO
```
```sh title="Then, seed it:"
INSERT INTO posts (user_id, post_id, title, content) VALUES (1, 1, 'My First Post', 'This is Alice''s first post.'), (1, 2, 'Another Post', 'Alice writes again!'), (2, 3, 'Bob''s Post', 'Bob shares his thoughts.'), (3, 4, 'Hello World', 'Charlie joins the conversation.');
GO
```
```sh title="Finally, we can check the posts were generated:"
SELECT posts.post_id, posts.title, posts.content, users.name AS author FROM posts JOIN users ON posts.user_id = users.user_id;
```
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_sqlserver
```
In `app/connector/my_sqlserver/configuration.json`, you'll see schema updated to include operations for the `posts`
table. In `app/metadata/my_sqlserver.hml`, you'll see `posts` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_sqlserver posts
```
#### Step 11.3. Kill your services
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
#### Step 11.4. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.5 Restart your services
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
userId
postId
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"userId": 1,
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"userId": 1,
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"userId": 2,
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"userId": 3,
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 13. Create a relationship
```sh title="Since there's already a foreign key on the posts table in SQL Server, we can easily add the relationship:"
ddn relationship add my_sqlserver posts
```
You'll see a new metadata object added to the `app/metadata/posts.hml` file of kind `Relationship` explaining the
relationship between `posts` and `users`.
### Step 14. Rebuild your project
Bring down the services by pressing `CTRL+C` in the terminal tab logging their activity.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring everything back up:"
ddn run docker-start
```
### Step 15. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
postId
title
content
user {
userId
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"postId": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"userId": 1,
"name": "Alice",
"age": 25
}
},
{
"postId": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"userId": 2,
"name": "Bob",
"age": 30
}
},
{
"postId": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"userId": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with SQL Server! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local SQL Server database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our [SQL Server docs](/reference/connectors/sqlserver/index.mdx) to learn more about how to use Hasura
DDN with SQL Server. Or, if you're ready, get started with adding
[permissions](/reference/metadata-reference/permissions.mdx) to control access to your API.
==============================
# with-storage.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-storage
# Get Started with Hasura DDN and Cloud Storage
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a cloud storage instance
- Generate Hasura metadata
- Create a build
- Run your first query
- Mutate data
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
:::info Available Cloud Storage
In this tutorial, we'll use the Storage connector's local file system, but you can easily configure the connector to
work with:
- [AWS S3](https://aws.amazon.com/pm/serv-s3)
- [Google Cloud Storage](https://cloud.google.com/storage)
- [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs)
- [Cloudflare R2](https://www.cloudflare.com/developer-platform/products/r2/)
- [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces)
Hasura will never modify your source schema. Learn more about these sources in the connector's configuration section.
:::
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Storage connector
```sh title="In your project directory, run:"
ddn connector init my_storage -i
```
Select `hasura/storage` from the list of connectors. You can start typing `storage` to quickly filter the list; hit
`ENTER` to accept any default values for which you're prompted.
### Step 4. Update your `configuration.yaml`
```yaml title="Update the app/connector/my_storage/configuration.yaml to utilize the local file system by replacing its contents with:"
# yaml-language-server: $schema=https://raw.githubusercontent.com/hasura/ndc-storage/main/jsonschema/configuration.schema.json
clients:
- id: fs
type: fs
defaultDirectory:
value: /home/nonroot/data
concurrency:
query: 5
mutation: 1
runtime:
maxDownloadSizeMBs: 20
maxUploadSizeMBs: 20
generator:
promptqlCompatible: false
```
:::info Navigating the file system
"Local" in this case refers to the container in which the connector is running. The files we create and query will be
ephemeral and unavailable after the container is stopped.
:::
### Step 5. Introspect your cloud storage
```sh title="Next, use the CLI to introspect your cloud storage:"
ddn connector introspect my_storage
```
As the schema for this connector is mapped to file system conventions for S3-compatible providers, the connector does
not generate any local configuration files. Instead, the introspection command above will generate a `my_storage.hml`
file that contains all the [models](/reference/metadata-reference/models.mdx) and
[commands](/reference/metadata-reference/commands.mdx) necessary to interact with your file system via the exposed
GraphQL API.
```sh title="You can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_storage
```
### Step 6. Add your models
The connector contains two models: `storage_buckets` and `storage_objects`.
```sh title="Track them both with this command:"
ddn model add my_storage "*"
```
Open the `app/metadata` directory and you'll find newly-generated file for both of these models. The DDN CLI will use
these Hasura Metadata Language files to represent the available bucket(s) and objects within it.
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Storage connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_ALL_BUCKETS {
storageBuckets(args: {}) {
name
}
}
```
```json title="You'll get the following response:"
{
"data": {
"storageBuckets": [
{
"name": "/home/nonroot/data"
}
]
}
}
```
This is the same directory you provided in your `configuration.yaml` file and where we'll soon write new files to via
the API.
### Step 10. Add your commands
Let's write a text file to this directory (bucket). To do so, we can use one of the pre-existing commands in our
`my_storage.hml` file.
```sh title="Run the following to add the UploadStorageObjectAsText as command:"
ddn command add my_storage "upload_storage_object_as_text"
```
We'll also need another command that allows us to download the file as text.
```sh title="We can add that using the following:"
ddn command add my_storage "download_storage_object_as_text"
```
As before, these commands will generate new HML files in the `app/metadata` directory.
### Step 11. Create a new build and restart your services
```sh title="Create the new build:"
ddn supergraph build local
```
```sh title="Kill your locally running services with CTRL+C and then restart them with:"
ddn run docker-start
```
### Step 12. Create a new text file via a mutation
```graphql title="From the GraphiQL explorer in your console, execute the following mutation:"
mutation ADD_TXT_FILE {
uploadStorageObjectAsText(data: "This is a sample text file.", name: "sample.txt", bucket: "/home/nonroot/data") {
name
size
}
}
```
```json title="Which will return a value like this:"
{
"data": {
"uploadStorageObjectAsText": {
"name": "sample.txt",
"size": 27
}
}
}
```
### Step 13. Query the new file's contents
```graphql title="You can now query that file's text value:"
query GET_TEXT_VALUE_FROM_OBJECT {
downloadStorageObjectAsText(name: "sample.txt") {
data
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"downloadStorageObjectAsText": {
"data": "This is a sample text file."
}
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project to interact with cloud storage! π
Here's what you just accomplished:
- You started with a fresh project and connected it to the Storage connector.
- You set up metadata to represent your file system and methods of interacting with it, which acts as the blueprint for
your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- You utilized commands to write a file and then access its text value.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Storage connector docs to learn more about how to use Hasura DDN with cloud storage providers. Or, if
you're ready, get started with adding [permissions](/reference/metadata-reference/permissions.mdx) to control access to
your API.
==============================
# with-stripe.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-stripe
# Get Started with Hasura DDN and Stripe
## Overview
This tutorial takes about ten minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Stripe account
- Generate Hasura metadata
- Create a build
- Run your first query
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting with an existing [Stripe account](https://stripe.com/) which has some
[products](https://docs.stripe.com/api/products) and [checkout sessions](https://docs.stripe.com/api/checkout/sessions).
Though, by the end, you'll be able to query any information stored in Stripe.
Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Stripe connector
```sh title="In your project directory, run:"
ddn connector init my_stripe -i
```
From the dropdown, select `hasura/stripe` (you can type to filter the list), then hit enter to accept the default of all
the options.
You'll be prompted for an environment variable:
| Variable | Description | Example |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| `STRIPE_BEARER_AUTH_TOKEN` | A **secret** key for your account, retrievable from your [API Keys dashboard in Stripe](https://dashboard.stripe.com/test/apikeys). | `sk__` |
### Step 4. Introspect your Stripe account
```sh title="Next, use the CLI to introspect your Stripe account:"
ddn connector introspect my_stripe
```
After running this, you should see a representation of the Stripe API's schema in the `app/metadata/my_stripe.hml` file;
you can view this using `cat` or open the file in your editor.
Under the hood, the Stripe connector utilizes the [`ndc-http` connector](https://github.com/hasura/ndc-http) via
[Stripe's OpenAPI spec](https://github.com/stripe/openapi). This ensures you can incorporate any resources available via
Stripe's REST API into your supergraph.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_stripe
```
### Step 5. Add your command
```sh title="Let's start by tracking the GetProducts endpoint from Stripe's API:"
ddn command add my_stripe GetProducts
```
Open the `app/metadata` directory and you'll find a newly-generated file: `GetProducts.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `GetProducts` endpoint from Stripe in your API as a
[command](/reference/metadata-reference/commands.mdx).
### Step 6. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 7. Start your local services
```sh title="Start your local Hasura DDN Engine and Stripe connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 8. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_ALL_PRODUCTS {
getProducts {
data {
id
name
description
}
}
}
```
```json title="You'll get a response listing all of your products. For example:"
{
"data": {
"getProducts": {
"data": [
{
"id": "3bef8a40-3c33-11ee-bb29-070df467ec94",
"name": "Tee",
"description": "A great t-shirt!"
},
{
"id": "e0a70b16-65b6-11ed-8788-8fa2504d64a3",
"name": "Sticker Sheet",
"description": "Who doesn't love stickers??"
},
{
"id": "a44eda7c-65b6-11ed-997b-53b5bdb7117e",
"name": "Hasuras in The Cloud Tee",
"description": "The cloud for your cloud."
},
{
"id": "8aa93f86-65b6-11ed-901c-f320d4e17bb2",
"name": "Dark Furry Logo Tee",
"description": "Furry for furries."
}
]
}
}
}
```
### Step 9. Add another command
```sh title="Add the GetCheckoutSessions command:"
ddn command add my_stripe GetCheckoutSessions
```
In `app/metadata` you'll find a newly-generated file: `GetCheckoutSessions.hml`. This will be used to expose customer
checkout sessions and their related information via your supergraph API.
### Step 10. Rebuild your project
```sh title="Next, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 11. Query your new build
```graphql title="Head back to your console and query the GetCheckoutSessions endpoint:"
query GET_CHECKOUT_SESSIONS {
getCheckoutSessions {
data {
id
amountTotal
customerDetails {
name
email
}
}
}
}
```
```json title="Because of the schema of Stripe's API, you'll see results like this, which contain nested information, such as customer details:"
{
"data": {
"getCheckoutSessions": {
"data": [
{
"id": "cs_test_",
"amountTotal": 1000,
"customerDetails": {
"name": "Rob",
"email": "robdemo@hasura.io"
}
},
{
"id": "cs_test_",
"amountTotal": 1000,
"customerDetails": {
"name": "Sandeep",
"email": "sandeepdemo@hasura.io"
}
}
]
}
}
}
```
:::info create relationships to your own data
You can create relationships between resources in your Stripe account and data living in other data sources.
As an example, you may have a PostgreSQL database with a `users` model exposed and which contains an `email` field. This
field can be used to create a [relationship](/reference/metadata-reference/relationships.mdx) between the `users` model
and a `CheckoutSession` (or any other resource that utilizes a unique `email`) to return information across your sources
in a single query.
:::
## Next steps
Congratulations on completing your first Hasura DDN project with Stripe! π
Here's what you just accomplished:
- You started with a fresh project and connected it to an existing Stripe account.
- You set up metadata to represent the various endpoints on Stripe's API, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data Stripe data to other data sources, empowering you to iterate and
scale with confidence. Great work!
The Stripe connector also supports mutations; you can create, update, or delete products; create new checkout sessions;
refund payments; etc.
Take a look at our Stripe docs to learn more about how to use Hasura DDN with [Stripe](https://docs.stripe.com/). Or, if
you're ready, get started with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-trino.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-trino
# Get Started with Hasura DDN and Trino
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Trino instance, backed by a locally-running PostgreSQL database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch, but you can easily follow the steps if you already have data seeded;
Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Initialize your Trino connector
```sh title="In your project directory, run:"
ddn connector init my_trino -i
```
From the dropdown, select `hasura/trino` (you can type to filter the list). Then, enter the following JDBC URL:
```plaintext
jdbc:trino://local.hasura.dev:8080/postgres/public?user=myuser
```
This will allow your connector to connect to the PostgreSQL instance integrated with the Trino server you'll run
locally.
### Step 4. Create the containers with Trino and PostgreSQL
```sh title="Begin by creating a compose file for the Trino service:"
touch app/connector/my_trino/compose.trino.yaml
```
```yaml title="Then, open the file and add the following:"
services:
postgres:
image: postgres:15
container_name: postgres
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser"]
interval: 5s
timeout: 5s
retries: 5
trino:
image: trinodb/trino:latest
container_name: trino
depends_on:
postgres:
condition: service_healthy
ports:
- "8080:8080"
command: |
/bin/sh -c "
mkdir -p /etc/trino/catalog &&
echo 'connector.name=postgresql' > /etc/trino/catalog/postgres.properties &&
echo 'connection-url=jdbc:postgresql://postgres:5432/mydb' >> /etc/trino/catalog/postgres.properties &&
echo 'connection-user=myuser' >> /etc/trino/catalog/postgres.properties &&
echo 'connection-password=mypassword' >> /etc/trino/catalog/postgres.properties &&
/usr/lib/trino/bin/run-trino
"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/v1/info"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
```
```sh title="Run the container:"
docker compose -f app/connector/my_trino/compose.trino.yaml up -d
```
The Trino container will start; you can access the UI β which will give you general information about your clusters β
here: [`http://localhost:8080/`](http://localhost:8080/).
### Step 5. Create a table in your PostgreSQL database
```sh title="Enter the PostgreSQL container:"
docker exec -it postgres psql -U myuser -d mydb
```
```sql title="Then, create your first table and seed the database:"
--- Create the table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
--- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
```
You can verify this worked by querying the users table directly in the psql session:
```sql
SELECT * FROM users;
```
### Step 6. Introspect your Trino instance
```sh title="Next, in a new tab, use the CLI to introspect your Trino database:"
ddn connector introspect my_trino
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_trino/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_trino
```
### Step 7. Add your model
```sh title="Now, track the table from Trino server as a model in your DDN metadata:"
ddn model add my_trino users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from Trino in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 8. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 9. Start your local services
```sh title="Start your local Hasura DDN Engine and Trino connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 10. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 11. Iterate on your Trino schema
```sh title="Let's re-enter the PostgreSQL container:"
docker exec -it postgres psql -U myuser -d mydb
```
```sql title="Then, add a new table for posts:"
-- Create the posts table
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert some seed data
INSERT INTO posts (user_id, title, content) VALUES
(1, 'My First Post', 'This is Alice''s first post.'),
(1, 'Another Post', 'Alice writes again!'),
(2, 'Bob''s Post', 'Bob shares his thoughts.'),
(3, 'Hello World', 'Charlie joins the conversation.');
```
```sql title="To verify this, query the joined data:"
SELECT
posts.id AS post_id,
posts.title,
posts.content,
posts.created_at,
users.name AS author
FROM
posts
JOIN
users ON posts.user_id = users.id;
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 12. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 12.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_trino
```
In `app/connector/my_trino/configuration.json`, you'll see schema updated to include operations for the `posts` table.
In `app/metadata/my_trino.hml`, you'll see `DOCS.PUBLIC.POSTS` present in the metadata as well.
#### Step 12.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_trino posts
```
#### Step 12.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 12.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 13. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 14. Create a relationship
```yaml title="Find the Posts.hml file in your connector's metadata directory and add the following relationship object to the bottom:"
---
kind: Relationship
version: v1
definition:
name: user
sourceType: Posts
target:
model:
name: Users
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: id
```
This will create a relationship that maps the `userId` for any post to the `id` of a user, allowing for nested queries.
### Step 15. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 16. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
id
title
content
user {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"id": 2,
"name": "Bob",
"age": 30
}
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"id": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
## Next steps
Congratulations on completing your first Hasura DDN project with Trino! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Trino instance.
- You set up metadata to represent your tables, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Trino docs to learn more about how to use Hasura DDN with Trino. Or, if you're ready, get started
with adding [permissions](/reference/metadata-reference/permissions.mdx) to control access to your API.
==============================
# with-turso.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-turso
# Get Started with Hasura DDN and Turso
## Overview
This tutorial takes about twenty minutes to complete. You'll learn how to:
- Set up a new Hasura DDN project
- Connect it to a Turso-hosted database
- Generate Hasura metadata
- Create a build
- Run your first query
- Create relationships
- Mutate data
Additionally, we'll familiarize you with the steps and workflows necessary to iterate on your API.
This tutorial assumes you're starting from scratch; you'll connect a hosted Turso instance to Hasura, but you can easily
follow the steps if you already have data seeded. Hasura will never modify your source schema.
## Prerequisites
**Install the DDN CLI**
:::info Minimum version requirements
To use this guide, ensure you've installed/updated your CLI to at least `v2.28.0`.
:::
Simply run the installer script in your terminal:
{`curl -L https://graphql-engine-cdn.hasura.io/ddn/cli/${props.revision || "v4"}/get.sh | bash`}
Currently, the CLI does not support installation on ARM-based Linux systems.
- Download the latest DDN CLI installer for Windows.
- Run the `DDN_CLI_Setup.exe` installer file and follow the instructions. This will only take a minute.
- By default, the DDN CLI is installed under `C:\Users\{Username}\AppData\Local\Programs\DDN_CLI`
- The DDN CLI is added to your `%PATH%` environment variable so that you can use the `ddn` command from your terminal.
**Install [Docker](https://docs.docker.com/engine/install/)**
The Docker-based workflow helps you iterate and develop locally without deploying any changes to Hasura DDN, making the
development experience faster and your feedback loops shorter. **You'll need Docker Compose `v2.20` or later.**
**Validate the installation**
You can verify that the DDN CLI is installed correctly by running:
```ddn
ddn doctor
```
## Tutorial
### Step 1. Authenticate your CLI
```sh title="Before you can create a new Hasura DDN project, you need to authenticate your CLI:"
ddn auth login
```
This will launch a browser window prompting you to log in or sign up for Hasura DDN. After you log in, the CLI will
acknowledge your login, giving you access to Hasura Cloud resources.
### Step 2. Scaffold out a new local project
```sh title="Next, create a new local project:"
ddn supergraph init my-project && cd my-project
```
Once you move into this directory, you'll see your project scaffolded out for you. You can view the structure by either
running `ls` in your terminal, or by opening the directory in your preferred editor.
### Step 3. Create and seed a new Turso database
#### Step 3.1. Create a new database
Head to [Turso](https://turso.tech) and create an account if you don't already have one. Then, create a new group and
database using their onboarding wizard.
After you finish creating the database, you'll be able to access it from your [dashboard](https://app.turso.tech/).
#### Step 3.2. Create an auth token
From the database, create an auth token and save it.
#### Step 3.3. Seed the database
```sh title="Replacing the db-name, username, and auth token with your values, run the following command to create your first table and seed it with data:"
curl -L -X POST 'https://-.turso.io/v2/pipeline' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"requests": [
{
"type": "execute",
"stmt": {
"sql": "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL)"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO users (name, age) VALUES (\"Alice\", 25)"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO users (name, age) VALUES (\"Bob\", 30)"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO users (name, age) VALUES (\"Charlie\", 35)"
}
},
{
"type": "close"
}
]
}'
```
```sh title="You can then verify this by running the following:"
curl -L -X POST 'https://-.turso.io/v2/pipeline' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"requests": [
{
"type": "execute",
"stmt": {
"sql": "SELECT * FROM users"
}
},
{
"type": "close"
}
]
}'
```
### Step 4. Initialize your Turso connector
```sh title="In your project directory, run:"
ddn connector init my_turso -i
```
From the dropdown, select `hasura/turso` (you can type to filter the list), then hit enter to accept the default of all
the options.
You'll be prompted for two environment variables:
| Variable | Description | Example |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `TURSO_URL` | The connection string for the Turso database, using `libsql` protocol. You can generate this from the database's overview in your Turso dashboard. | `libsql://dbname-username.turso.io` |
| `TURSO_AUTH_TOKEN` | The Turso auth token you genreated in the previous step. | `eyJ...` |
### Step 5. Introspect your Turso database
```sh title="Next, use the CLI to introspect your Turso database:"
ddn connector introspect my_turso
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_turso/config.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Additionally, you can check which resources are available βΒ and their status β at any point using the CLI:"
ddn connector show-resources my_turso
```
### Step 6. Add your model
```sh title="Now, track the table from your Turso database as a model in your DDN metadata:"
ddn model add my_turso users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from Turso in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 7. Create a new build
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
### Step 8. Start your local services
```sh title="Start your local Hasura DDN Engine and Turso connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
### Step 9. Run your first query
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
id
name
age
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": 1,
"name": "Alice",
"age": 25
},
{
"id": 2,
"name": "Bob",
"age": 30
},
{
"id": 3,
"name": "Charlie",
"age": 35
}
]
}
}
```
### Step 10. Iterate on your Turso schema
```sql title="Add a new table and insert some data to your Turso database, taking care to update your values accordingly:"
curl -L -X POST 'https://-.turso.io/v2/pipeline' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"requests": [
{
"type": "execute",
"stmt": {
"sql": "CREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE)"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO posts (user_id, title, content) VALUES (1, \"My First Post\", \"This is Alice'\''s first post.\")"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO posts (user_id, title, content) VALUES (1, \"Another Post\", \"Alice writes again!\")"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO posts (user_id, title, content) VALUES (2, \"Bob'\''s Post\", \"Bob shares his thoughts.\")"
}
},
{
"type": "execute",
"stmt": {
"sql": "INSERT INTO posts (user_id, title, content) VALUES (3, \"Hello World\", \"Charlie joins the conversation.\")"
}
},
{
"type": "close"
}
]
}'
```
```sql title="Verify this by running the following query:"
curl -L -X POST 'https://-.turso.io/v2/pipeline' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"requests": [
{
"type": "execute",
"stmt": {
"sql": "SELECT posts.id AS post_id, posts.title, posts.content, posts.created_at, users.name AS author FROM posts JOIN users ON posts.user_id = users.id"
}
},
{
"type": "close"
}
]
}'
```
You should see a list of posts returned with the author's information joined from the `users` table
### Step 11. Refresh your metadata and rebuild your project
:::tip
The following steps are necessary each time you make changes to your **source** schema. This includes, adding,
modifying, or dropping tables.
:::
#### Step 11.1. Re-introspect your data source
```sh title="Run the introspection command again:"
ddn connector introspect my_turso
```
In `app/connector/my_turso/config.json`, you'll see schema updated to include operations for the `posts` table. In
`app/metadata/my_turso.hml`, you'll see `posts` present in the metadata as well.
#### Step 11.2. Update your metadata
```sh title="Add the posts model:"
ddn model add my_turso "posts"
```
#### Step 11.3. Create a new build
```sh title="Next, create a new build:"
ddn supergraph build local
```
#### Step 11.4. Restart your services
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 12. Query your new build
```graphql title="Head back to your console and query the posts model:"
query GetPosts {
posts {
id
title
content
}
}
```
```json title="You'll get a response like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post."
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!"
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts."
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation."
}
]
}
}
```
### Step 13. Create a relationship
```sh title="Since there's already a foreign key on the posts table in Turso, we can easily add the relationship:"
ddn relationship add my_turso "posts"
```
You'll see a new metadata object added to the `app/metadata/posts.hml` file of kind `Relationship` explaining the
relationship between `posts` and `users`.
### Step 14. Rebuild your project
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 15. Query using your relationship
```graphql title="Now, execute a nested query using your relationship:"
query GetPosts {
posts {
id
title
content
user {
id
name
age
}
}
}
```
```json title="Which should return a result like this:"
{
"data": {
"posts": [
{
"id": 1,
"title": "My First Post",
"content": "This is Alice's first post.",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 2,
"title": "Another Post",
"content": "Alice writes again!",
"user": {
"id": 1,
"name": "Alice",
"age": 25
}
},
{
"id": 3,
"title": "Bob's Post",
"content": "Bob shares his thoughts.",
"user": {
"id": 2,
"name": "Bob",
"age": 30
}
},
{
"id": 4,
"title": "Hello World",
"content": "Charlie joins the conversation.",
"user": {
"id": 3,
"name": "Charlie",
"age": 35
}
}
]
}
}
```
### Step 16. Add all commands
We'll track the available operations β for inserting, updating, and deleting β on our `users` and `posts` tables as
commands.
```sh title="Add all available commands:"
ddn command add my_turso "*"
```
You'll see newly-generated metadata files in the `metadata` directory for your connector that represent insert, update,
and delete operations.
```sh title="As your metadata has changed, create a new build:"
ddn supergraph build local
```
```sh title="Bring down the services by pressing CTRL+C and start them back up:"
ddn run docker-start
```
### Step 17. Insert new data
```graphql title="Create a new post for Charlie:"
mutation InsertSinglePost {
insertPostsOne(
object: {
content: "I am an expert in Bird Law and I demand satisfaction."
title: "Charlie has more to say"
userId: 3
id: 5
}
) {
id
title
content
}
}
```
You should see a response that returns your inserted data.
## Next steps
Congratulations on completing your first Hasura DDN project with Turso! π
Here's what you just accomplished:
- You started with a fresh project and connected it to a local Turso database.
- You set up metadata to represent your tables and relationships, which acts as the blueprint for your API.
- Then, you created a build β essentially compiling everything into a ready-to-use API β and successfully ran your first
GraphQL queries to fetch data.
- Along the way, you learned how to iterate on your schema and refresh your metadata to reflect changes.
- Finally, we looked at how to enable mutations and insert data using your new API.
Now, you're equipped to connect and expose your data, empowering you to iterate and scale with confidence. Great work!
Take a look at our Turso docs to learn more about how to use Hasura DDN with Turso. Or, if you're ready, get started
with adding [permissions](/auth/permissions/index.mdx) to control access to your API.
==============================
# with-others.mdx
URL: https://hasura.io/docs/3.0/docs/how-to-build-with-ddn/with-others
# Get Started with Hasura DDN and other Sources
On the [Connector Hub](https://hasura.io/connectors), you can find information about our wealth of data connectors.
These data connectors allow you to connect nearly any data source β be them relational, vector, custom business logic,
or even a third-party API β to Hasura DDN and build an API on top of it.
We recommend using the [Quickstart](/quickstart.mdx) and referencing the individual connector's docs from βοΈ to get
started with a connector-specific setup.
==============================
# overview.mdx
URL: https://hasura.io/docs/3.0/docs/data-sources/overview
# Basics
## What is a data source?
When we talk about data, many people first think of structured tables in a database. While thatβs a significant part of
the picture, data comes in many formsβsome structured, others not. A data source, in the context of Hasura DDN, can be
as straightforward as a relational database or as dynamic as the output of a custom function.
With Hasura DDN, you can connect it all and serve it from a single endpoint.
## How do I connect my data to Hasura DDN?
We use **native data connectors** to make your data accessible through a GraphQL API. These connectors are tailored for
specific sources, including relational databases, NoSQL, external GraphQL APIs, REST APIs, and more.
We have the following data connectors available today:
## Building your own data connector
You are also able to build your own data connector. This is useful if you have a custom data source that is not
supported. You can find information for this in the [NDC Spec](https://github.com/hasura/ndc-spec) and the rendered
[NDC Spec data connector tutorial](https://hasura.github.io/ndc-spec/tutorial/index.html) as well as the SDKs for
various languages:
- [Rust](https://github.com/hasura/ndc-sdk-rs)
- [TypeScript](https://github.com/hasura/ndc-sdk-typescript)
- [Python](https://github.com/hasura/ndc-sdk-python)
- [Go](https://github.com/hasura/ndc-sdk-go)
## Get started
- Learn how to [connect your data](/data-sources/connect-to-a-source.mdx)
==============================
# connect-to-a-source.mdx
URL: https://hasura.io/docs/3.0/docs/data-sources/connect-to-a-source
# Connect to a source
## Introduction
This guide explains how to initialize a connector, configure its environment variables, and link it to your data source.
Once initialized, you'll be ready to introspect the source and integrate it into your API.
You'll need a [project](/reference/cli/commands/ddn_supergraph_init.mdx) before initializing a connector.
## Step 1. Initialize a connector
Regardless which connector you're using, you'll always begin by initializing it with a unique name:
```sh title="Initialize a new connector in a project directory:"
ddn connector init -i
```
A wizard will appear with a dropdown list. If you know the name of the connector, start typing the name. Otherwise, use
the arrows to scroll through the list. Hit `ENTER` when you've selected your desired connector; you'll then be prompted
to enter some values.
:::info Customization
You can customize which subgraph this connector is added to by
[changing your project's context](/reference/cli/commands/ddn_context.mdx) or using flags. More information can be found
in the [CLI docs](/reference/cli/commands/ddn_connector_init.mdx) for the `ddn connector init` command.
:::
## Step 2. Add environment variables
The CLI will assign a random port for the connector to use during local development. You can hit `ENTER` to accept the
suggested value or enter your own. Then, depending on your connector, there may be a set of environment variables that
it requires:
| ENV | Example | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `JDBC_URL` | `jdbc:bigquery://https://www.googleapis.com/bigquery/v2:443;ProjectId=project-id;DefaultDataset=dataset;OAuthType=0;OAuthServiceAcctEmail=service-account-email;OAuthPvtKey=/etc/connector/key.json;` | The JDBC URL to connect to the BigQuery database. |
:::info BigQuery specific characteristics
See [here](/how-to-build-with-ddn/with-bigquery.mdx) for instructions on saving your service account key file
:::
| ENV | Example | Description |
| ------------------- | ------------------- | --------------------------------------------------------- |
| `CONNECTION_STRING` | `https://host:8123` | The HTTP(S) connection string to the ClickHouse instance. |
| `USERNAME` | `default` | The database username. |
| `PASSWORD` | `default` | The database password. |
| ENV | Example | Description |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `JDBC_URL` | `jdbc:databricks://:/default;transportMode=http;ssl=1;AuthMech=3;httpPath=/sql/1.0/warehouses/;UID=token;PWD=;ConnCatalog=main;` | You can construct the base of this using your Databricks UI under `SQL Warehouses` Β» `` Β» `Connection details`. |
| `JDBC_SCHEMAS` | `default,public` | A comma-separated list of schemas within the referenced catalog. |
| ENV | Example | Description |
| ------------ | ------------------- | ---------------------------------------------------------------------------------------- |
| `DUCKDB_URL` | `/path/to/*.duckdb` | The path to the DuckDB database file which will be mounted to the connector's container. |
| ENV | Example | Description |
| ----------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ELASTICSEARCH_URL` | `https://example.es.gcp.cloud.es.io:9200` | The comma-separated list of Elasticsearch host addresses for connection (Use `local.hasura.dev` instead of `localhost` if your connector is running on your local machine) |
| `ELASTICSEARCH_USERNAME` | `default` | The username for authenticating to the Elasticsearch cluster |
| `ELASTICSEARCH_PASSWORD` | `default` | The password for the Elasticsearch user account |
| `ELASTICSEARCH_API_KEY` | `ABCzYWk0NEI0aDRxxxxxxxxxx1k6LWVQa2gxMUpRTUstbjNwTFIzbGoyUQ==` | The Elasticsearch API key for authenticating to the Elasticsearch cluster |
| `ELASTICSEARCH_CA_CERT_PATH` | `/etc/connector/cacert.pem` | The path to the Certificate Authority (CA) certificate for verifying the Elasticsearch server's SSL certificate |
| `ELASTICSEARCH_INDEX_PATTERN` | `hasura*` | The pattern for matching Elasticsearch indices, potentially including wildcards, used by the connector |
The HTTP connector won't prompt you for any environment variables. When you initialize the connector, a `config.yaml`
will be generated where you can list valid specs for your API(s).
Learn more [here](/reference/connectors/http/index.mdx).
| ENV | Example | Description |
| ------------------ | ------------------------------------------- | ------------------------------------------ |
| `GRAPHQL_ENDPOINT` | `https://spacex-production.up.railway.app/` | The GraphQL endpoint for the existing API. |
:::info The GraphQL connector is highly configurable
When you first initialize the connector, the CLI will only ask for the API's endpoint. You can further configure the
connector β such as choosing different configurations for introspection vs. execution β using the connector's
configuration file.
Learn more [here](/reference/connectors/graphql/configuration.mdx).
:::
| ENV | Example | Description |
| -------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------- |
| `MONGO_DB_URL` | `mongodb://host:27017/db_name` | The full connection string βΒ **including the database name** β used to connect to the MongoDB instance. |
| ENV | Example | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| `CONNECTION_URI` | `Server=local.hasura.dev,1433;Database=master;Uid=sa;Password=Password123;TrustServerCertificate=true` | `TrustServerCertificate=true` should only be added for local databases. |
| ENV | Example | Description |
| ---------- | ---------------------------------------------- | ----------------------------------- |
| `JDBC_URL` | `jdbc:mysql://user:password@host:3306/db_name` | This connector requires a JDBC URL. |
| ENV | Example | Description |
| ---------------- | --------------------------------------------- | ---------------------------------------------------------------------- |
| `CONNECTION_URI` | `postgresql://user:password@host:5432/dbname` | The full connection string used to connect to the PostgreSQL database. |
| ENV | Example | Description |
| ---------------- | ----------------------- | ---------------------------------------------------------------------------------------------------- |
| `CONNECTION_URL` | `https://:` | The full connection string used to connect to the Prometheus server. By default, the port is `9090`. |
| ENV | Example | Description |
| ---------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `QDRANT_URL` | `https://..:` | The connection string for the Qdrant database, including the port. You can generate this by selecting `Connect` under the Cluster in your dashboard. |
| `QDRANT_API_KEY` | `eyJ...` | The Qdrant API key presented to you when the cluster was provisioned. |
| ENV | Example | Description |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `JDBC_URL` | `jdbc:snowflake://.snowflakecomputing.com?user=YOUR_USERNAME&&password=YOUR_PASSWORD&db=YOUR_DATABASE&warehouse=YOUR_WAREHOUSE&schema=YOUR_SCHEMA&role=YOUR_ROLE` | This connector requires a JDBC URL. |
:::info Optional parameters for Snowflake
Snowflake allows you to set a number of defaults. Your JDBC can minimal (i.e., only including the account identifier,
username, and password) or more granular depending on your settings within Snowflake.
:::
| ENV | Example | Description |
| ------------------- | ------------------------------------------ | -------------------------------------------------------- |
| `ACCESS_KEY_ID` | `AKIAIOSFODNN7EXAMPLE` | Your AWS access key ID used to authenticate with S3. |
| `SECRET_ACCESS_KEY` | `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` | Your AWS secret access key used to authenticate with S3. |
| `STORAGE_ENDPOINT` | `https://s3.amazonaws.com` | The S3 service endpoint URL. |
| `DEFAULT_BUCKET` | `my-app-bucket` | The default S3 bucket name where files will be stored. |
:::info These environment variables are AWS-specific
Upon initialization, the connector will prompt you for the environment variables listed above. However, you can
configure this connector to work with any S3-compatible cloud provider. Check the configuration docs for more
information.
:::
| ENV | Example | Description |
| -------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `STRIPE_BEARER_AUTH_TOKEN` | `sk__` | A **secret** key for your account, retrievable from your [API Keys dashboard in Stripe](https://dashboard.stripe.com/test/apikeys). |
| ENV | Example | Description |
| ---------- | ---------------------------------------------------------------- | ----------------------------------- |
| `JDBC_URL` | `jdbc:trino://://?user=` | This connector requires a JDBC URL. |
| ENV | Example | Description |
| ------------------ | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TURSO_URL` | `libsql://dbname-username.turso.io` | The connection string for the Turso database, using `libsql` protocol. You can generate this from the database's overview in your Turso dashboard. |
| `TURSO_AUTH_TOKEN` | `eyJ...` | A Turso auth token with access to the same database; this is also available via the Turso dashboard. |
If your data source requires a connection string or endpoint, the CLI will confirm that it successfully tested the
connection to your source. Additionally, it generates configuration files, which you can find in the `connector`
directory of the subgraph where you added the connector (default: `app`). Finally, the CLI will create a
[DataConnectorLink](/reference/metadata-reference/data-connector-links.mdx) in your connector's `metadata` directory.
## Next steps
Now that you've initialized a connector and connected it to your data, you're ready to introspect the source and
populate the configuration files with source-specific information that Hasura will need to build your API. Check out the
[introspection page](/data-sources/introspect-a-source.mdx) to learn more.
==============================
# introspect-a-source.mdx
URL: https://hasura.io/docs/3.0/docs/data-sources/introspect-a-source
# Introspect a source
## Introduction
This guide explains how to use a connector to introspect a data source. After introspection, you'll be ready to begin
generating Hasura metadata that represents resources (such as tables or collections) in your data source.
You'll need an [initialized data connector](/data-sources/connect-to-a-source.mdx) before attempting to introspect a
source.
## Step 1. Run the introspect command
```sh title="All connectors use the same command to introspect a data source:"
ddn connector introspect
```
:::tip Is your Docker daemon running?
When you run the introspection command, the connector will start. As it runs as a Docker service, the Docker daemon must
be running.
:::
The connector will reach out to your data source and update the configuration files with information about your source
schema. This will vary depending on the connector but, at a minimum, you'll see source-specific information updated in
two files, both located in your connector's directory:
| File | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `configuration.json` | An [Open Data Domain Specification](https://github.com/hasura/open-data-domain-specification) description of your data source. The CLI uses this file to update the DataConnectorLink object with information about your data source. |
| `.hml` | Contains the [DataConnectorLink](/reference/metadata-reference/data-connector-links.mdx) for your connector. |
:::info When to use this command
This command will be used at least once with every connector; Hasura **must** introspect your data source in order to
understand the resources that are present.
**Additionally, any time you modify your source schema, you should re-run this command to ensure your Hasura metadata is
up-to-date with your source schema.**
:::
## Next steps
With your source introspected and the configuration files populated, you can now start creating Hasura metadata that
will represent your source's schema in your API. Check out the [Data Modeling](/data-modeling/overview.mdx) section to
begin creating models, commands, and relationships for your data.
==============================
# troubleshooting.mdx
URL: https://hasura.io/docs/3.0/docs/data-sources/troubleshooting
# Troubleshooting
## Common issues
### My connector can't connect to the data source
When initializing a new connector, we verify the connection string you provide to ensure your data source is reachable.
If this process fails, try the following solutions:
#### Your source isn't accessible to the public internet
If your data source is hosted behind a firewall or restricts access to specific IP addresses, it may block external
connections. Unless you're using [Private DDN](https://hasura.io/pricing), you need to allow access from all IP
addresses. You can do this by adding the following to your allowlist:
```plaintext
0.0.0.0/0
```
#### Docker networking issues
Hasura DDN resolves `local.hasura.dev` to your machine's `localhost`. This avoids Docker networking conflicts, where
`localhost` might point to your source's container instead of your local machine. To fix this, update any `localhost`
references in your connection strings to `local.hasura.dev`.
### My connector won't introspect my data source
If you encounter an error like the following:
```plaintext
ERR Failed building the container: exit status 17
```
You'll likely see the following in the logs:
```plaintext
Failed to resolve source metadata for ghcr.io/hasura/: failed to authorize: failed to fetch oauth token: unexpected status from GET request to https://ghcr.io/token?scope=repository%3Ahasura%2F%3Apull&service=ghcr.io: 403 Forbidden
```
When this happens, your GitHub access token located in your Docker credentials store is likely out-of-date.
To resolve this, generate a new PAT with `package` scope and run the following:
```sh
echo "" | docker login ghcr.io -u --password-stdin
```
Your terminal should return `Login Succeeded`.
### There's a port conflict for my connector
Use `docker ps` to check for any running processes that would conflict with your source's container. If you find one,
kill it and then try initializing the connector again.
### My connector isn't reflecting schema changes in my data source
You may have not re-run the introspection steps. Remember, each time your data source's schema changes, you'll need to
re-run this command to make Hasura DDN aware of the new schema. Follow the guide
[here](/data-sources/introspect-a-source.mdx).
### My connector isn't reflecting schema changes in my API
If you've modified your metadata and are getting errors when trying to use your API, you may need to force a rebuild of
the connector.
Identify the connector's container and image; kill the container and delete the image. Then, re-run the
`ddn run docker-start` command to rebuild the connector's image in addition to your other services.
### My data source operations time out
If a data source operation like `ddn introspect` hangs, eventually times out, and you encounter a log entry like the
following:
```plaintext
INF Waiting for the Connector service to be healthy...
```
Follow these steps to resolve the issue:
#### Step 1: Enable Debug Logging
Add the `--log-level DEBUG` flag to your command to generate more detailed logs. If the debug logs include the following
entry:
```plaintext
dial tcp: lookup local.hasura.dev: no such host
```
Then the issue is related to DNS resolution.
#### Step 2: Diagnose the DNS Issue
The most common cause of this DNS error is that your DHCP server is setting the Domain Search option. This is specified
in [DHCP Option 119/RFC 3397](https://www.rfc-editor.org/rfc/rfc3397).
#### Step 3: Resolve the DNS Issue
To resolve this issue, you have two options:
- **Disable the Domain Search Option**
Disabling this option in your DHCP server settings will prevent the DNS error. Consult your network administrator if
you are unsure how to make this change.
- **Add a Static Hostname Entry**
If disabling the Domain Search option is not feasible, you can manually add a static entry to your hosts file. This
approach will override DNS lookup for the hostname `local.hasura.dev`.
**Windows:** Follow the instructions
[here](https://www.howtogeek.com/27350/beginner-geek-how-to-edit-your-hosts-file/) to edit your hosts file.
**Linux/Unix/MacOS:** Add the following line to your `/etc/hosts` file:
```plaintext
127.0.0.1 local.hasura.dev
```
This entry maps the hostname `local.hasura.dev` to the IP address `127.0.0.1` and does not interfere with other
applications.
By following these steps, you should be able to resolve the introspection hang or timeout issue with your data source.
### I have a lot of tables
Typically, you won't experience any issues when connecting to or introspecting a database with a large number of tables.
However, _builds_ of your supergraph may exceed timeouts given the size of your project.
```sh title="If possible, consider removing unused models via the CLI:"
ddn model remove
```
```sh title="Then, retry your supergraph build:"
ddn supergraph build
```
:::info I need all of my tables
If all tracked tables are needed, and you're running into build issues,
[raise a ticket or drop us a line on Discord](/help/overview.mdx).
:::
## Get help
### Discord
We're available on Discord! Check out
[the `#v3-help-forum`](https://discord.com/channels/407792526867693568/1205357708677480468) to post your question and
get help from the community and Hasura team members!
### GitHub
Each connector has a public repository on GitHub. Typically, they'll follow the naming convention of
`hasura/ndc-`. Create issues on these repositories to get help directly from the teams responsible for
the connectors.
You can search the list of public repositories [here](https://github.com/orgs/hasura/repositories).
==============================
# publish-your-own-connector.mdx
URL: https://hasura.io/docs/3.0/docs/data-sources/publish-your-own-connector
# Publish a Data Connector
## Introduction
This guide is for **connector authors** and details the automated process for publishing new connectors and updating
existing versions on the [Hasura Connector Hub](https://hasura.io/connectors), which is powered by
[the `ndc-hub` repository](https://github.com/hasura/ndc-hub).
The automated publication process simplifies and accelerates the deployment of NDC connectors by:
- Managing documentation updates
- Streamlining the approval workflow
- Uploading connector packages to Hasura DDN
- Updating the Hasura Hub Registry database so connectors can be deployed on DDN
- Making a new connector/connector version available in the DDN ecosystem
:::info How do I build a connector?
You can find information about building a connector in the [NDC Spec](https://github.com/hasura/ndc-spec) and the
rendered [NDC Spec data connector tutorial](https://hasura.github.io/ndc-spec/tutorial/index.html) as well as the SDKs
for various languages:
- [Rust](https://github.com/hasura/ndc-sdk-rs)
- [TypeScript](https://github.com/hasura/ndc-sdk-typescript)
- [Python](https://github.com/hasura/ndc-sdk-python)
- [Go](https://github.com/hasura/ndc-sdk-go)
:::
## Publish a new connector
### Step 1. Clone the repository
```sh title="Clone the ndc-hub repository:"
git clone https://github.com/hasura/ndc-hub.git
```
### Step 2. Create a new directory for your connector in the registry {#step-2}
Create a new folder structure in [the `registry` directory](https://github.com/hasura/ndc-hub/tree/main/registry) of the
`ndc-hub` repo:
```plaintext
registry/
βββ [namespace]/
βββ [connector-name]/
```
Replace `[namespace]` with your organization's namespace and `[connector-name]` with the name of your connector.
### Step 3. Create a `connector-packaging.json` {#step-3}
Within your newly created connector's directory in the registry, create a new `releases` directory with a folder for
your first release and a `connector-packaging.json` for it:
```plaintext {4-6}
registry/
βββ [namespace]/
βββ [connector-name]/
βββ releases/
βββ v1.0.0/
βββ connector-packaging.json
```
#### Format
Below is the required shape for the `connector-packaging.json` file for each connector version:
```json
{
"version": "1.0.0",
"uri": "https://github.com/hasura/ndc-mongodb/releases/download/v0.0.1/connector-definition.tgz",
"checksum": {
"type": "sha256",
"value": "2cd3584557be7e2870f3488a30cac6219924b3f7accd9f5f473285323843a0f4"
},
"source": {
"hash": "c32adbde478147518f65ff465c40a0703239288a"
}
}
```
#### Required fields:
- **version**: The version of the connector (e.g., "1.0.0").
- **uri**: The URL to download the connector package. This should be a tarball containing the connector package
definition and must be accessible without authentication.
- **checksum**: The checksum of the connector package.
- **type**: The algorithm used for the checksum (currently only "sha256" is supported).
- **value**: The actual checksum value.
- **source**: Information about the source code used to build the package.
- **hash**: The commit hash of the source code used to build the connector package.
Ensure all fields are correctly filled out for each new connector version you publish.
### Step 4. Add the other required files
Add the following files to your connector's directory in the registry; details are below.
```plaintext {5-7}
registry/
βββ [namespace]/
βββ [connector-name]/
ββ releases/
ββ logo.(png|svg)
ββ metadata.json
ββ README.md
```
- `logo.png` or `logo.svg`: The logo for your connector (preferably in SVG format).
- `README.md`: Documentation for your connector, including description, usage instructions, and any other relevant
information.
- `metadata.json`: Metadata file containing information about the connector.
The `metadata.json` file in your connector's folder contains crucial information about your connector:
```json
{
"overview": {
"namespace": "your_namespace",
"description": "A brief description of your connector",
"title": "Your Connector Title",
"logo": "logo.png",
"tags": [],
"latest_version": "v1.0.0"
},
"author": {
"support_email": "support@example.com",
"homepage": "https://www.example.com",
"name": "Your Organization Name"
},
"is_verified": false,
"is_hosted_by_hasura": false,
"source_code": {
"is_open_source": true,
"repository": "https://github.com/your-org/your-connector-repo"
}
}
```
**Field explanations:**
- **overview**: General information about your connector.
- **namespace**: Your organization's namespace (e.g., `sqlserver` for the SQL Server connector).
- **description**: A brief description of your connector.
- **title**: The title of your connector (e.g., BigQuery, SQL Server).
- **logo**: Filename containing your connector logo (acceptable formats: PNG, SVG).
- **tags**: Keywords related to your connector.
- **latest_version**: The most recent version of your connector.
- **author**: Information about the connector's author or organization.
- **is_verified**: Set this to `false` as only connectors developed by Hasura can be tagged as verified.
- **is_hosted_by_hasura**: Set this to `false` as connectors not developed by Hasura cannot be hosted by Hasura.
### Step 5. Submit for review
1. Commit all your changes to a new branch.
2. Create a pull request targeting the `main` branch of the `ndc-hub` repository.
3. In the pull request description provide any additional context about your new connector.
4. Wait for review and approval from the Hasura team.
#### Post-approval process
Once your pull request is approved and merged:
- The new connector will be added to the Hasura Hub Registry.
- It will become available in the staging environment for testing.
- After successful testing, it will be published to the production environment.
## Create a new release for existing connectors
### Step 1. Check repository structure
Ensure your connector and connector versions follow this directory structure in the `ndc-hub` repository:
```
registry/
βββ [namespace]/
β βββ [connector-name]/
β β βββ releases/
β β β βββ [version]/
β β β β βββ connector-packaging.json
β β βββ logo.(png|svg)
β β βββ README.md
```
### Step 2. Create a new connector version
1. Create a new folder under `registry/[namespace]/[connector-name]/releases/` with the version number.
2. The version must start with the letter 'v', for example: `v1.0.0`.
3. Add a new `connector-packaging.json` file in this folder with the connector metadata.
### Step 3. Update connector information
- To update the logo: Modify the `logo.png` or `logo.svg` file in the connector's root folder.
- To update the documentation: Modify the `README.md` file in the connector's root folder.
### Step 4. Create a PR
1. Commit your changes to a new branch.
2. Create a pull request targeting the `main` branch of the `ndc-hub` repository.
3. Wait for approval from a Hasura team member.
#### Post-approval process
Once the pull request is approved, the GitHub workflow will automatically:
- Run the registry automation program.
- Upload connector packages.
- Update the Hasura Hub Registry database.
## Development workflow
The connector publication automation is designed to run automatically for every commit made to a pull request targeting
the `main` branch. This process ensures that your changes are continuously validated and updated in our staging
environment. Here's how it works:
### PR creation
When you create a pull request against the `main` branch with changes in the registry folder, it triggers the automation
process.
### Commit-based triggers
Every new commit to the pull request will trigger
[the `registry-updates` GitHub Actions workflow](https://github.com/hasura/ndc-hub/blob/main/.github/workflows/registry-updates.yaml).
This includes:
- Initial commits when opening the PR.
- Additional commits pushed to the same PR.
- Commits made in response to review comments.
### Staging environment updates
For each commit:
- The `registry-updates` workflow runs automatically.
- It validates the changes in the registry folder, including the `connector-packaging.json` file.
- If validation is successful, it updates the connector information on the `staging` Hasura DDN environment.
- Each new commit overwrites the previous version of that connector on the `staging` environment.
### Continuous updates
This process allows for continuous iteration and testing in the staging environment:
- You can push multiple commits to refine your connector changes.
- Each commit provides a new opportunity to test and verify your changes in the staging environment.
- Reviewers can check the latest version of your connector in the staging DDN at any time during the review process.
### Production deployment
Once the pull request is approved and merged into the `main` branch:
- The final version of the connector (based on the last commit in the PR) is automatically published to the `production`
Hasura DDN.
- This process ensures that only thoroughly reviewed and tested connector versions reach the `production` environment.
### Important Notes
- The automation only triggers for changes made in the `registry` folder.
- Ensure your `connector-packaging.json` file is valid for each commit to avoid automation failures.
- Multiple connector versions or updates to multiple connectors can be included in a single PR, and the automation will
handle all changes appropriately.
## Troubleshooting
### Pull request automation doesn't trigger.
Ensure the PR targets the main branch and modifies files under the `registry/` directory.
### Automation fails due to missing environment variables.
Ping `@scriptnull` or `@codingkarthik` in the PR.
==============================
# overview.mdx
URL: https://hasura.io/docs/3.0/docs/data-modeling/overview
# Data Modeling
## Introduction
Hasura DDN defines your API schema declaratively. An approach that centralizes all your data collections, operations,
relationships, and permissions in one place. This makes it easy to organize, modify, secure, reason about, and grow the
schema which represents your API.
## Lifecycle
Hasura DDN uses data connectors to connect to your data sources.
Data connectors introspect the source to understand the source schema.
The DDN CLI uses the introspection results to generate the API schema metadata objects. This can also be done manually.
The metadata is then "built" by the DDN CLI into a format which the Hasura GraphQL engine can use to serve your API.
## Metadata Objects
There are [many types of metadata objects](/reference/metadata-reference/index.mdx) which define your API, but the most
- [Models](/data-modeling/model.mdx) which read data
- [Commands](/data-modeling/command.mdx) which modify data
- [Relationships](/data-modeling/relationship.mdx) which connect data
- [Permissions](/data-modeling/permissions.mdx) which protect data
We will cover each of these in more detail in the following sections.
==============================
# Queries Overview
URL: https://hasura.io/docs/3.0/docs/graphql-api/overview
# Basics
## Introduction
When you connect a source to Hasura DDN using a
[data connector](/data-sources/overview.mdx#how-do-i-connect-my-data-to-hasura-ddn) and generate Hasura metadata to
represent its resources, the source becomes accessible through a unified GraphQL API. Your Hasura metadata lets you
control how the data is exposed and interacted with.
Every data connector supports querying and subscribing to data out-of-the-box. Some connectors also provide
auto-generated mutations for inserting, updating, or deleting data. For most connectors without auto-generated
mutations, you can define custom native mutations.
Queries, subscriptions, and mutations are exposed as root-level fields, enabling you to fetch, monitor, or modify data
directly through the GraphQL API.
:::tip Check out connector docs
For questions about the capabilities of individual connectors, check out their
[reference docs](/reference/connectors/index.mdx).
:::
The sections below explain how to interact with your data using the GraphQL API.
## Learn more
- [Queries](/graphql-api/queries/index.mdx)
- [Mutations](/graphql-api/mutations/index.mdx)
- [Subscriptions](/graphql-api/subscriptions/index.mdx)
- [Global IDs](/graphql-api/global-ids.mdx)
- [API Versioning](/graphql-api/versioning.mdx)
- [Apollo Federation](/graphql-api/apollo-federation.mdx)
- [Errors](/graphql-api/errors.mdx)
- [GraphQL Schema Diff](/graphql-api/graphql-schema-diff.mdx)
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/
# Basics of Queries
## Introduction
GraphQL queries are used to **read data** by interacting with [models](/reference/metadata-reference/models.mdx) in your
Hasura DDN project.
Queries can be used to return multiple records while specifying exactly which fields to include in the response. You can
also query for unique records with the same level of control, retrieving a single result that matches your desired
fields.
Further, you can filter queries to only return records that match your specific conditions **where** certain fields meet
explicit criteria.
In addition to fetching records, queries can perform aggregations, which allow you to compute and summarize data. For
example, you can count the number of records, calculate averages, or find totals directly in the query response.
Since the GraphQL API is self-documenting, you can write queries manually or use tools like auto-completion and the
GraphiQL explorer in the Hasura DDN console to help you build and test them. GraphiQL displays the available root-level
types, fields, and relationships, making it easier to construct queries accurately.
## Configuration
You can configure the overall usage of queries in your GraphQL API using
[the `GraphQlConfig` object](/reference/metadata-reference/graphql-config.mdx#graphqlconfig-querygraphqlconfig) in your
metadata. Additionally, you can customize individual queries by modifying
[the `GraphQlDefinition` metadata object](/reference/metadata-reference/models.mdx#model-modelgraphqldefinitionv2) for a
model.
## Next steps
Depending on the features enabled by a data connector, you will have access to a range of queries as defined here.
- [Simple Queries](/graphql-api/queries/simple-queries.mdx)
- [Nested Queries](/graphql-api/queries/nested-queries.mdx)
- [Sort Query Results](/graphql-api/queries/sorting.mdx)
- [Paginate Query Results](/graphql-api/queries/pagination.mdx)
- [Multiple Arguments](/graphql-api/queries/multiple-arguments.mdx)
- [Multiple Queries](/graphql-api/queries/multiple-queries.mdx)
- [Variables, Aliases, Fragments, Directives](/graphql-api/queries/variables-aliases-fragments-directives.mdx)
- [Filtering Queries](/graphql-api/queries/filters/index.mdx)
- [Comparing values](/graphql-api/queries/filters/comparison-operators.mdx)
- [Boolean expressions](/graphql-api/queries/filters/boolean-operators.mdx)
- [Text](/graphql-api/queries/filters/text-search-operators.mdx)
- [Nested objects](/graphql-api/queries/filters/nested-objects.mdx)
:::info Not sure what your connector supports?
For questions about feature support, check out the [connector reference docs](/reference/connectors/index.mdx).
:::
==============================
# simple-queries.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/simple-queries
# Simple Object Queries
## Introduction
You can fetch a single node or multiple nodes of the same type using a simple object query.
## Fetch a list of objects
**Example:** Fetch a list of authors:
## Fetch an object using its primary key
**Example:** Fetch an author using their primary key:
## Fetch list of objects with pagination
**Example:** Fetch 2 articles after removing the 1st article from the result set.
:::caution Warning
Without an `order_by` in `limit` queries, the results may be unpredictable.
:::
## Fetch list of objects with filtering
**Example:** Fetch a list of articles whose title contains the word "The":
## Fetch list of objects with sorting
**Example:** Fetch a list of articles with `article_id` in descending order:
## Fetch objects using model arguments
**Example:** Fetch the articles for the given `author_id`:
==============================
# nested-queries.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/nested-queries
# Nested Object Queries
## Introduction
You can use the object (one-to-one) or array (one-to-many) relationships defined in your metadata to make a nested
queries, i.e. fetch data for one type along with data from a nested or related type.
## Fetch nested object using an object relationship
The following is an example of a nested object query using the **object relationship** between an article and an author.
**Example:** Fetch a list of articles and the name of each articleβs author:
## Fetch nested objects using an array relationship
The following is an example of a nested object query using the **array relationship** between an author and articles.
**Example:** Fetch a list of authors and a related, nested list of each authorβs articles:
==============================
# aggregation-queries.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/aggregation-queries
# Aggregation Queries
## **Aggregate** fields
You can fetch aggregations on columns along with nodes using an aggregation query.
The **name of the aggregate field** is of the form `Aggregate`.
Common aggregation functions are `count`, `sum`, `avg`, `max`, `min`, etc. You can see the complete specification of the
aggregate field in the [metadata reference](/reference/metadata-reference/aggregate-expressions.mdx). Note that not all
aggregation functions are available for all data types.
## Fetch aggregated data of an object
**Example:** Fetch a list of posts with aggregated data:
## Fetch aggregated data on nested objects {#nested-aggregate}
The following is an example of a nested object query with aggregations on the **array relationship** between a user and
posts.
**Example:** Fetch user with an `id` of `1` and a nested list of posts with aggregated data:
==============================
# sorting.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/sorting
# Sort Query Results
## The **order_by** argument
Results from your query can be sorted by using the `order_by` argument. The argument can be used to sort nested objects
too.
The sort order (ascending vs. descending) is set by specifying the `Asc` or `Desc` enum value for the column name in the
`order_by` input object, e.g. `{name: Desc}`.
By default, for ascending ordering `null` values are returned at the end of the results and for descending ordering
`null` values are returned at the start of the results.
The `order_by` argument takes an array of objects to allow sorting by multiple columns.
You can also use nested objects' fields to sort the results. Only columns from object relationships** and **aggregates
from array relationships\*\* can be used for sorting.
The following are example queries for different sorting use cases:
## Sorting objects
**Example:** Fetch a list of authors sorted by their names in ascending order:
## Sorting nested objects {#pg-nested-sort}
**Example:** Fetch a list of authors sorted by their names with a list of their articles that is sorted by their rating:
## Sorting by Nested Object Fields
Only **columns from object relationships** can be used for sorting.
### For object relationships
For object relationships only columns can be used for sorting.
**Example:** Fetch a list of articles that are sorted by their author's ids in descending order:
## Sorting by multiple fields
:::info Key order is not preserved
Key order in input object for `order_by` is not preserved. This means you should only have a single key per object, or
you may see unexpected behavior.
:::
==============================
# pagination.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/pagination
# Paginate Query Results
## The **limit** & **offset** arguments
The operators `limit` and `offset` are used for pagination.
`limit` specifies the number of rows to retain from the result set and `offset` determines which slice to retain from
the results.
The following are examples of different pagination scenarios:
## Limit results
**Example:** Fetch the first 5 authors from the list of all authors:
## Limit results from an offset
**Example:** Fetch 5 authors from the list of all authors, starting with the 6th one:
## Limit results in a nested object {#pg-nested-paginate}
**Example:** Fetch a list of authors and a list of their first 2 articles:
## Keyset cursor based pagination
Cursors are used to traverse across rows of a dataset. They work by returning a pointer to a specific row which can then
be used to fetch the next batch of data.
Keyset cursors are a column (or a set of columns) of the data that are used as the cursor. The column(s) used as the
cursor must be unique and sequential. This ensures that data is read after a specific row rather than relying on the
position of the row in the dataset as done by `offset`, and that duplicate records are not fetched again.
**For example**, consider the following query to fetch a list of authors with a `where` clause used in place of
`offset`:
Here we are fetching authors where the value of `id` is greater than 5. This will always skip the previously fetched
results which would have been ids 1 to 5, ensuring no duplicate results. Column `id` is acting as the cursor here,
unique and sequential.
The choice of cursor columns depends on the order of the expected results i.e. if the query has an `order_by` clause,
the column(s) used in the `order_by` need to be used as the cursor.
Columns such as `id` (auto-incrementing integer/big integer) or `created_at` (timestamp) are commonly used as cursors
when an order is not explicit, as they should be unique and sequential.
:::info Where vs Offset
Keyset cursor based pagination using `where` is more performant than using `offset` because we can leverage database
indexes on the columns that are being used as cursors.
:::
:::info No order_by clause
Because we ran the above example without an `order_by` clause, it is accidental that we received those results. Running
a query without an `order_by` clause will return results in an arbitrary order.
:::
==============================
# comparison-operators.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/filters/comparison-operators
# Filter by Comparing Values
## Introduction
Comparison operators are used to compare values of the same type. For example, to compare two numbers, two strings, two
dates, etc.
## Equality operators (\_eq, \_neq)
The `_eq` (equal to) or the `_neq` (not equal to) operators are compatible with any type other than `json` or `jsonB`
(like `Integer`, `Float`, `Double`, `Text`, `Boolean`, `Date`/`Time`/`Timestamp`, etc.).
[//]: # '[//]: # "For more details on equality operators and PostgreSQL equivalents, refer to the"'
[//]: # '[//]: # "[API reference](/api-reference/graphql-api/query.mdx#generic-operators)."'
The following are examples of using the equality operators on different types.
**Example: Integer (works with Double, Float, Numeric, etc.)**
Fetch data about an author whose `id` _(an integer field)_ is equal to 3:
**Example: String or Text**
Fetch a list of authors with `name` _(a text field)_ as "Sidney":
**Example: Boolean**
Fetch a list of articles that have not been published (`is_published` is a boolean field):
**Example: Date (works with Time, Timezone, etc.)**
Fetch a list of articles that were published on a certain date (`published_on` is a Date field):
**Example: Integer (works with Integer, Float, Double, etc.)**
Fetch a list of users whose age is _not_ 30 (`age` is an Integer field):
:::info Caveat for "null" values
By design, the `_eq` or `_neq` operators will not return rows with `null` values.
To also return rows with `null` values, the `_is_null` operator needs to be used along with these joined by the `_or`
operator.
For example, to fetch a list of articles where the `is_published` column is either `false` or `null`:
:::
## Greater than or less than operators (\_gt, \_lt, \_gte, \_lte)
The `_gt` (greater than), `_lt` (less than), `_gte` (greater than or equal to), `_lte` (less than or equal to) operators
are compatible with any type other than `json` or `jsonB` (like `Integer`, `Float`, `Double`, `Text`, `Boolean`,
`Date`/`Time`/`Timestamp`, etc.).
[//]: # '[//]: # "For more details on greater than or less than operators and PostgreSQL equivalents, refer to the"'
[//]: # '[//]: # "[API reference](/api-reference/graphql-api/query.mdx#generic-operators)."'
The following are examples of using these operators on different types:
**Example: Integer (works with Double, Float, Numeric, etc.)**
This query retrieves all users whose age is less than 30. The `_lt` operator is a comparison operator that means "less
than". It is used to filter records based on a specified value.
**Example: String or Text**
Fetch a list of authors whose names begin with M or any letter that follows M _(essentially, a filter based on a
dictionary sort)_:
**Example: Integer (works with Double, Float, etc.)**
Fetch a list of all products with a price less than or equal to 10.
**Example: Integer (works with Double, Float, etc.)**
Fetch a list of articles rated 4 or more (`rating` is an integer field):
**Example: Date (works with Time, Timezone, etc.)**
Fetch a list of articles that were published on or after date "01/01/2018":
## List based search operators (\_in)
The `_in` (in a list) operator is used to compare field values to a list of values. They are compatible with any type
other than `json` or `jsonB` (like `Integer`, `Float`, `Double`, `Text`, `Boolean`, `Date`/`Time`/`Timestamp`, etc.).
The following are examples of using these operators on different types:
**Example: Integer (works with Double, Float, etc.)**
Fetch a list of articles rated 1, 3 or 5:
:::caution Using _in operator with multiple values
When using the `_in` operator with multiple values in GraphiQL or your application code, always use proper array syntax with square brackets:
```
where: { rating: { _in: [1, 3, 5] } } // CORRECT
```
Do not use comma-separated strings, as this will not work correctly:
```
where: { rating: { _in: "1, 3, 5" } } // INCORRECT
```
While GraphiQL may sometimes auto-generate queries using the incorrect string format, these queries will not function properly when executed against the GraphQL server.
:::
## Filter or check for null values (\_is_null)
Checking for null values can be achieved using the `_is_null` operator.
**Example: Filter null values in a field**
Fetch a list of articles that have a value in the `published_on` field:
==============================
# boolean-operators.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/filters/boolean-operators
# Filter by Boolean Expressions
## Filter based on failure of some criteria (\_not)
The `_not` operator can be used to fetch results for which some condition does not hold true. i.e. to invert the filter
set for a condition.
**Example: \_not**
Fetch all authors who don't have any published articles:
## Using multiple filters in the same query (\_and, \_or)
You can group multiple parameters in the same `where` argument using the `_and` or the `_or` operators to filter results
based on more than one criterion.
:::info Note
You can use the `_or` and `_and` operators along with the `_not` operator to create arbitrarily complex boolean
expressions involving multiple filtering criteria.
:::
**Example: \_and**
Fetch a list of articles published in a specific time-frame (for example: in year 2017):
:::info Note
Certain `_and` expressions can be expressed in a simpler format using some syntactic sugar.
[//]: # '[//]: # "See the [API reference](/api-reference/graphql-api/query.mdx#andexp) for more details."'
:::
**Example: \_or**
Fetch a list of articles rated more than 4 or published after "01/01/2018":
==============================
# text-search-operators.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/filters/text-search-operators
# Filter by Text
## Introduction
The `_like`, `_nlike`, `_ilike`, `_nilike`, `_similar`, `_nsimilar`, `_regex`, `_nregex`, `_iregex`, `_niregex`
operators are used for pattern matching on string/text fields.
[//]: # '[//]: # "For more details on text search operators and PostgreSQL equivalents, refer to the"'
[//]: # '[//]: # "[API reference](/api-reference/graphql-api/query.mdx#text-operators)."'
## \_like
Fetch a list of articles whose titles contain the word βametβ:
## \_ilike
This query will return all users whose name contains the string "john", regardless of case.
## \_nilike
This query would return all users whose name does not contain the string "John".
:::info Note
`_like` is case-sensitive. Use `_ilike` for case-insensitive search.
:::
## \_similar
Fetch a list of authors whose names begin with A or C:
## \_nsimilar
Fetch a list of authors whose names do not begin with A or C:
:::info Note
`_similar` and `_nsimilar` are case-sensitive.
:::
## \_regex
Fetch a list of articles whose titles match the regex `[ae]met`:
## \_iregex
This query will return all users whose name matches the regular expression `/^joh?n$/i`, which matches "John" and "Jon".
## \_nregex
The \_nregex operator in this GraphQL query is a negated regular expression filter that matches all users whose names do
not start with the letter "J".
:::info Note
`_regex` is case-sensitive. Use `_iregex` for case-insensitive search.
:::
:::info Note
`regex` operators are supported in `v2.0.0` and above
:::
==============================
# nested-objects.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/filters/nested-objects
# Filter Based on Nested Objects Fields
## Introduction
You can filter your query results by specifying conditions of nested fields of an object.
For example:
```graphql {2}
query {
articles(where: { author: { name: { _eq: "Sam Jones" } } }) {
id
title
}
}
```
## Applicable Fields for Nested Field Comparisons
Nested field comparisons can be made in the GraphQL API if:
- The field's type is an [Object Type](reference/metadata-reference/types.mdx#objecttype-objecttype)
- The field is a [Relationship](reference/metadata-reference/relationships.mdx)
## Filtering by Object Type Fields
To filter results based on a field within an object type, apply a condition to the nested field.
**Example:**
In this query, `profile` is an [Object Type](reference/metadata-reference/types.mdx#objecttype-objecttype) field, and we
filter users based on the condition that their `profile.age` is greater than 30.
## Filtering by Relationship Fields
You can filter results based on conditions applied to the fields of the target model in a relationship. The relationship
can be either an object or array type.
**Example:** Filter by `Array` relationship fields.
In this query, `books` is an Array relationship, and we filter authors who have written a book titled "GraphQL Basics".
**Example:** Filter by `Object` relationship fields.
In this query, `author` is an object relationship field, and we filter books where the `author.name` is "Alice Johnson".
### Data Connector Capability
For relationship filtering to be processed efficiently, the data connector must support the `relation_comparisons`
[relationships capability](https://hasura.github.io/ndc-spec/specification/capabilities.html). This allows the query
engine to push down relationship comparisons directly to the data connector, ensuring that filtering based on
relationships is handled at the data source level, improving performance and reducing unnecessary data transfer.
However, if the `relation_comparisons` capability is absent, the query engine will handle the relationship comparisons
at its own layer. In this case, the query engine fetches the relevant mapping field values of the relationship and
constructs the necessary comparison expressions. While this ensures that the query can still execute, it may result in
reduced efficiency and slower responses, as more data needs to be transferred to the query engine for evaluation. See
[Performance of Relationship Comparisons](graphql-api/queries/filters/performance-relationship-comparisons.mdx) for more
details.
#### Compatibility Date
To utilize relationship comparisons without the `relation_comparisons` capability, you must update the compatibility
date. For detailed instructions, please refer to the
[Compatibility Config](reference/metadata-reference/compatibility-config.mdx#enable-relationships-in-predicates-to-be-used-even-if-the-data-connector-does-not-support-it).
### Remote Relationships
A relationship between two entities from different data connectors is known as a **remote relationship**. This type of
relationship allows you to integrate and query data across multiple sources seamlessly. Filtering with a predicate from
a model in a remote data source using remote relationships enhances query flexibility, enabling precise data retrieval
directly on the server. This eliminates the need for client-side filtering, which not only simplifies your code but also
reduces network data transfer, leading to improved application performance.
For example:
```graphql {2}
query UsersCompletedOrders {
users(where: { orders: { status: { _eq: "complete" } } }) {
id
name
orders {
id
status
}
}
}
```
This query retrieves a list of users along with their completed orders. Here, users and orders stored in separate data
sources.
:::info Limitations
- Only remote relationships where the targets are
[models](reference/metadata-reference/relationships.mdx#object-type-to-a-model) are supported in comparisons, with
support for remote relationships targeting
[commands](reference/metadata-reference/relationships.mdx#object-type-to-a-command) coming soon.
- Remote relationships defined across subgraphs are currently not supported.
- Fields involved in
[relationship mapping](reference/metadata-reference/relationships.mdx#relationship-relationshipmapping) must be backed
by data connector columns that have support for the `equal` comparison operator.
:::
#### Understanding Predicate Resolution
The predicate of a remote relationship is resolved independently of the data connector's `relation_comparisons`
capability. When a remote relationship is used in a comparison, the engine retrieves the relevant data from the remote
model and constructs the necessary comparison expressions to filter the results, similar to how local relationships are
resolved without the [`relation_comparisons`](#data-connector-capability) capability.
**Consequently, the performance of remote predicates can vary significantly based on the efficiency of the underlying
data sources and the complexity of the relationships being queried. For more details, see
[Performance of Relationship Comparisons](graphql-api/queries/filters/performance-relationship-comparisons.mdx).**
==============================
# performance-relationship-comparisons.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/filters/performance-relationship-comparisons
# Performance of Relationship Comparisons
Relationship comparisons in GraphQL allow for powerful query filtering across related data. However, performance varies
in Hasura DDN depending on how these comparisons are processed.
This page explains the performance implications of relationship comparisons, detailing both optimized query execution
through data connectors and the fallback mechanisms at the query engine level when certain capabilities are unavailable
or when handling remote relationships.
## How Relationship Comparisons Work
Relationship comparisons filter data based on fields in related objects. For example, filtering books by their author's
name is a common relationship comparison:
```graphql {2}
query {
books(where: { author: { name: { _eq: "Alice Johnson" } } }) {
id
title
author {
name
}
}
}
```
This query filters books where the authorβs name is "Alice Johnson", effectively creating a relationship comparison
between books and authors.
## Data Connector Capability: `relation_comparisons`
To achieve optimal performance, the query engine relies on the data connectorβs ability to perform the comparison at the
data source level. This capability is known as `relation_comparisons` (For more details see
[the native data connector spec](https://hasura.github.io/ndc-spec/specification/capabilities.html)). When the data
connector supports this feature, the query engine pushes down the comparison logic to the underlying data connector to
handle the filtering at the data source layer.
## Missing Data Connector Capability or Remote Relationships
When a data connector lacks support for the `relation_comparisons` capability or when handling predicates from a remote
relationship, the query engine cannot push relationship comparisons down to the data connector for processing. Instead,
these comparisons are handled internally by the query engine.
### How It Works
The query engine performs the following steps:
1. **Fetch Related Data**: The query engine retrieves required fields from the related/remote model.
2. **Construct Comparison Expressions**: Using the fetched data, the engine constructs the necessary comparison
expressions to filter the results.
3. **Fetch Data**: The engine applies these expressions while fetching data from the primary model.
### Performance Implications
- **Increased Data Transfer**: More data must be transferred from the data connector to the query engine for evaluation,
which can lead to higher latency.
- **Higher Query Engine Load**: Performing comparisons in the query engine increases its workload, which can degrade
performance, particularly with large datasets.
- **Potential Bottlenecks**: As the amount of related data increases, processing comparisons at the query engine level
may become less efficient, which can result in slower query responses.
### Example
Consider a query to filter books by the author's name when the `relation_comparisons` capability is not available:
```graphql
query {
books(where: { author: { name: { _eq: "Alice Johnson" } } }) {
id
title
author {
name
}
}
}
```
In the above query `author` is an Object Relationship with `book.author_id -> author.id` field mapping. Without
`relation_comparisons`, the query engine will:
1. Fetch the `id`s of all authors whose `name` equals "Alice Johnson".
2. Create a comparison expression to check if `book.author_id` matches any of the author IDs obtained in step 1.
3. Query the books using the constructed comparison expression to filter based on `book.author_id`.
4. Return the filtered books to the client.
## Monitoring Query Performance
To ensure your queries perform optimally, monitor the trace details for relationship comparisons. Specifically, look for
the span labeled `Resolve relationship comparison expression: ` in the query trace. This span shows the time and
resources spent resolving the relationship comparison, which can help you identify performance issues. If the query
engine is handling the comparison internally due to missing `relation_comparisons` capability, you might notice
increased execution times.
**Trace Example:**
==============================
# multiple-arguments.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/multiple-arguments
# Use Multiple Query Arguments
Multiple arguments can be used together in the same query.
For example, you can use the `where` argument to filter the results and then use the `order_by` argument to sort them.
**For example**, fetch a list of authors and only 2 of their published articles that are sorted by their date of
\*publication:
==============================
# multiple-queries.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/multiple-queries
# Multiple Queries in a Request
## Execution
You can fetch objects of different unrelated types in the same query.
## Run multiple top level queries in the same request
**For example**, fetch a list of `authors` and a list of `articles`:
==============================
# variables-aliases-fragments-directives.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/queries/variables-aliases-fragments-directives
# Use Variables / Aliases / Fragments in Queries
## Using variables
In order to make a query re-usable, it can be made dynamic by using variables.
**Example:** Fetch an author by their `author_id`:
## Using aliases
Aliases can be used to return objects with a different name than their field name. This is especially useful while
fetching the same type of objects with different arguments in the same query.
**Example:** First, fetch all articles. Second, fetch the two top-rated articles. Third, fetch the worst-rated article:
## Using fragments
Sometimes, queries can get long and confusing. A fragment is a set of fields with any chosen name. This fragment can
then be used to represent the defined set.
**Example:** Creating a fragment for a set of `article` fields (`id` and `title`) and using it in a query:
==============================
# model.mdx
URL: https://hasura.io/docs/3.0/docs/data-modeling/model
# Models Read Data
## Introduction
In DDN, models represent entities or collections that can be queried in your data sources, such as tables, views,
collections, native queries, and more.
## Lifecycle
The lifecycle in creating a model in your metadata is as follows:
1. Have some entity in your data source that you want to make queryable via your API.
2. Introspect your data source using the DDN CLI with the relevant data connector to fetch the entity resources.
3. Add the model to your metadata with the DDN CLI.
4. Create a build of your supergraph API with the DDN CLI.
5. Serve your build as your API with the Hasura engine either locally or in the cloud.
6. Iterate on your API by repeating this process or by editing your metadata manually as needed.
## Create a model
To add a model you will need to have a data connector already set up and connected to the data source. Follow the
relevant tutorial for your data source in [How to Build with DDN](/how-to-build-with-ddn/overview.mdx) to get to that
point.
### From a source entity
```bash title="Introspect your data source:"
ddn connector introspect
```
Whenever you update your data source, you can run the above command to fetch the latest resources.
```bash title="Show the resources discovered from your data source:"
ddn connector show-resources
```
This is an optional step and will output a list of resources that DDN discovered in your data source in the previous
step.
```bash
ddn model add
```
Or you can optionally add all the models by specifying `"*"`.
```bash
ddn model add "*"
```
This will add models with their accompanying metadata definitions to your metadata.
You can now build your supergraph API, serve it, and query your data.
:::info Context for CLI commands
Note that the above CLI commands work without also adding the relevant subgraph to the command with the `--subgraph`
flag because this has been set in the CLI context. You can learn more about creating and switching contexts in the
[CLI context](/reference/cli/commands/ddn_context.mdx) section.
:::
For a walkthrough on how to create a model, see the [Model Tutorials](#model-tutorials) section.
### Via native query
You can use the query syntax of the underlying data source to create a native query and expose it as a model in your
supergraph API.
The process of adding a native query is more or less unique for each data connector as each data source by nature has
its own syntax.
The process for adding a native query for PostgreSQL is:
- Create a new SQL file in your connector directory.
- Name the file with how you want to reference it in your supergraph API.
- Add the SQL for your native query to the file.
- Use the DDN CLI plugin for the PostgreSQL connector to add the native query to the connector's configuration eg:
```bash title="Add the native query to the connector's configuration:"
ddn connector plugin \
--connector subgraph_name/connector/connector_name/connector.yaml \
-- native-operation create \
--operation-path path/to/sql_file_name.sql \
--kind query
```
- Introspect your PostgreSQL data connector to fetch the latest resources.
```bash
ddn connector introspect
```
- Show the found resources to see the new native query.
```bash
ddn connector show-resources
```
- Add the model for the native query.
```bash
ddn model add
```
- Rebuild and serve your supergraph API.
Find out more about
[native queries for PostgreSQL here](/reference/connectors/postgresql/native-operations/native-queries.mdx).
The process for adding a native query for MongoDB is:
- Create a new MongoDB [aggregation pipeline](/reference/connectors/mongodb/native-operations/native-queries.mdx) which
defines the native query in your connector's directory.
- Name the file with how you want to reference it in your supergraph API.
- Use the DDN CLI plugin for the MongoDB connector to add the aggregation pipeline to the connector's configuration eg:
```bash
ddn connector plugin \
--connector subgraph_name/connector/connector_name/connector.yaml \
-- native-query create path/to/aggregation_pipeline_filename.json \
--collection collection_name
```
- Introspect your MongoDB data connector to fetch the latest resources.
```bash
ddn connector introspect
```
- Show the found resources to see the new native query.
```bash
ddn connector show-resources
```
- Add the model for the native query.
```bash
ddn model add
```
- Rebuild and serve your supergraph API.
Find out more about
[native queries for MongoDB here](/reference/connectors/mongodb/native-operations/native-queries.mdx).
The process for adding a native query for ClickHouse is:
- Create a new SQL file in your connector directory.
- Use ClickHouse parameter syntax in the SQL file to define arguments.
- Create a JSON configuration file in your connector directory specifying the SQL file path and the return type.
```json
{
"tables": {},
"queries": {
"Name": {
"exposed_as": "collection",
"file": "path/to/sql_file_name.sql",
"return_type": {
"kind": "definition",
"columns": {
"column_name": "column_type"
}
}
}
}
}
```
- Introspect your ClickHouse data connector to fetch the latest resources.
```bash
ddn connector introspect
```
- Show the found resources to see the new native query.
```bash
ddn connector show-resources
```
- Add the model for the native query.
```bash
ddn model add
```
- Rebuild and serve your supergraph API.
Find out more about
[native queries for ClickHouse here](/reference/connectors/clickhouse/native-operations/native-queries.mdx).
## Update a model
If you want to update your model to reflect a change that happened in the underlying data source you should first
introspect to get the latest resources and then update the relevant model.
```bash title="Introspect your data source:"
ddn connector introspect
```
```bash title="Then, update your existing model:"
ddn model update
```
You will see an output which explains how new resources were added or updated in the model.
You can now build your supergraph API, serve it, and query your data with the updated model.
You can also update the model by editing the metadata manually.
For a walkthrough on how to update a model, see the [Model Tutorials](#model-tutorials) section.
## Extend a model
A model can be extended in order to return nested data or to enrich or add to the data.
For example you can extend a model like `Customers` to also return the related `Orders` for each customer.
Or you can add a custom piece of logic on a model like `Orders` to compute and return the current currency conversion of
the total price of the order.
The way this is done is via a `Relationship`. Read more about
[creating relationships here](/data-modeling/relationship.mdx).
## Delete a model
```bash title="If you no longer need a model, you can delete it:"
ddn model remove users
```
In addition to removing the `Model` object itself, the DDN CLI will also remove the associated metadata definitions.
## Tutorials {#model-tutorials}
The tutorials below follow on from each particular tutorial in the
[How to Build with DDN](/how-to-build-with-ddn/overview.mdx) section. Select the relevant data connector to follow the
tutorial.
### Creating a model
To query data from your API, you'll first need to create a model that represents that data.
#### From a source entity
**Via a new table or view**
```sql title="Create a new table or view in your PostgreSQL database:"
CREATE TABLE public.comments (
id serial PRIMARY KEY,
comment text NOT NULL,
user_id integer NOT NULL,
post_id integer NOT NULL
);
INSERT INTO public.comments (comment, user_id, post_id)
VALUES
('Great post! Really enjoyed reading this.', 1, 2),
('Thanks for sharing your thoughts!', 2, 1),
('Interesting perspective.', 3, 1);
```
```bash title="Use the DDN CLI to introspect your PostgreSQL instance:"
ddn connector introspect my_pg
```
```bash title="Then, add your model:"
ddn model add my_pg comments
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your table:"
query {
comments {
id
comment
user_id
post_id
}
}
```
```json title="With a response like this:"
{
"data": {
"comments": [
{
"id": 1,
"comment": "Great post! Really enjoyed reading this.",
"user_id": 1,
"post_id": 2
},
{
"id": 2,
"comment": "Thanks for sharing your thoughts!",
"user_id": 2,
"post_id": 1
},
{
"id": 3,
"comment": "Interesting perspective.",
"user_id": 3,
"post_id": 1
}
]
}
}
```
**Via a new collection**
```js title="Create a new collection in your MongoDB database, and populate it with data:"
db.createCollection("comments");
db.comments.insertMany([
{
comment_id: 1,
comment: "Great post! Really enjoyed reading this.",
user_id: 1,
post_id: 2,
},
{
comment_id: 2,
comment: "Thanks for sharing your thoughts!",
user_id: 2,
post_id: 1,
},
{
comment_id: 3,
comment: "Interesting perspective.",
user_id: 3,
post_id: 1,
},
]);
```
```bash title="Use the DDN CLI to introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```bash title="Then, add your model:"
ddn model add my_mongo comments
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your table:"
query {
comments {
comment_id
comment
user_id
post_id
}
}
```
```json title="With a response like this:"
{
"data": {
"comments": [
{
"comment_id": 1,
"comment": "Great post! Really enjoyed reading this.",
"user_id": 1,
"post_id": 2
},
{
"comment_id": 2,
"comment": "Thanks for sharing your thoughts!",
"user_id": 2,
"post_id": 1
},
{
"comment_id": 3,
"comment": "Interesting perspective.",
"user_id": 3,
"post_id": 1
}
]
}
}
```
**Via a new table or view**
```sql title="Create a new table or view in your ClickHouse database:"
CREATE TABLE comments (
id UInt32,
comment String,
user_id UInt32,
post_id UInt32
)
ENGINE = MergeTree()
ORDER BY id;
INSERT INTO comments (id, comment, user_id, post_id) VALUES
(1, 'Great post! Really enjoyed reading this.', 1, 2),
(2, 'Thanks for sharing your thoughts!', 2, 1),
(3, 'Interesting perspective.', 3, 1);
```
```bash title="Use the DDN CLI to introspect your ClickHouse instance:"
ddn connector introspect my_clickhouse
```
```bash title="Then, add your model:"
ddn model add my_clickhouse comments
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your table:"
query {
comments {
id
comment
user_id
post_id
}
}
```
```json title="With a response like this:"
{
"data": {
"comments": [
{
"id": 1,
"comment": "Great post! Really enjoyed reading this.",
"user_id": 1,
"post_id": 2
},
{
"id": 2,
"comment": "Thanks for sharing your thoughts!",
"user_id": 2,
"post_id": 1
},
{
"id": 3,
"comment": "Interesting perspective.",
"user_id": 3,
"post_id": 1
}
]
}
}
```
#### Via native query
Within your connector's directory, you can add a new file with a `.sql` extension to define a native query.
```sh title="Create a new directory to store your native queries:"
mkdir -p app/connector/my_pg/native_operations/queries/
```
```sql title="Create a new file in your connector's directory:"
-- native_operations/queries/order_users_of_same_age.sql
SELECT
id,
name,
age,
RANK() OVER (PARTITION BY age ORDER BY name ASC) AS rank_within_age
FROM
users
WHERE
age = {{ age }}
```
Arguments are passed to the native query as variables surrounded by double curly braces `{{ }}`.
```sh title="Then, use the PostgreSQL connector's plugin to add the native query to your connector's configuration:"
ddn connector plugin \
--connector app/connector/my_pg/connector.yaml \
-- \
native-operation create \
--operation-path native_operations/queries/order_users_of_same_age.sql \
--kind query
```
```sh title="Introspect your PostgreSQL instance:"
ddn connector introspect my_pg
```
```sh title="Show the found resources:"
ddn connector show-resources my_pg
```
```sh title="Then, add your model:"
ddn model add my_pg order_users_of_same_age
```
Let's add a few more users to make this native query example more interesting:
```sql
INSERT INTO users (name, age) VALUES ('Dan', 25), ('Erika', 25), ('Fatima', 25), ('Gabe', 25);
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the following query to see the results:
```graphql
query UsersOfSameAge {
orderUsersOfSameAge(args: { age: 25 }) {
id
name
age
orderWithinAge
}
}
```
Let's create a [native query](/reference/connectors/mongodb/native-operations/native-queries.mdx) that ranks users
within their age group by name using an
[aggregation pipeline](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/).
```sh title="Create a new directory to store your native queries:"
mkdir -p /connector//native_queries/
```
```json title="Create a new aggregation pipeline file in your connector's directory:"
// native_queries/users_ranked_by_age.json
{
"name": "usersRankedByAge",
"representation": "collection",
"description": "Rank users within their age group by name",
"inputCollection": "users",
"arguments": {
"age": { "type": { "scalar": "int" } }
},
"resultDocumentType": "UserRank",
"objectTypes": {
"UserRank": {
"fields": {
"_id": { "type": { "scalar": "objectId" } },
"name": { "type": { "scalar": "string" } },
"age": { "type": { "scalar": "int" } },
"rank": { "type": { "scalar": "int" } }
}
}
},
"pipeline": [
{
"$match": {
"age": "{{ age }}"
}
},
{
"$setWindowFields": {
"partitionBy": "$age",
"sortBy": { "name": 1 },
"output": {
"rank": {
"$rank": {}
}
}
}
}
]
}
```
This query will return a list of users sorted by age, and within each age group, sorted by name.
```bash title="Use the DDN CLI to introspect your MongoDB instance:"
ddn connector introspect
```
```bash title="Show the found resources:"
ddn connector show-resources
```
```sh title="Then, add the model:"
ddn model add usersRankedByAge
```
We can insert some more users to make the query result more interesting:
```bash title="In your terminal, insert some more users:"
docker exec -it mongodb mongosh my_database --eval "
db.users.insertMany([
{ user_id: 1, name: 'Dinesh', age: 25 },
{ user_id: 2, name: 'Bertram', age: 25 },
{ user_id: 3, name: 'Erlich', age: 25 }
]);
"
```
In the console, run the following query:
```graphql
query MyQuery {
usersRankedByAge(args: { age: 25 }) {
name
age
rank
id
}
}
```
You should see the following output:
```json
{
"data": {
"usersRankedByAge": [
{
"name": "Alice",
"age": 25,
"rank": 1,
"id": "67ae6b1225e762d63aa00aa1"
},
{
"name": "Bertram",
"age": 25,
"rank": 2,
"id": "67ae85a5a437b6a167a00aa1"
},
{
"name": "Dinesh",
"age": 25,
"rank": 3,
"id": "67ae85a5a437b6a167a00aa3"
},
{
"name": "Erlich",
"age": 25,
"rank": 4,
"id": "67ae85a5a437b6a167a00aa2"
}
]
}
}
```
Within your connector's directory, you can add a new SQL configuration file to define a
[native query](/reference/connectors/clickhouse/native-operations/native-queries.mdx).
```sh title="Create a new directory to store your native queries:"
mkdir -p /connector//queries/
```
```sql title="Create a new file in your connector's directory:"
// queries/UsersByName.sql
SELECT *
FROM "default"."users"
WHERE "users"."name" = {name: String}
```
Note this uses the
[ClickHouse parameter syntax](https://clickhouse.com/docs/en/interfaces/cli#cli-queries-with-parameters-syntax)
```json title="Update your the queries section in your configuration.json file:"
// configuration.json
{
"tables": {},
"queries": {
"UserByName": {
"exposed_as": "collection",
"file": "queries/UserByName.sql",
"return_type": {
"kind": "definition",
"columns": {
"id": "Int32",
"name": "String"
}
}
}
}
}
```
```bash title="Use the DDN CLI to introspect your ClickHouse instance:"
ddn connector introspect
```
```sh title="Then, update your models:"
ddn model add UserByName
```
### Updating a model
Your underlying data source may change over time. You can update your model to reflect these changes.
You'll need to update the mapping of your model to the data source by updating the
[DataConnectorLink](/reference/metadata-reference/data-connector-links.mdx) object.
```bash title="Introspect your data source:"
ddn connector introspect
```
```bash title="Then, update your model:"
ddn model update
```
This will find changed resources in the data source and attempt to merge them into the model.
If you'd like to completely add the model again, you can first run the `model remove` command (below) and then re-create
your model.
### Extending a model {#tutorial-extend-model}
Find tutorials about extending a model with related information or custom logic in the
[Relationships](/data-modeling/relationship.mdx) section.
### Deleting a model
```bash title="If you no longer need a model, you can delete it:"
ddn model remove users
```
## Reference
You can learn more about models in the metadata reference [docs](/reference/metadata-reference/models.mdx).
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/mutations/
# Basics of Mutations
## Introduction
GraphQL mutations are used to **modify data** by invoking [commands](/reference/metadata-reference/commands.mdx) in your
Hasura DDN project. Mutations allow you to insert, update, or delete records while maintaining control over what data is
affected and returned.
Mutations can perform inserts to add new records, specifying values for each field in the command. You can also update
existing records by applying changes to fields that match certain conditions. Similarly, you can delete records that
meet specific criteria, ensuring precise control over which data is removed.
Just like queries, mutations allow you to specify fields in the response. This means you can retrieve the affected data
after a mutation, such as returning the new or updated values of fields in the records you modified.
Since the GraphQL API is self-documenting, you can write mutations manually or use tools like auto-completion and the
GraphiQL explorer in the Hasura DDN console to help you build and test them. GraphiQL shows the available mutation
types, fields, and input arguments, making it easier to construct and validate mutations.
## Configuration
Currently, you can perform mutations via the GraphQL API using the following methods:
- Some data connectors support mutations out-of-the-box and β when
[adding `commands`](/reference/cli/commands/ddn_command_add.mdx) that are
[procedures](/reference/metadata-reference/commands.mdx#command-procedurename) β will generate them automatically.
- Some data connectors support authoring native mutations β such as with the
[MongoDB connector](/reference/connectors/mongodb/native-operations/native-mutations.mdx) β to enable you to write
custom logic for inserts, updates, and deletes.
- You can mutate data via any lambda connector using the Command Query Separation (CQS) pattern. Learn more
[here](/business-logic/tutorials/1-add-custom-logic.mdx).
:::info Not sure what your connector supports?
For questions about feature support, check out the [connector reference docs](/reference/connectors/index.mdx).
:::
You can configure the overall usage of mutations in your GraphQL API using
[the `GraphQlConfig` object](/reference/metadata-reference/graphql-config.mdx#graphqlconfig-mutationgraphqlconfig) in
your metadata. Additionally, you can customize individual mutations by modifying
[the `GraphQlDefinition` metadata object](/reference/metadata-reference/commands.mdx#command-commandgraphqldefinition)
for a command.
## Learn more
- [Insert data](/graphql-api/mutations/insert-data.mdx)
- [Update data](/graphql-api/mutations/update-data.mdx)
- [Delete data](/graphql-api/mutations/delete-data.mdx)
==============================
# insert-data.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/mutations/insert-data
# Insert Data
## Auto-generated insert mutation schema
Depending on your connector, you'll have a range of insert mutations available to mutate data via your API.
### Insert a single object
Insert a single object of a type by passing the object as an argument to the mutation, including the fields you want to
insert.
### Insert multiple objects of the same type
Insert multiple objects of a type by passing an array of objects as an argument to the mutation.
### Insert an object and return a nested response
Insert an object and return a nested response by querying nested fields in the response.
## Custom insert mutations
For connectors that support native mutations, you can also create custom insert mutations to insert data into your data
source. This allows you to write any custom logic you need to insert data using your data source's native capabilities.
Learn more in your [connector's reference documentation](/reference/connectors/index.mdx).
==============================
# update-data.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/mutations/update-data
# Update Data
Depending on your connector, you'll have a range of update mutations available to mutate data via your API.
## Update an object by a unique field
You can update an object by targeting it using a unique key according to the source schema and then setting the new
value.
## Custom update mutations
For connectors that support native mutations, you can also create custom update mutations to update data in your data
source. This allows you to write any custom logic you need to update data using your data source's native capabilities.
Learn more in your [connector's reference documentation](/reference/connectors/index.mdx).
==============================
# delete-data.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/mutations/delete-data
# Delete Data
## Delete an object
You can delete an object by targeting it using a unique key according to the source schema.
## Custom delete mutations
For connectors that support native mutations, you can also create custom delete mutations to delete data in your data
source. This allows you to write any custom logic you need to delete data using your data source's native capabilities.
Learn more in your [connector's reference documentation](/reference/connectors/index.mdx).
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/subscriptions/
# Basics of Subscriptions
## Introduction
Subscriptions allow you to push **real-time updates** from your data sources to clients, making them ideal for building
reactive applications without constant polling.
:::warning Currently in beta
Subscriptions are in beta and are free to use during this period.
:::
## Configuration
You can enable subscriptions on your GraphQL API in the following ways:
### New Hasura DDN project
If you're starting fresh, set up a Hasura DDN project using the [quickstart](/quickstart.mdx) guide. After adding your
models to the supergraph using the [`ddn model add`](/reference/cli/commands/ddn_model_add.mdx) command, subscription
capabilities are automatically generated! For further customization, you can
[edit the metadata](#metadata-configuration).
### Existing Hasura DDN project
To enable subscriptions in an existing DDN project, use the following CLI command:
```bash
ddn codemod upgrade-graphqlconfig-subscriptions
```
This command updates the local metadata files, adding subscription-specific configurations. After running this, create a
supergraph build and test the subscriptions on the API. For further customization, you can
[edit the metadata](#metadata-configuration).
### Metadata configuration
You can configure the overall usage of subscriptions in your GraphQL API using
[the `GraphQlConfig` object](/reference/metadata-reference/graphql-config.mdx#graphqlconfig-subscriptiongraphqlconfig)
in your metadata.
Additionally, you can customize individual subscriptions by modifying the
[`SubscriptionGraphQlDefinition` field](/reference/metadata-reference/models.mdx#model-subscriptiongraphqldefinition) on
any model's `selectUniques`, `selectMany`, or `aggregate` fields.
## Learn more
Subscriptions are supported for the following queries:
- [Select unique](/graphql-api/subscriptions/select-unique.mdx)
- [Select many](/graphql-api/subscriptions/select-many.mdx)
- [Aggregates](/graphql-api/subscriptions/aggregates.mdx)
==============================
# select-unique.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/subscriptions/select-unique
# Select Unique Subscription
The following subscription will automatically update with new notifications for the **specified** user whenever they're
added to the data source.
```graphql
subscription UserNotificationSubscription {
notifications(where: { user_id: { _eq: 123 } }) {
id
created_at
message
}
}
```
==============================
# select-many.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/subscriptions/select-many
# Select Many Subscription
The following subscription will automatically update with new notifications for **all** users whenever they're added to
the data source.
```graphql
subscription AllNotificationSubscription {
notifications {
id
created_at
message
}
}
```
==============================
# aggregates.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/subscriptions/aggregates
# Aggregates Subscription
The following subscription will automatically update the aggregate for `_count` when new records are added to the data
source.
```graphql
subscription NotificationSubscription {
notificationsAggregate {
_count
}
}
```
==============================
# command.mdx
URL: https://hasura.io/docs/3.0/docs/data-modeling/command
# Commands Modify Data
## Introduction
In DDN, commands represent operations in your API that can be executed such as those which modify data in your data
sources, (inserts, updates, deletes), or that perform complex read operations or execute custom business logic.
## Lifecycle
The lifecycle in creating a command in your metadata is as follows:
1. Have some operation in your data source that you want to make executable via your API.
2. Introspect your data source using the DDN CLI with the relevant data connector to fetch the operation resources.
3. Add the command to your metadata with the DDN CLI.
4. Create a build of your supergraph API with the DDN CLI.
5. Serve your build as your API with the Hasura engine either locally or in the cloud.
6. Iterate on your API by repeating this process or by editing your metadata manually as needed.
## Create a command
To add a command you will need to have a data connector already set up and connected to the data source. Follow the
[Quickstart](/quickstart.mdx) or the tutorial in [How to Build with DDN](/how-to-build-with-ddn/overview.mdx) to get to
that point.
### From a source operation
Some data connectors support the ability to introspect your data source to discover the commands that can be added to
your supergraph automatically.
```bash title="Introspect your data source:"
ddn connector introspect
```
```bash title="Show the resources discovered from your data source:"
ddn connector show-resources
```
```bash title="Add the command from the discovered resources to your metadata:"
ddn command add
```
Or you can optionally add all the commands by specifying `"*"`.
```bash title="Add all commands from your data source:"
ddn command add "*"
```
This will add commands with their accompanying metadata definitions to your metadata.
### Via native operations {#via-native-operations-how-to}
Some data connectors support the ability to add commands via native operations so that you can add any operation that is
not supported by the automatic introspection process.
For classic database connectors, this will be native query code for that source. This can be, for example, a more
complex read operation or a way to run custom business logic, which can be exposed as queries or mutations in the
GraphQL API.
For Lambda connectors, eg: (TypeScript, Go, Python, etc) this will be a function (read-only) or procedure (mutation or
other side-effects) that can also be exposed as a query or mutation in the GraphQL API.
The process of creating a native operation for PostgreSQL is the following.
Within your connector's directory, you can add a new file with a `.sql` extension to define a native operation.
Then, use the PostgreSQL connector's plugin to add the native operation to your connector's configuration:
```sh title="Then, use the PostgreSQL connector's plugin to add the native operation to your connector's configuration:"
ddn connector plugin \
--connector //connector.yaml \
-- \
native-operation create \
--operation-path //native-operations//.sql \
--kind mutation
```
By specifying the `--kind mutation` flag, you are indicating that the operation is a mutation. If you specify
`--kind query`, the operation will be a query.
```sh title="Introspect your PostgreSQL instance:"
ddn connector introspect
```
```sh title="Show the found resources:"
ddn connector show-resources
```
```sh title="Then, add your model:"
ddn command add
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the mutation with GraphQL.
See the tutorial for creating a native mutation in PostgreSQL [here](#via-native-operation-tutorials).
In MongoDB, you can create a [native operation](/reference/connectors/mongodb/native-operations/index.mdx) by using an
[aggregation pipeline](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/) in a JSON file by adding it to
your connector's directory.
See the syntax for MongoDB native operations [here](/reference/connectors/mongodb/native-operations/syntax.mdx).
Store your native operation in an appropriate directory, possibly named for the operation type, in your connector's
directory.
```sh title="Introspect your MongoDB instance:"
ddn connector introspect
```
```sh title="Show the found resources:"
ddn connector show-resources
```
```sh title="Then, add your model:"
ddn command add
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the operation with GraphQL.
See the tutorial for creating a native operation in MongoDB [here](#via-native-operation-tutorials).
Commands are currently not supported on ClickHouse
You can run whatever arbitrary code you want in your TypeScript connector and expose it as a GraphQL mutation or query
in your supergraph API.
```sh title="Initalize a new connector and select hasura/nodejs from the list:"
ddn connector init my_ts -i
```
```ts title="Replace the functions.ts contents with your own custom code:"
/**
* @readonly
*/
export function myCustomCode(myInput: string): string {
// Do something with the input
return "My output";
}
```
By adding the `@readonly` tag, we are indicating that this function is a read-only operation to be exposed as an NDC
function which will ulitmately show up as a GraphQL query. Leaving the tag off will expose the function as an NDC
procedure which will be a GraphQL mutation.
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
```bash title="Track the function:"
ddn command add my_ts myCustomCode
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your native operation:"
query MyCustomCode {
myCustomCode(myInput: "My input")
}
```
```json title="You will see a response like this:"
{
"data": {
"myCustomCode": "My output"
}
}
```
You can run whatever arbitrary code you want in your Python connector and expose it as a GraphQL mutation or query in
your supergraph API.
```sh title="Initalize a new connector and select hasura/python from the list:"
ddn connector init my_py -i
```
```py title="Replace the functions.py contents with your own custom code:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
connector = FunctionConnector()
@connector.register_query
def my_custom_code(my_input: str) -> str:
# Do something with the input
return "My output"
if __name__ == "__main__":
start(connector)
```
By adding the `@connector.register_query` decorator, we are indicating that this function is to be exposed as an NDC
function which will ultimately show up as a GraphQL query. If you use `@connector.register_mutation` instead, the
function will be exposed as an NDC procedure which will be a GraphQL mutation.
```bash title="Introspect the connector:"
ddn connector introspect my_py
```
```bash title="Track the function:"
ddn command add my_py my_custom_code
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your native operation:"
query MyCustomCode {
myCustomCode(myInput: "My input")
}
```
```json title="You will see a response like this:"
{
"data": {
"myCustomCode": "My output"
}
}
```
You can run whatever arbitrary code you want in your Go connector and expose it as a GraphQL mutation or query in your
supergraph API.
```sh title="Initialize a new connector and select hasura/go from the list:"
ddn connector init my_go -i
```
```go title="Replace the functions.go contents with your own custom code:"
package functions
"context"
"hasura-ndc.dev/ndc-go/types"
)
// InputArguments represents the input of the native operation.
type InputArguments struct {
MyInput string `json:"myInput"`
}
// OutputResult represents the output of the native operation.
type OutputResult struct {
MyOutput string `json:"myOutput"`
}
// ProcedureCustomCode is a native operation that can be called from the API.
func ProcedureCustomCode(ctx context.Context, state *types.State, arguments *InputArguments) (*OutputResult, error) {
// Do something with the input
return &OutputResult{
MyOutput: "My output",
}, nil
}
```
Using the prefix `Procedure` ensures ProcedureCustomCode() is exposed as a mutation in our API. Prefixing with
`Function` identifies it as a function to be exposed as a query in your API.
Both have typed input arguments and return strings, which the connector will use to generate the corresponding GraphQL
schema.
```bash title="Introspect the connector:"
ddn connector introspect my_go
```
```bash title="Track the function:"
ddn command add my_go customCode
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your native operation:"
query MyCustomCode {
customCode(myInput: "My input")
}
```
```json title="You will see a response like this:"
{
"data": {
"myCustomCode": "My output"
}
}
```
You can now build your supergraph API, serve it, and execute your commands.
For a walkthrough on how to create a command, see the [Command Tutorials](#command-tutorials) section below.
## Update a command
If you want to update your command to reflect a change that happened in the underlying data source you should first
introspect to get the latest resources and then update the relevant command.
```bash title="Introspect your data source:"
ddn connector introspect
```
```bash title="Then, update your existing command:"
ddn command update
```
You will see an output which explains how new resources were added or updated in the command.
You can now build your supergraph API, serve it, and execute your commands with the updated definitions.
You can also update the command by editing the command's metadata manually.
## Delete a command
```bash title="If you no longer need a command, you can delete it:"
ddn command remove
```
Along with the command itself, the associated metadata is also removed.
## Tutorials {#command-tutorials}
The tutorials below follow on from each particular tutorial in the
[How to Build with DDN](/how-to-build-with-ddn/overview.mdx) section. Select the relevant data connector to follow the
tutorial.
### Creating a command
To modify data, you first need to create a command that maps to a specific operation within your data source.
#### From a source operation
First, show a list of resouces identified by the connector.
```bash title="Use the DDN CLI to introspect your PostgreSQL database:"
ddn connector introspect
```
```bash
ddn connector show-resources my_pg
```
You will see a list of Commands (Mutations) which have been automatically detected by the connector since they are
defined by a foreign key relationship in the database.
```sh title="Then, add your command:"
ddn command add my_pg insert_users
```
Rebuild your supergraph API.
```bash title="Create a new build:"
ddn supergraph build local
```
Serve your build.
```bash title="Serve your build:"
ddn run docker-start
ddn console --local
```
Run the mutation:
```graphql
mutation InsertUser {
insertUsers(objects: { age: "21", name: "Sean" }, postCheck: {}) {
affectedRows
returning {
id
name
age
}
}
}
```
```json
{
"data": {
"insertUsers": {
"affectedRows": 1,
"returning": [
{
"id": 4,
"name": "Sean",
"age": 21
}
]
}
}
}
```
The MongoDB data connector defines custom commands via
[native mutations](/reference/connectors/mongodb/native-operations/native-mutations.mdx).
Within your connector's directory, you can add a new JSON configuration file to define a native mutation.
```sh title="Create a new directory to store your native mutations:"
mkdir -p app/connector/my_mongo/native_mutations/
```
```json title="Create a new file in your connector's directory:"
// native_mutations/insert_user.json
{
"name": "insertUser",
"description": "Inserts a user record into the database",
"arguments": {
"name": { "type": { "scalar": "string" } }
},
"resultType": {
"object": "InsertUser"
},
"objectTypes": {
"InsertUser": {
"fields": {
"ok": { "type": { "scalar": "double" } },
"n": { "type": { "scalar": "int" } }
}
}
},
"command": {
"insert": "users",
"documents": [{ "name": "{{ name }}" }]
}
}
```
```bash title="Use the DDN CLI to introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the resources discovered from your MongoDB instance:"
ddn connector show-resources my_mongo
```
```sh title="Then, add your command:"
ddn command add my_mongo insertUser
```
Rebuild your supergraph API.
```bash title="Create a new build:"
ddn supergraph build local
```
Serve your build.
```bash title="Serve your build:"
ddn run docker-start
ddn console --local
```
Run the mutation:
```graphql
mutation InsertUser {
insertUser(name: "Sam") {
ok
n
}
}
```
```json
{
"data": {
"insertUser": {
"ok": 1,
"n": 1
}
}
}
```
Commands are currently not supported on ClickHouse.
#### Via native operations {#via-native-operation-tutorials}
Within your connector's directory, you can add a new file with a `.sql` extension to define a native mutation.
```sh title="Create a new directory to store your native mutations:"
mkdir -p app/connector/my_pg/native_operations/mutations/
```
Let's create a mutation using a `SQL` `UPDATE` statement that updates the title of all posts from user's of that age by
appending their age to the title.
```sql title="Create a new file in your connector's directory:"
-- native_operations/mutations/update_post_titles_by_age.sql
UPDATE posts
SET title = CASE
WHEN title ~ ' - age \d+
Let's create a [native mutation](/reference/connectors/mongodb/native-operations/native-mutations.mdx) that adds a new
user to the database with a name and age using an
[aggregation pipeline](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/) in a JSON file.
See the syntax for MongoDB native operations [here](/reference/connectors/mongodb/native-operations/syntax.mdx).
```sh title="Create a new directory to store your native mutations:"
mkdir -p app/connector/my_mongo/native_operations/mutations/
```
```json title="Create a new native mutation file in your connector's directory:"
// native_mutations/create_user.json
{
"name": "createUser",
"description": "Create a new user with name and age",
"resultType": {
"object": "CreateUserResult"
},
"arguments": {
"name": {
"type": {
"scalar": "string"
}
},
"age": {
"type": {
"scalar": "int"
}
}
},
"objectTypes": {
"CreateUserResult": {
"fields": {
"ok": {
"type": {
"scalar": "int"
}
},
"n": {
"type": {
"scalar": "int"
}
}
}
}
},
"command": {
"insert": "users",
"documents": [
{
"name": "{{ name }}",
"age": "{{ age }}",
"user_id": {
"$size": {
"$ifNull": [
{
"$objectToArray": "$ROOT"
},
[]
]
}
}
}
]
}
}
```
```sh title="Introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the found resources:"
ddn connector show-resources my_mongo
```
```sh title="Then, add your model:"
ddn command add my_mongo createUser
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the following query to see the results:
```graphql title="Run the following mutation:"
mutation CreateUser {
createUser(age: 25, name: "Peter") {
n
ok
}
}
```
```json title="The response will be:"
{
"data": {
"createUser": {
"n": 1,
"ok": 1
}
}
}
```
Commands are currently not supported on ClickHouse
```sh title="Initalize a new connector and select hasura/nodejs from the list:"
ddn connector init my_ts -i
```
```ts title="Replace the functions.ts contents with the following:"
/**
* @readonly
*/
export function shoutName(name: string): string {
return name.toUpperCase();
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
```bash title="Track the function:"
ddn command add my_ts shoutName
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
```sh title="Initalize a new connector and select hasura/python from the list:"
ddn connector init my_python -i
```
```python title="Replace the functions.py contents with the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
connector = FunctionConnector()
@connector.register_query
def shout_name(name: str) -> str:
return name.upper()
if __name__ == "__main__":
start(connector)
```
```bash title="Introspect the connector:"
ddn connector introspect my_python
```
```bash title="Track the function:"
ddn command add my_python shout_name
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
```sh title="Initalize a new connector and select hasura/go from the list:"
ddn connector init my_go -i
```
```go title="Rename hello.go to nameToUpperCase.go:"
package functions
"context"
"fmt"
"hasura-ndc.dev/ndc-go/types"
"strings"
)
// NameArguments defines the input arguments for the function
type NameArguments struct {
Name string `json:"name"` // required argument
}
// NameResult defines the output result for the function
type NameResult string
// FunctionShoutName converts a name string to uppercase
func FunctionShoutName(ctx context.Context, state *types.State, arguments *NameArguments) (*NameResult, error) {
if arguments.Name == "" {
return nil, fmt.Errorf("name cannot be empty")
}
upperCaseName := NameResult(strings.ToUpper(arguments.Name))
return &upperCaseName, nil
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_go
```
```bash title="Track the function:"
ddn command add my_go shoutName
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
:::info Lambda connectors
Lambda connectors allow you to execute custom business logic directly via your API. You can learn more about Lambda
connectors in the [docs](/business-logic/overview.mdx).
:::
### Updating a command
Your underlying data source may change over time. You can update your command to reflect these changes.
For an automatically detected command, you can update the command metadata.
```bash title="Use the DDN CLI to introspect your PostgreSQL database:"
ddn connector introspect my_pg
```
```sh title="Show the resources discovered from your PostgreSQL database:"
ddn connector show-resources my_pg
```
```sh title="Then, update your commands:"
ddn command update my_pg "*"
```
For example if you have a [native mutation](/reference/connectors/mongodb/native-operations/native-mutations.mdx) that
inserts a user you can modify it to include a second field on new documents.
```json title="Edit the existing native mutation in your connector's directory:"
// native_mutations/create_user.json
{
"name": "createUser",
"description": "Create a new user with name and age",
"resultType": {
"object": "CreateUserResult"
},
"arguments": {
"name": {
"type": {
"scalar": "string"
}
},
"age": {
"type": {
"scalar": "int"
}
},
"role": {
"type": {
"nullable": {
"scalar": "string"
}
}
} // add an argument
},
"objectTypes": {
"CreateUserResult": {
"fields": {
"ok": {
"type": {
"scalar": "int"
}
},
"n": {
"type": {
"scalar": "int"
}
}
}
}
},
"command": {
"insert": "users",
"documents": [
{
"name": "{{ name }}",
"age": "{{ age }}",
"role": "{{ role }}",
"user_id": {
"$size": {
"$ifNull": [
{
"$objectToArray": "$ROOT"
},
[]
]
}
}
}
]
}
}
```
```bash title="Use the DDN CLI to introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the resources discovered from your MongoDB instance:"
ddn connector show-resources my_mongo
```
Then, either update the specific command or update all commands.
```sh title="Update the specific command:"
ddn command update my_mongo insertUser
```
```sh title="Update all commands:"
ddn command update my_mongo "*"
```
Commands are currently not supported on ClickHouse.
You can update the command metadata for the Node.js lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_ts
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_ts
```
```sh title="Then, update your command:"
ddn command update my_ts shoutName
```
```sh title="Or, update all commands:"
ddn command update my_ts "*"
```
You can update the command metadata for the Python lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_py
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_py
```
```sh title="Then, update your command:"
ddn command update my_py shout_name
```
```sh title="Or, update all commands:"
ddn command update my_py "*"
```
You can update the command metadata for the Go lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_go
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_go
```
```sh title="Then, update your command:"
ddn command update my_go shoutName
```
```sh title="Or, update all commands:"
ddn command update my_go "*"
```
### Deleting a command
```bash title="If you no longer need a command, you can delete it with the CLI:"
ddn command remove
```
## Reference
You can learn more about commands in the metadata reference [docs](/reference/metadata-reference/commands.mdx).
THEN regexp_replace(title, ' - age \d+
Let's create a [native mutation](/reference/connectors/mongodb/native-operations/native-mutations.mdx) that adds a new
user to the database with a name and age using an
[aggregation pipeline](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/) in a JSON file.
See the syntax for MongoDB native operations [here](/reference/connectors/mongodb/native-operations/syntax.mdx).
```sh title="Create a new directory to store your native mutations:"
mkdir -p app/connector/my_mongo/native_operations/mutations/
```
```json title="Create a new native mutation file in your connector's directory:"
// native_mutations/create_user.json
{
"name": "createUser",
"description": "Create a new user with name and age",
"resultType": {
"object": "CreateUserResult"
},
"arguments": {
"name": {
"type": {
"scalar": "string"
}
},
"age": {
"type": {
"scalar": "int"
}
}
},
"objectTypes": {
"CreateUserResult": {
"fields": {
"ok": {
"type": {
"scalar": "int"
}
},
"n": {
"type": {
"scalar": "int"
}
}
}
}
},
"command": {
"insert": "users",
"documents": [
{
"name": "{{ name }}",
"age": "{{ age }}",
"user_id": {
"$size": {
"$ifNull": [
{
"$objectToArray": "$ROOT"
},
[]
]
}
}
}
]
}
}
```
```sh title="Introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the found resources:"
ddn connector show-resources my_mongo
```
```sh title="Then, add your model:"
ddn command add my_mongo createUser
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the following query to see the results:
```graphql title="Run the following mutation:"
mutation CreateUser {
createUser(age: 25, name: "Peter") {
n
ok
}
}
```
```json title="The response will be:"
{
"data": {
"createUser": {
"n": 1,
"ok": 1
}
}
}
```
Commands are currently not supported on ClickHouse
```sh title="Initalize a new connector and select hasura/nodejs from the list:"
ddn connector init my_ts -i
```
```ts title="Replace the functions.ts contents with the following:"
/**
* @readonly
*/
export function shoutName(name: string): string {
return name.toUpperCase();
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
```bash title="Track the function:"
ddn command add my_ts shoutName
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
```sh title="Initalize a new connector and select hasura/python from the list:"
ddn connector init my_python -i
```
```python title="Replace the functions.py contents with the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
connector = FunctionConnector()
@connector.register_query
def shout_name(name: str) -> str:
return name.upper()
if __name__ == "__main__":
start(connector)
```
```bash title="Introspect the connector:"
ddn connector introspect my_python
```
```bash title="Track the function:"
ddn command add my_python shout_name
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
```sh title="Initalize a new connector and select hasura/go from the list:"
ddn connector init my_go -i
```
```go title="Rename hello.go to nameToUpperCase.go:"
package functions
"context"
"fmt"
"hasura-ndc.dev/ndc-go/types"
"strings"
)
// NameArguments defines the input arguments for the function
type NameArguments struct {
Name string `json:"name"` // required argument
}
// NameResult defines the output result for the function
type NameResult string
// FunctionShoutName converts a name string to uppercase
func FunctionShoutName(ctx context.Context, state *types.State, arguments *NameArguments) (*NameResult, error) {
if arguments.Name == "" {
return nil, fmt.Errorf("name cannot be empty")
}
upperCaseName := NameResult(strings.ToUpper(arguments.Name))
return &upperCaseName, nil
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_go
```
```bash title="Track the function:"
ddn command add my_go shoutName
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
:::info Lambda connectors
Lambda connectors allow you to execute custom business logic directly via your API. You can learn more about Lambda
connectors in the [docs](/business-logic/overview.mdx).
:::
### Updating a command
Your underlying data source may change over time. You can update your command to reflect these changes.
For an automatically detected command, you can update the command metadata.
```bash title="Use the DDN CLI to introspect your PostgreSQL database:"
ddn connector introspect my_pg
```
```sh title="Show the resources discovered from your PostgreSQL database:"
ddn connector show-resources my_pg
```
```sh title="Then, update your commands:"
ddn command update my_pg "*"
```
For example if you have a [native mutation](/reference/connectors/mongodb/native-operations/native-mutations.mdx) that
inserts a user you can modify it to include a second field on new documents.
```json title="Edit the existing native mutation in your connector's directory:"
// native_mutations/create_user.json
{
"name": "createUser",
"description": "Create a new user with name and age",
"resultType": {
"object": "CreateUserResult"
},
"arguments": {
"name": {
"type": {
"scalar": "string"
}
},
"age": {
"type": {
"scalar": "int"
}
},
"role": {
"type": {
"nullable": {
"scalar": "string"
}
}
} // add an argument
},
"objectTypes": {
"CreateUserResult": {
"fields": {
"ok": {
"type": {
"scalar": "int"
}
},
"n": {
"type": {
"scalar": "int"
}
}
}
}
},
"command": {
"insert": "users",
"documents": [
{
"name": "{{ name }}",
"age": "{{ age }}",
"role": "{{ role }}",
"user_id": {
"$size": {
"$ifNull": [
{
"$objectToArray": "$ROOT"
},
[]
]
}
}
}
]
}
}
```
```bash title="Use the DDN CLI to introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the resources discovered from your MongoDB instance:"
ddn connector show-resources my_mongo
```
Then, either update the specific command or update all commands.
```sh title="Update the specific command:"
ddn command update my_mongo insertUser
```
```sh title="Update all commands:"
ddn command update my_mongo "*"
```
Commands are currently not supported on ClickHouse.
You can update the command metadata for the Node.js lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_ts
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_ts
```
```sh title="Then, update your command:"
ddn command update my_ts shoutName
```
```sh title="Or, update all commands:"
ddn command update my_ts "*"
```
You can update the command metadata for the Python lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_py
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_py
```
```sh title="Then, update your command:"
ddn command update my_py shout_name
```
```sh title="Or, update all commands:"
ddn command update my_py "*"
```
You can update the command metadata for the Go lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_go
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_go
```
```sh title="Then, update your command:"
ddn command update my_go shoutName
```
```sh title="Or, update all commands:"
ddn command update my_go "*"
```
### Deleting a command
```bash title="If you no longer need a command, you can delete it with the CLI:"
ddn command remove
```
## Reference
You can learn more about commands in the metadata reference [docs](/reference/metadata-reference/commands.mdx).
, ' - age ' || {{ age }})
ELSE title || ' - age ' || {{ age }}
END
FROM users
WHERE posts.user_id = users.id
AND users.age = {{ age }}
RETURNING
posts.id,
posts.title,
posts.user_id,
users.name,
users.age;
```
Arguments are passed to the native mutation as variables surrounded by double curly braces `{{ }}`.
```sh title="Then, use the PostgreSQL connector's plugin to add the native mutation to your connector's configuration:"
ddn connector plugin \
--connector app/connector/my_pg/connector.yaml \
-- \
native-operation create \
--operation-path native_operations/mutations/update_post_titles_by_age.sql \
--kind mutation
```
```sh title="Introspect your PostgreSQL instance:"
ddn connector introspect my_pg
```
```sh title="Show the found resources:"
ddn connector show-resources my_pg
```
```sh title="Then, add your model:"
ddn command add my_pg update_post_titles_by_age
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the following mutation to see the results:
```graphql title="Run the following mutation:"
mutation UpdatePostTitlesByAge {
updatePostTitlesByAge(age: "25") {
affectedRows
returning {
id
title
}
}
}
```
```json title="The response will be:"
{
"data": {
"updatePostTitlesByAge": {
"affectedRows": 2,
"returning": [
{
"id": 1,
"title": "My First Post - age 25"
},
{
"id": 2,
"title": "Another Post - age 25"
}
]
}
}
}
```
Let's create a [native mutation](/reference/connectors/mongodb/native-operations/native-mutations.mdx) that adds a new
user to the database with a name and age using an
[aggregation pipeline](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/) in a JSON file.
See the syntax for MongoDB native operations [here](/reference/connectors/mongodb/native-operations/syntax.mdx).
```sh title="Create a new directory to store your native mutations:"
mkdir -p app/connector/my_mongo/native_operations/mutations/
```
```json title="Create a new native mutation file in your connector's directory:"
// native_mutations/create_user.json
{
"name": "createUser",
"description": "Create a new user with name and age",
"resultType": {
"object": "CreateUserResult"
},
"arguments": {
"name": {
"type": {
"scalar": "string"
}
},
"age": {
"type": {
"scalar": "int"
}
}
},
"objectTypes": {
"CreateUserResult": {
"fields": {
"ok": {
"type": {
"scalar": "int"
}
},
"n": {
"type": {
"scalar": "int"
}
}
}
}
},
"command": {
"insert": "users",
"documents": [
{
"name": "{{ name }}",
"age": "{{ age }}",
"user_id": {
"$size": {
"$ifNull": [
{
"$objectToArray": "$ROOT"
},
[]
]
}
}
}
]
}
}
```
```sh title="Introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the found resources:"
ddn connector show-resources my_mongo
```
```sh title="Then, add your model:"
ddn command add my_mongo createUser
```
```title="Build and serve your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now in your console you can run the following query to see the results:
```graphql title="Run the following mutation:"
mutation CreateUser {
createUser(age: 25, name: "Peter") {
n
ok
}
}
```
```json title="The response will be:"
{
"data": {
"createUser": {
"n": 1,
"ok": 1
}
}
}
```
Commands are currently not supported on ClickHouse
```sh title="Initalize a new connector and select hasura/nodejs from the list:"
ddn connector init my_ts -i
```
```ts title="Replace the functions.ts contents with the following:"
/**
* @readonly
*/
export function shoutName(name: string): string {
return name.toUpperCase();
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
```bash title="Track the function:"
ddn command add my_ts shoutName
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
```sh title="Initalize a new connector and select hasura/python from the list:"
ddn connector init my_python -i
```
```python title="Replace the functions.py contents with the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
connector = FunctionConnector()
@connector.register_query
def shout_name(name: str) -> str:
return name.upper()
if __name__ == "__main__":
start(connector)
```
```bash title="Introspect the connector:"
ddn connector introspect my_python
```
```bash title="Track the function:"
ddn command add my_python shout_name
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
```sh title="Initalize a new connector and select hasura/go from the list:"
ddn connector init my_go -i
```
```go title="Rename hello.go to nameToUpperCase.go:"
package functions
"context"
"fmt"
"hasura-ndc.dev/ndc-go/types"
"strings"
)
// NameArguments defines the input arguments for the function
type NameArguments struct {
Name string `json:"name"` // required argument
}
// NameResult defines the output result for the function
type NameResult string
// FunctionShoutName converts a name string to uppercase
func FunctionShoutName(ctx context.Context, state *types.State, arguments *NameArguments) (*NameResult, error) {
if arguments.Name == "" {
return nil, fmt.Errorf("name cannot be empty")
}
upperCaseName := NameResult(strings.ToUpper(arguments.Name))
return &upperCaseName, nil
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_go
```
```bash title="Track the function:"
ddn command add my_go shoutName
```
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your development console:"
ddn console --local
```
```graphql title="You can now query your expanded table with the transformed name:"
query ShoutTheName {
shoutName(name: "Alice")
}
```
```json title="With a response like this:"
{
"data": {
"shoutName": "ALICE"
}
}
```
:::info Lambda connectors
Lambda connectors allow you to execute custom business logic directly via your API. You can learn more about Lambda
connectors in the [docs](/business-logic/overview.mdx).
:::
### Updating a command
Your underlying data source may change over time. You can update your command to reflect these changes.
For an automatically detected command, you can update the command metadata.
```bash title="Use the DDN CLI to introspect your PostgreSQL database:"
ddn connector introspect my_pg
```
```sh title="Show the resources discovered from your PostgreSQL database:"
ddn connector show-resources my_pg
```
```sh title="Then, update your commands:"
ddn command update my_pg "*"
```
For example if you have a [native mutation](/reference/connectors/mongodb/native-operations/native-mutations.mdx) that
inserts a user you can modify it to include a second field on new documents.
```json title="Edit the existing native mutation in your connector's directory:"
// native_mutations/create_user.json
{
"name": "createUser",
"description": "Create a new user with name and age",
"resultType": {
"object": "CreateUserResult"
},
"arguments": {
"name": {
"type": {
"scalar": "string"
}
},
"age": {
"type": {
"scalar": "int"
}
},
"role": {
"type": {
"nullable": {
"scalar": "string"
}
}
} // add an argument
},
"objectTypes": {
"CreateUserResult": {
"fields": {
"ok": {
"type": {
"scalar": "int"
}
},
"n": {
"type": {
"scalar": "int"
}
}
}
}
},
"command": {
"insert": "users",
"documents": [
{
"name": "{{ name }}",
"age": "{{ age }}",
"role": "{{ role }}",
"user_id": {
"$size": {
"$ifNull": [
{
"$objectToArray": "$ROOT"
},
[]
]
}
}
}
]
}
}
```
```bash title="Use the DDN CLI to introspect your MongoDB instance:"
ddn connector introspect my_mongo
```
```sh title="Show the resources discovered from your MongoDB instance:"
ddn connector show-resources my_mongo
```
Then, either update the specific command or update all commands.
```sh title="Update the specific command:"
ddn command update my_mongo insertUser
```
```sh title="Update all commands:"
ddn command update my_mongo "*"
```
Commands are currently not supported on ClickHouse.
You can update the command metadata for the Node.js lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_ts
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_ts
```
```sh title="Then, update your command:"
ddn command update my_ts shoutName
```
```sh title="Or, update all commands:"
ddn command update my_ts "*"
```
You can update the command metadata for the Python lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_py
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_py
```
```sh title="Then, update your command:"
ddn command update my_py shout_name
```
```sh title="Or, update all commands:"
ddn command update my_py "*"
```
You can update the command metadata for the Go lambda connector with the following workflow.
Make changes to the function in your editor, then run the following commands to update the command metadata.
```bash title="Use the DDN CLI to introspect your connector:"
ddn connector introspect my_go
```
```sh title="Show the resources discovered from your connector:"
ddn connector show-resources my_go
```
```sh title="Then, update your command:"
ddn command update my_go shoutName
```
```sh title="Or, update all commands:"
ddn command update my_go "*"
```
### Deleting a command
```bash title="If you no longer need a command, you can delete it with the CLI:"
ddn command remove
```
## Reference
You can learn more about commands in the metadata reference [docs](/reference/metadata-reference/commands.mdx).
==============================
# relationship.mdx
URL: https://hasura.io/docs/3.0/docs/data-modeling/relationship
# Relationships Connect Data
## Introduction
Relationships allow you to connect data, enabling you to query data across multiple entities.
Examples include:
- Querying a `Customer` and at the same time getting their `Orders` and each `Product` item in those orders. (Model to
Model)
- Querying a `Customer` and also getting the analytics of their app usage from another data source. (Model to Model in
another subgraph or data connector)
- Querying an `Order` and also getting a live currency conversion for the value of the order enabled with a lambda data
connector with a connection to a currency exchange API. (Model to Command)
Relationships can be added between _any_ kind of semantically related models and/or commands. They do not need to be
related in the data source by, for example, a foreign key. They also do not need to be backed by the same data source or
be in the same subgraph.
## Lifecycle
Many relationships can be created automatically by the DDN CLI from detected underlying connections such as foreign
keys. In such cases the lifecycle in creating a relationship in your metadata is as follows:
1. Introspect your data source using the DDN CLI with the relevant data connector to fetch the entity resources.
2. Add the detected relationships to your metadata with the DDN CLI.
3. Create a build of your supergraph API with the DDN CLI.
4. Serve your build as your API with the Hasura engine either locally or in the cloud.
5. Iterate on your API by repeating this process or by editing your metadata manually as needed.
If the relationship cannot be detected automatically, you can easily manually create a relationship in your metadata and
then perform lifecycle steps 3-5 from above as needed.
## Create a relationship
Relationships are defined in metadata from an
[object type](/reference/metadata-reference/types.mdx#objecttype-objecttype), to a
[model](/reference/metadata-reference/models.mdx) or [command](/reference/metadata-reference/commands.mdx). But since
models and commands are also defined with object types, you can think of relationships as being between models and/or
commands.
The target command can be enabled with a a custom piece of business logic on a lambda data connector, or a native
mutation operation.
### Using the DDN CLI
The DDN CLI and your data connectors will detect many relationships in your data sources automatically, for instance
from foreign keys in a relational database, and once introspected, you can add them to your metadata.
```bash title="Introspect your data source:"
ddn connector introspect
```
```bash title="Show the found relationships:"
ddn connector show-resources
```
```bash title="Add a relationship to your metadata:"
ddn relationship add
```
Or optionally add all relationships found for a connector at once:
```bash
ddn relationship add "*"
```
:::info Context for CLI commands
Note that the above CLI commands work without also adding the relevant subgraph to the command with the `--subgraph`
flag because this has been set in the CLI context. You can learn more about creating and switching contexts in the
[CLI context](/) section. {/* TODO: Add link */}
:::
### Manually creating a relationship
Relationships can also be manually added to your metadata.
The [VS Code extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) can help you to author
relationships.
For example, you can configure a relationship so that you can also get Orders when querying a Customer.
```yaml title="Create a relationship in your metadata:"
---
kind: Relationship
version: v1
definition:
sourceType: Customers # The existing source object type which also defines the model
name: orders # A name we want to use when we query the Orders from the Customer
target:
model: # The target can be a model or a command
name: Orders # The existing model that we want to access when we query the Orders from the Customer
relationshipType: Array # The relationship type which can be Object or Array. Since a customer can have many orders, we use an Array.
mapping: # The mapping defines which field on the source object type maps to which field on the target model
- source:
fieldPath:
- fieldName: customerId # The existing field on the source object type that we want to map to the target model
target:
modelField:
- fieldName: customerId # The existing field on the target model that we want to map to the source object type
```
By defining this `Relationship` object, all other [models](/reference/metadata-reference/models.mdx) or
[commands](/reference/metadata-reference/commands.mdx) whose output type is the source object type in the relationship
will now have a connection to the target model or command.
Learn more about the [Relationship](/reference/metadata-reference/relationships.mdx) object.
## Update a relationship
Your underlying data source may change over time. You can update your relationship to reflect these changes.
If you have an automatically detected relationship and a property on the source object type has changed, you can update
the relationship to reflect this change.
First, update your connector configuration and models.
```bash title="Update your source introspection:"
ddn connector introspect
```
```bash title="Then, update your model:"
ddn model update
```
Now, you can either delete the existing `Relationship` object and use the DDN CLI to add it again:
```bash title="Delete your existing relationship manually and add it again:"
ddn relationship add
```
Or you can update the `Relationship` object manually. Learn more about the
[Relationship](/reference/metadata-reference/relationships.mdx) object.
## Delete a relationship
If you no longer need a relationship, simply delete the `Relationship` metadata object manually. It is fully
self-contained.
## Tutorials
These tutorials follow on from the tutorials in the
[How to Build with DDN section](/how-to-build-with-ddn/overview.mdx).
### Creating a relationship
#### Between a model and a model
Following on from the [PostgreSQL tutorial in the How to Build with DDN](/how-to-build-with-ddn/with-postgresql.mdx)
section, you can create a relationship between two models which already have a foreign key relationship defined in the
database.
In the tutorial, we created a `posts` to `users` relationship. Let's now add the inverse, detected automatically by the
same foreign key as a relationship between the `Users` to `Posts` models.
```bash title="View the available resources from the data connector:"
ddn connector show-resources my_pg
```
Add the users realtionship which was found automatically due to the foreign key.
```bash title="Add the relationship:"
ddn relationship add my_pg users
```
Now, you can build your supergraph API, serve it, and query your data.
```bash title="Build your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now you can query your data with your relationship in the console get users and their posts.
```bash title="Query your data:"
ddn console --local
```
```graphql
query UsersWithPosts {
users {
id
posts {
title
}
name
}
}
```
```json
{
"data": {
"users": [
{
"id": 1,
"name": "Alice"
"posts": [
{
"title": "My First Post"
},
{
"title": "Another Post"
}
],
},
{
"id": 2,
"name": "Bob"
"posts": [
{
"title": "Bob's Post"
}
],
},
{
"id": 3,
"name": "Charlie"
"posts": [
{
"title": "Hello World"
}
],
}
]
}
}
```
Following on from the [MongoDB tutorial in the How to Build with DDN](/how-to-build-with-ddn/with-mongodb.mdx) section,
you can manually create a relationship between two models which have no foreign-key-like relationship defined in the
database which the data connector would be able to detect.
In the tutorial, we created a `posts` to `users` relationship. Let's now add the inverse as a relationship from the
`Users` to `Posts` models.
```yaml title="Open the Users.hml file and add the following to the end:"
---
kind: Relationship
version: v1
definition:
name: posts
sourceType: Users
target:
model:
name: Posts
relationshipType: Array
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: userId
```
Now you can build your supergraph API, serve it, and query your data.
```bash title="Build and run your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now you can query your data with your relationship in the console get users and their posts.
```bash title="Query your data:"
ddn console --local
```
```graphql
query UsersWithPosts {
users {
name
age
posts {
id
content
title
}
}
}
```
```json
{
"data": {
"users": [
{
"name": "Alice",
"age": 25,
"posts": [
{
"id": "67585252ee01950f5dfc0421",
"content": "This is Alice's first post.",
"title": "My First Post"
},
{
"id": "67585252ee01950f5dfc0422",
"content": "Alice writes again!",
"title": "Another Post"
}
]
},
{
"name": "Bob",
"age": 30,
"posts": [
{
"id": "67585252ee01950f5dfc0423",
"content": "Bob shares his thoughts.",
"title": "Bob's Post"
}
]
},
{
"name": "Charlie",
"age": 35,
"posts": [
{
"id": "67585252ee01950f5dfc0424",
"content": "Charlie joins the conversation.",
"title": "Hello World"
}
]
}
]
}
}
```
Following on from the [ClickHouse tutorial in the How to Build with DDN](/how-to-build-with-ddn/with-clickhouse.mdx)
section, you can manually create a relationship between two models which have no foreign-key-like relationship defined
in the database which the data connector would be able to detect.
In the tutorial, we created a `posts` to `users` relationship. Let's now add the inverse as a relationship from the
`Users` to `Posts` models.
```yaml title="Open the Users.hml file and add the following to the end:"
---
kind: Relationship
version: v1
definition:
name: posts
sourceType: Users
target:
model:
name: Posts
relationshipType: Array
mapping:
- source:
fieldPath:
- fieldName: userId
target:
modelField:
- fieldName: userId
```
Now you can build your supergraph API, serve it, and query your data.
```bash title="Build and run your supergraph API:"
ddn supergraph build local
ddn run docker-start
```
Now you can query your data with your relationship in the console get users and their posts.
```bash title="Query your data:"
ddn console --local
```
```graphql
query UsersWithPosts {
users {
name
age
posts {
content
title
userId
}
}
}
```
```json
{
"data": {
"users": [
{
"name": "Alice",
"age": 25,
"posts": [
{
"content": "This is Alice's first post.",
"title": "My First Post",
"userId": 1
},
{
"content": "Alice writes again!",
"title": "Another Post",
"userId": 1
}
]
},
{
"name": "Bob",
"age": 30,
"posts": [
{
"content": "Bob shares his thoughts.",
"title": "Bob's Post",
"userId": 2
}
]
},
{
"name": "Charlie",
"age": 35,
"posts": [
{
"content": "Charlie joins the conversation.",
"title": "Hello World",
"userId": 3
}
]
}
]
}
}
```
#### Between a model and a command
We will use a business logic function defined in a lambda data connector to build a model to command relationship for
all connector types.
Create a custom function with the TypeScript lambda data connector to use in the tutorials
Initialize a new data connector with the TypeScript connector.
```bash title="Run the following command in your DDN project directory:"
ddn connector init my_ts -i
```
- Select `hasura/nodejs` from the list of connectors.
- Accept the suggested port.
- Edit the `functions.ts` file in the connector directory with the `shoutName` function.
```ts
export function shoutName(name: string) {
return `${name.toUpperCase()}`;
}
```
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
Then, we can add the model made available from the introspection.
```bash title="Track the function:"
ddn command add my_ts shoutName
```
To create a relationship between the `Users` model and the `shoutName` function (see the drop-down above for how to
implement this function) we can create the following relationship. Using the
[VS Code extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) makes the authoring and
validation of the relationship easier.
```yaml title="Create a relationship:"
---
kind: Relationship
version: v1
definition:
name: shoutName # Define a name to expose in the supergraph API
sourceType: Users # The existing source object type (which also defines the source model Users)
target:
command: # The target is a command
name: ShoutName # The name of the existing command we have defined in metadata
subgraph: app # The existing subgraph the command is defined in
mapping:
- source:
fieldPath:
- fieldName: name # The field on the source object type that we want to provide to the target command as an argument
target:
argument:
argumentName: name # The name of the argument on the target command that we want to map to the source field
```
```bash title="Create a build of your supergraph API:"
ddn supergraph build local
```
```bash title="Serve your build as your API locally:"
ddn run docker-start
ddn console --local
```
We can now query the `Users` model and also return the result of the `shoutName` function with its default argument of
`name`.
```graphql title="Query the Users model:"
query {
users {
name
shoutName
}
}
```
```json title="Result:"
{
"data": {
"users": [
{
"name": "Alice",
"shoutName": "ALICE"
},
{
"name": "Bob",
"shoutName": "BOB"
},
{
"name": "Charlie",
"shoutName": "CHARLIE"
}
]
}
}
```
With relationships and custom commands we can transform or enrich any data.
### Updating a relationship
Your underlying data source may change over time. You can update your relationship to reflect these changes.
If you have an automatically detected relationship and a property on the source object type has changed, you can update
the relationship to reflect this change.
First, update your connector configuration and models.
```bash title="Update your source introspection:"
ddn connector introspect
```
```bash title="Then, update your model:"
ddn model update
```
Now, you can either delete the existing `Relationship` object and use the DDN CLI to add it again:
```bash title="Delete your existing relationship manually and add it again:"
ddn relationship add
```
Or you can update the `Relationship` object manually. Learn more about the
[Relationship](/reference/metadata-reference/relationships.mdx) object.
### Deleting a relationship
If you no longer need a relationship, simply delete the `Relationship` metadata object manually. It is fully
self-contained.
## Reference
You can learn more about relationships in the metadata reference
[docs](/reference/metadata-reference/relationships.mdx).
==============================
# permissions.mdx
URL: https://hasura.io/docs/3.0/docs/data-modeling/permissions
# Permissions Protect Data
## Introduction
Permissions keep data secure by allowing you to control what can be accessed in your API by which user roles.
When an authentication mode is enabled, the Hasura engine will look for session variables on every API request, it can
then use permissions defined in metadata and the session variables to determine if the request is allowed to proceed.
## Lifecycle
Hasura DDN uses Role Based Access Control (RBAC) to determine which user roles can access which data.
The DDN CLI will automatically create permissions for your models and commands when they are added to your metadata for
only the `admin` role by default.
All other permissions for all other user roles must be added manually.
## Create permissions
### Row access
You can create a `ModelPermission` object to implement row-level security and restrict which rows a user can access.
For example, to only allow users to access their own records in the `Users` table:
```yaml title=""
---
# e.g., Users.hml
kind: ModelPermissions
version: v1
definition:
modelName: Users
permissions:
# admin is present by default
- role: admin
select:
filter: null
#highlight-start
- role: user
select:
filter:
fieldComparison:
field: id
operator: _eq
value:
sessionVariable: x-hasura-user-id
#highlight-end
```
The highlighted role above will filter responses from the `Users` field in your API to only those whose `id` matches the
`x-hasura-user-id` passed in the header of the request.
### Field access
To restrict which fields can be queried, you can create a `TypePermission` object.
Below, the user role can only access the `name` field, not the `id` field which the admin role can.
```yaml title="The user role can only access their name field:"
# e.g., Users.hml
---
kind: TypePermissions
version: v1
definition:
typeName: Users
permissions:
# admin is present by default
- role: admin
output:
allowedFields:
- id
- name
#highlight-start
- role: user
output:
allowedFields:
- name
#highlight-end
```
### Command (mutation) access
To determine commands can be executed by which roles, you can create a `CommandPermission` object.
```yaml title="In this example, we'll make it so a user can update their own record:"
# e.g., UpdateUsersById.hml
---
kind: CommandPermissions
version: v1
definition:
commandName: UpdateUsersById # Specify the existing command
permissions:
- role: admin
allowExecution: true
#highlight-start
- role: user
allowExecution: true
argumentPresets: # Specify the arguments and their values which need to be passed to the command
- argument: keyId
value:
sessionVariable: "x-hasura-user-id" # The value of the argument must equal the session variable
#highlight-end
```
## Update permissions
Since all permissions are stored in metadata, you can use your text editor to find and update them easily.
For example, to check everything which the `user` role can access, search for `- role: user` and analyze the results.
## Deleting permissions
If you no longer need a role, find all mentions of it in your metadata and remove them all.
If you no longer need a particular permission, simply remove it from the relevant `ModelPermissions`, `TypePermissions`,
or `CommandPermissions` object.
## Reference
You can learn more about permissions in the metadata reference [docs](/reference/metadata-reference/permissions.mdx).
==============================
# Global IDs
URL: https://hasura.io/docs/3.0/docs/graphql-api/global-ids
## Introduction
A Global ID is a unique identifier for an object across the entire application, not just within a specific table or
type. Think of it as an `id` which you can use to fetch any object in your entire supergraph directly, regardless of
what kind of object it is. This is different from typical database IDs, which are often guaranteed unique only within a
particular table.
Hasura's Global ID implementation can be used to provide options for GraphQL clients, such as
[Relay](https://relay.dev/), to elegantly handle caching and data re-fetching in a predictable and standardized way.
The Global ID generated by Hasura DDN follows the
[GraphQL Global Object Identification spec](https://graphql.org/learn/global-object-identification/).
## Using the Global ID
As the example below shows, our users have Global IDs enabled and we can request back the `id` Global ID field for any
particular user. Then, this `id` can be used to fetch this user directly using a `node` query.
For the following GraphQL request on a model which has enabled Global IDs:
```graphql
{
userById(user_id: 1) {
id # This is the global ID of the object in the supergraph
user_id # This is the unique identifier of object. Eg: a row in a table.
name
}
}
```
The response obtained should look like the following:
```json
{
"data": {
"userById": {
"id": "eyJ2ZXJzaW9uIjoxLCJ0eXBlbmFtZSI6IlVzZXJzIiwiaWQiOnsidXNlcl9pZCI6IjEifX0=",
"user_id": 1,
"name": "Bob"
}
}
}
```
**Global ID vs Unique Identifier**
- `user_id`: In this example, this is the unique identifier of the object. Eg: a row in a table.
- `id`: This is the global ID of the object in the graph. It is unique across the entire supergraph. It must be named
`id` to conform with the GraphQL spec.
To enable Global IDs on a Model which already have an `id` field, you will need to remap the existing `id` field to
another name. [See here](#globalid-remap-id-field)
Now, with the Global ID received above, the `User` object corresponding to `user_id: 1` can be retrieved, as shown
below.
```graphql
{
node(id: "eyJ2ZXJzaW9uIjoxLCJ0eXBlbmFtZSI6IlVzZXJzIiwiaWQiOnsidXNlcl9pZCI6IjEifX0=") {
id
__typename
... on User {
name
}
}
}
```
The response to the above request should identify the `User` with `user_id: 1`.
```json
{
"node": {
"id": "eyJ2ZXJzaW9uIjoxLCJ0eXBlbmFtZSI6IlVzZXJzIiwiaWQiOnsidXNlcl9pZCI6IjEifX0=",
"__typename": "User",
"name": "Bob"
}
}
```
:::info Global ID format
The Global ID is a base64 encoded string. Eg:
```text
eyJ2ZXJzaW9uIjoxLCJ0eXBlbmFtZSI6IlVzZXJzIiwiaWQiOnsidXNlcl9pZCI6IjEifX0=
```
Is decoded as the following:
```string
{"version":1,"typename":"Users","id":{"user_id":"1"}}
```
This strategy guarantees that the Global ID is unique across the entire supergraph.
:::
## Enabling Global ID in metadata
You will need to edit a minimum of two objects in metadata to enable Global ID:
1. In the `ObjectType` `definition`, specify which field, or set of fields, should be used as the source(s) to create
the Global ID . The following example uses a `user_id` field to create it:
```yaml
globalIdFields: [user_id]
```
See more in the [ObjectType definition](/reference/metadata-reference/types.mdx#objecttype-objecttypev1).
2. Then add the following to the `definition` section of the `Model`:
```yaml
globalIdSource: true
```
See more in the [Model definition](/reference/metadata-reference/models.mdx#model-modelv1).
## Enabling Global ID for Models which already have an `id` field {#globalid-remap-id-field}
It's a common occurrence to have an existing field in your `ObjectType` named `id`. Since the GraphQL spec mandates that
the `id` field should be for the Global ID, you will need to remap the existing `id` field to a different field name.
With the help of the Hasura VS Code extension and the output errors from the metadata build service in the Hasura CLI,
you can easily determine which fields need to be remapped.
In the ObjectType definition, you can remap the `id` field to a different field name `user_id` for example, as shown
below:
```yaml {5,7-8,18-20}
kind: ObjectType
version: v1
definition:
name: Users
globalIdFields: [user_id]
fields:
- name: user_id
type: Uuid!
- name: name
type: Text!
graphql:
typeName: Users
inputTypeName: UsersInput
dataConnectorTypeMapping:
- dataConnectorName: postgres_connector
dataConnectorObjectType: users
fieldMapping:
user_id:
column:
name: id
name:
column:
name: name
```
You will then also need to update the `Model`, `TypePermissions` and `Relationships` definitions and anywhere else where
the previous identifier of `id` for the `User` model was used.
You will also need to edit the `target` metadata for `Relationships` in other Models too so that they reference the new
identifier of `user_id`.
As mentioned, the Hasura VS Code extension and the output errors from the metadata build service in the Hasura CLI will
help you find all the places where the `id` field needs to be remapped.
==============================
# API Versioning through Field Deprecation
URL: https://hasura.io/docs/3.0/docs/graphql-api/versioning
# API Versioning through Field Deprecation
## Introduction
As your API grows and adapts to new features or functionality, you might need to adjust its structure. But changing or
removing fields in API responses can cause breaking changes. These changes may disrupt client applications that depend
on those fields, leading to errors or unexpected behavior.
Hasura supports the `@deprecated` directive in GraphQL, making it straightforward to mark fields as deprecated. You can
include an optional reason to help explain the change, signaling to consumers which fields are outdated or updated.
As an example, imagine we have a type `Car` with an existing field named `engine` which is deprecated in favor of
`motor`:
```graphql
type Car {
id: ID!
make: String!
model: String!
engine: EngineSpec @deprecated(reason: "Use field 'motor' instead")
motor: MotorSpec
}
```
Consumers of our API will know that `engine` has been deprecated, _why_ we've deprecated it, and which field to use in
its place.
:::info Learn more
You can learn more about this directive in [the spec](https://spec.graphql.org/October2021/#sec-Field-Deprecation).
:::
## Using field deprecation in DDN
The following metadata objects have field deprecation in their GraphQL configuration.
### Model
The following example shows how to deprecate a field in a [model](/reference/metadata-reference/models.mdx):
```yaml
kind: Model
version: V1
definition:
name: Cars
objectType: Car
orderableFields:
- Id
graphql:
selectUniques:
- queryRootField: selectCar
uniqueIdentifier:
- make
- model
deprecated:
reason: Use selectCarById instead
- queryRootField: selectCarById
uniqueIdentifier:
- Id
```
And the resulting schema:
```graphql
type Query {
selectCar(make: String!, model: String): Car @deprecated(reason: "use selectCarById instead")
selectCarById(Id: ID!): Car
}
```
### Command
The following example shows how to deprecate a field in a [command](/reference/metadata-reference/commands.mdx):
```yaml
kind: Command
version: V1
definition:
name: GetEngineSpec
outputType: Engine
graphql:
rootFieldName: getEngineSpec
rootFieldKind: Query
deprecated:
reason: "Fuel Engines are no longer supported from Jan 01 2035"
```
And the resulting schema:
```graphql
type Query {
getEngineSpec: Engine @deprecated(reason: "Fuel Engines are no longer supported from Jan 01 2035")
}
```
### ObjectType
The following example shows how to deprecate a field in an
[ObjectType](/reference/metadata-reference/types.mdx#objecttype-objecttype):
```yaml
kind: ObjectType
version: V1
definition:
name: Car
fields:
- name: Id
type: String
- name: make
type: String
- name: model
type: String
- name: engine
type: String
deprecated:
reason: Use motor field instead
- name: motor
type: String
graphql:
typeName: Car
```
And the resulting schema:
```graphql
type Car {
Id: String
make: String
model: String
engine: String @deprecated(reason: "Use motor field instead")
motor: String
}
```
### Relationship
The following example shows how to deprecate a field in a
[relationship](/reference/metadata-reference/relationships.mdx):
```yaml
kind: Relationship
version: V1
definition:
name: engineSpec
source: Car
target:
command:
name: GetEngineSpec
mapping:
- source:
fieldPath:
- fieldName: engine
target:
argument:
argumentName: name
deprecated:
reason: Engines on cars are no longer supported from Jan 01 2035
```
And the resulting schema:
```graphql
type Car {
Id: String
make: String
model: String
engine: String @deprecated(reason: "Use motor field instead")
motor: String
engineSpec: Engine @deprecated(reason: "Engines on cars are no longer supported from Jan 01 2035")
motorSpec: Motor
}
```
## Default deprecation reason
In cases where no deprecation reason is explicitly provided, the `@deprecated` directive defaults to
`No longer supported` as the reason.
The following example from [ObjectType](/reference/metadata-reference/types.mdx#objecttype-objecttype) metadata
illustrates deprecating a field without specifying a reason.
```yaml
kind: ObjectType
version: V1
definition:
name: Car
fields:
- name: Id
type: String
- name: make
type: String
- name: model
type: String
- name: engine
type: String
deprecated:
reason: null
- name: motor
type: String
graphql:
typeName: Car
```
And the resulting schema:
```graphql
type Car {
Id: String
model: String
engine: String @deprecated(reason: "No longer supported")
motor: String
}
```
==============================
# Apollo Federation
URL: https://hasura.io/docs/3.0/docs/graphql-api/apollo-federation
## Introduction
Hasura DDN itself can be used as a subgraph in a supergraph created by
[Apollo Federation](https://www.apollographql.com/docs/federation/). This page is to help you understand how to use
Hasura in conjunction with an existing Apollo supergraph. Alternatively, if you're looking for a guide on how to build a
federated Hasura DDN supergraph, check out our [getting started](/how-to-build-with-ddn/overview.mdx) guide.
Apollo Federation is a way to compose multiple GraphQL services (called subgraphs) into a unified API (called a
supergraph).
:::info Supergraphs and Subgraph terminology in Hasura DDN and Apollo Federation
Some of the naming used in Apollo Federation conflicts with the same names used in Hasura. Here is a quick glossary to
help you understand the terms better:
| Term | Hasura | Apollo Federation |
| ---------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| Subgraph | A subgraph in Hasura is the notion of a module of Hasura supergraph metadata. | A subgraph in Apollo Federation is a **standalone GraphQL service.** |
| Supergraph | A supergraph in Hasura is the collection of subgraph metadata and the resultant GraphQL API that is created. | A supergraph in Apollo Federation is a unified GraphQL API that is created by stitching multiple subgraph APIs. |
:::
## Using DDN as a subgraph
Hasura DDN is compliant with the
[Apollo Federation subgraph specification](https://www.apollographql.com/docs/federation/subgraph-spec/), so you can
plug Hasura DDN in as a subgraph in your Apollo federated supergraph.
```mermaid
graph LR;
clients(Clients);
router([Apollo Supergraph Router]);
serviceA[GraphQL API Subgraph A];
serviceB[Hasura DDN Subgraph B];
router --- serviceA & serviceB;
clients -.- router;
class clients secondary;
```
## Enabling Apollo Federation fields in Hasura DDN metadata
You will need to edit the
[`GraphqlConfig`](/reference/metadata-reference/graphql-config.mdx#graphqlconfig-graphqlconfig) for the supergraph to
enable the fields required for schema stitching by the Apollo supergraph router. You will have to add the following in
the `definition` for `GraphqlConfig` (usually in `supergraph/graphql-config.hml` file):
```yaml
apolloFederation:
enableRootFields: true
```
An example of the `graphql-config.hml` file with apollo federation fields enabled:
```yaml {8-9}
kind: GraphqlConfig
version: v1
definition:
query:
rootOperationTypeName: Query
mutation:
rootOperationTypeName: Mutation
apolloFederation:
enableRootFields: true
```
## Marking a Type as an Apollo Entity
Types defined in Hasura DDN can also be marked as
[Apollo Entities](https://www.apollographql.com/docs/federation/entities/). This will allow the same type to be resolved
from DDN (and your other subgraphs that defines the type) as well.
To mark a type as an entity, you will have to edit the metadata for the `ObjectType` and the `Model` which will be used
to resolve the type.
1. Add the keys for the `ObjectType`. The [keys](https://www.apollographql.com/docs/federation/entities/#1-define-a-key)
can be defined as following in the
[`definition.graphql`](/reference/metadata-reference/types#objecttype-objecttypegraphqlconfiguration) for
`ObjectType`:
```yaml
apolloFederation:
keys: # The fields that uniquely identifies the entity
- fields:
- id
```
2. Mark the `Model` that should act as the source for the entity. This can be done by adding the following in the
[`definition.graphql`](/reference/metadata-reference/models#model-modelgraphqldefinition) for `Model`:
```yaml
apolloFederation:
entitySource: true
```
:::info Single direction Federation support
Apollo Federation support in DDN only allows extending Apollo subgraphs with DDN types.
The other way i.e. extending DDN types with other Apollo subgraphs is not currently possible.
Consider the following configuration:
The `Review` type defined in DDN:
```graphql
type Review {
id: ID!
productId: ID!
rating: Int!
comment: String
}
```
The `Product` type defined in an Apollo subgraph:
```graphql
type Product @key(fields: "id") {
id: ID!
name: String!
}
```
In this example we cannot extend the `Review` type in DDN with the `Product` type from another Apollo subgraph.
:::
==============================
# GraphQL API Errors
URL: https://hasura.io/docs/3.0/docs/graphql-api/errors
# GraphQL API Errors
## Introduction
The GraphQL API returns an error response when the requested operation fails to execute successfully. Hasura provides
the error responses that adhere to the [GraphQL spec](https://spec.graphql.org/October2021/#sec-Errors). Each error
object contains only the `message` field, providing a string description of the error intended for the API consumer.
:::note
Note that in future iterations, the error object could be expanded with additional information, such as path and error
code.
:::
Below is an example of an error response triggered by using a negative value as the `limit` argument:
```json
{
"data": null,
"errors": [
{
"message": "Unexpected value: expecting NON-NEGATIVE 32-bit INT, but found: -10"
}
]
}
```
:::info Error status code
GraphQL API errors will always be returned with a `200 OK` status code. The status code is not indicative of the success
or failure of the operation. The `errors` array in the response object should be checked to determine the success or
failure of the operation. Most GraphQL clients will automatically handle this for you.
:::
## Internal Errors
Internal errors are unexpected exceptions **within** the API execution cycle. These often originate from system-level
issues such as database inconsistencies or network disruptions which lie beyond the control and scope of the endpoint
API consumer.
GraphQL API responses for internal errors do not contain detailed error messages, as API consumers lack, and should not
have, the necessary context to resolve such issues. Internal errors may contain sensitive details not meant to be
exposed due to privacy and security concerns. Therefore, the API response for internal errors is simplified to only
include the message `internal error`.
Example:
```json
{
"data": null,
"errors": [
{
"message": "internal error"
}
]
}
```
However as a Hasura supergraph author, you have access to details of most internal errors, including but not limited to:
- [DDN metadata](/reference/metadata-reference/index.mdx) errors:
- Such as the absence of a data source for model or command root fields or arguments.
- The omission of required session variables in the request.
- [Data connector](/data-sources/overview.mdx) interactions:
- These are useful in debugging connection or interface errors with your data connectors.
The details are available through [traces](/observability/built-in/traces). It is recommended to review all error spans
within the trace to obtain details of the internal error.
If the trace still does not reveal the details of the error, it may originate from Hasura internals. In such cases,
please reach out to [Hasura support](https://hasura.io/help/) with the trace ID.
==============================
# GraphQL Schema Diff
URL: https://hasura.io/docs/3.0/docs/graphql-api/graphql-schema-diff
# GraphQL Schema Diff
When you are managing a GraphQL API, you often need to make changes to the schema. These changes could be adding new
fields, removing existing fields, or changing the types of fields. When you make these changes, you need to understand
the impact of these changes on the existing queries and mutations. The schema diff feature helps you understand these
impacts by comparing the schemas of two supergraph builds.
## Using the GraphQL Schema Diff feature in Console
1. Go to [DDN console](https://console.hasura.io) and select a project. (Make sure you have a project with at least two
supergraph builds if you don't have two supergraph builds, head to [Getting Started](/quickstart.mdx) to create
builds to get started).
2. Select a build that you want to compare from the **Builds** header toolbar.
3. In the DDN project console, navigate to the **Builds** section from the sidebar
4. In the **Builds** section, navigate to the **Schema Diff** tab.
5. In the **Schema Diff** tab, you can select two supergraph builds to compare their schemas.
6. You can see the differences between the two schemas in the **Schema Diff** tab.
## Reference and Compare Builds
Imagine that you already have supergraph build **A** applied to your Hasura DDN project and you want to see the impact
of applying a new supergraph build **B**. You can select build **A** as the `Reference Build` and build B as the
`Compare Build` to compare the schemas.
Which means if there are fields added in build **B** without removing any fields from build **A**, the schema diff will
show these as safe changes and list them in the `Safe` section. If there are fields removed in build **B**, the schema
diff will show these as breaking changes and list them in the `Breaking` section.
## Schema Diff Analysis
The schema diff analysis in Hasura DDN is divided into three sections: `Breaking`, `Dangerous`, and `Safe`.
---
### Breaking Changes
**Definition:** Breaking changes are modifications that can disrupt existing GraphQL operations such as queries,
mutations, or subscriptions. These changes typically involve removing or renaming fields, types, or arguments.
**Examples:**
1. **Field Removal:**
- **Source Build:**
```graphql
type Query {
sales_hello: String
}
```
- **Target Build:**
```graphql
type Query {
// sales_hello field is removed
}
```
- **Impact:** Any client queries requesting `sales_hello` will fail.
2. **Type Removal:**
- **Source Build:**
```graphql
type User {
id: ID!
name: String!
}
```
- **Target Build:**
```graphql
// User type is removed
```
- **Impact:** Any client queries or mutations involving the `User` type will fail.
---
### Dangerous Changes
**Definition:** Dangerous changes are modifications that can potentially alter the behavior of existing GraphQL
operations without necessarily breaking them. These changes include modifications to field types, arguments, or default
values.
**Examples:**
1. **Field Type Change:**
- **Source Build:**
```graphql
type Query {
sales_count: Int
}
```
- **Target Build:**
```graphql
type Query {
sales_count: Float
}
```
- **Impact:** Queries that expect an `Int` return type might misinterpret the `Float` value, potentially causing
issues in client-side logic.
2. **Argument Default Value Change:**
- **Source Build:**
```graphql
type Query {
products(limit: Int = 10): [Product]
}
```
- **Target Build:**
```graphql
type Query {
products(limit: Int = 20): [Product]
}
```
- **Impact:** Queries relying on the default value of `limit` will get more results than expected.
---
### Safe Changes
**Definition:** Safe changes are modifications that do not disrupt existing GraphQL operations. These changes typically
involve adding new fields, types, or arguments, enhancing the schema without affecting current functionality.
**Examples:**
1. **Field Addition:**
- **Source Build:**
```graphql
type Query {
sales_total: Float
}
```
- **Target Build:**
```graphql
type Query {
sales_total: Float
sales_average: Float // New field added
}
```
- **Impact:** Existing queries remain unaffected, and new queries can utilize the `sales_average` field.
2. **Type Addition:**
- **Source Build:**
```graphql
type Query {
product(id: ID!): Product
}
```
- **Target Build:**
```graphql
type Query {
product(id: ID!): Product
category(id: ID!): Category // New type and query added
}
```
- **Impact:** Existing queries remain unaffected, and new queries can utilize the `category` query.
==============================
# response-size-limit.mdx
URL: https://hasura.io/docs/3.0/docs/graphql-api/response-size-limit
# Connector Response Size Limit
The maximum size for responses from connectors is **30 MB**. Beyond this threshold, Hasura will reject the response to
ensure optimum performance and data processing. It's important to be mindful of this constraint when making data queries
to Hasura's [GraphQL API](/graphql-api/overview/).
To prevent hitting the response size limit, API consumers are encouraged to utilize the
[limit argument](/graphql-api/queries/pagination#limit-results) in their queries to avoid over-fetching data from
sources via data connectors.
When GraphQL API requests exceed this size limit, they will result in an
[internal error](/graphql-api/errors#internal-errors) API response.
```json
{
"data": null,
"errors": [
{
"message": "internal error"
}
]
}
```
Hasura users are advised to check the traces for more detailed error information, which includes the actual response
size from the connector.
:::note Response size limit increases
If you require an increase in the response size limit, please reach out to [Hasura support](https://hasura.io/help/) for
assistance.
:::
==============================
# JSON:API Overview
URL: https://hasura.io/docs/3.0/docs/json-api/overview
# Basics
## Introduction
Hasura DDN provides a JSON:API-compliant interface as an alternative to [GraphQL](/graphql-api/overview.mdx) for
accessing your data. This implementation adheres to the [JSON:API specification](https://jsonapi.org), offering a
standardized REST-based approach to interact with your resources.
When you connect a source to Hasura DDN using a [data connector](/data-sources/overview.mdx) and generate Hasura
metadata, the source becomes accessible through both GraphQL and JSON:API interfaces. Your Hasura metadata lets you
control how the data is exposed and interacted with across both APIs.
JSON:API endpoints are automatically generated from your data models, providing a seamless way to interact with your
data without additional configuration. These endpoints use the same permission system as your GraphQL API, ensuring
unified security across both interfaces. Hasura's optimization engine enhances performance, making queries efficient and
scalable while maintaining a consistent experience. Since JSON:API support is built directly into Hasura DDN, it works
out-of-the-box with your existing setup.
:::warning Alpha release
The JSON:API implementation in Hasura DDN is currently in alpha. While we aim to follow the JSON:API specification, some
features may be incomplete or differ from the standard. Check the documentation for the latest supported features.
:::
## Learn more
- [Explore the JSON:API schema](/json-api/schema.mdx)
- [Query your data with JSON:API](/json-api/queries/index.mdx)
- [Filter query results](/json-api/queries/filters.mdx)
- [Include relationships](/json-api/queries/relationships.mdx)
- [Sort query results](/json-api/queries/sorting.mdx)
- [Paginate through results](/json-api/queries/pagination.mdx)
==============================
# JSON:API Error Responses
URL: https://hasura.io/docs/3.0/docs/json-api/errors
# JSON:API Error Responses
## Introduction
When a request to the JSON:API endpoint fails, Hasura returns error responses that conform to the
[JSON:API specification](https://jsonapi.org/format/#errors). Each error response includes appropriate HTTP status codes
and a structured error document.
## Error format
Error responses are returned as JSON objects containing an `errors` array.
```json title="Each error object in the array a status code and message:"
{
"errors": [
{
"status": "404",
"detail": "invalid route or path"
}
]
}
```
| Parameter | Description | Example |
| --------- | ------------------------------------------------------------ | ---------------- |
| `status` | The HTTP status code applicable to this error (as a string). | `"418"` |
| `detail` | A human-readable explanation of the error. | `"I'm a teapot"` |
## Common status codes
The API uses [standard HTTP status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to indicate the type
of error:
- `400 Bad Request`: Invalid request syntax or parameters
- `401 Unauthorized`: Missing or invalid authentication
- `403 Forbidden`: Valid authentication but insufficient permissions
- `404 Not Found`: Requested resource doesn't exist
- `500 Internal Server Error`: Unexpected server-side error
## Error Types
### Request Errors
```json title="Request errors occur when the client sends an invalid request. E.g.:"
{
"errors": [
{
"status": "400",
"detail": "Invalid filter parameter: unknown operator 'contains'"
}
]
}
```
### Permission Errors
```json title="When a user attempts to access a resource without proper permissions:"
{
"errors": [
{
"status": "403",
"detail": "Access forbidden"
}
]
}
```
### Internal Errors
```json title="For security reasons, internal errors return a generic message without exposing implementation details:"
{
"errors": [
{
"status": "500",
"detail": "Internal error"
}
]
}
```
:::info Debugging Errors
Error responses to clients are intentionally generic for security reasons. However, detailed error information is
available in the [request traces](/observability/built-in/traces.mdx) for debugging purposes.
:::
==============================
# JSON:API Schema
URL: https://hasura.io/docs/3.0/docs/json-api/schema
# JSON:API Schema
## Introduction
Hasura DDN provides an [OpenAPI 3.1](https://swagger.io/specification/) schema endpoint for the JSON:API interface,
making it easier to explore, test, and integrate with your API. This schema gives you a complete overview of your API's
structure, including available endpoints, models, and operations.
With the OpenAPI specification, you can generate interactive documentation using tools like Swagger UI or Redoc, making
it simple to explore and understand your API. If you're building a client application, OpenAPI code generators can help
you quickly scaffold client code. For testing, you can import the schema into Postman to validate requests and
responses. Additionally, the schema enables automatic type generation for various programming languages, ensuring type
safety in your applications.
By leveraging this schema, you can streamline API development, debugging, and integration, all while maintaining a
standardized approach to working with JSON:API in Hasura DDN.
## Schema endpoint
```http title="To retrieve the OpenAPI specification for your JSON:API endpoints, send a GET request to:"
GET /v1/jsonapi/__schema
```
The response will be a standard OpenAPI 3.1 specification in JSON format, describing all available endpoints, models,
and operations.
### Authentication
The schema endpoint follows the same authentication rules as your other API endpoints. Make sure to include appropriate
authentication headers as configured in your [authentication settings](/auth/overview.mdx).
### Response format
The response is a JSON document conforming to the OpenAPI 3.1 specification. It includes:
- API information and version
- Available endpoints and operations
- Model schemas and relationships
- Request/response formats
```json title="Example response:"
{
"openapi": "3.1.0",
"info": {
"title": "Hasura JSONAPI (alpha)",
"description": "REST API generated to match the JSON:API spec: https://jsonapi.org",
"version": "0.1"
},
"paths": {
// Available endpoints and operations
},
"components": {
// Reusable components and schemas
}
}
```
## Integration examples
### Swagger UI
To view your API documentation in Swagger UI:
1. Visit [Swagger Editor](https://editor.swagger.io)
2. Copy your schema JSON from the `/__schema` endpoint
3. Paste it into the editor
### Postman
To import your API into Postman:
1. Copy the URL of your schema endpoint
2. In Postman, click "Import" > "OpenAPI"
3. Paste the URL or import the JSON directly
## Learn more
- [Query your data with JSON:API](/json-api/queries/index.mdx)
- [Filter query results](/json-api/queries/filters.mdx)
- [Include relationships](/json-api/queries/relationships.mdx)
- [Sort query results](/json-api/queries/sorting.mdx)
- [Paginate through results](/json-api/queries/pagination.mdx)
==============================
# Authentication and Authorization Overview
URL: https://hasura.io/docs/3.0/docs/auth/overview
# Auth
## Introduction
Hasura is agnostic about how you authenticate users. You can integrate many popular auth services or use your own custom
solution.
After authentication, session variables are passed via either a valid JWT or webhook to the engine to be checked against
your access control rules or "permissions" to determine what data the user can access.
## Private vs Public
You can choose to make your Hasura DDN API public or private. [Read more](/auth/private-vs-public.mdx).
## AuthConfig options
Authentication in Hasura DDN can be set up in one of three modes or multiple modes. These modes and their configuration
options are specified in the `AuthConfig` object within your metadata.
You can configure a single authentication mode or multiple authentication modes using the `alternativeModes` field in
AuthConfig v4. When using multiple authentication modes, you can specify which mode to use for a particular request by
including the `X-Hasura-Auth-Mode` header with the identifier of the desired authentication mode. [Read more about
multiple auth modes](/auth/multiple-auth-modes.mdx).
### JWT mode
Your authentication service must issue JWTs which contain session variables that are passed to the Hasura Engine by the
client on each request. [Read more](/auth/jwt/index.mdx).
### Webhook mode
Hasura Engine will call a webhook on each request with the client headers forwarded. On successful authentication, the
webhook must return a valid `http` response with session variables in the body. [Read more](/auth/webhook/index.mdx).
### NoAuth mode
No authentication is required for a specific role to access the data. [Read more](/auth/noauth-mode.mdx).
==============================
# private-vs-public.mdx
URL: https://hasura.io/docs/3.0/docs/auth/private-vs-public
# Private vs Public
You can choose to make your Hasura DDN API public or private.
## Private
A `private` Hasura DDN API is only accessible to collaborators on your project.
Queries to a private Hasura DDN API must include a special reserved header `x-hasura-ddn-token` with a valid JWT token
which the Hasura console generates and regenerates every hour. Currently this token is only available in the console.
If a `private` API is also set to JWT or Webhook mode, rather than `noAuth` mode, queries must **also** include the JWT
or webhook authentication values to be successful in addition to the `x-hasura-ddn-token` header.
Projects set to `private` mode are not meant to be used in production.
## Public
A public Hasura DDN API is accessible to everyone.
If a `public` API is also set to JWT or Webhook mode, rather than `noAuth` mode, queries must include the JWT or webhook
authentication values to be successful.
Queries to a public Hasura DDN API do not require the `x-hasura-ddn-token` header.
:::danger Public APIs with noAuth mode
If set to `public` with `noAuth` mode, queries do not require any authentication and the API is fully public.
:::
## Changing the API mode
### DDN CLI
Set to `private` mode:
```bash
ddn project set-api-access-mode private
```
Set to `public` mode:
```bash
ddn project set-api-access-mode public
```
### Hasura console
Click on the `Settings` gear icon in the bottom left of the sidebar navigation and then the `Summary` tab to access the
API access mode toggle at `https://console.hasura.io/project//settings/project-summary`.
==============================
# overview.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/overview
# Basics of Business Logic in Hasura DDN
## Introduction
In Hasura DDN, custom business logic is treated as a first-class citizen and like any other data source. This means,
using a **lambda connector**, you can expose a function written in your language of choice return data from it directly
to your GraphQL API.
We treat custom business logic like a data source because it allows for seamless integration into your GraphQL API, just
like databases or other external services. This approach enables you as the API author to unify your data and logic,
ensuring consistent access patterns, security rules, and relationships across all parts of the API.
This custom logic can be used to query or mutate data independently or to extend the functionality of existing
[models](/reference/metadata-reference/models.mdx) in your API. By connecting custom logic to other resources, you can
enhance and expand the capabilities of data from different sources.
Treating custom logic as a data source simplifies your architecture, reduces duplication, and makes your APIs more
flexible and maintainable.
:::info Which languages are supported?
Currently, we have lambda connectors for TypeScript, Python, and Go; these connectors and their custom functions can be
hosted by Hasura or on your own infrastructure.
:::
## Functions and procedures
Regardless of which language you prefer, your connector will generate a
[command](/reference/metadata-reference/commands.mdx) in your metadata for each function. These commands will be
identified in your metadata as either [functions](/reference/metadata-reference/commands.mdx#command-functionname) β for
querying data β or [procedures](/reference/metadata-reference/commands.mdx#command-procedurename) β for modifying data β
via your API.
Each connector has its own conventions for determining if the custom logic you write is identified as either a function
or a procedure.
## Learn more
- [Learn how to add a lambda connector](/business-logic/add-a-lambda-connector.mdx)
- [Learn how to add independent custom logic to your API](/business-logic/tutorials/1-add-custom-logic.mdx)
- [Learn how to add custom logic to an existing model in your API](/business-logic/tutorials/2-extend-a-model.mdx)
:::info What about custom native operations?
If you're curious about native queries and mutations, check out the
[connector-specific reference docs](/reference/connectors/index.mdx) for generating queries and mutations using the
native capabilities of your data source.
:::
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/
# JWT Mode
In JWT mode, session variables are passed to the Hasura Engine on each request in JSON Web Tokens (JWTs).
## JWT mode setup
- [How to set up JWT mode](/auth/jwt/jwt-mode.mdx)
- [JWT configuration information](/auth/jwt/jwt-configuration.mdx)
## Integrations with third-party services
- [Auth0 JWT integration](/auth/jwt/tutorials/integrations/1-auth0.mdx)
- [AWS Cognito JWT integration](/auth/jwt/tutorials/integrations/2-aws-cognito.mdx)
- [Firebase JWT integration](/auth/jwt/tutorials/integrations/3-firebase.mdx)
- [Clerk JWT integration](/auth/jwt/tutorials/integrations/4-clerk.mdx)
==============================
# jwt-mode.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/jwt-mode
# JWT Mode
## Introduction
JWT mode requires that the client making the query sends a valid JSON Web Token to the Hasura Engine endpoint. This JWT
is provided by an auth service such as Auth0, AWS Cognito, Firebase, Clerk, or your own custom solution.
Hasura then verifies and decodes the JWT to extract `x-hasura-*` session variable claim values from a defined namespace
in the token.
The `x-hasura-default-role` and `x-hasura-allowed-roles` session variables are required, and you will also most likely
utilize the user id and any other information which you need to determine access to your data.
The token can be passed in the header of the request in a dedicated key, or using the `Authorization` header with the
`Bearer` prefix, or as a cookie. All these options are defined in the `AuthConfig` object in your metadata.
## Session variable requirements
Session variables passed via JWT or webhook can contain any information you want, but must at least contain an
`x-hasura-default-role` property and `x-hasura-allowed-roles` array.
An `x-hasura-role` value can optionally be sent as a plain header in the request to indicate the role which should be
used. If this is not provided, the engine will use the `x-hasura-default-role` value in the JWT.
To clarify, the `x-hasura-role` header is optional and can be used to override the default role in the JWT allowing the
same verified JWT to be used for different roles.
Only keys prefixed with `x-hasura-` will be accessible by the engine.
Session variable keys are case-insensitive. Values are case-sensitive.
## Enabling JWT authentication
You can enable your Hasura DDN instance to use JWTs in just a few steps.
### Step 1. Update your AuthConfig
Hasura utilizes an [AuthConfig](/reference/metadata-reference/auth-config.mdx) object that allows you to define the
configuration for your authentication service. In a standard setup the `auth-config.hml` file can be found in your
`globals` directory.
:::tip Hasura DDN VS Code extension
You can use [Hasura's VS Code extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) to
scaffold out your `AuthConfig` object by typing `AuthConfig` and selecting this object from the list of available
options. As you navigate through the skeleton, you can type `CTRL+SPACEBAR` at any point to reveal options for the
different key-value pairs.
:::
Below, we're showing using the `BearerAuthorization` header location format using a fixed secret key from an environment
variable. However, Hasura DDN supports other methods for
[where the engine can locate the JWT](reference/metadata-reference/auth-config.mdx#authconfig-jwttokenlocation) and
[how it is verified](reference/metadata-reference/auth-config.mdx#authconfig-jwtalgorithm).
```yaml title="globals/metadata/auth-config.hml"
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: HS256
key:
valueFromEnv: AUTH_SECRET
```
Read more about other setup options [here](/reference/metadata-reference/auth-config.mdx#authconfig-jwtconfig).
### Step 2. Define the JWT with custom claims
Your auth service should include an object with a key of `claims.jwt.hasura.io` in the JWT. Within this, each claim
should be prefixed with `x-hasura-*` and include the relevant information. Note that an extra optional `x-hasura-role`
**header** can be passed to override the default role found in the JWT's custom claims.
| Key | Required | Value |
| ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------- |
| `x-hasura-default-role` | Yes | The role that will be used when the optional x-hasura-role header is not passed |
| `x-hasura-allowed-roles` | Yes | A list of allowed roles for the user making the request. |
| `x-hasura-[custom]` | No | Where `[custom]` is any string you wish (e.g., `org`, `user-id`, `customer`). The value can be any JSON value. |
In the simple example below, we're including the required claims by stating the default role is `admin` and the list of
available roles is limited to `user` and `admin`. Additionally, we're passing a custom key of `x-hasura-user-id` which
can be used with [permissions](/reference/metadata-reference/permissions.mdx) when executing queries.
[Read more about the default claims here](/auth/jwt/jwt-configuration.mdx#hasura-jwt-format).
```json title="Example JWT payload"
{
"iat": 1735916718,
"exp": 1796916677,
"claims.jwt.hasura.io": {
"x-hasura-default-role": "admin",
"x-hasura-allowed-roles": ["user", "admin"],
"x-hasura-user-id": 1234
}
}
```
Your auth service will encode this object using a secret and create a token which can then be passed to Hasura. You can
see an example of the above token encoded
[here](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzU5MTY3MTgsImV4cCI6MTc5NjkxNjY3NywiY2xhaW1zLmp3dC5oYXN1cmEuaW8iOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiYWRtaW4iLCJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbInVzZXIiLCJhZG1pbiJdLCJ4LWhhc3VyYS11c2VyLWlkIjoxMjM0fX0.1Dv6B077loe9wGcUFwK_JAHRUtLOZHOEp9EE-OehUzY).
The signature secret to verify this token with the HS256 algorithm is `ultra-secret-very-secret-super-secret-key`.
:::warning Setting audience check
Certain JWT providers (like Firebase) share JWKs between multiple tenants. They use the `aud` claim of JWT to specify
the intended tenant for the JWT. Setting the `audience` field in the Hasura JWT configuration will make sure that the
`aud` claim from the JWT is also checked during verification. Not doing this check will allow JWTs issued for other
tenants to be valid as well.
In these cases, you **MUST** set the `audience` field to appropriate value. **Failing to do so is a major security
vulnerability**. Learn how to set this [here](reference/metadata-reference/auth-config.mdx#authconfig-jwtconfig).
If your [Compatibility Config date](/reference/metadata-reference/compatibility-config.mdx) is set to `2025-03-11` or
newer, you _must_ set the `audience` field if your JWTs will contain an `aud` claim. If you do not, the JWT will be
rejected as invalid and authentication will fail.
:::
### Step 3. Add permissions to an object in your supergraph
Let's add some example `TypePermissions` so that an admin role can access all fields in the Orders type, but we restrict
a user role from accessing the `deliveryDate` field.
```bash title="Example TypePermissions for Orders type"
---
kind: TypePermissions
version: v1
definition:
typeName: Orders
permissions:
- role: admin
output:
allowedFields:
- createdAt
- deliveryDate
- id
- isReviewed
- productId
- status
- updatedAt
- userId
# highlight-start
- role: user
output:
allowedFields:
- createdAt
- id
- isReviewed
- productId
- status
- updatedAt
- userId
# highlight-end
```
Let's also add some example `ModelPermissions` so that an admin role can access all rows in the Orders model, but a user
role can only access rows where the userId field matches the user id session variable in the JWT.
```bash title="Example ModelPermissions for Orders model"
---
kind: ModelPermissions
version: v1
definition:
modelName: Orders
permissions:
- role: admin
select:
filter: null
allowSubscriptions: true
# highlight-start
- role: user
select:
filter:
fieldComparison:
field: userId
operator: _eq
value:
sessionVariable: x-hasura-user-id
# highlight-end
```
:::info Example JWT payload
For these examples we'll set the payload of the JWT to specify a `user` role and a UUID for the user id.
```json title="Example JWT payload which we will send to the Hasura Engine"
{
"iat": 1735916718,
"exp": 1796916677,
"claims.jwt.hasura.io": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb"
}
}
```
:::
### Step 4. Rebuild your supergraph
Once you've updated your `AuthConfig` object in `auth-config.hml` and updated your claims, you can rebuild your
supergraph and test it locally.
```bash title="For example, from the root of your project, run:"
ddn supergraph build local
```
### Step 5. Make an authenticated request
In the example above, we're using the `BearerAuthorization` method. As such, as we can make a request to our Hasura DDN
instance by including a header with the key-value of `Authorization: Bearer `. For testing, you can
pass this value in the Hasura DDN console's header section.
If we run a query for Orders, we can see that we only get the orders which this user has made and are not able to access
the deliveryDate field.
### Step 6. Set your API to public
Now that you have implemented JWT authentication, you can set your API to public. See here for more information on
[setting your API to public](/auth/private-vs-public.mdx).
## Next steps
If you're looking for step-by-step help to get started with common authentication providers, check
[this section](/auth/jwt/tutorials/index.mdx) of tutorials.
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/
# Business Logic Tutorials
In this section, you'll find tutorials that walk you through implementing custom business logic with lambda connectors
step-by-step. Initially, we recommend checking out two very common use cases:
- [Adding custom logic](/business-logic/tutorials/1-add-custom-logic.mdx)
- [Extending an existing model](/business-logic/tutorials/2-extend-a-model.mdx)
From there, check out these advanced use cases:
- [Formatting datetime objects](/business-logic/tutorials/3-format-datetime-objects.mdx)
- [Hashing passwords](/business-logic/tutorials/3-hash-passwords.mdx)
- [Translating content](/business-logic/tutorials/4-translate-content.mdx)
- [Enriching data with an LLM](/business-logic/tutorials/5-enrich-data-with-an-llm.mdx)
- [Validating credentials](/business-logic/tutorials/6-validate-credentials.mdx)
- [HTTP header forwarding](/business-logic/tutorials/7-http-header-forwarding.mdx)
==============================
# jwt-configuration.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/jwt-configuration
# JWT Configuration
This section describes the JSON Web Token (JWT) configuration options available in Hasura DDN.
## Payload Definition
Example JSON Web Token (JWT) payload configuration definition:
```yaml title="globals/metadata/auth-config.hml"
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: HS256
key:
value: ultra-secret-very-secret-super-secret-key
audience: ["myapp-1234", "myapp-6789"]
allowedSkew: 60
issuer: https://my-auth-server.com
```
As a minimum, either the `claimsConfig`, `tokenLocation`, **and** `key` values **have to be present**.
## Example Decoded Payload
```json
{
"iat": 1735916718,
"exp": 1796916677,
"claims.jwt.hasura.io": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user", "admin"],
"x-hasura-user-id": "123",
"x-hasura-org-id": "456",
"x-hasura-custom": "custom-value"
}
}
```
## Example Encoded JWT
```text
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzU5MTY3MTgsImV4cCI6MTc5NjkxNjY3NywiY2xhaW1zLmp3dC5oYXN1cmEuaW8iOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciIsImFkbWluIl0sIngtaGFzdXJhLXVzZXItaWQiOiIxMjMiLCJ4LWhhc3VyYS1vcmctaWQiOiI0NTYiLCJ4LWhhc3VyYS1jdXN0b20iOiJjdXN0b20tdmFsdWUifX0.5bwSMgxsyULY1uhCJxYd-sO35rCdznRCZ4YMLwDD5u8
```
**Note:** `x-hasura-default-role` and `x-hasura-allowed-roles` are mandatory, while the rest of the claims are optional.
[See here for the JWT debugger](https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzU5MTY3MTgsImV4cCI6MTc5NjkxNjY3NywiY2xhaW1zLmp3dC5oYXN1cmEuaW8iOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciIsImFkbWluIl0sIngtaGFzdXJhLXVzZXItaWQiOiIxMjMiLCJ4LWhhc3VyYS1vcmctaWQiOiI0NTYiLCJ4LWhhc3VyYS1jdXN0b20iOiJjdXN0b20tdmFsdWUifX0.5bwSMgxsyULY1uhCJxYd-sO35rCdznRCZ4YMLwDD5u8)
of this example JWT token. The signature secret to verify this token with the HS256 algorithm is
`ultra-secret-very-secret-super-secret-key`.
## Hasura JWT format
The `x-hasura-role` value can be sent as a plain **header** in the request to indicate the role which should be used.
When your auth server generates the JWT, the custom claims in the JWT **must contain the following** in a custom
namespace. This namespace can be any string you choose, as long as it matches the `namespace.location` defined in your
AuthConfig. Using `claims.jwt.hasura.io` will match our examples.
1. A `x-hasura-default-role` field. The role that will be used when the optional `x-hasura-role` _header_ is **not
passed**.
2. A `x-hasura-allowed-roles` field. A list of allowed roles for the user i.e. acceptable values of the optional
`x-hasura-role` _header_.
3. Add any other optional `x-hasura-*` claim fields (required as per your defined permissions) to the custom namespace.
To summarize, `x-hasura-allowed-roles` session variable contains a list of all the roles that the user can assume and
the `x-hasura-role` header tells the Hasura Engine which role to use for the request, and if that is missing then the
`x-hasura-default-role` session variable will be used.
This setup makes it more convenient for a JWT to only need to be issued once with a list of allowed roles for the user,
and then allow the client to decide which of those roles to actually use for a request. This prevents the user needing
to log in again or unnecessary JWT re-issuance.
If, for example, your app will not need to switch user roles and the user only needs one role, for instance: `user`, you
can just issue a JWT with `x-hasura-default-role` set to `user` and `x-hasura-allowed-roles` set to `["user"]` and not
send the `x-hasura-role` header in the request.
This setup is designed so that there is one authoritative way to construct your JWT token for the Hasura Engine which
can cover a wide range of use cases.
## Hasura JWT Claim Description
### x-hasura-default-role
The `x-hasura-default-role` will be the role that the user falls back to when no `x-hasura-role` value is specified in
the header of the request. Usually, this will be the role with the least privileges and can be overridden by the
`x-hasura-role` header when making a request.
### x-hasura-allowed-roles
The `x-hasura-allowed-roles` list can contain all the roles which a particular user can assume, eg:
`[ "user", "manager", "owner" ]`. Usually, these will have varying degrees of access to your data as specified in
Permissions and by specifying this list it lets the Hasura Engine know that this user can assume any of them.
### x-hasura-\*
The JWT can have other user-defined `x-hasura-*` fields and their values can only be strings (they will be converted to
the right type automatically). You can use these `x-hasura-*` values in your permission rules.
The JWT will normally also contain standard (`sub`, `iat` etc.) and custom (`name`, `admin` etc.) claims depending on
your auth provider.
## JWT Notes
- JWT claim fields eg: `x-hasura-default-role` are case-insensitive.
- Hasura Engine only has access to headers and JWT claims which are prefixed with `x-hasura-`.
- Hasura Engine only has access to JWT claims in namespace defined in the `AuthConfig` object in metadata.
- All `x-hasura-*` values should be of type `String`, they will be converted to the right type automatically.
## Hasura JWT configuration options
### claimsConfig
You can specify where the engine should look for the claims within the decoded token either with one of `namespace` and `locations` options.
#### namespace {#jwt-claims-config-namespace}
The `namespace` option is used when all of the Hasura claims are present in a single object within the decoded JWT.
Our example uses `claims.jwt.hasura.io` in the [Example Decoded Payload](#example-decoded-payload).
```yaml
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
```
The `location` field indicates the location of the namespace object that uses [RFC 6901 JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901) string syntax.
The `claimsFormat` field indicates whether the Hasura-specific claims are a regular JSON object or a stringified JSON. The following possible values are allowed: `Json`, `StringifiedJson`.
This is required because providers like AWS Cognito only allow strings in the JWT claims.
[See #1176](https://github.com/hasura/graphql-engine/issues/1176).
**Example**:
If `claimsFormat` is `Json` then the JWT claims should look like:
```json
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"claims.jwt.hasura.io": {
"x-hasura-allowed-roles": ["editor", "user", "mod"],
"x-hasura-default-role": "user",
"x-hasura-user-id": "1234567890",
"x-hasura-org-id": "123",
"x-hasura-custom": "custom-value"
}
}
```
If `claimsFormat` is `StringifiedJson` then the JWT claims should look like:
```json
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"claims.jwt.hasura.io": "{\"x-hasura-allowed-roles\":[\"editor\",\"user\",\"mod\"],\"x-hasura-default-role\":\"user\",\"x-hasura-user-id\":\"1234567890\",\"x-hasura-org-id\":\"123\",\"x-hasura-custom\":\"custom-value\"}"
}
```
#### locations {#jwt-claims-config-locations}
This `locations` option can be used when Hasura claims are not all present in the single object, but individual claims are provided a JSON pointer within the decoded JWT.
In this option, you can indicate:
- a literal value.
- or a JSON pointer path for individual claims and an optional default value if the claim doesn't exist.
`x-hasura-default-role` and `x-hasura-allowed-roles` claims are required. Other custom claims are optionally configured.
The literal values should be of type `String`, except for the `x-hasura-allowed-roles` claim which expects a string
array.
**Example: JWT config with JSON path values**
```json
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"user": {
"id": "ujdh739kd"
},
"hasura": {
"all_roles": ["user", "editor"]
}
}
```
The mapping for `x-hasura-allowed-roles`, `x-hasura-default-role` and `x-hasura-user-id` session variables can be
specified in the `locations` configuration as follows:
```yaml
claimsConfig:
locations:
x-hasura-default-role:
path:
path: /hasura/all_roles/0
x-hasura-allowed-roles:
path:
path: /hasura/all_roles
x-hasura-user-id:
path:
path: /user/id
```
**Example: JWT config with JSON path values and default values**
```json
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"hasura": {
"all_roles": ["user", "editor"]
}
}
```
```yaml
claimsConfig:
locations:
x-hasura-default-role:
path:
path: /hasura/all_roles/0
x-hasura-allowed-roles:
path:
path: /hasura/all_roles
x-hasura-user-id:
path:
path: /user/id
default: ujdh739kd
```
In the above case, since the `/user/id` doesn't exist in the JWT token, the default value of the `x-hasura-user-id` i.e
`ujdh739kd` will be used
**Example: JWT config containing literal values**
```json
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"user": {
"id": "ujdh739kd"
}
}
```
The corresponding JWT config should be:
```yaml
claimsConfig:
locations:
x-hasura-default-role:
literal: user
x-hasura-allowed-roles:
literal: ["user", "editor"]
x-hasura-user-id:
path:
path: /user/id
```
In the above example, the `x-hasura-allowed-roles` and `x-hasura-default-role` values are set in the JWT config and the
value of the `x-hasura-user-id` is a JSON path to the value in the JWT token.
### tokenLocation
Indicates the token location where request header to read the JWT from.
The following are the possible values:
#### BearerAuthorization
In this mode, Hasura expects an `Authorization` header with a `Bearer` token.
```yaml
tokenLocation:
type: BearerAuthorization
```
The JWT header should look like:
```none
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWI...
```
#### Cookie
In the cookie mode, Hasura will try to parse the cookie header with the given cookie name. The value of the cookie
should be the exact JWT.
```yaml
tokenLocation:
type: Cookie
name: cookie_name
```
The JWT header should look like:
```none
Cookie: cookie_name=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWI...
```
#### Header
In the custom header mode, Hasura expects a `header_name` header with the exact JWT token value.
```yaml
tokenLocation:
type: Header
name: header_name
```
The JWT header should look like:
```none
header_name: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWI...
```
### key {#jwt-json-key}
This field specifies the JWT key configuration according to which the incoming JWT will be decoded.
You can configure either a `fixed` algorithm key or a remote JWK URL.
#### fixed {#jwt-json-key-fixed}
In this option, you must indicate a JWT key and its algorithm so the engine can decode and verify the JWT token.
```yaml
key:
fixed:
algorithm: HS256
key:
value: ultra-secret-very-secret-super-secret-key
# valueFromEnv: AUTH_JWT_KEY
```
The `algorithm` field specifies the cryptographic signing algorithm which is used to sign the JWTs. Valid values are: `HS256`, `HS384`,
`HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `PS256`, `PS384`, `PS512`, `EdDSA`.
The `key` field can be a literal value or an environment variable name.
- In the case of a symmetric key (i.e. a HMAC-based key), just the key as is. (e.g. -"abcdef..."). The key must be long
enough for the chosen algorithm, (e.g. for HS256 it must be at least 32 characters long).
- In the case of an asymmetric key (RSA, EdDSA, ECDSA etc.), only the **public** key, in a PEM-encoded string or as an
X509 certificate.
#### jwkFromUrl {#jwt-json-jwk_url}
An URL where a provider publishes their JWKs (JSON Web Keys - which are used for signing the JWTs). The URL **must**
publish the JWKs in the standard format as described [here](https://tools.ietf.org/html/rfc7517).
For example:
- Auth0 publishes their JWK url at: `https://.auth0.com`.
- Firebase publishes their JWK url at:
`https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com`.
```yaml
key:
jwkFromUrl: https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com
```
The JWTs must be signed by the JWK published at the given URL. They can be signed by any algorithm that is compatible
with the key (eg. `RS256`, `RS384`, `RS512` algorithms require a JWK with an RSA key).
DDN does not currently support rotating JWKs.
### audience
This is an optional field. Certain providers might set a claim which indicates the intended audience for the JWT. This
must be checked by setting this field.
When this field is set, during the verification process of the JWT, the `aud` claim in the JWT will be checked to see
whether it is equal to the `audience` field given in the configuration. If they are not equal then the JWT will be
rejected.
See the [RFC](https://tools.ietf.org/html/rfc7519#section-4.1.3) for more details.
:::warning
If your [Compatibility Config date](/reference/metadata-reference/compatibility-config.mdx) is set to `2025-03-11` or
newer, you _must_ set the `audience` field if your JWTs will contain an `aud` claim. If you do not, the JWT will be
rejected as invalid and authentication will fail.
:::
This field must be a list of strings.
Examples:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
# ...
audience: ["myapp-1234", "myapp-6789"]
```
:::danger Audience Security Vulnerability
Certain JWT providers share JWKs between multiple tenants. They use the `aud` claim of the JWT to specify the intended
audience. Setting the `audience` field in the Hasura JWT configuration will make sure that the `aud` claim from the JWT
is also checked during verification. Not doing this check will allow JWTs issued for other tenants to be valid as well.
In these cases, you **MUST** set the `audience` field to the appropriate value. Failing to do so is a **major security
vulnerability**.
:::
### issuer
This is an optional field. It takes a string value.
When this field is set, during the verification process of the JWT, the `iss` claim in the JWT will be checked to see
whether it is equal to the `issuer` field given in the configuration. If they are not equal then the JWT will be
rejected.
See [RFC](https://tools.ietf.org/html/rfc7519#section-4.1.1) for more details.
Examples:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
# ...
issuer: https://my-auth-server.com
```
#### Issuer Notes
- Certain providers require you to verify the `iss` claim on the JWT. To do that you can set this field to the
appropriate value.
- A JWT configuration without an issuer will match any issuer field present in an incoming JWT.
- An incoming JWT without an issuer specified will match a configuration even if it specifies an issuer.
### allowed_skew
`allowedSkew` is an optional field to provide some leeway (to account for clock skews) while comparing the JWT expiry
time. This field expects an integer value which will be the number of seconds of the skew value.
### Hasura JWT Config Examples
#### HMAC-SHA based
Your auth server is using HMAC-SHA algorithms to sign JWTs, and is using a 256-bit key. In this case, the JWT config
will look like:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: HS256
key:
value: 3EK6FD+o0+c7tzBNVfjpMkNDi2yARAAKzQlk8O2IKoxQu4nF7EdAh8s3TwpHwrdWT6R
```
The `key` is the actual shared secret, which is used by Hasura and the external auth server.
#### RSA based
If your auth server is using the RSA algorithm to sign JWTs, and is using a 512-bit key, the JWT config only needs to
have the public key.
**Example 1**: public key in PEM format (not OpenSSH format):
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: RS512
key:
value: '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\nUWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\nHUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\no2kQ+X5xK9cipRgEKwIDAQAB\n-----END PUBLIC KEY-----\n'
```
**Example 2**: public key as X509 certificate:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: RS512
key:
value: '-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIINw9gva8BPPIwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTgQt7dIsMTIU9k1SUrFviZOGnmHWtIAw\nmtYBcM9I0f9/ka45JIRp5Y1NKpAMFSShs7Wv0m1JS1kXQHdJsPSmjmDKcwnBe3R/\nTU3foRRywR/3AJRM15FNjTqvUm7TeaW16LkkRoECAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBADfY2DEmc2gb8/pqMNWHYq/nTYfJPpK4VA9A0lFTNeoq\nzmnbGwhKj24X+Nw8trsvkrKxHvCI1alDgBaCyzjGGvgOrh8X0wLtymp1yj6PWwee\nR2ZPdUaB62TCzO0iRv7W6o39ey+mU/FyYRtxF0ecxG2a0KNsIyFkciXUAeC5UVDo\nBNp678/SDDx9Ltuxc6h56a/hpBGf9Yzhr0RvYy3DmjBs6eopiGFmjnOKNxQrZ5t2\n339JWR+yiGEAtoHqk/fINMf1An6Rung1xYowrm4guhCIVi5unAvQ89fq0I6mzPg6\nLhTpeP0o+mVYrBmtYVpDpv0e71cfYowSJCCkod/9YbY=\n-----END CERTIFICATE-----'
```
**Example 3**: public key published as JWKs:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
jwkFromUrl: https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com
```
#### EdDSA based
If your auth server is using EdDSA to sign JWTs, and is using the Ed25519 variant key, the JWT config only needs to have
the public key.
**Example 1**: public key in PEM format (not OpenSSH format):
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: Ed25519
key:
value: '-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAG9I+toAAJicilbPt36tiC4wi7E1Dp9rMmfnwdKyVXi0=\n-----END PUBLIC KEY-----'
```
**Example 2**: public key as X509 certificate:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: Ed25519
key:
value: '-----BEGIN CERTIFICATE REQUEST-----\nMIIBAzCBtgIBADAnMQswCQYDVQQGEwJERTEYMBYGA1UEAwwPd3d3LmV4YW1wbGUu\nY29tMCowBQYDK2VwAyEA/9DV/InajW02Q0tC/tyr9mCSbSnNP1txICXVJrTGKDSg\nXDBaBgkqhkiG9w0BCQ4xTTBLMAsGA1UdDwQEAwIEMDATBgNVHSUEDDAKBggrBgEF\nBQcDATAnBgNVHREEIDAegg93d3cuZXhhbXBsZS5jb22CC2V4YW1wbGUuY29tMAUG\nAytlcANBAKbTqnTyPcf4ZkVuq2tC108pBGY19VgyoI+PP2wD2KaRz4QAO7Bjd+7S\nljyJoN83UDdtdtgb7aFgb611gx9W4go=\n-----END CERTIFICATE REQUEST-----'
```
#### EC based
If your auth server is using ECDSA to sign JWTs, and is using the ES variant with a 256-bit key, the JWT config only
needs to have the public key.
**Example 1**: public key in PEM format (not OpenSSH format):
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: ES256
key:
value: '-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\nq9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END PUBLIC KEY-----'
```
**Example 2**: public key as X509 certificate:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
fixed:
algorithm: ES256
key:
value: '"-----BEGIN CERTIFICATE-----\nMIIBbjCCARWgAwIBAgIUGn02F6Y6s88dDGmIfwiNxWxDjhswCgYIKoZIzj0EAwIw\nDTELMAkGA1UEBhMCSU4wHhcNMjMwNTI0MTAzNTI4WhcNMjgwNTIyMTAzNTI4WjAN\nMQswCQYDVQQGEwJJTjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBFbP6OfrkG0\n4y93Icpy+MF4FINkfavVFPCOZhKL1H/OkGe5DgSIycKp8w9aJmoHhB1sB3QTugfn\nRWm5nU/TzsajUzBRMB0GA1UdDgQWBBSaqFjzps1qG+x2DPISjaXTWsTOdDAfBgNV\nHSMEGDAWgBSaqFjzps1qG+x2DPISjaXTWsTOdDAPBgNVHRMBAf8EBTADAQH/MAoG\nCCqGSM49BAMCA0cAMEQCIBDHHWa/uLAVdGFEk82auTmw995+MsRwv52VXLw2Z+ji\nAiAXzOWIcGN8p25uhUN/7v9gEcADGIS4yUiv8gsn/Jk2ow==\n-----END CERTIFICATE-----'
```
**Example 3**: public key published as JWKs:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: BearerAuthorization
key:
jwkFromUrl: https://www.gstatic.com/iap/verify/public_key-jwk
```
## Security considerations
### Setting audience check
Certain JWT providers share JWKs between multiple tenants (like Firebase). They use the `aud` claim of JWT to specify
the intended tenant for the JWT. Setting the `audience` field in the Hasura JWT configuration will make sure that the
`aud` claim from the JWT is also checked during verification. Not doing this check will allow JWTs issued for other
tenants to be valid as well.
In these cases, you **MUST** set the `audience` field to appropriate value. **Failing to do so is a major security
vulnerability**.
## JWT with the WebSocket protocol
When executing a subscription (or query or mutation) over the WebSocket protocol, the authentication step is executed on
`connection_init` when the websocket is connected to Hasura Engine and is valid until the expiry of the JWT when in JWT
mode.
Once authenticated, all operations are allowed without further check, until the authentication expires.
## Popular providers and known issues
### AWS Cognito
AWS Cognito and ELB (Elastic Load Balancer) has a known issue where it adds additional padding (using = characters) to
the JWT token that is generated from Cognito.
This is a known issue and is documented by AWS in
[their docs](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-authenticate-users.html#user-claims-encoding):
> Standard libraries are not compatible with the padding that is included in the Application Load Balancer
> authentication token in JWT format.
Currently, there is no workaround possible in Hasura. Even if Hasura strips the additional padding the signature
verification of the token would fail (as Hasura had to tamper the token).
### Firebase
Firebase publishes the JWKs at:
[https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com](https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com)
If you are using Firebase and Hasura, use this config:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: /claims.jwt.hasura.io
tokenLocation:
type: Header
name: Authorization
key:
jwkFromUrl: https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com
issuer: https://securetoken.google.com/
audience:
```
### Auth0 {#auth0-issues}
Refer to the [Auth0 JWT tutorial](/auth/jwt/tutorials/integrations/1-auth0.mdx) for a detailed guide on integrating
Auth0 with Hasura.
### Clerk
Clerk integrates with Hasura GraphQL Engine using JWTs.
Clerk publishes their JWK under: `https:///.well-known/jwks.json`
Refer to the [Clerk JWT tutorial](/auth/jwt/tutorials/integrations/4-clerk.mdx) to set up authenticated requests to
Hasura with Clerk.
### Keycloak
By default, Keycloak uses the `RSA-OAEP` algorithm, which the Hasura DDN engine doesn't support. Remove the algorithm in
the `Realm Settings -> Keys -> Add Providers` tab.
## Propagating claims to traces
You can forward selected session variables (derived from JWT claims) into OpenTelemetry trace attributes using the
`sessionVariableToTraceAttributeMap` field in your `AuthConfigV4`.
This is useful for correlating traces by tenant, user, or organization in multi-tenant setups.
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
# ... your JWT config ...
sessionVariableToTraceAttributeMap:
- sessionVariable: x-hasura-tenant-id
traceAttribute: tenant-id
- sessionVariable: x-hasura-org-id
traceAttribute: org-id
```
The mapped values will appear as attributes on every span and be propagated via the `baggage` HTTP header to all
downstream services (NDC connectors, webhooks, plugins).
For more details, see the [AuthConfig reference](/reference/metadata-reference/auth-config).
==============================
# 1-add-custom-logic.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/1-add-custom-logic
# Add Custom Logic
## Introduction
In this tutorial, you'll use a lambda connector to add custom business logic to your supergraph API. The connector and
DDN CLI will automatically determine the argument and return types from your custom logic and add them to your API's
GraphQL schema.
Each connector uses its own conventions to determine whether a function should be exposed as a query or mutation; in
this tutorial, you'll add both.
This tutorial should take about five minutes.
## Step 1. Initialize a new local DDN project
```sh title="Create a new project using the DDN CLI:"
ddn supergraph init lambda-tutorial
```
## Step 2. Initialize the lambda connector
```bash title="Run the following command:"
ddn connector init my_ts -i
```
- Select `hasura/nodejs` from the list of connectors.
- Choose a port (press enter to accept the default recommended by the CLI).
If you open the `app/connector/my_ts` directory, you'll see the `functions.ts` file generated by the CLI; this will be
the entrypoint for your connector.
```bash title="Run the following command:"
ddn connector init my_python -i
```
- Select `hasura/python` from the list of connectors.
- Choose a port (press enter to accept the default recommended by the CLI).
If you open the `app/connector/my_python` directory, you'll see the `functions.py` file generated by the CLI; this will
be the entrypoint for your connector.
```bash title="Run the following command:"
ddn connector init my_go -i
```
- Select `hasura/go` from the list of connectors.
- Choose a port (press enter to accept the default recommended by the CLI).
If you open the `app/connector/my_go` directory, you'll see Go files in the `functions` folder; these will serve as the
entrypoint for your connector.
## Step 3. Add custom logic
```bash title="From the connector directory, install the necessary packages:"
cd app/connector/my_ts && npm install
```
```typescript title="Then, replace the contents of functions.ts with the following:"
/**
* @readonly Exposes the function as an NDC function (the function should only query data without making modifications)
*/
export function hello(name?: string) {
return `hello ${name ?? "world"}`;
}
/**
* As this is missing the readonly tag, this will expose the function as an NDC procedure (the function will be exposed as a mutation in the API)
*/
export function encode(username: string) {
return Buffer.from(username).toString("base64");
}
```
Including the `@readonly` tag ensures `hello()` is exposed as a query in our API. By omitting this from `encode()`,
we're ensuring this second function is exposed as a mutation when we build our API.
Both have typed input arguments and implicitly return strings, which the connector will use to generate the
corresponding GraphQL schema.
```python title="Replace the contents of functions.py with the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
from pydantic import BaseModel, Field
from hasura_ndc.errors import UnprocessableContent
from typing import Annotated
connector = FunctionConnector()
@connector.register_query
def hello(name: str) -> str:
return f"Hello {name}"
@connector.register_mutation
def encode(username: str) -> str:
return base64.b64encode(username.encode("utf-8")).decode("utf-8")
if __name__ == "__main__":
start(connector)
```
Using the `@connector.register_query` decorator ensures `hello()` is exposed as a query in our API. By using the
`@connector.register_mutation` decorator with `encode()`, we're ensuring this second function is exposed as a mutation
when we build our API.
Both have typed input arguments and return strings, which the connector will use to generate the corresponding GraphQL
schema.
```go title="Add the following to a new file called app/connector/my_go/functions/encode.go:"
package functions
"context"
"encoding/base64"
"hasura-ndc.dev/ndc-go/types"
)
// EncodeUsernameArguments represents the input arguments for encoding a username.
type EncodeUsernameArguments struct {
Username string `json:"username"`
}
// EncodeUsernameResult represents the result of the Base64 encoding.
type EncodeUsernameResult struct {
EncodedUsername string `json:"encodedUsername"`
}
// ProcedureEncodeUsername encodes the given username into Base64 format.
func ProcedureEncode(ctx context.Context, state *types.State, arguments *EncodeUsernameArguments) (*EncodeUsernameResult, error) {
encoded := base64.StdEncoding.EncodeToString([]byte(arguments.Username))
return &EncodeUsernameResult{
EncodedUsername: encoded,
}, nil
}
```
Using the prefix `Procedure` ensures `ProcedureEncode()` is exposed as a mutation in our API. If you open the `hello.go`
file in the `functions` directory, you'll notice the `FunctionHello()` is prefixed differently, identifying it as a
function to be exposed as a query in your API.
Both have typed input arguments and return strings, which the connector will use to generate the corresponding GraphQL
schema.
## Step 4. Introspect the source file(s)
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
```bash title="Then, we can generate a metadata file for each function using the following command:"
# alternatively, use ddn command add my_ts "*" for bulk adds
ddn command add my_ts hello
ddn command add my_ts encode
```
```bash title="Introspect the connector:"
ddn connector introspect my_python
```
```bash title="Then, we can generate a metadata file for each function using the following command:"
# alternatively, use ddn command add my_python "*" for bulk adds
ddn command add my_python hello
ddn command add my_python encode
```
```bash title="Introspect the connector:"
ddn connector introspect my_go
```
```bash title="Then, we can generate a metadata file for each function using the following command:"
# alternatively, use ddn command add my_go "*" for bulk adds
ddn command add my_go hello
ddn command add my_go encode
```
The commands introspected your connector's entrypoint, identified functions with their argument and return types, and
generated Hasura metadata for each. Look for `Hello.hml` and `Encode.hml` to see the CLI-generated metadata.
## Step 5. Create a new build and test
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your local console:"
ddn console --local
```
```graphql title="Using the GraphiQL explorer, you can now use hello() as a query:"
# Which will return hello, Hasura
query HelloQuery {
hello(name: "Hasura")
}
```
:::tip Using the Go connector?
The boilerplate `hello()` function requires an argument of `greeting` and for you to include a return type in the query.
:::
```graphql title="And encode() as a mutation:"
# Which will return aGFzdXJh
mutation EncodeMutation {
encode(username: "hasura")
}
```
## Next steps
Now that you've seen how easy it is to add custom business logic directly to your supergraph API, consider these next
steps:
- [Extend a model](/business-logic/tutorials/2-extend-a-model.mdx)
- [Use env vars](/business-logic/add-env-vars-to-a-lambda.mdx)
==============================
# 2-extend-a-model.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/2-extend-a-model
# Extend a Model
## Introduction
In this tutorial, you'll learn how to extend an existing field on a model to enhance its functionality. We'll
demonstrate this using PostgreSQL as the data source, **but the steps apply to any data source supported by Hasura
DDN**. By the end, you'll have created a relationship that integrates custom logic with your API. This process will show
you how to:
- Initialize a DDN project and connect to a data source.
- Add a model to your metadata from a database table.
- Create and implement custom logic in a lambda connector.
- Establish relationships between models and custom commands.
This tutorial should take about ten minutes.
## Step 1. Initialize a new local DDN project
```sh title="Create a new project using the DDN CLI:"
ddn supergraph init lambda-tutorial
```
## Step 2. Prepare the PostgreSQL data
```sh title="In your project directory, run:"
ddn connector init my_pg -i
```
From the dropdown, start typing `PostgreSQL` and hit enter to advance through all the options.
The CLI will output something similar to this:
```plaintext
HINT To access the local Postgres database:
- Run: docker compose -f app/connector/my_pg/compose.postgres-adminer.yaml up -d
- Open Adminer in your browser at http://localhost:5143 and create tables
- To connect to the database using other clients use postgresql://user:password@local.hasura.dev:8105/dev
```
```sh title="Use the hint from the CLI output:"
docker compose -f app/connector/my_pg/compose.postgres-adminer.yaml up -d
```
Run `docker ps` to see on which port Adminer is running. Then, you can navigate to the address below to access it:
```plaintext
http://localhost:
```
```sql title="Next, via Adminer select SQL command from the left-hand nav, then enter the following:"
--- Create the table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
--- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
```
You can verify this worked by using Adminer to query all records from the `users` table:
```sql
SELECT * FROM users;
```
```sh title="Next, use the CLI to introspect your PostgreSQL database:"
ddn connector introspect my_pg
```
```sh title="Now, track the table from your PostgreSQL database as a model in your DDN metadata:"
ddn models add my_pg users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
## Step 3. Initialize the lambda connector
```bash title="Run the following command:"
ddn connector init my_ts -i
```
- Select `hasura/nodejs` from the list of connectors.
- Choose a port (press enter to accept the default recommended by the CLI).
If you open the `app/connector/my_ts` directory, you'll see the `functions.ts` file generated by the CLI; this will be
the entrypoint for your connector.
```bash title="Run the following command:"
ddn connector init my_python -i
```
- Select `hasura/python` from the list of connectors.
- Choose a port (press enter to accept the default recommended by the CLI).
If you open the `app/connector/my_python` directory, you'll see the `functions.py` file generated by the CLI; this will
be the entrypoint for your connector.
```bash title="Run the following command:"
ddn connector init my_go -i
```
- Select `hasura/go` from the list of connectors.
- Choose a port (press enter to accept the default recommended by the CLI).
If you open the `app/connector/my_go` directory, you'll see Go files in the `functions` folder; these will serve as the
entrypoint for your connector.
## Step 4. Add your custom logic
```bash title="From the connector directory, install the necessary packages:"
cd app/connector/my_ts && npm install
```
```typescript title="Then, replace the contents of functions.ts with the following:"
/**
* @readonly
*/
export function shoutName(name: string) {
return `${name.toUpperCase()}`;
}
```
```python title="Replace the contents of functions.py with the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
from pydantic import BaseModel, Field
from hasura_ndc.errors import UnprocessableContent
from typing import Annotated
connector = FunctionConnector()
@connector.register_query
def shoutName(name: str) -> str:
return f"{name}".upper()
if __name__ == "__main__":
start(connector)
```
```go title="Add the following to a new file called app/connector/my_go/functions/shout.go:"
package functions
"context"
"strings"
"hasura-ndc.dev/ndc-go/types"
)
// UppercaseNameArguments represents the input arguments for converting a Name to uppercase.
type UppercaseNameArguments struct {
Name string `json:"Name"`
}
// FunctionShoutName converts the Name to uppercase and returns it as a string.
func FunctionShoutName(ctx context.Context, state *types.State, arguments *UppercaseNameArguments) (string, error) {
uppercase := strings.ToUpper(arguments.Name)
return uppercase, nil
}
```
## Step 5. Introspect your lambda connector
```bash title="Introspect the connector:"
ddn connector introspect my_ts
```
```bash title="Then, we can generate a metadata file for each function using the following command:"
# alternatively, use ddn command add my_ts "*" for bulk adds
ddn command add my_ts shoutName
```
```bash title="Introspect the connector:"
ddn connector introspect my_python
```
```bash title="Then, we can generate a metadata file for each function using the following command:"
# alternatively, use ddn command add my_python "*" for bulk adds
ddn command add my_python shoutName
```
```bash title="Introspect the connector:"
ddn connector introspect my_go
```
```bash title="Then, we can generate a metadata file for each function using the following command:"
# alternatively, use ddn command add my_go "*" for bulk adds
ddn command add my_go shoutName
```
## Step 6. Create a relationship
```yaml title="In Users.hml, add the following Relationship object:"
---
kind: Relationship
version: v1
definition:
name: shoutName # Define a name to expose in the supergraph API
sourceType: Users # The existing source object type (which also defines the source model Users)
target:
command: # The target is a command
name: ShoutName # The name of the existing command we have defined in metadata
subgraph: app # The existing subgraph the command is defined in
mapping:
- source:
fieldPath:
- fieldName: name # The field on the source object type that we want to provide to the target command as an argument
target:
argument:
argumentName: name # The name of the argument on the target command that we want to map to the source field
```
## Step 7. Create a new build and test
```bash title="Create a new build:"
ddn supergraph build local
```
```bash title="Start your services:"
ddn run docker-start
```
```bash title="Open your local console:"
ddn console --local
```
```graphql title="Using the GraphiQL explorer, you can now use shout() as a field on the Users model:"
query UsersWithShoutedName {
users {
id
name
shoutName
}
}
```
## Next steps
Now that you know how to extend your existing data sources using custom business logic, check out our advanced use cases
in this section:
- [Formatting datetime objects](/business-logic/tutorials/3-format-datetime-objects.mdx)
- [Hashing passwords](/business-logic/tutorials/3-hash-passwords.mdx)
- [Translating content](/business-logic/tutorials/4-translate-content.mdx)
- [Enriching data with an LLM](/business-logic/tutorials/5-enrich-data-with-an-llm.mdx)
- [Validating credentials](/business-logic/tutorials/6-validate-credentials.mdx)
- [HTTP header forwarding](/business-logic/tutorials/7-http-header-forwarding.mdx)
==============================
# 3-format-datetime-objects.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/3-format-datetime-objects
# Format Datetime Objects
## Introduction
In this recipe, you'll learn how to convert an existing datetime object from your supergraph into a human-readable
format. This approach is perfect when you want to streamline your frontend by formatting data at the API level, allowing
the UI to easily render the formatted result with minimal effort.
:::info Prerequisites
Before continuing, ensure you have:
- A [local Hasura DDN project](/quickstart.mdx).
- A [lambda connector](/business-logic/overview.mdx) added to your project.
- A type in your supergraph that is a valid datetime object.
**NB: The type will vary with your database-of-choice, but anything that is
[ISO-8601-compliant](https://www.iso.org/iso-8601-date-and-time-format.html) will generally work for what's listed
below. You can adapt this recipe to fit your individual needs.**
:::
## Recipe
### Step 1. Write the function
```typescript title="In your functions.ts file, add the following:"
/**
* @readonly
*/
export function formattedDate(dateString: string): string {
const date = new Date(dateString);
return date.toLocaleString("en-US", {
year: "numeric",
month: "long",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: true,
});
}
```
```python title="In your functions.py file, add the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
from datetime import datetime
connector = FunctionConnector()
@connector.register_query
async def formatted_date(dateString: str) -> str:
date = datetime.fromisoformat(date_string)
return date.strftime("%B %d, %Y %I:%M %p")
if __name__ == "__main__":
start(connector)
```
```go title="In a Go file inside your functions directory, add the following:"
package functions
"context"
"fmt"
"time"
"hasura-ndc.dev/ndc-go/types"
)
// DatetimeArguments defines the input arguments for the function
type DatetimeArguments struct {
DateString string `json:"date_string"` // required argument
}
// DatetimeResult defines the output result for the function
type DatetimeResult string
// FunctionFormattedDate formats a datetime string
func FunctionFormattedDate(ctx context.Context, state *types.State, arguments *DatetimeArguments) (*DatetimeResult, error) {
date, err := time.Parse(time.RFC3339, arguments.DateString)
if err != nil {
return nil, fmt.Errorf("failed to parse date: %v", err)
}
formattedDate := date.Format("January 02, 2006 03:04 PM")
result := DatetimeResult(formattedDate)
return &result, nil
}
```
### Step 2. Track your function
To add your function, generate the related metadata that will link together any functions in your lambda connector's
source files and your API:
```bash
ddn connector introspect
```
Then, you can generate an `hml` file for the function using the following command:
```bash
ddn command add "*"
```
### Step 3. Create a relationship (optional)
Assuming the input argument's type matches that of a type belonging to one or more of your models, you can create a
relationship to the command. This will enable you to make nested queries that will invoke your custom business logic
using the value of the field from the related model!
Create a relationship in the corresponding model's HML file.
```yaml title="For example, if we have an Orders model:"
---
kind: Relationship
version: v1
definition:
name: formattedDate
sourceType: Orders
target:
command:
name: FormattedDate
mapping:
- source:
fieldPath:
- fieldName: createdAt
target:
argument:
argumentName: dateString
```
### Step 4. Test your function
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
In your project's explorer, you should see the new function exposed as a type and should be able to make a query like
this:
If you created a relationship, you can make a query like this, too:
## Wrapping up
In this guide, you learned how to enhance your API and enrich the data it serves for its consumers by incorporating
custom business logic directly into your supergraph. By leveraging lambda connectors with
[relationships](/reference/metadata-reference/relationships.mdx), you can not only add custom business logic, but easily
pass values to it and return this information as part of existing models.
## Learn more about lambda connectors
- [TypeScript](/business-logic/overview.mdx) Node.js connector.
- [Python](/business-logic/overview.mdx) connector.
- [Go](/business-logic/overview.mdx) connector.
## Similar recipes
- [Custom business logic recipes](/business-logic/tutorials/index.mdx)
==============================
# 4-translate-content.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/4-translate-content
# Translate Content
## Introduction
In this recipe, you'll learn how to translate existing content from your supergraph into another language. This is great
for taking care of translations on the API-end to reach users worldwide. Your supergraph's consumers can choose which
language(s) they want to return, all without worrying about various language configurations on the client-side.
:::info Prerequisites
Before continuing, ensure you have:
- A [local Hasura DDN project](/quickstart.mdx).
- A [lambda connector](/business-logic/overview.mdx) added to your project.
- A [Google Cloud Translation](https://cloud.google.com/translate) API key.
**NB: This API key is _free_ for the first 500,000 characters each month.**
:::
## Recipe
### Step 1. Write the function
```sh title="In your connector's directory, install the Google Cloud Translate package:"
npm install @google-cloud/translate
```
```typescript title="In your functions.ts file, add the following:"
// This can also be stored as an environment variable
// in the connector's .env file.
const CLOUD_TRANSLATION_API_KEY = "your_cloud_translation_api_key";
/**
* @readonly
*/
export async function translateText(targetLanguage: string, content: string): Promise {
const translate = new v2.Translate({ key: CLOUD_TRANSLATION_API_KEY });
const [translation] = await translate.translate(content, targetLanguage);
return translation;
}
```
```plaintext title="In your connector's directory, add the Google API Python Client package to your requirements.txt:"
google-api-python-client==v2.146.0
```
```python title="In your functions.py file, add the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
from googleapiclient.discovery import build
# This can also be stored as an environment variable
# in the connector's .env file.
API_KEY = "your_cloud_translation_api_key"
connector = FunctionConnector()
@connector.register_query
def translate_text(target_language: str, content: str) -> str:
service = build("translate", "v2", developerKey=API_KEY)
# Make the translation request
response = service.translations().list(
target=target_language,
q=[content]
).execute()
# Return the translated text
return response['translations'][0]['translatedText']
if __name__ == "__main__":
start(connector)
```
### Step 2. Track your function
To add your function, generate the related metadata that will link together any functions in your lambda connector's
source files and your API:
```bash
ddn connector introspect
```
Then, you can generate an `hml` file for the function using the following command:
```bash
ddn command add "*"
```
### Step 3. Create a relationship (optional)
Assuming the input argument's type matches that of a type belonging to one or more of your models, you can create a
relationship to the command. This will enable you to make nested queries that will invoke your custom business logic
using the value of the field from the related model!
Create a relationship in the corresponding model's HML file.
```yaml title="For example, if we have a Reviews model:"
---
kind: Relationship
version: v1
definition:
name: translatedReview
sourceType: Reviews
target:
command:
name: TranslateText
mapping:
- source:
fieldPath:
- fieldName: text
target:
argument:
argumentName: content
```
### Step 4. Test your function
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
In your project's explorer, you should see the new function exposed as a type and should be able to make a query like
this:
If you created a relationship, you can make a query like this, too:
## Wrapping up
In this guide, you learned how to enhance your API and enrich the data it serves for its consumers by incorporating
custom business logic directly into your supergraph. By leveraging lambda connectors with
[relationships](/reference/metadata-reference/relationships.mdx), you can not only add custom business logic, but easily
pass values to it and return this information as part of existing models.
## Learn more about lambda connectors
- [TypeScript](/business-logic/overview.mdx) Node.js connector.
- [Python](/business-logic/overview.mdx) connector.
- [Go](/business-logic/overview.mdx) connector.
## Similar recipes
- [Custom business logic recipes](/business-logic/tutorials/index.mdx)
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/
# Authentication
## Introduction
In this section of tutorials, we'll provide you with concise up-to-date descriptions of how to connect your preferred
authentication provider to Hasura DDN.
## Tutorials
- [Auth0](/auth/jwt/tutorials/integrations/1-auth0.mdx)
- [AWS Cognito](/auth/jwt/tutorials/integrations/2-aws-cognito.mdx)
- [Firebase](/auth/jwt/tutorials/integrations/3-firebase.mdx)
- [Clerk](/auth/jwt/tutorials/integrations/4-clerk.mdx)
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/integrations/
# Authentication
## Introduction
In this section of tutorials, we'll provide you with concise up-to-date descriptions of how to connect your preferred
authentication provider to Hasura DDN.
## Tutorials
- [Auth0](/auth/jwt/tutorials/integrations/1-auth0.mdx)
- [AWS Cognito](/auth/jwt/tutorials/integrations/2-aws-cognito.mdx)
- [Firebase](/auth/jwt/tutorials/integrations/3-firebase.mdx)
- [Clerk](/auth/jwt/tutorials/integrations/4-clerk.mdx)
==============================
# 1-auth0.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/integrations/1-auth0
# Auth0
## Introduction
In this tutorial, you'll learn how to configure an existing [Auth0 application](https://auth0.com) and generate a JWT
which you can pass in the header of your requests to Hasura. After setting up your
[AuthConfig](/reference/metadata-reference/auth-config.mdx) object to use JWT mode, this will allow you to validate
users' identities and create [permission rules](/reference/metadata-reference/permissions.mdx) which can limit access to
underlying data served by Hasura DDN.
:::info Prerequisites
Before continuing, ensure you have:
- An Auth0 [application](https://manage.auth0.com/dashboard).
- A local application that you're actively developing, built with any language or framework supported by
[Auth0's SDKs](https://auth0.com/docs/libraries).
- A local Hasura DDN project.
:::
## Tutorial
### Step 1. Create a new Auth0 application
From your Auth0 dashboard, click `Applications` in the sidebar and then click on `Create Application`. Enter a name for
your application, then choose the application type that best suits your needs.
After creating the application, go to `APIs` in the sidebar and create a new API with your GraphQL endpoint as the
identifier.
### Step 2. Create a new Auth0 Action
From your Auth0 dashboard, click `Actions` in the sidebar and choose `Triggers`.
Under `Sign Up & Login`, select the `post-login` trigger, click on the `+` icon, and then choose `Built from Scratch` to
create a new Action.
Enter a name for your Action such as `Hasura JWT Claims` and paste the following code:
```javascript
exports.onExecutePostLogin = async (event, api) => {
const namespace = "claims.jwt.hasura.io";
// Here, you'll need to fetch the user's role from Hasura DDN using an admin-level authenticated request
// Learn more here: https://hasura.io/docs/3.0/auth/authentication/jwt/special-roles
// Below, we're hard-coding the value for now
const user_role = "user"; // the role returned from your request βοΈ
api.idToken.setCustomClaim(namespace, {
"x-hasura-default-role": user_role,
"x-hasura-allowed-roles": [user_role],
"x-hasura-user-id": event.user.user_id,
// Add any other custom claims you wish to include
});
// Set the necessary access token claims for Hasura to authenticate the user
api.accessToken.setCustomClaim(namespace, {
"x-hasura-default-role": user_role,
"x-hasura-allowed-roles": [user_role],
"x-hasura-user-id": event.user.user_id,
});
};
```
This will add the required Hasura namespace with the keys that Hasura DDN expects when decoding a JWT. You can modify
the keys to suit your Hasura DDN [roles](/reference/metadata-reference/permissions.mdx#typepermissions).
Click `Deploy`.
:::tip Custom claims
You can create any custom keys you wish and reference them in your permissions using session variables. Above,
`x-hasura-user-id` is simply an example. Any claim prefixed with `x-hasura-` is accessible to the Hasura DDN Engine.
:::
### Step 3. Update your AuthConfig
Update your AuthConfig object to use JWT mode and your
[Auth0 JWKs](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets):
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: "/claims.jwt.hasura.io"
issuer: ""
key:
jwkFromUrl: "https:///.well-known/jwks.json"
audience: [""]
tokenLocation:
type: Header
name: Auth-Token
```
Then, create a new build of your supergraph:
```sh
ddn supergraph build local
```
### Step 4. Test your configuration
The easiest way to verify your setup is to generate a new JWT by logging into your client application that uses Auth0.
These values aren't typically displayed to users, so you'll need to log them while in development. You can then add that
value as a header in the console and test any permissions you have in your metadata. If you're unfamiliar with this, we
have a sample repo [here](https://github.com/hasura/ddn-auth-examples/tree/main/auth0).
:::info Auth0 API Debugger
Auth0 does provide an extension: the `Auth0 Authentication API Debugger` for testing configurations. However, custom
claims have been known to cause issues. You can find more information about using their debugger
[here](https://auth0.com/docs/customize/extensions/authentication-api-debugger-extension).
:::
### Step 5. Service account access token (optional)
In certain cases, you may need to generate a service account access token to access your Hasura DDN supergraph when
performing backend operations. You can do this by creating a new Auth0 application named `Service Account` with a type
of `Machine to Machine Applications`.
After creating the application, go to `Triggers`, located underneath `Actions` in the sidebar, and click on the
`credentials-exchange` trigger. Similar to the `post-login` trigger, create a new Action and paste the code below:
```javascript
exports.onExecuteCredentialsExchange = async (event, api) => {
const namespace = "claims.jwt.hasura.io";
const service_role = "service_account";
api.accessToken.setCustomClaim(namespace, {
"x-hasura-default-role": service_role,
"x-hasura-allowed-roles": [service_role],
});
};
```
This will generate a new JWT token with the `service_account` role which can then be used to access your Hasura DDN
supergraph.
You can generate a new access token using the following Python code:
```python
conn = http.client.HTTPSConnection("")
payload = "{\"client_id\":\"\",\"client_secret\":\"\",\"audience\":\"\",\"grant_type\":\"client_credentials\"}"
headers = { 'content-type': "application/json" }
conn.request("POST", "/oauth/token", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
```
The response will look like:
```json
{
"access_token": "",
"token_type": "Bearer"
}
```
You can modify your metadata to allow the `service_account` role to access the necessary models.
:::tip Best Practices for Service Accounts
Instead of granting a service account full admin access, create a custom role with only the necessary permissions. This
approach follows the principle of least privilege, thereby limiting potential impact in case of any errors or
oversights.
:::
## Wrapping up
In this guide, you learned how to integrate Auth0 with Hasura DDN to create a secure and scalable identity management
solution using JWTs. By leveraging custom claims in conjunction with
[permissions](/reference/metadata-reference/permissions.mdx), you can define precise access-control rules, ensuring that
your application remains secure and meets your users' needs.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
If you encounter issues or need further customization, consider reviewing our related documentation or exploring
additional Auth0 features that can enhance your authentication flows.
==============================
# 2-aws-cognito.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/integrations/2-aws-cognito
# AWS Cognito
## Introduction
In this tutorial, you'll learn how to configure an existing
[AWS Cognito user pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools.html) and generate
a JWT which you can pass in the header of your requests to Hasura. After setting up your
[AuthConfig](/reference/metadata-reference/auth-config.mdx) object to use JWT mode, this will allow you to validate
users' identities and create [permission rules](/reference/metadata-reference/permissions.mdx) which can limit access to
underlying data served by Hasura DDN.
:::info Prerequisites
Before continuing, ensure you have:
- An AWS Cognito [user pool](https://manage.auth0.com/dashboard) with a domain configured.
- A local application that can integrate with Cognito for authentication.
- A local Hasura DDN project.
:::
## Tutorial
### Step 1. Create a Lambda trigger for modifying JWT claims
To add the custom claims that Hasura requires, you will need to create an AWS Lambda function and set it as a trigger
for your Cognito user pool.
In the [AWS Lambda console](https://console.aws.amazon.com/lambda/home), create a new Lambda function. Select the
`Author from scratch` option and provide a name, runtime, architecture, and any advanced settings you wish to configure.
After your Lambda is created, you'll be redirected to an editor where you can modify the Lambda's handler. Add the
following code to modify the Cognito JWT and inject the custom Hasura namespace claims:
```javascript
export const handler = async (event) => {
// Here, you'll need to fetch the user's role from Hasura DDN using an admin-level authenticated request
// Learn more here: https://hasura.io/docs/3.0/auth/authentication/jwt/special-roles
// Below, we're hard-coding the value for now
const user_role = "user"; // the role returned from your request βοΈ
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
"claims.jwt.hasura.io": JSON.stringify({
"x-hasura-user-id": event.request.userAttributes.sub,
"x-hasura-default-role": user_role,
"x-hasura-allowed-roles": ["user"],
}),
},
},
};
return event;
};
```
This will add the required Hasura namespace with the keys that Hasura DDN expects when decoding a JWT. You can modify
the keys to suit your Hasura DDN [roles](/reference/metadata-reference/permissions.mdx#typepermissions).
Click `Deploy`.
:::tip Custom claims
You can create any custom keys you wish and reference them in your permissions using session variables. Above,
`x-hasura-user-id` is simply an example. Any claim prefixed with `x-hasura-` is accessible to the Hasura DDN Engine.
:::
### Step 2. Add the Lambda as an Authentication trigger
From your user pool's dashboard, select the `User pool properties` tab and then click `Add Lambda trigger`. Choose
`Authentication` as the trigger type and choose `Pre token generation trigger`.
Then, select the Lambda you generated in the previous step and click `Add Lambda trigger`.
### Step 3. Update your AuthConfig
Update your AuthConfig object to use JWT mode and your
[Cognito JWKs](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html),
which you can find on your User pool overview card:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: StringifiedJson
location: "/claims.jwt.hasura.io"
key:
jwkFromUrl: "https://cognito-idp..amazonaws.com/_.well-known/jwks.json"
tokenLocation:
type: Header
name: Auth-Token
```
Then, create a new build of your supergraph:
```sh
ddn supergraph build local
```
### Step 4. Test your configuration
Generate a new JWT by logging into your application. These values aren't typically displayed to users, so you'll need to
log them while in development. You can then add that value as a header in the console and test any permissions you have
in your metadata.
## Wrapping up
In this guide, you learned how to integrate AWS Cognito with Hasura DDN to create a secure and scalable identity
management solution using JWTs. By leveraging custom claims in conjunction with
[permissions](/reference/metadata-reference/permissions.mdx), you can define precise access-control rules, ensuring that
your application remains secure and meets your users' needs.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
If you encounter issues or need further customization, consider reviewing our related documentation or exploring
additional AWS Cognito features that can enhance your authentication flows.
==============================
# 3-firebase.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/integrations/3-firebase
# Firebase
## Introduction
In this tutorial, you'll learn how to configure an existing [Firebase project](https://console.firebase.google.com/) and
generate a JWT which you can pass in the header of your requests to Hasura. After setting up your
[AuthConfig](/reference/metadata-reference/auth-config.mdx) object to use JWT mode, this will allow you to validate
users' identities and create [permission rules](/reference/metadata-reference/permissions.mdx) which can limit access to
underlying data served by Hasura DDN.
:::info Prerequisites
Before continuing, ensure you have:
- A [Firebase project](https://console.firebase.google.com/) created, with at least one authentication method enabled.
- A local application that can integrate with Firebase for authentication. We'll provide an example Node.js application
below.
- A private key from the project's `Service Accounts` section via the
[Firebase console](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk).
- A local Hasura DDN project.
:::
## Tutorial
### Step 1. Add Firebase to your local application
#### Step 1.1 Add the firebase-admin package
Firebase works with a number of [languages and frameworks](https://firebase.google.com/docs/admin/setup#add-sdk). In the
example(s) we show below, we'll use a Node.js application.
```sh
npm i firebase-admin
```
#### Step 1.2 Initialize firebase-admin
Then, initialize the module in your application:
```javascript
const admin = require("firebase-admin");
// service_account.json points to the private key from the prerequisites
admin.initializeApp({ credential: admin.credential.cert(require("./service_account.json")) });
```
#### Step 1.3 Add the custom claims
```javascript
// Here, you'll need to fetch the user's role from Hasura DDN using an admin-level authenticated request
// Learn more here: https://hasura.io/docs/3.0/auth/authentication/jwt/special-roles
// Below, we're hard-coding the value for now
const user_role = "user"; // the role returned from your request βοΈ
const customClaims = {
"claims.jwt.hasura.io": {
"x-hasura-default-role": user_role,
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": decodedToken.uid,
},
};
// Set custom claims for the user based on their uid
await admin.auth().setCustomUserClaims(decodedToken.uid, customClaims);
```
This will add the required Hasura namespace with the keys that Hasura DDN expects when decoding a JWT. You can modify
the keys to suit your Hasura DDN [roles](/reference/metadata-reference/permissions.mdx#typepermissions).
:::tip Custom claims
You can create any custom keys you wish and reference them in your permissions using session variables. Above,
`x-hasura-user-id` is simply an example. Any claim prefixed with `x-hasura-` is accessible to the Hasura DDN Engine.
:::
### Step 2. Update your AuthConfig
Update your AuthConfig object to use JWT mode and your
[Firebase JWKs](https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com) and audience:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
audience: ["your-firebase-project-name"]
claimsConfig:
namespace:
claimsFormat: Json
location: "/claims.jwt.hasura.io"
key:
jwkFromUrl: "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"
tokenLocation:
type: Header
name: Auth-Token
```
:::info Firebase JWKs
Firebase uses the same set of JWKs for all applications. It distinguishes between different apps by specifying the
audience (`aud`) claim in the JWT. Make sure to set the audience field to match your Firebase project name in the
AuthConfig object to ensure proper validation.
:::
Then, create a new build of your supergraph:
```sh
ddn supergraph build local
```
### Step 3. Test your configuration
Generate a new JWT by logging into your application. These values aren't typically displayed to users, so you'll need to
log them while in development. You can then add that value as a header in the console and test any permissions you have
in your metadata.
Here's the complete sample Node.js server.
```javascript
const express = require("express");
const admin = require("firebase-admin");
const bodyParser = require("body-parser");
const axios = require("axios");
// Initialize Firebase Admin SDK
admin.initializeApp({
credential: admin.credential.cert(require("./service_account.json")),
});
const app = express();
app.use(bodyParser.json());
// Firebase API key from your Firebase project settings
const FIREBASE_API_KEY = "your API key found on the Firebase project's console";
// Route to handle user login with email and password
app.post("/login", async (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ message: "Email and password are required" });
}
try {
// Call Firebase REST API to sign in the user with email and password
const response = await axios.post(
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${FIREBASE_API_KEY}`,
{
email,
password,
returnSecureToken: true,
},
);
const { idToken } = response.data;
// Verify the token using Firebase Admin SDK
const decodedToken = await admin.auth().verifyIdToken(idToken);
// Here, you'll need to fetch the user's role from Hasura DDN using an admin-level authenticated request
// Learn more here: https://hasura.io/docs/3.0/auth/authentication/jwt/special-roles
// Below, we're hard-coding the value for now
const user_role = "user"; // the role returned from your request βοΈ
const customClaims = {
"claims.jwt.hasura.io": {
"x-hasura-default-role": user_role,
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": decodedToken.uid,
},
};
// Set custom claims for the user based on their uid
await admin.auth().setCustomUserClaims(decodedToken.uid, customClaims);
// Send the updated JWT back in the response
res.status(200).json({
idToken,
});
} catch (error) {
console.error("Error logging in:", error.response?.data || error.message);
res.status(401).json({ message: "Invalid credentials", error: error.response?.data || error.message });
}
});
app.listen(4000, () => {
console.log("Server running on port 4000");
});
```
## Wrapping up
In this guide, you learned how to integrate Firebase with Hasura DDN to create a secure and scalable identity management
solution using JWTs. By leveraging custom claims in conjunction with
[permissions](/reference/metadata-reference/permissions.mdx), you can define precise access-control rules, ensuring that
your application remains secure and meets your users' needs.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
If you encounter issues or need further customization, consider reviewing our related documentation or exploring
additional Firebase features that can enhance your authentication flows.
==============================
# 4-clerk.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/integrations/4-clerk
# Clerk
## Introduction
In this tutorial, you'll learn how to configure an existing [Clerk application](https://clerk.com/) and generate a JWT
which you can pass in the header of your requests to Hasura. After setting up your
[AuthConfig](/reference/metadata-reference/auth-config.mdx) object to use JWT mode, this will allow you to validate
users' identities and create [permission rules](/reference/metadata-reference/permissions.mdx) which can limit access to
underlying data served by Hasura DDN.
:::info Prerequisites
Before continuing, ensure you have:
- A [Clerk application](https://clerk.com/docs/quickstarts/setup-clerk).
- A local application that can integrate with Clerk for authentication.
- A local Hasura DDN project.
### Step 1. Create a JWT template
From your [Clerk application's dashboard](https://dashboard.clerk.com/), click `JWT templates` in the sidenav and create
a new blank template. You can name this whatever you wish along with configuring properties like the token's lifetime,
clock skew, etc.
In the claims editor, add the following:
```javascript
{
"claims.jwt.hasura.io": {
"x-hasura-user-id": "{{user.id}}",
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": [
"user"
]
}
}
```
This will add the required Hasura namespace with the keys that Hasura DDN expects when decoding a JWT. You can modify
the keys to suit your Hasura DDN [roles](/reference/metadata-reference/permissions.mdx#typepermissions).
You can also see that we're hard-coding the user's role. You can dynamically set a user's role using Clerk's metadata to
set a `role` field on the `User` object whenever a user registers. You can learn more
[here](https://clerk.com/docs/users/metadata).
This enables you to then pass the value of `{{user.publicMetadata.role}}` in the custom claims of the JWT.
:::tip Custom claims
You can create any custom keys you wish and reference them in your permissions using session variables. Above,
`x-hasura-user-id` is simply an example. Any claim prefixed with `x-hasura-` is accessible to the Hasura DDN Engine.
:::
### Step 2. Update your AuthConfig
Update your AuthConfig object to use JWT mode and your Clerk JWKs, which rely on your Clerk domain name found in the
sidenav under `Domains`:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: "/claims.jwt.hasura.io"
key:
jwkFromUrl: "https:///.well-known/jwks.json"
tokenLocation:
type: Header
name: Auth-Token
```
Then, create a new build of your supergraph:
```sh
ddn supergraph build local
```
### Step 3. Test your configuration
Generate a new JWT by logging into your application. These values aren't typically displayed to users, so you'll need to
log them while in development. You can then add that value as a header in the console and test any permissions you have
in your metadata.
Clerk's [various SDKs](https://clerk.com/docs/references/overview) make this easy, as you can pass the name of a JWT
template and get back the encoded token.
```javascript title="As an example, using the JavaScript SDK:"
const jwt = await session.getToken({ template: "hasura" });
```
## Wrapping up
In this guide, you learned how to integrate Clerk with Hasura DDN to create a secure and scalable identity management
solution using JWTs. By leveraging custom claims in conjunction with
[permissions](/reference/metadata-reference/permissions.mdx), you can define precise access-control rules, ensuring that
your application remains secure and meets your users' needs.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
If you encounter issues or need further customization, consider reviewing our related documentation or exploring
additional Clerk features that can enhance your authentication flows.
==============================
# setup-test-jwt.mdx
URL: https://hasura.io/docs/3.0/docs/auth/jwt/tutorials/setup-test-jwt
# Set up a JWT for Testing
By default, your supergraph uses your Hasura Cloud authentication token, also known as a personal access token (PAT),
for authentication. This is convenient for testing from the console but should not be used in an application or shared
with others.
Instead, to test authentication you need to set up an
[`AuthConfig` object](/reference/metadata-reference/auth-config.mdx) and then generate the corresponding token.
## Step 1: Install the jwt-cli
Install the [`jwt-cli`](https://github.com/mike-engel/jwt-cli), which allows you to generate tokens from the command
line. You can follow their list of installation instructions found
[here](https://github.com/mike-engel/jwt-cli?tab=readme-ov-file#installation).
## Step 2: Generate a random string
Generate a random string that we'll use as the JWT secret key:
```bash title="In your teminal, run the following command"
openssl rand -hex 16
```
Copy the value returned by the terminal.
:::info Creating a random string
If you don't want to use openssl, you can use any other random string generators. The only requirement is that the
string must be at least 32 characters.
:::
## Step 3: Set up your AuthConfig object
Set up an `AuthConfig` object in your project which uses this secret key.
```yaml title="In globals/metadata/auth-config.hml:"
kind: AuthConfig
version: v4
definition:
mode:
jwt:
claimsConfig:
namespace:
claimsFormat: Json
location: "/claims.jwt.hasura.io"
key:
fixed:
algorithm: HS256
key:
value: ""
tokenLocation:
type: Header
name: Auth-Token
```
## Step 4: Create a new supergraph build
Create a supergraph build using this `AuthConfig`.
```bash title="From the root of your project, run:"
ddn supergraph build create \
--description "use jwt-based authconfig" \
--supergraph supergraph.yaml
```
## Step 5: Generate a JWT
For testing, you can use the `jwt-cli` to encode and generate a new token with the different claims written to match
your testing needs.
```bash title="Run the following with your own values:"
jwt encode --secret="" '{"exp": 1739905122,"iat": 1708369122,"claims.jwt.hasura.io":{"x-hasura-default-role": "admin","x-hasura-allowed-roles":["admin"]}}'
```
In the example above, we're setting the following values:
- The issued (`iat`) time as `Feb. 19 2024, at 18:58:42` as a Unix epoch timestamp.
- The expiration (`exp`) time as `Feb. 18, 2025 at 18:58:42`.
- The default role as `admin`.
- The allowed roles as `admin`.
For more information about the claims Hasura expects, check out [this page](/auth/jwt/jwt-configuration.mdx).
## Step 6: Test your AuthConfig
In the Hasura console, add the JWT generated by the console as the value of a new header called `Auth-token` on the
GraphiQL explorer. You should now be able to execute queries with your custom JWT.
:::info Using environment variables
If you're storing your secret key's value as an environment variable, ensure you've updated the `subgraph.yaml` in the
`globals` subgraph to include this envMapping.
:::
==============================
# 3-hash-passwords.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/3-hash-passwords
# Hash Passwords
## Introduction
In this recipe, you'll learn how to securely hash passwords to protect user credentials in your application. Password
hashing is an essential part of securing sensitive information before storing it in your database.
:::info Prerequisites
Before continuing, ensure you have:
- A [local Hasura DDN project](/quickstart.mdx).
- A [lambda connector](/business-logic/overview.mdx) added to your project.
- Familiarity with bcrypt for hashing passwords.
**NB: Bcrypt is a common, well-tested library for password hashing in various languages, and it's widely supported
across many systems.**
:::
## Recipe
### Step 1. Write the function
```sh title="In your connector's directory, install the bcrypt package:"
npm install bcryptjs
```
```typescript title="In your functions.ts file, add the following:"
export async function hashPassword(password: string): Promise {
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
// Add your own logic here to hit your Hasura endpoint and perform an insertion
return hashedPassword;
}
```
```plaintext title="In your requirements.txt, add the bcrypt package:"
bcrypt==4.2.0
```
```python title="In your functions.py file, add the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
connector = FunctionConnector()
@connector.register_mutation
async def hash_password(password: str) -> str:
salt = bcrypt.gensalt()
hashedPassword = bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8")
# Add your own logic here to hit your Hasura endpoint and perform an insertion
return hashedPassword
if __name__ == "__main__":
start(connector)
```
```sh title="Add the bcrypt package and its dependencies to your connector's go.mod:"
go get golang.org/x/crypto/bcrypt
go get golang.org/x/net/idna@v0.26.0
```
```go title="In a Go file inside your functions directory, add the following:"
package functions
"context"
"fmt"
"golang.org/x/crypto/bcrypt"
"hasura-ndc.dev/ndc-go/types"
)
// HashPasswordArguments defines the input arguments for the function
type HashPasswordArguments struct {
Password string `json:"password"`
}
// HashPasswordResult defines the output result for the function
type HashPasswordResult string
// ProcedureHashPassword hashes a password string and returns it as a string result
func ProcedureHashPassword(ctx context.Context, state *types.State, arguments *HashPasswordArguments) (*HashPasswordResult, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(arguments.Password), bcrypt.DefaultCost)
if err != nil {
return nil, fmt.Errorf("failed to hash password: %v", err)
}
// Add your own logic here to hit your Hasura endpoint and perform an insertion
result := HashPasswordResult(string(hashedPassword))
return &result, nil
}
```
### Step 2. Track your function
To add your function, generate the related metadata that will link together any functions in your lambda connector's
source files and your API:
```bash
ddn connector introspect
```
Then, you can generate an `hml` file for the function using the following command:
```bash
ddn command add "*"
```
### Step 3. Test your function
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
In your project's explorer, you should see the new function exposed as a type and you should be able to execute a
mutation like this:
## Wrapping up
In this guide, you learned how to enhance your API by securely hashing passwords before storing them in your database.
Your API clients can invoke this mutation and you can handle all of the logic of hashing and inserting the new record
directly from your API.
## Learn more about lambda connectors
- [TypeScript](/business-logic/overview.mdx) Node.js connector.
- [Python](/business-logic/overview.mdx) connector.
- [Go](/business-logic/overview.mdx) connector.
## Similar recipes
- [Custom business logic recipes](/business-logic/tutorials/index.mdx)
==============================
# 6-validate-credentials.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/6-validate-credentials
# Validate Credentials
## Introduction
In this recipe, you'll learn how to compare a raw text value, such as a user password, with a stored hashed password.
This is critical when authenticating users securely in your application.
:::info Prerequisites
Before continuing, ensure you have:
- A [local Hasura DDN project](/quickstart.mdx).
- A [lambda connector](/business-logic/overview.mdx) added to your project.
- Familiarity with bcrypt for hashing and comparing passwords.
**NB: The bcrypt library is a secure and widely supported method for password handling across various systems.**
:::
## Recipe
### Step 1. Write the function
```sh title="In your connector's directory, install the bcrypt package:"
npm install bcryptjs
```
```typescript title="In your functions.ts file, add the following:"
/**
* @readonly
*/
export async function comparePassword(password: string, hashedPassword: string): Promise {
return await bcrypt.compare(password, hashedPassword);
}
```
```plaintext title="In your requirements.txt, add the bcrypt package:"
bcrypt==4.2.0
```
```python title="In your functions.py file, add the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
connector = FunctionConnector()
@connector.register_query
async def compare_password(password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(password.encode("utf-8"), hashed_password.encode("utf-8"))
if __name__ == "__main__":
start(connector)
```
```sh title="Add the bcrypt package and its dependencies to your connector's go.mod:"
go get golang.org/x/crypto/bcrypt
go get golang.org/x/net/idna@v0.26.0
```
```go title="In a Go file inside your functions directory, add the following:"
package functions
"context"
"golang.org/x/crypto/bcrypt"
"hasura-ndc.dev/ndc-go/types"
)
// ComparePasswordArguments defines the input arguments for the function
type ComparePasswordArguments struct {
Password string `json:"password"`
HashedPassword string `json:"hashed_password"`
}
// ComparePasswordResult defines the output result for the function
type ComparePasswordResult string
// FunctionComparePassword compares a password with a hashed password
func FunctionComparePassword(ctx context.Context, state *types.State, arguments *ComparePasswordArguments) (*ComparePasswordResult, error) {
err := bcrypt.CompareHashAndPassword([]byte(arguments.HashedPassword), []byte(arguments.Password))
if err != nil {
result := ComparePasswordResult("false")
return &result, nil
}
result := ComparePasswordResult("true")
return &result, nil
}
```
### Step 2. Track your function
To add your function, generate the related metadata that will link together any functions in your lambda connector's
source files and your API:
```bash
ddn connector introspect
```
Then, you can generate an `hml` file for the function using the following command:
```bash
ddn command add "*"
```
### Step 3. Create a relationship (optional)
It's a safe assumption that the argument's input type matches that of a `password` field belonging to a User model; you
can create a relationship from the type to the command. This will enable you to make nested queries that will invoke
your custom business logic using the value of the field from the related model!
Create a relationship in the corresponding model's HML file.
```yaml title="For example, if we have a Users model:"
---
kind: Relationship
version: v1
definition:
name: comparePassword
sourceType: Users
target:
command:
name: ComparePassword
mapping:
- source:
fieldPath:
- fieldName: password
target:
argument:
argumentName: hashedPassword
```
### Step 4. Test your function
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
In your project's explorer, you should see the new function exposed as a type and should be able to make a query like
this:
If you created a relationship, you can make a query like this, too:
## Wrapping up
In this guide, you learned how to securely compare raw text values to hashed passwords to authenticate users in your
API. By leveraging lambda connectors with [relationships](/reference/metadata-reference/relationships.mdx), you can add
custom business logic to your authentication flows.
## Learn more about lambda connectors
- [TypeScript](/business-logic/overview.mdx) Node.js connector.
- [Python](/business-logic/overview.mdx) connector.
- [Go](/business-logic/overview.mdx) connector.
## Similar recipes
- [Custom business logic recipes](/business-logic/tutorials/index.mdx)
==============================
# 5-enrich-data-with-an-llm.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/5-enrich-data-with-an-llm
# Enrich data with LLMs
## Introduction
In this recipe, you'll learn how to interact with OpenAI's API to send prompts and receive responses. This can be used
to integrate AI-driven features, such as generating text or completing tasks, into your API. In the example below, we'll
hard-code a prompt and have OpenAI apply it to existing content in our supergraph.
:::info Prerequisites
Before continuing, ensure you have:
- A [local Hasura DDN project](/quickstart.mdx).
- A [lambda connector](/business-logic/overview.mdx) added to your project.
- An OpenAI API key (You can get one by signing up at [OpenAI](https://beta.openai.com/signup)).
:::
## Recipe
### Step 1. Write the function
```sh title="In your connector's directory, install the OpenAI package:"
npm install openai
```
```typescript title="In your functions.ts file, add the following:"
// We can store this in the project's .env file and reference it here
const OPENAI_API_KEY =
"your_openai_api_key";
const client = new OpenAI({
apiKey: OPENAI_API_KEY,
});
/**
* @readonly
*/
export async function generateSeoDescription(input: string): Promise {
const response = await client.chat.completions.create({
messages: [
{
role: "system",
content:
"You are a senior marketing associate. Take the product description provided and improve upon it to rank well with SEO.",
},
{ role: "user", content: input },
],
model: "gpt-4o",
});
return response.choices[0].message.content;
}
```
```plaintext title="In your connector's directory, add the OpenAI Python client package to your requirements.txt:"
openai==1.46.1
```
```python title="In your functions.py file, add the following:"
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
from openai import OpenAI
connector = FunctionConnector()
# We can store this in the project's .env file and referene it here
OPENAI_API_KEY = "your_openai_api_key"
client = OpenAI(
api_key=OPENAI_API_KEY,
)
@connector.register_query
def generate_seo_description(input: str) -> str:
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "You are a senior marketing associate. Take the product description provided and improve upon it to rank well with SEO."},
{"role": "user", "content": input}
],
)
return response.choices[0].message.content
if __name__ == "__main__":
start(connector)
```
```go title="In a Go file inside your functions directory, add the following:"
package functions
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"hasura-ndc.dev/ndc-go/types"
)
type SEOArguments struct {
Input string `json:"input"`
}
// OpenAIRequest represents the request payload for OpenAI's Chat API
type OpenAIRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
}
// Message defines the structure for messages sent to OpenAI
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
// OpenAIResponse represents the response from OpenAI
type OpenAIResponse struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
}
// GenerateSeoDescription sends a request to OpenAI and generates an SEO-optimized product description
func FunctionGenerateSeoDescription(ctx context.Context, state *types.State, arguments *SEOArguments) (string, error) {
// We can store this in the project's .env file and reference it here
apiKey := "your_openai_api_key"
// Prepare the request payload
reqBody, _ := json.Marshal(OpenAIRequest{
Model: "gpt-4",
Messages: []Message{
{Role: "system", Content: "You are a senior marketing associate. Take the product description provided and improve upon it to rank well with SEO."},
{Role: "user", Content: arguments.Input},
},
})
// Create a new request to the OpenAI Chat API
req, _ := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(reqBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("failed to send request: %v", err)
}
defer resp.Body.Close()
// Parse the response from OpenAI
body, _ := ioutil.ReadAll(resp.Body)
var openAIResp OpenAIResponse
json.Unmarshal(body, &openAIResp)
return openAIResp.Choices[0].Message.Content, nil
}
```
### Step 2. Track your function
To add your function, generate the related metadata that will link together any functions in your lambda connector's
source files and your API:
```bash
ddn connector introspect
```
Then, you can generate an `hml` file for the function using the following command:
```bash
ddn command add "*"
```
### Step 3. Create a relationship (optional)
Assuming the input argument's type matches that of a type belonging to one or more of your models, you can create a
relationship to the command. This will enable you to make nested queries that will invoke your custom business logic
using the value of the field from the related model!
Create a relationship in the corresponding model's HML file.
```yaml title="For example, if we have a Prompts model:"
---
kind: Relationship
version: v1
definition:
name: optimizedDescription
sourceType: Products
target:
command:
name: generateSeoDescription
mapping:
- source:
fieldPath:
- fieldName: description
target:
argument:
argumentName: input
```
### Step 4. Test your function
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
In your project's explorer, you should see the new function exposed as a type and should be able to make a query like
this:
If you created a relationship, you can make a query like this, too:
## Wrapping up
In this guide, you learned how to send prompts to OpenAI's API and receive responses in your application. By leveraging
lambda connectors with [relationships](/reference/metadata-reference/relationships.mdx), you can easily incorporate
AI-driven capabilities into your existing supergraph.
## Learn more about lambda connectors
- [TypeScript](/business-logic/overview.mdx) Node.js connector.
- [Python](/business-logic/overview.mdx) connector.
- [Go](/business-logic/overview.mdx) connector.
## Similar recipes
- [Custom business logic recipes](/business-logic/tutorials/index.mdx)
==============================
# 7-http-header-forwarding.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/tutorials/7-http-header-forwarding
# HTTP Header Forwarding
## Introduction
Hasura DDN can be configured to forward HTTP request headers to functions implemented in a lambda connector. It can also
be configured to respond to HTTP requests involving the lambda connector with HTTP headers returned by functions in the
lambda connector.
:::info Prerequisites
Before continuing, ensure you have:
- A [local Hasura DDN project](/quickstart.mdx).
- A [lambda connector](/business-logic/overview.mdx) added to your project.
:::
## Recipe - Receiving HTTP request headers
To configure your functions to receive HTTP request headers, perform the following steps:
### Step 1. Add a `headers` function parameter
First, you should modify all functions that you want to receive HTTP request headers and add a `headers` function
parameter, like so:
```typescript title="An example hello function in your functions.ts, updated with a headers parameter"
/** @readonly */
export function hello(headers: sdk.JSONValue, name?: string): string {
const headersMap = headers.value as Record;
return `hello ${name ?? "world"}`;
}
```
The `headers` function parameter will be passed as a JSON object that represents the HTTP request headers. To extract it, we cast the `headers.value` property to the `Record` type.
```go title="An example hello function in your functions.go, updated with a headers parameter"
type HelloArguments struct {
Headers map[string]string `json:"headers,omitempty"`
Name string `json:"name"`
}
func FunctionHello(ctx context.Context, state *types.State, arguments *HelloArguments) (string, error) {
log.Printf("request headers: %v", arguments.Headers)
name := arguments.Name
if name == "" {
name = "world"
}
return "hello " + name, nil
}
```
Support coming soon!
:::note
You don't need to call the function parameter `headers`. You can use any name you wish, but it must be the same name
across all functions and it must always be used to receive HTTP headers.
:::
### Step 2. Update the metadata
Next, we need to re-introspect the connector given that we just changed the definition of our functions.
```bash title="Introspect the connector"
ddn connector introspect my_ts
```
Then, we need to open the HML file that contains the `DataConnectorLink` metadata object for the connector (usually
found in `/metadata/.hml`) and edit it.
:::note
From CLI v2.18.0, you can use the codemod `ddn codemod configure-header-forwarding` to configure the `DataConnectorLink`
to forward headers to the connector.
:::
We use
[argument presets](/reference/metadata-reference/data-connector-links.mdx#dataconnectorlink-dataconnectorargumentpreset)
to automatically set our `headers` function parameters with the HTTP request headers. Below is an example where we
forward the `X-Test-Header` header to the connector. **Keep in mind that only headers listed here are forwarded to the
connector.**
```yaml title=my_ts.hml
kind: DataConnectorLink
version: v1
definition:
name: my_ts
url:
readWriteUrls:
read:
valueFromEnv: MY_SUBGRAPH_MY_TS_READ_URL
write:
valueFromEnv: MY_SUBGRAPH_MY_TS_WRITE_URL
headers:
Authorization:
valueFromEnv: MY_SUBGRAPH_MY_TS_AUTHORIZATION_HEADER
# highlight-start
argumentPresets:
- argument: headers
value:
httpHeaders:
forward:
- X-Test-Header
additional: {}
# highlight-end
schema: ...
```
### Step 3. Create a new API build and test
Now, we can rebuild the supergraph and test our changes:
```bash title="Run:"
ddn supergraph build local
```
:::warning `headers` already mapped error
If you get a build error saying "the argument headers is mapped to the data connector argument headers which is already
used as an argument preset in the DataConnectorLink" then you need to open HML file containing the `Command` mentioned
in the error and remove the headers argument from the `Command`'s `arguments` list and from the `argumentMapping`, if it
exists.
This is because when you preset arguments in your `DataConnectorLink`, they are automatically set by the DDN engine and
therefore are not defined on the `Command` as arguments that API users can set.
:::
:::tip Start your engines!
Don't forget to start your GraphQL engine using the following command.
```bash title="From the root of your project, run:"
ddn run docker-start
```
This reads the `docker-start` script from the context config at `.hasura/context.yaml` and starts your Hasura engine,
any connectors, and observability tools.
:::
Launch the Hasura console to see and test your GraphQL API using:
```bash title="Run:"
ddn console --local
```
## Recipe - Returning HTTP response headers
To configure your lambda connector to return HTTP response headers, perform the following steps:
### Step 1. Modify the function return type
First, you should add a helper type that you can re-use that will contain your headers as well as the actual value you
want to return from your function:
```typescript title="In your functions.ts, add the following"
type HeadersResponse = {
headers: sdk.JSONValue
response: T
}
```
```go title="In your functions.go, add the following"
type HeadersResponse[T any] struct {
Headers map[string]string `json:"headers"`
Response T `json:"response"`
}
```
Support coming soon!
Then, you can modify your function return value to use this type. In the following example, we take the headers received
in the previous section and add an additional header `X-Test-ResponseHeader` to be returned on the response. We then
return that, as well as our response string, inside our new `HeadersResponse` object.
```typescript title="An example hello function in your functions.ts, updated to return a HeadersResponse type"
/** @readonly */
export function hello(headers: sdk.JSONValue, name?: string): HeadersResponse {
const headersMap = headers.value as Record;
headersMap["X-Test-ResponseHeader"] = "I set this in the code";
return {
headers: new sdk.JSONValue(headersMap),
response: `hello ${name ?? "world"}`
};
}
```
```go title="An example hello function in your functions.go, updated to return a HeadersResponse type"
func FunctionHello(ctx context.Context, state *State, arguments *HelloArguments) (HeadersResponse[string], error) {
headersMap := arguments.Headers
if headersMap == nil {
headersMap = map[string]string{}
}
headersMap["X-Test-ResponseHeader"] = "I set this in the code"
name := arguments.Name
if name == "" {
name = "world"
}
return HeadersResponse[string]{
Headers: headersMap,
Response: "hello " + name,
}, nil
}
```
Support coming soon!
### Step 2. Update the metadata
Next, we need to re-introspect the connector given that we just changed the definition of our functions.
```bash title="Introspect the connector"
ddn connector introspect my_ts
```
Then, we need to open the HML file that contains the `DataConnectorLink` metadata object for the connector (usually
found in `/metadata/.hml`) and edit it.
We will be using the
[`responseHeaders` configuration property](/reference/metadata-reference/data-connector-links.mdx#dataconnectorlink-responseheaders)
to configure which headers returned by our connector functions we want returned as a part of our HTTP response headers.
In the below example, the `X-Test-Header` and `X-Test-ResponseHeader` headers are listed under `forwardHeaders` to
ensure they are added to the HTTP response if they are returned by the connector function. We also set the
`headersField` and `resultField` properties to the two property names we defined on the `HeadersResponse` type we
defined earlier.
```yml title=my_ts.hml
kind: DataConnectorLink
version: v1
definition:
name: my_ts
url:
readWriteUrls:
read:
valueFromEnv: MY_SUBGRAPH_MY_TS_READ_URL
write:
valueFromEnv: MY_SUBGRAPH_MY_TS_WRITE_URL
headers:
Authorization:
valueFromEnv: MY_SUBGRAPH_MY_TS_AUTHORIZATION_HEADER
argumentPresets:
- argument: headers
value:
httpHeaders:
forward:
- X-Test-Header
additional: {}
# highlight-start
responseHeaders:
headersField: headers
resultField: response
forwardHeaders:
- X-Test-Header
- X-Test-ResponseHeader
# highlight-end
schema: ...
```
### Step 3. Create a new API build and test
Now, we can rebuild the supergraph and test our changes:
```bash title="Run:"
ddn supergraph build local
```
:::warning type is not defined in the agent schema error
The [`outputType`](/reference/metadata-reference/commands.mdx#command-commandv1) of `Command`s that represent our
functions should be the [OpenDD Scalar Type](/reference/metadata-reference/types.mdx) used to represent type of the
`HeadersResponse.response` property, not the `HeadersResponse` type itself.
So, using our above example, the `Hello` `Command`'s `outputType` should be `String!`, not `HeaderResponseString!`. If
it is incorrectly configured, you may get a build error such as "NDC validation error: type String is not defined in the
agent schema".
:::
:::tip Start your engines!
Don't forget to start your GraphQL engine using the following command.
```bash title="From the root of your project, run:"
ddn run docker-start
```
This reads the `docker-start` script from the context config at `.hasura/context.yaml` and starts your Hasura engine,
any connectors, and observability tools.
:::
Launch the Hasura console to see and test your GraphQL API using:
```bash title="Run:"
ddn console --local
```
==============================
# add-a-lambda-connector.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/add-a-lambda-connector
# Add a Lambda Connector
## Introduction
You can add a lambda connector just like any other [data source](/data-sources/overview.mdx).
## Add a connector
```sh title="Initialize a new connector in a project directory:"
ddn connector init -i
```
When you add the `hasura/nodejs` connector, the CLI will generate a Node.js package with a `functions.ts` file. This
file is the entrypoint for your connector.
As this is a Node.js project, you can easily add any dependencies you desire by running `npm i ` from this
connector's directory.
When you add the `hasura/python` connector, the CLI will generate a Python application with a `functions.py` file. This
file is the entrypoint for your connector.
As this is a Python project, you can easily add any dependencies you desire by adding them to the `requirements.txt` in
this connector's directory.
When you add the `hasura/go` connector, the CLI will generate a Go application with a `/functions` directory. The
connector will use this directory β and any `*.go` file in it β as the entrypoint for your connector.
As this is a Go project, you can easily add any dependencies you desire by adding them to the `go.mod` file and running
`go mod tidy` from this connector's directory.
:::info Customization
You can customize which subgraph this connector is added to by
[changing your project's context](/reference/cli/commands/ddn_context.mdx) or using flags. More information can be found
in the [CLI docs](/reference/cli/commands/ddn_connector_init.mdx) for the `ddn connector init` command.
:::
## Next steps
After adding a connector, learn [how to add custom business logic](/business-logic/tutorials/1-add-custom-logic.mdx)
written in your language of choice and expose it via your API.
==============================
# webhook-mode.mdx
URL: https://hasura.io/docs/3.0/docs/auth/webhook/webhook-mode
# Webhook Mode
## Introduction
You can enable your Hasura DDN instance to use an auth webhook in just a few steps.
You will need to provide a URL that Hasura will call with the original request headers, and it should return a body with
the session variables after the request is authenticated.
## Session variable requirements
The only session variable required is `x-hasura-role` appearing in the response body.
In contrast to JWT mode, you do not have to pass `x-hasura-allowed-roles` or `x-hasura-default-role` session variables
and a `x-hasura-role` header will no be checked.
Session variable keys are case-insensitive. Values are case-sensitive.
## Enabling Webhook authentication
## Step 1. Update your AuthConfig {#update-authconfig}
Hasura utilizes an [AuthConfig](/reference/metadata-reference/auth-config.mdx) object that allows you to define the
configuration for your authentication service. In a standard setup the `auth-config.hml` file can be found in your
`globals` directory.
:::tip Hasura DDN VS Code extension
You can use [Hasura's VS Code extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) to
scaffold out your `AuthConfig` object by typing `AuthConfig` and selecting this object from the list of available
options. As you navigate through the skeleton, you can type `CTRL+SPACEBAR` at any point to reveal options for the
different key-value pairs.
:::
In the example below, we're demonstrating a sample authentication webhook.
```yaml title="globals/metadata/auth-config.hml"
kind: AuthConfig
version: v4
definition:
mode:
webhook:
url:
valueFromEnv: AUTH_WEBHOOK_URL
method: POST
customHeadersConfig:
body:
headers:
forward:
- authorization
- content-type
headers:
additional:
user-agent: "Hasura DDN"
```
```yaml title="Example .env file"
AUTH_WEBHOOK_URL=http://auth_hook:3050/validate-request
```
### GET vs POST
For a POST request, the headers received by the DDN engine on the query can be forwarded to the webhook in a JSON object
in the body of the request under a `headers` key.
For a GET request, the headers received by the DDN engine on the query can be forwarded to the webhook as actual headers
on the request and the body will be empty.
What we've provided above is a sample configuration as a POST request but it can be configured as a GET request. See the
reference for `AuthHookConfig` [here](/reference/metadata-reference/auth-config.mdx#authconfig-authhookconfigv3). For
the sample configuration above, the webhook will receive the `Authorization` and `Content-Type` headers in the body of
the request:
```json title="Example body of a POST request"
{
"headers": {
"Authorization": "Bearer some-token",
"Content-Type": "application/json"
}
}
```
Also, the webhook will receive the `user-agent` header as an actual header on the request.
:::tip Environment variables
You can use environment variables to dynamically and securely add the webhook URL to your AuthConfig. You can add these
values to your root-level `.env` and then map them in the `globals` subgraph.yaml file. Alternatively, you can include
raw strings here using `value` instead of `valueFromEnv`.
:::
## Step 2. Shaping the webhook request and response
### Request
Below is an example of the header object your webhook might receive in the body of a POST request:
```json title="Example header object"
{
"headers": {
"Authorization": "Bearer some-token",
"Content-Type": "application/json"
}
}
```
Headers are forwarded to the auth webhook from the client on each request received by the Hasura engine either as
headers for a GET request or as a JSON object in the body of a POST request under a `headers` key.
:::tip Custom Headers Configuration
You can configure the headers that are sent to the webhook in the optional `customHeadersConfig` field. If the field is
not provided, the default behavior is to forward all headers (after filtering out commonly used headers) received by the
DDN engine on the query to the webhook.
```yaml title="Ignored headers list"
Accept
Accept-Datetime
Accept-Encoding
Accept-Language
Cache-Control
Connection
Content-Length
Content-MD5
Content-Type
DNT
Host
Origin
Referer
User-Agent
```
When using `customHeadersConfig`, you can explicitly forward any headers, including those that are ignored by default.
This gives you full control over which headers are sent to the webhook.
It is recommended to use the `customHeadersConfig` field to configure only the headers that are required by the webhook
and not forward any other headers. This will help in reducing the size of the request and improve the performance of the
webhook.
:::
In this example, we're passing an encoded JWT in the `Authorization` header, however webhook mode is flexible and you
can pass any headers you wish.
:::tip Forward all headers
If you want to forward all headers received by the DDN engine on the query to the webhook, you can set `forward: "*"`.
This will forward all headers received by the DDN engine on the query to the webhook, excluding the ignored headers.
:::
### Token Parsing
In this example, the webhook is then responsible for validating and parsing the token passed in the header. It will need
to:
- **Extract the Token:** Retrieve the Authorization header from the incoming request and extract the token.
- **Validate the Token:** Use a library or your own logic to validate the token. This involves verifying the token's
signature with the secret key.
- **Extract Claims:** Decode the token to extract the claims.
### Response
Based on the validation result, the webhook will need to respond with either a `200` status code (for a valid token) or
a `401` status code (for an invalid or missing token).
You should respond with session variables beginning with `X-Hasura-*` in an object in the **body** of your response. The
value of each session variable can be any JSON value. These will be available to your
[permissions](/reference/metadata-reference/permissions.mdx) in Hasura.
You will, at least, need to set the `X-Hasura-Role` session variable to let the Hasura DDN know which role to use for
this request. Unlike [JWT auth mode](auth/jwt/jwt-mode.mdx), you do not have to pass `X-Hasura-Allowed-Roles` or
`X-Hasura-Default-Role` session variables.
In the example below the `X-Hasura-Is-Owner` and `X-Hasura-Custom` are examples of custom session variables which can be
used to enforce permissions in your supergraph.
```json title="Example response from your webhook to Hasura DDN"
HTTP/1.1 200 OK
Content-Type: application/json
{
"X-Hasura-Role": "user",
"X-Hasura-User-Id": 25,
"X-Hasura-Is-Owner": "true",
"X-Hasura-Custom": "custom value"
}
```
:::info Session Variables as Trace Attributes
When `sessionVariableToTraceAttributeMap` is configured on `AuthConfigV4`, session variable values returned by the
webhook are also mapped to OpenTelemetry trace attributes and propagated as baggage to downstream services.
If your webhook response includes explicit baggage values, these take precedence over values derived from the same
session variable via `sessionVariableToTraceAttributeMap`.
For more details, see the [AuthConfig reference](/reference/metadata-reference/auth-config).
:::
## Step 3. Define permissions
Let's add some example `TypePermissions` so that an admin role can access all fields in the Orders type, but we restrict
a user role from accessing the `deliveryDate` field.
```bash title="Example TypePermissions for Orders type"
---
kind: TypePermissions
version: v1
definition:
typeName: Orders
permissions:
- role: admin
output:
allowedFields:
- createdAt
- deliveryDate
- id
- isReviewed
- productId
- status
- updatedAt
- userId
# highlight-start
- role: user
output:
allowedFields:
- createdAt
- id
- isReviewed
- productId
- status
- updatedAt
- userId
# highlight-end
```
Let's also add some example `ModelPermissions` so that an admin role can access all rows in the Orders model, but a user
role can only access rows where the userId field matches the user id session variable in the JWT.
```bash title="Example ModelPermissions for Orders model"
---
kind: ModelPermissions
version: v1
definition:
modelName: Orders
permissions:
- role: admin
select:
filter: null
allowSubscriptions: true
# highlight-start
- role: user
select:
filter:
fieldComparison:
field: userId
operator: _eq
value:
sessionVariable: x-hasura-user-id
# highlight-end
```
## Step 4. Rebuild your supergraph
```bash title="For example, from the root of your project, run:"
ddn supergraph build local
```
## Step 5. Make an authenticated request
Here we're making a request to our Hasura DDN instance which will be validated by our webhook which returns a payload of
session variables.
```json title="Example response from our webhook to Hasura DDN"
{
"x-hasura-user-id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb",
"x-hasura-role": "user"
}
```
If we run a query for Orders, we can see that we only get the orders which this user has made and are not able to access
the deliveryDate field.
### Step 6. Set your API to public
Now that you have implemented webhook authentication, you can set your API to public. See here for more information on
[setting your API to public](/auth/private-vs-public.mdx).
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/auth/webhook/tutorials/
# Webhook Tutorials
## Introduction
This section shows you how to handle admin-level and unauthenticated requests when using webhook mode in Hasura DDN.
## Tutorials
- [Admin and unauthenticated requests](/auth/webhook/tutorials/special-roles.mdx)
==============================
# simple-webhook-auth-server.mdx
URL: https://hasura.io/docs/3.0/docs/auth/webhook/tutorials/simple-webhook-auth-server
# Simple Webhook Auth Server Demo Tutorial
This tutorial demonstrates how to create a basic demo webhook authentication server using Node.js and Express. This
example server shows the structure for handling authentication requests from Hasura DDN, but uses mock data instead of
actual validation. In a production environment, you would need to implement proper token validation and user
authentication.
## Prerequisites
- Node.js v16 or higher installed on your system
- Basic understanding of Express.js (v4.x)
- A Hasura DDN project set up
- Basic understanding of JWT tokens and OAuth (for production implementation)
## Project Setup
1. Create a new directory for your project and initialize it:
```bash
mkdir hasura-webhook-auth
cd hasura-webhook-auth
npm init -y
```
2. Install the required dependencies:
```bash
npm install express
```
3. Create a new file called `server.js` and add the following code:
```javascript
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
// Middleware to parse JSON bodies
app.use(express.json());
// Mock function to fetch user info from a token
function fetchUserInfo(token) {
// In a real application, you would:
// 1. Validate the token
// 2. Query your database or auth service
// 3. Return user information
return {
role: "user",
userId: "1",
// Add any other user information needed for your permissions
};
}
// Health check endpoint
app.get("/", (req, res) => {
res.send("Webhook auth server is running");
});
// Webhook endpoint for Hasura DDN
app.post("/webhook", (req, res) => {
// Extract the Authorization header
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({
error: "No authorization token provided",
});
}
try {
// Fetch user information based on the token
const userInfo = fetchUserInfo(token);
// Return the Hasura variables
const hasuraVariables = {
"X-Hasura-Role": userInfo.role,
"X-Hasura-User-Id": userInfo.userId,
// Add any other variables needed for your permissions
};
res.json(hasuraVariables);
} catch (error) {
res.status(500).json({
error: "Error processing authentication request",
});
}
});
// Start the server
app.listen(port, () => {
console.log(`Webhook auth server running on port ${port}`);
});
```
## Running the Server
1. Start the server:
```bash
node server.js
```
2. The server will start on port 3000 (or the port specified in the PORT environment variable).
## Testing the Webhook
You can test the webhook using curl:
```bash
curl -X POST http://localhost:3000/webhook \
-H "Authorization: Bearer your-token-here" \
-H "Content-Type: application/json"
```
The response should look like:
```json
{
"X-Hasura-Role": "user",
"X-Hasura-User-Id": "1"
}
```
## Configuring DDN
You can configure DDN to use your webhook by updating the `auth-config.hml` file in your Hasura DDN project.
```yaml title="globals/metadata/auth-config.hml"
kind: AuthConfig
version: v4
definition:
mode:
webhook:
url:
valueFromEnv: AUTH_WEBHOOK_URL
method: POST
customHeadersConfig:
body:
headers:
forward:
- authorization
- content-type
headers:
additional:
user-agent: "Hasura DDN"
```
## Next Steps
This is a basic implementation. In a production environment, you should:
1. Implement proper token validation with your auth provider:
- Use JWT validation libraries like `jsonwebtoken`
- Verify token signatures with your secret key
- Check token expiration and claims
- Example:
```js
const jwt = require("jsonwebtoken");
async function validateToken(token) {
try {
// Remove 'Bearer ' prefix if present
const tokenValue = token.replace("Bearer ", "");
// Verify JWT token
const decoded = jwt.verify(tokenValue, process.env.JWT_SECRET);
// Check required claims
if (!decoded.sub || !decoded.role) {
throw new Error("Invalid token claims");
}
// Check token expiration
if (decoded.exp && Date.now() >= decoded.exp * 1000) {
throw new Error("Token has expired");
}
return decoded;
} catch (err) {
console.error("Token validation failed:", err.message);
throw new Error("Invalid token");
}
}
```
- For OAuth tokens, validate with the auth provider:
```js
const fetch = require("node-fetch");
async function validateOAuthToken(token) {
try {
const response = await fetch("https://your-auth-provider/oauth2/v1/tokeninfo", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/x-www-form-urlencoded",
},
body: `access_token=${token}`,
});
if (!response.ok) {
throw new Error(`OAuth validation failed: ${response.statusText}`);
}
const data = await response.json();
// Check token validity and required scopes
if (!data.active || !data.scope.includes("required-scope")) {
throw new Error("Invalid or insufficient token permissions");
}
return data;
} catch (err) {
console.error("OAuth validation failed:", err.message);
throw new Error("Invalid OAuth token");
}
}
```
2. Add error handling and logging
3. Use environment variables for configuration
4. Add rate limiting and security measures
5. Implement proper user authentication and database integration
## Additional Resources
- [Webhook Mode Overview](/auth/webhook/webhook-mode.mdx)
- [Permissions Guide](/auth/permissions/index.mdx)
==============================
# special-roles.mdx
URL: https://hasura.io/docs/3.0/docs/auth/webhook/tutorials/special-roles
# Admin and Unauthenticated Requests
Hasura DDN projects enable an `admin` role by default which has access to all Models and Types in your supergraph.
## Making admin-level requests
To make an admin-level request, after [updating up your AuthConfig](/auth/webhook/webhook-mode.mdx#update-authconfig),
shape your webhook's response as follows:
```json title="Example response from your webhook to Hasura DDN"
HTTP/1.1 200 OK
Content-Type: application/json
{
"X-Hasura-Role": "admin",
}
```
Any request that triggers this response from your webhook will be treated as an `admin` request.
:::info Your JWT claims should be unique for each role
When designing or implementing an auth server, it is crucial to generate JWTs with different claims for each user role
so that each token enables the appropriate data access permissions for that user.
:::
## Making unauthenticated requests
To make an unauthenticated request (i.e., one that is publicly accessible without any authentication), you'll need to do
a few things.
### Step 1. Create the claims
In your authentication server, you can provide a response that identifies the user's role as `public`. This can be any
role name you wish, so long as it's not a role (such as `admin`) that already exists.
```json title="Example response from your webhook to Hasura DDN"
HTTP/1.1 200 OK
Content-Type: application/json
{
"X-Hasura-Role": "public",
}
```
### Step 2. Update ModelPermissions
For whatever [models](/reference/metadata-reference/models.mdx) you'd like to publicly expose, add a
[`ModelPermissions`](/reference/metadata-reference/permissions.mdx#modelpermissions-modelpermissions) rule for the
public role.
```yaml title="Example ModelPermission for an Events Model"
kind: ModelPermissions
version: v1
definition:
modelName: Events
permissions:
- role: admin
select:
filter: null
#highlight-start
- role: public
select:
filter: null
#highlight-end
```
### Step 3. Update TypePermissions
Then, determine which [types](/reference/metadata-reference/types.mdx) you'd like to publicly expose by updating
[TypePermissions](/reference/metadata-reference/permissions.mdx#typepermissions-typepermissions). Hasura DDN gives you
the ability to granularly determine which fields from each Model are available to each role.
```yaml title="Example TypePermissions for an Events Model"
kind: TypePermissions
version: v1
definition:
typeName: Events
permissions:
- role: admin
output:
allowedFields:
- id
- owner_id
- created_at
- updated_at
- is_live
- title
- date
- description
#highlight-start
- role: public
output:
allowedFields:
- id
- is_live
- title
- date
- description
#highlight-end
```
### Step 4. Rebuild your supergraph
Once you've updated your metadata files, you can rebuild your supergraph and test it locally.
```bash title="For example, from the root of your project, run:"
ddn supergraph build local
```
### Step 5. Make an unauthenticated request
Now you can make an unauthenticated request to your API. The request response body will include
`{"x-hasura-role": "public"}` and as such the engine will limit access to the fields that are returned to the ones
specified in the `TypePermissions` for that role.
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/auth/webhook/
# Authentication Using a Webhook
## Introduction
You can configure the Hasura DDN to use webhook mode in order to authenticate incoming requests.
This requires specifying a URL - which Hasura calls with the original request headers - that then returns a body
containing the session variables after authenticating the request.
## Webhook mode setup
- [How to set up webhook mode](/auth/webhook/webhook-mode.mdx)
- [Webhook admin and unauthenticated requests](/auth/webhook/tutorials/special-roles.mdx)
==============================
# add-env-vars-to-a-lambda.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/add-env-vars-to-a-lambda
# Add Custom Environment Variables
## Introduction
Custom environment variables allow you to define configuration values specific to your deployment. These variables can
be used to set API keys, connection strings, or other parameters that your application or connectors need. When you run
the `ddn connector env add` command, it automatically updates multiple files to ensure the environment variables are
available across your project:
- **`.env`**: Adds the variable to the [current context's](/reference/cli/commands/ddn_context.mdx) environment
configuration file for easy access during development.
- **`connector.yaml`**: Updates the connector's configuration to include the variable for deployment.
- **`compose.yaml`**: Ensures the variable is included in the Docker Compose configuration for runtime.
## Add an environment variable
```sh title="From a project directory, run:"
ddn connector env add --env FOO=bar
```
Repeat these steps for each additional variable.
## Use an environment variable
After creating a new build and running your connector, you can access your environment variable(s) via whatever
conventions your language of choice prefers. For detailed instructions, refer to the following resources:
- **Node.js**: Using [dotenv](https://github.com/motdotla/dotenv#readme)
- **Python**: Using [python-dotenv](https://pypi.org/project/python-dotenv/)
- **Go**: Using [godotenv](https://github.com/joho/godotenv)
==============================
# noauth-mode.mdx
URL: https://hasura.io/docs/3.0/docs/auth/noauth-mode
# NoAuth
## Introduction
NoAuth mode is a simple way to run your Hasura DDN instance without authentication. This is useful for testing or
development purposes or to run your Hasura supergraph API endpoint as fully public without any authentication.
:::danger Production Warning
Using NoAuth mode in production environments is not advisable unless you intend for your API to be completely public. If
your Hasura DDN supergraph contains any sensitive data, you should enable authentication and avoid using NoAuth mode in
production.
:::
When you create a new Hasura DDN project with the `ddn supergraph init` command, noAuth mode is enabled by default.
## Enabling NoAuth Mode
### Step 1. Update your AuthConfig
On a default, standard new Hasura DDN project you will find an `AuthConfig` file in your `config` directory already
configured for noAuth mode.
```yaml title="An AuthConfig for noAuth mode:"
kind: AuthConfig
version: v4
definition:
mode:
noAuth:
role: admin
sessionVariables: {}
```
#### `role`
The role to be assumed while running the engine in `noAuth` mode. If you intend for your production API to be fully
public, you can set this to a value such as `public` or `anonymous` so that it's explicit.
[Read more about the `Role` value](/reference/metadata-reference/auth-config.mdx#authconfig-role)
#### `sessionVariables`
Static session variables that will be used while running the engine without authentication. This is helpful when you
want to test requests using particular session variables, such as `x-hasura-user-id` with a non-admin role.
[Read more about the `SessionVariables` value](/reference/metadata-reference/auth-config.mdx#authconfig-sessionvariables)
### Step 2. Check permissions
On a default, standard new Hasura DDN project you will find permissions such as the below for an example `Posts` model:
```yaml title="TypePermissions:"
kind: TypePermissions
version: v1
definition:
typeName: Posts
permissions:
- role: admin
output:
allowedFields:
- authorId
- content
- postId
- title
```
```yaml title="ModelPermissions:"
kind: ModelPermissions
version: v1
definition:
modelName: Posts
permissions:
- role: admin
select:
filter: null
allowSubscriptions: true
```
You can see that the `admin` role has full access to the `Posts` model with no filtering in the `ModelPermissions` and
all fields being allowed in the `TypePermissions`.
### Step 3. Build your supergraph
```bash
ddn supergraph build local
```
### Step 4. Make an un-authenticated request
```title="Open the Hasura DDN console and make a request:"
ddn console --local
```
If you click on the `Authorization` tab in the console, you can see that `NoAuth` mode is enabled and for this request,
the static session variables are not being used and all fields and data are being returned.
==============================
# dev-mode.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/dev-mode
# Live Reloading with Compose Watch
## Introduction
When working with lambda connectors, you'll often want to make quick edits to your custom business logic and don't want
to go through the hassle of bringing down all your services, rebuilding them all, and restarting them. Instead you can
leverage [Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) to listen for changes to your connector's
entrypoint, and rebuild the connector on the fly.
:::info Are you adding new functions or modifying the signature of existing functions?
The workflow described in the guide below helps when you're making changes to your connector's internal logic without
altering the exposed function interfaces or metadata.
If you're adding new functions that will need to be exposed as [commands](/reference/metadata-reference/commands.mdx) in
your application, or modifying existing functions' arguments or return types, you'll still need to regenerate your
metadata and create a new build of your application.
Follow the steps [here](/data-sources/introspect-a-source.mdx) for more information.
:::
## Guide
### Step 1. Update your start script
Open your `.hasura/context.yaml` file and locate your `docker-start` script.
```yaml title="Add the --watch flag to your start script:" {4,7}
docker-start:
bash:
HASURA_DDN_PAT=$(ddn auth print-access-token) PROMPTQL_SECRET_KEY=$(ddn auth print-promptql-secret-key) docker
compose -f compose.yaml --env-file .env up --build --pull always --watch
powershell:
$Env:HASURA_DDN_PAT = ddn auth print-access-token; $Env:PROMPTQL_SECRET_KEY = ddn auth print-promptql-secret-key;
docker compose -f compose.yaml --env-file .env up --build --pull always --watch
```
By adding the `--watch` flag to the `docker compose up` command, you're telling Docker to monitor the files specified in
your service's `develop` section for changes. When changes are detected, Docker automatically triggers the action you
configured (in this case, we'll use `rebuild`) without requiring you to manually stop and restart your services. This
creates a much faster feedback loop while developing.
### Step 2. Update your connector's `compose.yaml`
```yaml title="Add the following adjusting the path as necessary:" {14-17}
services:
my_lambda_connector:
build:
context: .
dockerfile: .hasura-connector/Dockerfile
environment:
HASURA_SERVICE_TOKEN_SECRET: $MY_LAMBDA_CONNECTOR_HASURA_SERVICE_TOKEN_SECRET
OTEL_EXPORTER_OTLP_ENDPOINT: $MY_LAMBDA_CONNECTOR_OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_SERVICE_NAME: $MY_LAMBDA_CONNECTOR_OTEL_SERVICE_NAME
extra_hosts:
- local.hasura.dev:host-gateway
ports:
- 8203:8080
develop:
watch:
- action: rebuild
path: .
```
The `develop` section with `watch` tells Docker Compose which files or directories to monitor during development.
- `path: .` means "watch the entire context directory". You can narrow this down to the entrypoint file(s) if you want
finer control. For example, you can isolate on the `functions.ts` file for the TypeScript connector or the
`./functions` directory for the Go connector.
- `action: rebuild` means that if any file changes within the watched path, Docker will automatically rebuild and
restart the container. This setup ensures that your lambda connector is instantly rebuilt whenever you modify its code
β speeding up iteration dramatically without manual intervention.
You can verify this by running `ddn run docker-start` from the root of your project and then making a modification to
your lambda connector's logic. In your Docker logs, you should see the container for your connector being rebuilt and
the changes you've made instantly reflected in your API.
==============================
# multiple-auth-modes.mdx
URL: https://hasura.io/docs/3.0/docs/auth/multiple-auth-modes
# Multiple Authentication Modes
## Introduction
Hasura DDN supports configuring multiple authentication modes simultaneously, allowing you to use different
authentication methods for different scenarios or clients. This feature enables you to have both JWT and webhook
authentication enabled, or multiple configurations of the same authentication type with different settings.
:::info AuthConfig v4
Multiple authentication modes are available in `AuthConfig` **v4** and above.
:::
## How It Works
When multiple authentication modes are configured:
1. The primary mode specified in the `mode` field of the `AuthConfig` is used by default
2. Alternative modes can be accessed by passing the `X-Hasura-Auth-Mode` header with the identifier of the desired
authentication mode
3. Each alternative mode has its own complete authentication configuration
The default mode is used when:
- The `X-Hasura-Auth-Mode` header is not provided
- The provided `X-Hasura-Auth-Mode` header does not match any of the alternative mode identifiers
## Configuring Multiple Auth Modes
To configure multiple authentication modes, you need to use `AuthConfig` v4, which introduces the `alternativeModes`
field.
### Example Configuration
Here's an example of an `AuthConfig` with multiple authentication modes:
```yaml
kind: AuthConfig
version: v4
definition:
mode:
jwt:
key:
fixed:
algorithm: HS256
key:
valueFromEnv: JWT_KEY
tokenLocation:
type: BearerAuthorization
claimsConfig:
namespace:
claimsFormat: Json
location: "/https:~1~1hasura.io~1jwt~1claims"
alternativeModes:
- identifier: "user2"
config:
webhook:
url:
valueFromEnv: USER2_URL
method: "POST"
- identifier: "webhook"
config:
webhook:
url:
value: "https://my-auth-service.example.com/validate"
method: "POST"
- identifier: "jwt"
config:
jwt:
key:
jwkFromUrl: "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com"
audience: ["my-firebase-app"]
issuer: "https://securetoken.google.com/my-firebase-app"
claimsConfig:
namespace:
claimsFormat: Json
location: "/https:~1~1hasura.io~1jwt~1claims"
tokenLocation:
type: Header
name: "Authorization"
```
In this example:
- The default authentication mode is `jwt` with a fixed key
- Three alternative modes are configured:
- `user2`: A webhook authentication configuration for a different user
- `webhook`: A webhook authentication configuration
- `jwt`: A JWT authentication configuration for Firebase
## Using Multiple Auth Modes
To use a specific authentication mode when making a request to your Hasura DDN API, include the `X-Hasura-Auth-Mode`
header with the identifier of the desired mode.
### Example Requests
Default mode (JWT with fixed key):
```bash
curl https://your-hasura-endpoint.hasura-ddn.com/v1/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-fixed-key-jwt-token" \
-d '{"query": "{ users { id name } }"}'
```
Using the webhook mode:
```bash
curl https://your-hasura-endpoint.hasura-ddn.com/v1/graphql \
-H "Content-Type: application/json" \
-H "X-Hasura-Auth-Mode: webhook" \
-H "Authorization: Bearer your-auth-token" \
-d '{"query": "{ users { id name } }"}'
```
Using the JWT mode:
```bash
curl https://your-hasura-endpoint.hasura-ddn.com/v1/graphql \
-H "Content-Type: application/json" \
-H "X-Hasura-Auth-Mode: jwt" \
-H "Authorization: Bearer your-jwt-token" \
-d '{"query": "{ users { id name } }"}'
```
## Use Cases
Multiple authentication modes can be useful in various scenarios:
1. **Development and Testing**: Use fixed key JWT for development and webhook for testing, and switch to production
custom claims JWT mode when ready
2. **Multiple Client Types**: Support different authentication methods for different client applications
3. **Migration**: Gradually migrate from one authentication provider to another (or from one permission model to
another)
4. **Admin Access**: Provide a separate authentication mode for administrative access
## Security Considerations
When using multiple authentication modes:
- Set up role-based permissions for each authentication mode to control data access appropriately
- Keep in mind that the `X-Hasura-Auth-Mode` header may be included in standard HTTP request monitoring tools, just like
other headers
- Consider using network controls (like IP allowlists) or the [Allowlist Plugin](/plugins/allowlist/index.mdx) to manage
which clients can use specific authentication modes
## Next Steps
- Learn more about [JWT authentication](/auth/jwt/index.mdx)
- Explore [webhook authentication](/auth/webhook/index.mdx)
- Understand [NoAuth mode](/auth/noauth-mode.mdx)
==============================
# errors.mdx
URL: https://hasura.io/docs/3.0/docs/business-logic/errors
# Handle Errors with Lambda Connectors
## Introduction
By default, lambda connectors return a generic `internal error` message whenever an exception is encountered in your
custom business logic and **log the details of the error in the OpenTelemetry trace associated with the request**.
The Native Data Connector specification identifies a
[valid set of status codes](https://hasura.github.io/ndc-spec/specification/error-handling.html) which a connector can
return to your API consumers.
:::info How detailed should error messages be?
Exposing stack traces to end users is generally discouraged. Instead, API administrators can review traces logged in the
OpenTelemetry traces to access detailed stack trace information.
:::
## Return custom error messages
Lambda connectors allow you to throw classes of errors with your own custom message and metadata to indicate specific
error conditions. These classes are designed to provide clarity in error handling when interacting with your data
sources. To explore the available error classes, use your editor's autocomplete or documentation features to view all
supported classes and their usage details.
```typescript title="TypeScript examples:" {1,6,14,22}
/** @readonly */
export function updateResource(userRole: string): void {
if (userRole !== "admin") {
throw new sdk.Forbidden("User does not have permission to update this resource", { role: userRole });
}
console.log("Resource updated successfully.");
}
/** @readonly */
export function createResource(id: string, existingIds: string[]): void {
if (existingIds.includes(id)) {
throw new sdk.Conflict("Resource with this ID already exists", { existingId: id });
}
console.log("Resource created successfully.");
}
/** @readonly */
export function divide(x: number, y: number): number {
if (y === 0) {
throw new sdk.UnprocessableContent("Cannot divide by zero", { myErrorMetadata: "stuff", x, y });
}
return x / y;
}
```
```python title="Python examples:" {4}
# There are different error types including: BadRequest, Forbidden, Conflict, UnprocessableContent, InternalServerError, NotSupported, and BadGateway
@connector.register_query
def error():
raise UnprocessableContent(message="This is an error", details={"Error": "This is an error!"})
```
```go title="Go examples:" {7,29-31}
package functions
"context"
"fmt"
"github.com/hasura/ndc-sdk-go/schema"
"hasura-ndc.dev/ndc-go/types"
)
// A hello argument
type HelloArguments struct {
Greeting string `json:"greeting"`
Count *int `json:"count"`
}
// A hello result
type HelloResult struct {
Reply string `json:"reply"`
Count int `json:"count"`
}
func FunctionHello(ctx context.Context, state *types.State, arguments *HelloArguments) (*HelloResult, error) {
count := 1
authorized := false // This is just an example
if !authorized {
return nil, schema.UnauthorizeError("User is not authorized to perform this operation", map[string]any{
"function": "hello",
})
}
if arguments.Count != nil {
count = *arguments.Count + 1
}
return &HelloResult{
Reply: fmt.Sprintf("Hi! %s", arguments.Greeting),
Count: count,
}, nil
}
```
## Access OpenTelemetry traces
Traces β complete with your custom error messages β are available for each request. You can access these by clicking on
`View Trace` in the bottom-right corner of the GraphiQL explorer in the console after running a request.
Additionally, you can access the traces list under the `Insights` tab.
==============================
# model-permissions.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/model-permissions
# Model Permissions
To limit what **data** in a model is available to a role in your supergraph, you define a `ModelPermissions` object with
a `filter` expression.
By default, whenever a new model is created in your supergraph, all records are only accessible to the `admin` role. You
can think of these as permissions on rows in a typical relational database table.
You can restrict access to certain data by adding a new item to the `permissions` array in the `ModelPermissions`
object. Each item in the array should have a `role` field and a `select` field. The `select` field should contain a
`filter` expression that determines which rows are accessible to the role when selecting from the model.
Most commonly, you'll use session variables β accessed by Hasura Engine via your configured
[authentication mechanism](/auth/overview.mdx) in a JWT or body of a webhook response β to restrict access to rows based
on the user's role, identity or other criteria.
This filter expression can reference
- The fields in your Model
- Logical operators: `and`, `or` and `not`
- `fieldIsNull` predicate
- `fieldComparison` predicate
- Relationship predicates
- `null`
:::info Remote Relationships in Permissions
Remote relationships (relationships between different data connectors) across subgraphs are not supported in permission filters.
:::
To make a new `ModelPermission` or role available in your supergraph, after updating your metadata, you'll need to
[create a new build](/reference/cli/commands/ddn_supergraph_build_local.mdx) using the CLI.
### Examples
```yaml title="Allow admin to access all rows in the Articles model but allow user to access rows where the author_id field matches the user id session variable. Basically, their own articles."
---
kind: ModelPermissions
version: v1
definition:
modelName: Articles
# highlight-start
permissions:
- role: admin
select:
filter: null
- role: user
select:
filter:
fieldComparison:
field: author_id
operator: _eq
value:
sessionVariable: x-hasura-user-id
# highlight-end
```
## Reference
See the [ModelPermissions](/reference/metadata-reference/permissions.mdx) reference for more information.
==============================
# type-permissions.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/type-permissions
# Type Permissions
To make API **fields** available to a role in your supergraph, you define a `TypePermissions` object.
You can think of TypePermissions as being similar to column-level permissions in a relational database. Just as you can
restrict access to specific columns in a table based on the user's role, TypePermissions allow you to control access to
specific fields in a type within your supergraph.
By default, whenever a new type is created in your supergraph, each field is defined as being only accessible to the
`admin` role.
To add a new role, add a new item to the `permissions` array in the TypePermissions object.
Each item in the array should have a `role` field and an `output` field. The `output` field should contain an
`allowedFields` array, which lists the fields that are accessible to the role when the type is used in an output
context.
To make a new `TypePermission` object or role available in your supergraph, you'll need to
[create a new build](/reference/cli/commands/ddn_supergraph_build_local.mdx) using the CLI.
## Example
```yaml title="Allow admin to access all fields in the article type, disallow user from accessing the author_id field."
---
kind: TypePermissions
version: v1
definition:
typeName: article
permissions:
# highlight-start
- role: admin
output:
allowedFields:
- article_id
- author_id
- title
- role: user
output:
allowedFields:
- article_id
- title
# highlight-end
```
## Reference
See the [TypePermissions](/reference/metadata-reference/permissions.mdx) reference for more information.
==============================
# command-permissions.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/command-permissions
# Command Permissions
To limit what **commands** are available to a role in your supergraph, you define a `CommandPermissions` object.
By default, whenever a new command is created in your supergraph, it is only executable by the `admin` role.
You can enable or restrict access to commands by adding a new item to the `permissions` array in the
`CommandPermissions` object. Each item in the array should have a `role` field and an `allowExecution` field. The
`allowExecution` field should be set to `true` if the command is executable by the role.
You can also use argument presets to pass actual logical expressions to your data sources to control how they do things.
For example, a data connector might expose a `Command` called `delete_user_by_id` with two arguments - `user_id` and
`pre_check`. `user_id` is the primary key of the user you'd like to remove, and `pre_check` lets you provide a custom
boolean expression.
```yaml
kind: CommandPermissions
version: v1
definition:
commandName: delete_user_by_id
# highlight-start
permissions:
- role: admin
allowExecution: true
- role: user
allowExecution: true
argumentPresets:
- argument: pre_check
value:
booleanExpression:
fieldComparison:
field: is_invincible
operator: _eq
value:
literal: false
# highlight-end
```
Now, when `admin` role runs this command, once again, they can do what they want, and provide their own `pre_check` if
they want.
The `user` role however, is able to pass a `user_id` argument, but the `pre_check` expression is passed to the data
connector which will only let them delete the row if the row's `is_invincible` value is set to `false`.
To make a execution of a command available to a role in your supergraph, after updating your metadata, you'll need to
[create a new build](/reference/cli/commands/ddn_supergraph_build_local.mdx) using the CLI.
### Examples
```yaml title="Allow admin to execute the get_article_by_id command, restrict user to execute the get_article_by_id command with an id argument preset of 100."
---
kind: CommandPermissions
version: v1
definition:
commandName: get_article_by_id
# highlight-start
permissions:
- role: admin
allowExecution: true
- role: user
allowExecution: true
argumentPresets:
- argument: id
value:
literal: 100
# highlight-end
```
## Reference
See the [CommandPermissions](/reference/metadata-reference/permissions.mdx) reference for more information.
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/tutorials/
# Permissions Tutorials
## Introduction
In this section of tutorials, we'll provide step-by-step guides for common patterns in controlling users' access to data
via your supergraph.
If you're unfamiliar with how Hasura DDN handles authorization, [check out the docs](/auth/overview.mdx) before diving
deeper into one of these tutorials.
## Recipes
- [Limit data to users](/auth/permissions/tutorials/1-simple-user-permissions.mdx)
- [Public access](/auth/permissions/tutorials/2-public-access-role.mdx)
- [Service accounts](/auth/permissions/tutorials/4-service-account.mdx)
- [Role-based command execution](/auth/permissions/tutorials/5-restrict-command-execution-with-role-based-permissions.mdx)
==============================
# 1-simple-user-permissions.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/tutorials/1-simple-user-permissions
# Limit Data to Users
## Introduction
In this tutorial, you'll learn how to configure [permissions](/reference/metadata-reference/permissions.mdx) to limit
users to accessing only their data.
This can be done by passing a value in the header of each request to your supergraph; Hasura will then use that value to
return only the data which matches conditions you specify.
:::info Prerequisites
Before continuing, ensure you have:
- A local Hasura DDN project.
- Either JWT or Webhook mode enabled in your [AuthConfig](/reference/metadata-reference/auth-config.mdx).
:::
## Tutorial
### Step 1. Create your ModelPermissions {#step-one}
To create a new role, such as `user`, simply add the role to the list of `permissions` for the model to which you wish
to limit access. Then, set up your access control rules.
In the example below, we'll allow users with the role of `user` to access only their own rows from a `Users` model by
checking for a header value matching their `id`:
```yaml title="For example, in a Users.hml"
---
kind: ModelPermissions
version: v1
definition:
modelName: Users
permissions:
- role: admin
select:
filter: null
#highlight-start
- role: user
select:
filter:
fieldComparison:
field: id
operator: _eq
value:
sessionVariable: x-hasura-user-id
#highlight-end
```
You can modify this to meet your own data modeling by ensuring that the `field` is a column or type that can be compared
to the value of the session variable you send in the header of your request. In this example, we're using
`x-hasura-user-id`, but you can use any `x-hasura-` value you wish.
:::info Authentication tutorials
We have tutorials for popular authentication providers available [here](/auth/jwt/tutorials/integrations/index.mdx)!
:::
### Step 2. Create your TypePermissions
By adding ModelPermissions, we've made the model available to the new role. However, this role is not yet able to access
any of the fields from the model. We can do that by adding the new role to the list of `permissions` and including which
fields are accessible to it.
```yaml title="For example, in a Users.hml"
---
kind: TypePermissions
version: v1
definition:
typeName: Users
permissions:
- role: admin
output:
allowedFields:
- createdAt
- email
- favoriteArtist
- id
- isEmailVerified
- lastSeen
- name
- password
- updatedAt
#highlight-start
- role: user
output:
allowedFields:
- createdAt
- email
- favoriteArtist
- id
- isEmailVerified
- lastSeen
- name
- password
- updatedAt
#highlight-end
```
If you want to restrict which fields the new role can access, simply omit them from the list of `allowedFields`.
### Step 3. Test your permissions
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
Then, in a request, pass a header with the [session variable](#step-one) you identified earlier according to your
authentication configuration. You should see a schema limited to whatever ModelPermissions you defined for your new role
and β when executing a query β only see data meeting the filtering rule you included in the first step.
## Wrapping up
In this guide, you learned how to limit a user to see only their own data from a single type. However, you can use this
same tutorial and apply it to a variety of scenarios.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
## Learn more about permissions and auth
- [Permissions](/auth/permissions/index.mdx) with Hasura DDN
- [Auth](/auth/overview.mdx) with Hasura DDN
## Similar tutorials
- [Authorization tutorials](/auth/permissions/tutorials/index.mdx)
==============================
# 2-public-access-role.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/tutorials/2-public-access-role
# Public Access
## Introduction
In this tutorial, you'll learn how to configure [permissions](/reference/metadata-reference/permissions.mdx) to allow
for unauthenticated access to data in your supergraph. This can be done by creating a role and setting the `filter`
field to `null`.
:::warning A word of caution
Any requests made to your supergraph with the configuration demonstrated below will have unauthenticated access to
whatever resources you allow. Use with caution!
:::
:::info Prerequisites
Before continuing, ensure you have:
- A local Hasura DDN project.
- Either JWT or Webhook mode enabled in your [AuthConfig](/reference/metadata-reference/auth-config.mdx).
:::
## Tutorial
### Step 1. Create the claims
In your authentication server, you can provide a claims map that identifies the default role as `public`. This can be
any name you wish, so long as it's not a role (such as `admin`) that already exists.
```json title="E.g., a JWT claims configuration in an authentication service"
"claims.jwt.hasura.io": {
"x-hasura-default-role": "public",
"x-hasura-allowed-roles": ["public"],
}
```
### Step 2. Update ModelPermissions {#step-two}
For whatever [models](/reference/metadata-reference/models.mdx) you'd like to publicly expose, add a
[ModelPermissions](/reference/metadata-reference/permissions.mdx#modelpermissions-modelpermissions) rule for the public
role.
```yaml title="Example ModelPermission for an Events Model"
kind: ModelPermissions
version: v1
definition:
modelName: Events
permissions:
- role: admin
select:
filter: null
#highlight-start
- role: public
select:
filter: null
#highlight-end
```
### Step 3. Update TypePermissions
Then, determine which [types](/reference/metadata-reference/types.mdx) you'd like to publicly expose by updating
[TypePermissions](/reference/metadata-reference/permissions.mdx#typepermissions-typepermissions). Hasura DDN gives you
the ability to granularly determine which fields from each Model are available to each role.
```yaml title="Example TypePermissions for an Events Model"
kind: TypePermissions
version: v1
definition:
typeName: Events
permissions:
- role: admin
output:
allowedFields:
- id
- owner_id
- created_at
- updated_at
- is_live
- title
- date
- description
#highlight-start
- role: public
output:
allowedFields:
- id
- is_live
- title
- date
- description
#highlight-end
```
### Step 4. Test your permissions
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
Then, in a request, pass a header with the [role](#step-two) you identified earlier according to your authentication
configuration. You should see a schema limited to whatever ModelPermissions you defined for your new role and β when
executing a query β only see data meeting the filtering rule you included in the first step.
## Wrapping up
In this guide, you learned how to expose data in your supergraph to users without any authentication. This is valuable
for any public-facing resources clients may need to access.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
## Learn more about permissions and auth
- [Permissions](/auth/permissions/index.mdx) with Hasura DDN
- [Auth](/auth/overview.mdx) with Hasura DDN
## Similar tutorials
- [Authorization tutorials](/auth/permissions/tutorials/index.mdx)
==============================
# 4-service-account.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/tutorials/4-service-account
# Service Accounts
## Introduction
In this tutorial, you'll learn how to configure a JWT or webhook to allow for admin-level access to data in your
supergraph. This can be done by passing hard-coded session variables that match the `admin` role in Hasura DDN.
:::info Prerequisites
Before continuing, ensure you have:
- A local Hasura DDN project.
- Either JWT or Webhook mode enabled in your [AuthConfig](/reference/metadata-reference/auth-config.mdx).
:::
## Tutorial
### Step 1. Create a custom claim
To make an admin-level request, shape your claims as follows:
```json
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "admin",
"x-hasura-allowed-roles": ["admin"],
}
```
When the token is minted, it will include the hard-coded values and can be passed to act as an admin-level request to
your supergraph.
:::info Your JWT claims should be unique for each role
When designing or implementing an auth server, it is best practice to generate JWTs with different claims for each user
role so that each token enables the appropriate data access permissions for that user.
If you're unsure about setting up JWTs with Hasura, check out our
[tutorials](/auth/jwt/tutorials/integrations/index.mdx) for popular providers.
:::
To make an admin-level request, shape the response provided by your webhook as follows:
```json
HTTP/1.1 200 OK
Content-Type: application/json
{
"X-Hasura-Role": "admin",
}
```
### Step 2. Test your permissions
Create a new build of your supergraph:
```sh
ddn supergraph build local
```
Then, in a request, pass a header according to your authentication configuration. You should see all types and fields
available to the `admin` role.
## Wrapping up
In this guide, you learned how to expose all data in your supergraph to the `admin` role. While this is done by default,
you'll need to generate a JWT or include the session variables in your webhook response that will allow the request to
act as a service account.
As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components.
Always validate your configuration and regularly test your setup to ensure it functions as expected across different
roles and environments.
## Learn more about permissions and auth
- [Permissions](/auth/permissions/index.mdx) with Hasura DDN
- [Auth](/auth/overview.mdx) with Hasura DDN
## Similar tutorials
- [Authorization tutorials](/auth/permissions/tutorials/index.mdx)
==============================
# 5-restrict-command-execution-with-role-based-permissions.mdx
URL: https://hasura.io/docs/3.0/docs/auth/permissions/tutorials/5-restrict-command-execution-with-role-based-permissions
# Restrict Command Execution with Role-based Permissions
## Introduction
Often, you'll want to limit a user's ability to execute certain [commands](/data-modeling/command.mdx) β which power
mutations in your GraphQL API β based on some related data. In the example below, we'll build on the
[tutorial found in our PostgreSQL getting-started section](/how-to-build-with-ddn/with-postgresql.mdx) and **restrict
users to only being able to update posts of which they're the author**.
:::info Prerequisites
Before continuing, ensure you have:
- A local Hasura DDN project.
- Either JWT or Webhook mode enabled in your [AuthConfig](/reference/metadata-reference/auth-config.mdx).
:::
## Tutorial
## Step 1. Add an `author` role to your CommandPermissions object
```yaml title="Locate your UpdatePostsById.hml file and update it to the following:" {9-23}
---
kind: CommandPermissions
version: v1
definition:
commandName: UpdatePostsById
permissions:
- role: admin
allowExecution: true
- role: author
allowExecution: true
argumentPresets:
- argument: preCheck
value:
booleanExpression:
relationship:
# Here, `user` refers to the pre-generated relationship's name
name: user
predicate:
fieldComparison:
field: id
operator: _eq
value:
sessionVariable: x-hasura-user-id
```
This role grants the `author` role permission to execute the `UpdatePostsById` command, but only if the user who
authored the post has an `id` that matches the `x-hasura-user-id` session variable in the request header. This ensures
users can only update posts they have authored.
## Step 2. Add TypePermissions to your response type
The new `author` role will need access to return types for the `UpdatePostsByIdResponse` type.
```yaml title="Find the UpdatePostsByIdResponse TypePermissions object and add the following:" {11-14}
kind: TypePermissions
version: v1
definition:
typeName: UpdatePostsByIdResponse
permissions:
- role: admin
output:
allowedFields:
- affectedRows
- returning
- role: author
output:
allowedFields:
- affectedRows
```
In the configuration above, we're only allowing a user with the role of `author` to access the number of affected rows.
Alternatively, you could include `returning` in the `output` array and _then_ set ModelPermissions **and**
TypePermissions for the `author` role on the `Posts` type to allow for any or specific fields to be returned.
## Step 3. Create a new build and test {#build-and-test}
```yaml title="To test this, if you don't have JWT or Webhook mode enabled, we recommend replacing your AuthConfig with:"
kind: AuthConfig
version: v4
definition:
mode:
noAuth:
role: author
sessionVariables: { "x-hasura-user-id": 1 }
```
This will set your `x-hasura-role` session variable as `author` and the `x-hasura-user-id` as `1`, enabling you to
impersonate Alice.
```bash title="Create a new build and start your services:"
ddn supergraph build local && ddn run docker-start
```
```graphql title="Then, run the following query:"
mutation UPDATE_POST_TITLE {
updatePostsById(keyId: "1", updateColumns: { title: { set: "This is not Alice's first post" } }) {
affectedRows
}
}
```
```json title="As Alice is the owner of the post with the ID of 1, you should then see the following response:"
{
"data": {
"updatePostsById": {
"affectedRows": 1
}
}
}
```
```graphql title="Alternatively, if we run the following β which is on a post Alice does not own β we'll see a different return value:"
mutation UPDATE_POST_TITLE {
updatePostsById(keyId: "4", updateColumns: { title: { set: "Malicious Actions in the API" } }) {
affectedRows
}
}
```
```json title="Being 0, which is the number of rows affected:"
{
"data": {
"updatePostsById": {
"affectedRows": 0
}
}
}
```
## Wrapping up
In this tutorial, we've demonstrated the minimum sets of permissions necessary to enforce role-based execution of
commands. While the example illustrates limiting users to updating their own posts, the principles can be applied to any
scenario by which you want to limit command execution β and mutations β based on relationships.
## Learn more about permissions and auth
- [Permissions](/auth/permissions/index.mdx) with Hasura DDN
- [Auth](/auth/overview.mdx) with Hasura DDN
==============================
# Basics
URL: https://hasura.io/docs/3.0/docs/plugins/overview
# Engine Plugins
## Introduction
Hasura's engine plugins architecture allows you to integrate HTTP hooks at various stages of the API execution pipeline.
This functionality enables you to embed custom logic that reacts to specific events, effectively extending the core
capabilities of your API. These hooks can be triggered either before or after the execution of a query or mutation,
providing a powerful mechanism to implement user-defined workflows and event-driven customizations.
## Types of plugins
### Pre-Parse plugins
Pre-parse plugins are executed at the very beginning of the execution pipeline. They allow you to insert custom logic
**before** the query-parsing step occurs. This enables you to pre-process, or manipulate incoming queries, and modify
the behavior of your API at an early stage.
### Pre-Response plugins
Pre-response plugins are triggered at the final stage of the execution pipeline, **after the query has been executed but
before the response is sent to the client**. These plugins allow you to execute logic based on the response such as
calling third-party services. They can operate in asynchronous mode (default) for non-blocking operations like
notifications, or synchronous mode for response modifications and operations that must complete before the client
receives the response.
### Pre-Route plugins
Pre-route plugins are executed at the very beginning of request handling, **before predefined endpoints are processed**.
They allow you to insert custom HTTP handlers into your API, enabling features like REST-style GraphQL endpoints,
internal tools, or documentation interfaces such as a GraphQL schema visualizer or Swagger UI for a JSON API.
## Find out more
- [Learn more about how plugins work](/plugins/introduction.mdx)
- [Allowlist](/plugins/allowlist/index.mdx)
- [Caching](/plugins/caching/index.mdx)
- [RESTified Endpoints](/plugins/restified-endpoints/index.mdx)
- [Rate Limiting](/plugins/rate-limit/index.mdx)
==============================
# overview.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/overview
# Projects
## Introduction
A **project** in Hasura DDN is the foundation for building and managing your API. It contains a structured collection of
metadata files that define the behavior, relationships, and permissions for your API. These metadata files are organized
into **subgraphs**, each representing a distinct data domain.
Projects are designed to support both local development and cloud deployment. During development, you work with a local
version of your project, which is linked to a cloud project through a
[context file](/project-configuration/project-management/manage-contexts.mdx). This linkage enables seamless
development, testing, and deployment workflows. You can also define multiple contexts (e.g., `staging`) to manage
different environments, with unique configurations for environment variables and cloud resources.
## Data domains
A **data domain** in Hasura DDN represents a distinct area of responsibility or focus within your project, typically
aligned with a specific team or business function. These domains are managed as **subgraphs**, which are collections of
metadata files that describe the relationships, permissions, and structure for the data within that domain.
Organizing your project into subgraphs provides several benefits:
- **Team Ownership**: Each team can focus on their own data domain without interfering with others, making collaboration
simpler and reducing bottlenecks.
- **Clear Boundaries**: Subgraphs establish clear boundaries between domains, making it easier to define and enforce
data access permissions and relationships.
- **Scalability**: By breaking your project into manageable pieces, you can scale your API incrementally as your
organization and data requirements grow.
- **Flexibility**: Subgraphs can be independently updated and extended, allowing you to adapt your project to changing
needs without disrupting the overall API.
This structure ensures that your project remains organized, collaborative, and adaptable, enabling you to build and
maintain robust APIs efficiently.
:::info Multi-repo projects
Hasura DDN also supports multi-repository setups, giving teams greater autonomy in their development process. With this
setup:
- Each team can maintain their **subgraph** in a separate repository while still contributing to the shared
**supergraph**.
- Teams can develop, test, and deploy their subgraphs independently, enabling faster iteration and minimizing
dependencies on other teams.
- The supergraph integrates these subgraphs into a single API, ensuring that the overall API remains consistent and
cohesive.
This approach is ideal for large organizations where multiple teams work on distinct data domains but need to
collaborate through a unified API. For more information, check out
[this section](/project-configuration/subgraphs/index.mdx) of the docs.
:::
## Find out more
- [Tutorials](/project-configuration/tutorials/index.mdx)
- [Learn more about the supergraph concept](/project-configuration/supergraph.mdx)
- [Learn more about provisioning subgraphs](/project-configuration/subgraphs/index.mdx)
- [Learn how to manage a project across environments](/project-configuration/project-management/index.mdx)
- [Learn how to upgrade a legacy configuration](/project-configuration/upgrading-project-config/index.mdx)
==============================
# introduction.mdx
URL: https://hasura.io/docs/3.0/docs/plugins/introduction
# How Plugins Work
## Introduction
Engine plugins are HTTP servers that run alongside a Hasura DDN instance and can be written in any language capable of
running an HTTP server.
They are configured in DDN using metadata. The engine sends HTTP requests to the plugin at the specified execution step,
the plugin processes the request, and then sends a response back to the engine, which continues execution based on the
plugin's response.
Plugins can be applied at the following steps:
| Execution Step | Description | Example Usage |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| **Pre-Parse** | The first step in the execution pipeline, where custom logic can be applied before the query is parsed and its internal representation is generated. | Add an allowlist layer to restrict access to specific queries and mutations. |
| **Pre-NDC Request** | Applied before sending requests to data connectors, allowing modification of NDC requests or returning responses directly without calling the data connector. | Transform requests, implement caching by returning cached responses, or add request validation. |
| **Pre-NDC Response** | Applied after receiving responses from data connectors, allowing modification of NDC responses. | Transform or enrich data connector responses, or store responses for caching purposes. |
| **Pre-Response** | The final step in the execution pipeline, where custom logic can be added after the query is executed but before the response is sent to the client. | Trigger Slack notifications after a mutation is executed. |
| **Pre-Route** | The first step in the routing pipeline, where custom logic can be applied to the requests on other than pre-defined endpoints. | Add a custom endpoint to DDN. |
## Architecture
```mermaid
---
title: Engine Plugins Execution Pipeline
---
graph RL
Client[Client] -->|"Request (/graphql)"| Authentication
Authentication --> Query_Parsing_and_Planning["Query parsing and planning"]
Query_Parsing_and_Planning --> Execute_Query["Execute query"]
Execute_Query --> Pre_NDC_Request_Hooks["Pre-NDC request hooks"]
Pre_NDC_Request_Hooks -->|Fetch Data| Data_Connector["Data connector"]
Data_Connector --> Pre_NDC_Response_Hooks["Pre-NDC response hooks"]
Pre_NDC_Response_Hooks --> Post_Processing["Post-processing"]
Post_Processing --> Pre_Response_Hooks["Pre-response hooks"]
Post_Processing -->|Response| Client
subgraph DDN Engine
Authentication
Query_Parsing_and_Planning
Execute_Query
Post_Processing
end
Pre_Parse_Hooks["Pre-parse hooks"] <--> Query_Parsing_and_Planning
```
```mermaid
---
title: Pre-route plugin
---
graph RL
Client[Client] -->|"Request (/*)"| Route_Handler["Route handler"]
Route_Handler -->|Response| Client
subgraph DDN Engine
Route_Handler
end
Pre_Route_Hooks["Pre-route hooks"] <-->|Handle Request| Route_Handler["Route handler"]
```
## Plugin Configuration
Engine plugins are configured in DDN using metadata. The metadata specifies the URL of the engine plugin and the
execution step at which the plugin should be called. The configuration also can control the request that is sent to the
engine plugin.
```yaml title="Here is an example of a plugin configuration in DDN metadata:"
kind: LifecyclePluginHook
version: v1
definition:
name: cloudflare allowlist
url:
valueFromEnv: ALLOW_LIST_URL
pre: parse
config:
request:
headers:
additional:
hasura-m-auth:
value: "your-strong-m-auth-key"
session: {}
rawRequest:
query: {}
variables: {}
```
In this example, the plugin is configured to run at the `pre-parse` execution step. The plugin is called
`cloudflare allowlist`. The URL of the plugin is read from the `ALLOW_LIST_URL` environment variable. The plugin is
configured to add a `hasura-m-auth` header to the request with the value `your-strong-m-auth-key`.
Additionally, the request sent to the plugin includes the query and variables from the incoming GraphQL request, as well
as the session information from the incoming request.
## Pre-Parse Plugin
The `pre-parse` plugin is triggered at the first step in the execution pipeline, before the query is parsed. Use this
step to add custom logic before parsing begins.
For pre-parse plugin configuration
[click here](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecyclepreparsepluginhook).
### Pre-Parse Plugin Request
```json title="A sample request that is sent to the pre-parse plugin is as follows:"
{
"rawRequest": {
"query": "query MyQuery { getAuthorById(author_id: 10) { first_name } }",
"variables": {},
"operationName": "MyQuery"
},
"session": {
"role": "user",
"variables": {
"x-hasura-role": "user",
"x-hasura-user-id": "123"
}
}
}
```
:::info Customize the request
The request sent to the plugin can be customized based on the plugin's
[configuration](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecyclepreresponsepluginhookconfigrequest).
:::
### Pre-Parse Plugin Response
The `pre-parse` plugin has the ability to control the execution pipeline by returning a specific response to DDN. The
response determines whether execution continues, halts, or returns an error. The possible response types are:
| Response Type | HTTP Status Code | Response Body | Description |
| ------------------------- | ---------------- | -------------------- | ---------------------------------------------------------------------------------------------- |
| `Continue` | `204` | None | Execution continues without interruption. |
| `Response` | `200` | Response body | Execution halts, and the provided response is returned to the client. |
| `Continue with new query` | `299`\* | GraphQL query object | Execution continues with the modified GraphQL query. |
| `User Error` | `400` | Error object | Execution halts, and the provided error is returned to the client as a user error. |
| `Internal Error` | `500` | Error object | Execution halts, and the provided error is returned to the client as an internal server error. |
\*_Note: HTTP status code 299 is a non-standard code specifically used by DDN for query modification._
:::warning Response Validation
DDN does not validate the plugin's response. It is the plugin's responsibility to ensure the response is valid and
aligns with its intended logic.
:::
### Use Cases
The `pre-parse` plugin can be used to add a multitude of functionalities to DDN. Some use cases are:
- **Allowlist**: Add an allowlist layer to restrict access to specific queries and mutations based on the incoming
request and session information.
- **Basic Rate Limiting**: Implement rate limiting to restrict the total number of requests that can be made to DDN in a
given time period.
- **Custom Query Validation**: Add custom query validation logic to ensure that the incoming query is valid based on
custom business logic.
- **Cache Get**: Implement a cache get layer to fetch the response from the cache before executing the query.
- **Query Transformation**: Transform queries to use different field names or structures for API versioning
- **Deprecation Management**: Automatically update queries using deprecated fields to use current alternatives
- **Multi-tenant Query Injection**: Add tenant-specific filters to ensure data isolation
### Multiple Pre-Parse Plugins
You can configure multiple pre-parse plugins in DDN metadata. These plugins execute in the order they are listed in the
metadata configuration.
If a plugin returns a `Response`, `User Error`, or `Internal Error`, the execution stops immediately, and the response
is sent back to the client. Any plugins defined after that will not run.
Letβs consider an example where the engine uses two pre-parse plugins: `Pre-parse Hook 1` and `Pre-parse Hook 2`.
```mermaid
sequenceDiagram
participant Client
participant Engine as "DDN Engine"
participant Hook1 as "Pre-parse Hook 1"
participant Hook2 as "Pre-parse Hook 2"
participant Parser as "Query Parsing and Planning"
Client ->> Engine: GraphQL Request (Original Query)
Engine ->> Hook1: Original Query + Session
alt Hook1 returns Continue (204)
Hook1 ->> Engine: Continue (no changes)
Engine ->> Hook2: Original Query + Session
else Hook1 returns Continue with new query (299)
Hook1 ->> Engine: Modified Query A
Engine ->> Hook2: Modified Query A + Session
else Hook1 returns Response/Error (200/400/500)
Hook1 ->> Engine: Direct Response/Error
Engine ->> Client: Response/Error (skip remaining hooks)
end
alt Hook2 returns Continue (204)
Hook2 ->> Engine: Continue (no changes)
Engine ->> Parser: Current Query
else Hook2 returns Continue with new query (299)
Hook2 ->> Engine: Modified Query B
Engine ->> Parser: Modified Query B
else Hook2 returns Response/Error (200/400/500)
Hook2 ->> Engine: Direct Response/Error
Engine ->> Client: Response/Error (skip execution)
end
Parser ->> Engine: Parsed Query
Engine ->> Client: Final Response
```
Hereβs how the process works:
#### Case 1: Continue
If `Pre-parse Hook 1` returns a `Continue` response (HTTP status code 204), the engine proceeds to send the request to
`Pre-parse Hook 2`. This continues until all configured pre-parse plugins have been executed.
:::tip Continue response body
The `Continue` response body is ignored by DDN. Plugins returning a `Continue` response can safely leave the body empty.
:::
#### Case 2: Continue with new query
If `Pre-parse Hook 1` returns a `Continue with new query` response (HTTP status code 299), the engine proceeds to send
the request to `Pre-parse Hook 2` with the modified query. Each subsequent plugin in the chain receives the query as
modified by the previous plugin. This continues until all configured pre-parse plugins have been executed.
**Response Body Requirements:** The `Continue with new query` response body must contain a valid GraphQL query object.
The expected response body format is:
```json
{
"query": "query MyQuery($author_id: Int!) { getAuthorById(author_id: $author_id) { first_name last_name email } }",
"variables": { "author_id": 10 },
"operationName": "MyQuery"
}
```
**Error Handling:**
- If the query syntax is invalid, the engine will return a GraphQL syntax error to the client
- If the response body is malformed JSON, the engine will return a 400 Bad Request error
- If required fields (`query`) are missing, the engine will return a validation error
- Variables and operationName are optional but should match the query if provided
**Query Transformation Sequence:**
1. Original query β Pre-parse Hook 1 β Modified Query A
2. Modified Query A β Pre-parse Hook 2 β Modified Query B
3. Modified Query B β Query execution
#### Case 3: Response, User Error, or Internal Error
If `Pre-parse Hook 1` returns any of the following:
- `Response` (HTTP status code 200)
- `User Error` (HTTP status code 400)
- `Internal Error` (HTTP status code 500)
The engine stops further execution and sends the response to the client and other plugins will not be called.
```mermaid
sequenceDiagram
participant Client
participant Initial as "Initial Request"
participant PreParseHook1 as "Pre-parse Hook 1"
participant PreParseHook2 as "Pre-parse Hook 2"
participant QueryParsingAndPlanning as "Query Parsing and Planning"
Client->>Initial: Send Request
Initial->>PreParseHook1: Forward to Pre-parse Hook 1
PreParseHook1-->>Initial: Response from Hook 1
Initial-->>Client: Return Response (Query not executed)
```
:::info Will subsequent pre-response plugins execute?
Yes, even if a pre-parse plugin returns a `Response`, `User Error`, or `Internal Error`, the subsequent `pre-response`
plugins will still execute.
:::
If all pre-parse plugins return a `Continue` response (HTTP status code 204) or a `Continue with new query` response
(HTTP status code 299), the engine completes execution and sends its generated response to the client.
:::info Query Modification Capabilities
**Can pre-parse plugins modify the request?**
Yes, pre-parse plugins can modify the GraphQL query by returning a `Continue with new query` response (HTTP status code
299).
**Use Cases for Query Modification:**
- **Query allowlisting with transformation**: Transform blocked queries into allowed alternatives
- **Field-level access control**: Remove unauthorized fields from queries based on user permissions
- **Deprecation handling**: Automatically update queries using deprecated fields to use new alternatives
- **Multi-tenancy**: Inject tenant-specific filters into queries
- **Query normalization**: Standardize query format and structure
Please note that the session information will be carried forward and cannot be modified by pre-parse plugins.
:::
## Pre-Response Plugin
The `pre-response` plugin is triggered at the final step in the execution pipeline after the query is executed. Use this
step to add webhooks after the query is executed. Pre-response plugins can operate in two modes: asynchronous (default)
or synchronous.
For pre-response plugin configuration
[click here](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecyclepreresponsepluginhook).
### Pre-Response Plugin Request
```json title="A sample request that is sent to the pre-response plugin is as follows:"
{
"response": {
"data": {
"getAuthorById": {
"first_name": "John"
}
}
},
"session": {
"role": "user",
"variables": {
"x-hasura-role": "user",
"x-hasura-user-id": "123"
}
},
"rawRequest": {
"query": "query MyQuery { getAuthorById(author_id: 10) { first_name } }",
"variables": {},
"operationName": "MyQuery"
}
}
```
:::info Customize the request
The request sent to the plugin can be customized based on the plugin's
[configuration](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecyclepreresponsepluginhookconfigrequest).
:::
### Pre-Response Plugin Modes
Pre-response plugins can operate in two modes:
#### Asynchronous Mode (Default)
In asynchronous mode, the engine sends requests to the plugin but does not wait for a response. The engine immediately
sends the response to the client after executing the query. This is the default behavior if no mode is specified.
The response from asynchronous pre-response plugins is ignored by DDN.
```yaml title="Example configuration for an asynchronous pre-response plugin:"
kind: LifecyclePluginHook
version: v1
definition:
name: async-pre-response-plugin
url:
valueFromEnv: PRE_RESPONSE_URL
pre: response
config:
request:
headers:
additional:
hasura-m-auth:
value: "your-strong-m-auth-key"
session: {}
rawRequest:
query: {}
variables: {}
mode:
type: asynchronous
```
#### Synchronous Mode
In synchronous mode, the engine waits for the plugin to respond before sending the response to the client. This allows
the plugin to modify the response or perform actions that must complete before the client receives the response.
```yaml title="Example configuration for a synchronous pre-response plugin:"
kind: LifecyclePluginHook
version: v1
definition:
name: sync-pre-response-plugin
url:
valueFromEnv: PRE_RESPONSE_URL
pre: response
config:
request:
headers:
additional:
hasura-m-auth:
value: "your-strong-m-auth-key"
session: {}
rawRequest:
query: {}
variables: {}
mode:
type: synchronous
onPluginFailure: continue
```
The `config.mode.type: synchronous` parameter enables synchronous operation, and `config.mode.onPluginFailure: continue`
determines how the engine should handle failures in the plugin. The `onPluginFailure` can be set to either `continue` or
`fail`.
Synchronous pre-response plugins can return the following responses:
| Response Type | HTTP Status Code | Response Body | Description |
| ------------------- | ---------------- | ----------------- | -------------------------------------------------------------------------- |
| `Continue` | `204` | None | The original response will be sent to the client without modification. |
| `Modified Response` | `200` | Modified response | The modified response will be sent to the client instead of the original. |
| `User Error` | `400` | Error object | The plugin encountered a user error, which will be returned to the client. |
| `Internal Error` | `500` | Error object | Treated as an internal server error and returned to the client. |
:::note Performance Considerations
Synchronous pre-response plugins add latency to the request processing as the engine must wait for the plugin to respond
before sending the response to the client. Asynchronous plugins do not add latency as they run in parallel with the
response being sent to the client.
:::
### Use Cases
The `pre-response` plugin can be used to add a number of functionalities to DDN:
#### Asynchronous Use Cases
- **Slack Notifications**: Trigger Slack notifications after a query/mutation is executed.
- **Audit Logs**: Add audit logs to track the queries and mutations executed by the users.
- **Analytics**: Collect analytics data about API usage patterns.
- **Cache Set**: Implement a cache set layer to store the response in the cache for future requests.
- **Cache Invalidation**: Implement a cache invalidation layer to invalidate the cache based on the incoming request.
#### Synchronous Use Cases
- **Response Transformation**: Modify or enrich the response data before it reaches the client.
- **Data Masking**: Remove sensitive information from responses based on user permissions.
- **Response Validation**: Validate responses against business rules before sending to clients.
- **Adding Extensions**: Add custom extensions to the GraphQL response, such as timestamps, custom metadata, or computed
summary information.
### Multiple Pre-Response Plugins
Multiple `pre-response` plugins can be configured in DDN metadata.
#### Multiple Asynchronous Plugins
For asynchronous plugins, the engine sends requests to all configured plugins in parallel. It does not wait for
responses from the plugins and immediately sends the response generated by the engine to the client.
#### Multiple Synchronous Plugins
For synchronous plugins, the engine processes them in the order they are defined in the metadata. Each plugin receives
the response as potentially modified by the previous plugin, creating a chain of response transformations.
```mermaid
sequenceDiagram
participant Client
participant Engine as "DDN Engine"
participant Hook1 as "Sync Pre-response Hook 1"
participant Hook2 as "Sync Pre-response Hook 2"
Client->>Engine: GraphQL Request
Engine->>Engine: Execute Query
Engine->>Hook1: Original Response
alt Hook1 returns Continue (204)
Hook1->>Engine: Continue (no changes)
Engine->>Hook2: Original Response
else Hook1 returns Modified Response (200)
Hook1->>Engine: Modified Response A
Engine->>Hook2: Modified Response A
else Hook1 returns Error (400/500)
Hook1->>Engine: Error Response
Engine->>Client: Error Response (skip remaining hooks)
end
alt Hook2 returns Continue (204)
Hook2->>Engine: Continue (no changes)
Engine->>Client: Current Response
else Hook2 returns Modified Response (200)
Hook2->>Engine: Modified Response B
Engine->>Client: Modified Response B
else Hook2 returns Error (400/500)
Hook2->>Engine: Error Response
Engine->>Client: Error Response
end
```
:::tip Plugin Ordering
If you have multiple synchronous pre-response plugins that need to be executed in a specific order, define them in a
single HML file. This ensures they are processed in the exact sequence you specify.
:::
#### Combining Synchronous and Asynchronous Plugins
You can configure both synchronous and asynchronous pre-response plugins together. In this case:
1. **Asynchronous plugins start executing immediately** in the background (fire-and-forget)
2. **Synchronous plugins execute sequentially** in the order they are defined, creating a chain of response
transformations
3. **The client receives the response** after synchronous plugins finish, while asynchronous plugins continue running in
the background
```mermaid
sequenceDiagram
participant Client
participant Engine as "DDN Engine"
participant AsyncHook1 as "Async Plugin 1"
participant AsyncHook2 as "Async Plugin 2"
participant SyncHook1 as "Sync Plugin 1"
participant SyncHook2 as "Sync Plugin 2"
Client->>Engine: GraphQL Request
Engine->>Engine: Execute Query
Note over Engine,AsyncHook2: Asynchronous plugins start immediately (background)
par
Engine->>AsyncHook1: Original Response (fire-and-forget)
and
Engine->>AsyncHook2: Original Response (fire-and-forget)
end
Note over Engine,SyncHook2: Synchronous plugins execute sequentially
Engine->>SyncHook1: Original Response
SyncHook1->>Engine: Modified Response A
Engine->>SyncHook2: Modified Response A
SyncHook2->>Engine: Final Response
Engine->>Client: Final Response (async plugins continue in background)
```
This approach allows you to:
- **Start background tasks immediately** using asynchronous plugins for logging, notifications, or analytics
- **Transform responses** using synchronous plugins before the client receives them
- **Optimize performance** by running non-blocking operations in parallel while only adding latency for operations that
must complete before the response is sent
## Pre-Route Plugin
The `pre-route` plugin is triggered at the first step in the routing stage, before the request is routed to the handler.
Use this step to add custom HTTP handlers to DDN. Please note that the pre-route plugin can only handle requests that do
not match DDN's pre-defined endpoints (`/graphql`, `/v1/sql`, `/v1/jsonapi`, `/v1/explain`, `/healthz` and `/metrics`).
[See the reference here](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecyclepreroutepluginhook)
for pre-route plugin metadata configuration.
### Pre-Route Plugin Request
A sample request that is sent to the `pre-route` plugin is as follows:
```json
{
"path": "/v1/rest/users/5",
"method": "POST",
"query": "limit=10&offset=0"
"body": {
"name_like": "%foo%"
}
}
```
:::info Customize the request
The request sent to the plugin can be customized based on the plugin's
[configuration](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecyclepreroutepluginhookconfigrequest).
:::
### Pre-Route Plugin Response
The `pre-route` plugin have absolute control over the execution pipeline for the configured path. The plugin can return
any of the following responses to DDN:
| Response Type | HTTP Status Code | Response Body | Description |
| ---------------- | ---------------- | ------------- | ----------------------------------------------------------- |
| `Success` | `200` | Response body | Return the response to the client. |
| `User Error` | `400` | Error object | Stop the execution and return the error to the client. |
| `Internal Error` | `500` | Error object | Stop the execution and return internal error to the client. |
### Use Cases
The `pre-route` plugin can be used to add custom endpoints to DDN which are not part of the pre-defined DDN endpoints.
Some use cases are:
- **RESTified Endpoints**: Turn GraphQL queries into REST endpoints.
- **Internal tools**: Add internal tools like graphql schema visualizers like graphql-voyager, Swagger UI for JSON API,
etc.
### Multiple Pre-Route Plugins
Multiple `pre-route` plugins can be configured in DDN metadata to handle requests to different paths. However, if more
than one plugin matches the request path, the first plugin in the list is executed and the subsequent plugins are
ignored.
So, while defining multiple `pre-route` plugins, make sure that the more specific paths are defined first.
Let's take an example where the engine is configured with two `pre-route` plugins: `Pre-route hook 1` (for path
`/v1/api/users/admin`) and `Pre-route hook 2` (for path `/v1/api/users/*`). We want to handle the request to GET admin
user details (a more specific path) using `Pre-route hook 1` and the request to GET user with an ID (a less specific
path) using `Pre-route hook 2`. For this, we need to define the `Pre-route hook 1` first in the metadata.
```mermaid
sequenceDiagram
participant Client
participant RouteHandler as "Route Handler"
participant PreRouteHook1 as "Pre-route Hook 1"
participant PreRouteHook2 as "Pre-route Hook 2"
Client->>RouteHandler: Send Request
alt Path matches /v1/api/users/admin
RouteHandler->>PreRouteHook1: Forward to Pre-route Hook 1
PreRouteHook1-->>RouteHandler: Response from Hook 1
RouteHandler-->>Client: Return Response
else Path matches /v1/api/users/*
RouteHandler->>PreRouteHook2: Forward to Pre-route Hook 2
PreRouteHook2-->>RouteHandler: Response from Hook 2
RouteHandler-->>Client: Return Response
end
```
In this example, the engine is configured with two `pre-route` plugins. The engine sends the request to either
`Pre-route hook 1` or `Pre-route hook 2` based on the path. If the path matches `/v1/api/users/admin`, the engine sends
the request to `Pre-route hook 1`. If the path matches `/v1/api/users/*`, the engine sends the request to
`Pre-route hook 2`.
## Pre-NDC Request Plugin
The `pre-ndc-request` plugin is triggered before sending requests to data connectors. This plugin can either modify the
NDC request or return a response directly, bypassing the data connector call entirely.
There can only be one `pre-ndc-request` plugin configured per data connector.
For pre-ndc-request plugin configuration
[click here](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecycleprendcrequestpluginhook).
### Pre-NDC Request Plugin Request
A sample request that is sent to the `pre-ndc-request` plugin is as follows:
```json
{
"session": {
"role": "user",
"variables": {
"x-hasura-role": "user",
"x-hasura-user-id": "123"
}
},
"ndcRequest": {
"collection": "users",
"query": {
"fields": {
"id": {
"type": "column",
"column": "id"
},
"name": {
"type": "column",
"column": "name"
}
}
},
"arguments": {},
"collection_relationships": {}
},
"dataConnectorName": "my_connector",
"operationType": "query",
"ndcVersion": "v0.2.x"
}
```
:::info Customize the request
The request sent to the plugin can be customized based on the plugin's
[configuration](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecycleprendcrequestpluginhook).
:::
### Pre-NDC Request Plugin Response
The plugin can respond in the following ways:
| Response Type | HTTP Status Code | Response Body | Description |
| ------------------ | ---------------- | ------------------------ | -------------------------------------------------------------------------- |
| `Continue` | `204` | None | The original request will be used without modification. |
| `Modified Request` | `200` | `{"ndcRequest": {...}}` | A modified request that will replace the original. |
| `Direct Response` | `200` | `{"ndcResponse": {...}}` | A response that will be used instead of calling the data connector. |
| `User Error` | `400` | Error object | The plugin encountered a user error, which will be returned to the client. |
| `Internal Error` | `500` | Error object | Treated as an internal error. |
### Use Cases
The `pre-ndc-request` plugin can be used for various functionalities:
- **Request Transformation**: Modify NDC requests before they reach the data connector.
- **Request Validation**: Validate requests and return errors for invalid operations.
- **Caching**: Return cached responses directly without calling the data connector.
- **Request Logging**: Log all requests for audit purposes while allowing them to proceed.
- **Mock Responses**: Return mock data for testing or development environments.
### Example Configuration
```yaml title="Here is an example of a pre-ndc-request plugin configuration in DDN metadata:"
kind: LifecyclePluginHook
version: v1
definition:
name: my-pre-ndc-request-plugin
url:
valueFromEnv: PRE_NDC_REQUEST_PLUGIN_URL
pre: ndcRequest
connectors:
- my_postgres_connector
config:
request:
headers:
additional:
hasura-m-auth:
value: "your-strong-m-auth-key"
session: {}
ndcRequest: {}
```
## Pre-NDC Response Plugin
The `pre-ndc-response` plugin is triggered after receiving responses from data connectors but before further processing.
This plugin allows modification of NDC responses.
There can only be one `pre-ndc-response` plugin configured per data connector.
For pre-ndc-response plugin configuration
[click here](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecycleprendcresponsepluginhook).
### Pre-NDC Response Plugin Request
A sample request that is sent to the `pre-ndc-response` plugin is as follows:
```json
{
"session": {
"role": "user",
"variables": {
"x-hasura-role": "user",
"x-hasura-user-id": "123"
}
},
"ndcRequest": {
"collection": "users",
"query": {
"fields": {
"id": {
"type": "column",
"column": "id"
}
}
}
},
"ndcResponse": {
"rows": [
{
"id": 1
},
{
"id": 2
}
]
},
"dataConnectorName": "my_connector",
"operationType": "query",
"ndcVersion": "v0.2.x"
}
```
:::info Customize the request
The request sent to the plugin can be customized based on the plugin's
[configuration](reference/metadata-reference/engine-plugins.mdx#lifecyclepluginhook-lifecycleprendcresponsepluginhook).
:::
### Pre-NDC Response Plugin Response
The plugin can respond in the following ways:
| Response Type | HTTP Status Code | Response Body | Description |
| ------------------- | ---------------- | --------------------- | -------------------------------------------------------------------------- |
| `Continue` | `204` | None | The original response will be used without modification. |
| `Modified Response` | `200` | Modified NDC response | A modified NDC response that will replace the original. |
| `User Error` | `400` | Error object | The plugin encountered a user error, which will be returned to the client. |
| `Internal Error` | `500` | Error object | Treated as an internal error. |
### Use Cases
The `pre-ndc-response` plugin can be used for:
- **Response Transformation**: Modify or enrich data connector responses.
- **Data Filtering**: Filter sensitive data from responses based on user permissions.
- **Response Caching**: Store responses for future caching purposes.
- **Response Logging**: Log responses for audit or analytics purposes.
- **Data Enrichment**: Add additional data to responses from external sources.
### Example Configuration
```yaml title="Here is an example of a pre-ndc-response plugin configuration in DDN metadata:"
kind: LifecyclePluginHook
version: v1
definition:
name: my-pre-ndc-response-plugin
url:
valueFromEnv: PRE_NDC_RESPONSE_PLUGIN_URL
pre: ndcResponse
connectors:
- my_postgres_connector
config:
request:
headers:
additional:
hasura-m-auth:
value: "your-strong-m-auth-key"
session: {}
ndcRequest: {}
ndcResponse: {}
```
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/tutorials/
# Tutorials
## Introduction
This section provides tutorials to guide you through essential project configuration tasks. You'll learn how to manage
multiple environments, structure your project around subgraphs, and distribute your project across separate repositories
to enable independent ownership and development.
## Available tutorials
- [Manage multiple environments](/project-configuration/tutorials/manage-multiple-environments.mdx)
- [Work with multiple subgraphs](/project-configuration/tutorials/work-with-multiple-subgraphs.mdx)
- [Work with multiple subgraphs across separate repositories](/project-configuration/tutorials/work-with-multiple-repositories.mdx)
==============================
# manage-multiple-environments.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/tutorials/manage-multiple-environments
# Manage Multiple Environments
## Introduction
Managing multiple contexts in Hasura DDN allows you to replicate the functionality of traditional environments like
`staging` and `production`, but with greater flexibility. Instead of rigidly-defined environments, Hasura DDN uses
contexts to store key details such as project configurations, metadata, and environment variables. This approach lets
you switch between different setups without disrupting your workflow or end users.
In this tutorial, you'll learn how to:
- Set up multiple contexts to test and collaborate on your project
- Use the CLI to manage your project's contexts
- Build and deploy projects with shared metadata across different contexts
This tutorial should take less than twenty minutes.
## Setup
### Step 1. Initialize a new local project
```sh title="Create a new local project:"
ddn supergraph init environments-example && cd environments-example
```
This will scaffold out the necessary files for a Hasura DDN project in a new `environments-example` directory.
### Step 2. Add a data source and seed data
In this tutorial, we'll use the PostgreSQL connector and our sample PostgreSQL database:
```plaintext
postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app
```
```sh title="In your project directory, run the following, choose hasura/postrges, and pass the connection URI above when prompted:"
ddn connector init my_pg -i
```
### Step 3. Generate the Hasura metadata
```sh title="Next, use the CLI to introspect the PostgreSQL database:"
ddn connector introspect my_pg
```
After running this, you should see a representation of your database's schema in the
`app/connector/my_pg/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Now, track the table from your PostgreSQL database as a model in your DDN metadata:"
ddn models add my_pg users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 4. Create a new local build and test the API
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
```sh title="Start your local Hasura DDN Engine and PostgreSQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query {
users {
id
name
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb",
"name": "Sean"
},
{
"id": "82001336-65b7-11ed-b905-7fa26a16d198",
"name": "Rob"
},
{
"id": "86d5fba0-65b7-11ed-b906-afb985970e2e",
"name": "Marion"
},
{
"id": "8dea1160-65b7-11ed-b907-e3c5123cb650",
"name": "Sandeep"
},
{
"id": "9bd9d300-65b7-11ed-b908-571fef22d2ba",
"name": "Abby"
}
]
}
}
```
## Create a new context
### Step 5. Create a new project context and switch to it
```sh title="From the project directory, run:"
ddn context create-context staging
```
Verify this by opening the `.hasura/context.yaml` file. You'll see a `contexts` array with two entries: `default` and
your newly-created `staging`.
:::tip What can be stored in context?
Contexts store a series of key-value pairs that make it easy to switch between different setups. You can learn more
[here](/project-configuration/project-management/manage-contexts.mdx).
:::
### Step 6. Update the context values
```sh title="First, set the supergraph configuration:"
ddn context set supergraph "supergraph.yaml"
```
```sh title="Then, define which subgraphs to include:"
ddn context set subgraph "app/subgraph.yaml"
```
```sh title="Finally, stub out a local .env.staging file and add it to your staging context:"
touch .env.staging && ddn context set localEnvFile ".env.staging"
```
You'll see each of these key-value pairs added to your `staging` context.
:::tip Customize these values
The examples provided here are starting points and can be adapted to suit your project's requirements. Ideally, the
values included in your `.env.staging` file will reference resources such as testing database instances. Keep the keys
consistent with those in your production `.env` files, but assign different values tailored for staging or testing
environments.
:::
### Step 7. Create a new staging cloud project
```sh title="Using your new context, create a cloud project:"
ddn project init --env-file-name ".env.staging.cloud"
```
The CLI will add the project's name and your `cloudEnvFile` to your `staging` context.
### Step 8. Create a new build on your staging project
```sh
ddn supergraph build create
```
The CLI will output information about the build, including a console URL which you can open in your browser. Your local
metadata was used to create this API build on your `staging` project.
### Step 9. Create a new production cloud project
```sh title="First, switch contexts:"
ddn context set-current-context default
```
```sh title="Then, create a new project:"
ddn project init
```
In your `context.yaml`, you'll now see a `project` and `cloudEnvFile` value for your `default` context. We're
considering this our `production` instance.
### Step 10. Create a new build on your production project
```sh title="Since our current context is default, we can now use the same metadata to create a productioun build:"
ddn supergraph build create
```
Just as with our `staging` project, you can navigate to the console URL output by the CLI and explore your `production`
build, which should be identical to your `staging` build.
## Next steps
Now that you know how contexts can help you manage environments, see how easy it is to
[set up CI/CD](/deployment/hasura-ddn/ci-cd.mdx) using the CLI and contexts.
==============================
# work-with-multiple-subgraphs.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/tutorials/work-with-multiple-subgraphs
# Work with Multiple Subgraphs
## Introduction
Learn how to manage multiple subgraphs in Hasura DDN to streamline team ownership, enforce clear data boundaries, and
scale your API easily. This tutorial will show you how to structure your project for flexibility and collaboration,
ensuring efficient and adaptable API development.
In this tutorial, you'll learn how to:
- How to organize your project into subgraphs
- How to create a subgraph as part of your local project
- How to create relationships across subgraphs
- How to create a subgraph on a cloud project
We'll demonstrate this by creating a supergraph with two subgraphs: one owned by a `customers` team and another owned by
the `billing` team.
This tutorial should take less than twenty minutes.
## Setup
### Step 1. Initialize a new local project
```sh title="Create the project directory and move into it:"
ddn supergraph init subgraph-example && cd subgraph-example
```
### Step 2. Add a data source and seed data
In this tutorial, we'll use the PostgreSQL connector and our sample PostgreSQL database:
```plaintext
postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app
```
```sh title="In your project directory, run the following, choose hasura/postrges, and pass the connection URI above when prompted:"
ddn connector init customers_pg -i
```
### Step 3. Generate the Hasura metadata
```sh title="Next, use the CLI to introspect your PostgreSQL database:"
ddn connector introspect customers_pg
```
After running this, you should see a representation of your database's schema in the
`app/connector/customers_pg/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Now, track the table from your PostgreSQL database as a model in your DDN metadata:"
ddn models add customers_pg users
```
Open the `app/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this Hasura
Metadata Language file to represent the `users` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 4. Create a new local build and test the API
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
```sh title="Start your local Hasura DDN Engine and PostgreSQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_USERS {
users {
id
name
}
}
```
```json title="You'll get the following response:"
{
"data": {
"users": [
{
"id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb",
"name": "Sean"
},
{
"id": "82001336-65b7-11ed-b905-7fa26a16d198",
"name": "Rob"
},
{
"id": "86d5fba0-65b7-11ed-b906-afb985970e2e",
"name": "Marion"
},
{
"id": "8dea1160-65b7-11ed-b907-e3c5123cb650",
"name": "Sandeep"
},
{
"id": "9bd9d300-65b7-11ed-b908-571fef22d2ba",
"name": "Abby"
}
]
}
}
```
## Add a subgraph
### Step 5. Create a new `billing` subgraph
```sh title="Use the DDN CLI to create the subgraph in your local metadata:"
ddn subgraph init billing --graphql-type-name-prefix billing
```
You'll see a new `billing` directory added to your local project. The CLI pre-configured this with all the necessary
files and subdirectories to contain data connectors and their metadata.
Additionally, by adding the `--graphql-type-name-prefix` flag, we're ensuring any types generated by the CLI will not
conflict with existing types from our other subgraph. If we had concerns about conflicts of root-level GraphQL fields,
we could also add the flag `--graphql-root-field-prefix`.
```sh title="Then, add it to your supergraph.yaml config:"
ddn subgraph add billing --subgraph ./billing/subgraph.yaml --target-supergraph ./supergraph.yaml
```
```yaml title="You can verify this by opening the supergraph.yaml in the root of your project. You should see the following:"
kind: Supergraph
version: v2
definition:
subgraphs:
- globals/subgraph.yaml
- app/subgraph.yaml
# highlight-start
- billing/subgraph.yaml
# highlight-end
```
### Step 6. Switch contexts {#switch-contexts}
```sh title="Switch contexts to the billing subgraph:"
ddn context set subgraph billing/subgraph.yaml
```
This will simplify our subsequent CLI commands as the CLI will now know that β whenever the `--subgraph` flag is
required β we're referencing the `billing` subgraph.
### Step 7. Add a data source and seed data
As before, we'll use the PostgreSQL connector with our sample database:
```plaintext
postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app
```
```sh title="In your project directory, run:"
ddn connector init billing_pg -i
```
:::info Notice where this connector was added
Since you created the `billing` subgraph and switched contexts in [Step 6](#switch-contexts), the CLI added the
connector in the `billing` subgraph directory.
:::
### Step 8. Generate the Hasura metadata
```sh title="Next, use the CLI to introspect the PostgreSQL database:"
ddn connector introspect billing_pg
```
After running this, you should see a representation of the database's schema in the
`billing/connector/billing_pg/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Now, track the table from your PostgreSQL database as a model in your DDN metadata:"
ddn models add billing_pg orders
```
Open the `billing/metadata` directory and you'll find a newly-generated file: `PaymentInformation.hml`. The DDN CLI will
use this Hasura Metadata Language file to represent the `payment_information` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
### Step 9. Create a new local build and test the API
```sh title="To create a local build, run:"
ddn supergraph build local
```
```sh title="Kill your local services from their terminal tab:"
CTRL+C
```
```sh title="Restart your local Hasura DDN Engine and PostgreSQL connector:"
ddn run docker-start
```
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
You should see both root-level fields for `users` and `orders` in your GraphQL API. Additionally, if you navigate to the
`Explorer` tab in the left-hand navigation, you should see your supergraph's data domains organized into two separate
subgraphs.
### Step 10. Create a relationship across subgraphs
As our models are in different subgraphs, we'll need to explicitly define a
[relationship](/reference/metadata-reference/relationships.mdx) between these using Hasura metadata.
```yaml title="Add the following to your Users.hml file:"
---
kind: Relationship
version: v1
definition:
name: orders
sourceType: Users
target:
model:
#highlight-start
subgraph: billing
#highlight-end
name: Orders
relationshipType: Array
mapping:
- source:
fieldPath:
- fieldName: id
target:
modelField:
- fieldName: userId
```
By calling out the `billing` subgraph in the relationship object, the
[Hasura VS Code extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) can help you with
validating your configuration.
```sh title="Then, create a new build:"
ddn supergraph build local
```
```sh title="And kill your services with CTRL+C before restarting them:"
ddn run docker-start
```
```graphql title="You can now query across subgraphs:"
query GET_USERS_AND_ORDERS {
users {
id
name
orders {
id
createdAt
status
}
}
}
```
```json title="And get a response like:"
{
"data": {
"users": [
{
"id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb",
"name": "Sean",
"orders": [
{
"id": "7ff13435-b590-4d6b-957f-f7fd39d4528a",
"createdAt": "2023-10-29T17:02:50.958076+00:00",
"status": "complete"
}
]
},
{
"id": "82001336-65b7-11ed-b905-7fa26a16d198",
"name": "Rob",
"orders": [
{
"id": "9891596a-a732-4c1c-902c-1a112da48fec",
"createdAt": "2023-10-29T17:02:51.150084+00:00",
"status": "complete"
}
]
},
{
"id": "86d5fba0-65b7-11ed-b906-afb985970e2e",
"name": "Marion",
"orders": [
{
"id": "85581445-752a-4aef-9684-b648eb5d5f42",
"createdAt": "2023-10-29T17:02:51.085229+00:00",
"status": "complete"
}
]
},
{
"id": "8dea1160-65b7-11ed-b907-e3c5123cb650",
"name": "Sandeep",
"orders": [
{
"id": "c7406b75-6b24-41e4-9c5b-ff3feada9447",
"createdAt": "2023-10-29T17:02:50.889261+00:00",
"status": "processing"
}
]
},
{
"id": "9bd9d300-65b7-11ed-b908-571fef22d2ba",
"name": "Abby",
"orders": [
{
"id": "98612470-1feb-4b91-88f7-9289d652ee87",
"createdAt": "2023-10-29T17:02:51.021317+00:00",
"status": "complete"
}
]
}
]
}
}
```
### Step 11. Create a cloud project
```sh title="Using your default context, create a new cloud project:"
ddn project init
```
Your `.hasura/context.yaml` will be updated to reflect the new project name. The `default` context of this project is
now linked to the cloud project you just created.
Additionally, the CLI will output information about each subgraph that it generated in your cloud project, including
`billing`.
### Step 12. Create a new build on your production project
```sh title="Finally, create a new build on your cloud project:"
ddn supergraph build create
```
The CLI will return a console URL which you can navigate to; within the `Explorer` tab, you'll find your project's
subgraphs, including `billing`.
:::info Adding subgraphs after project initialization
When you **initialize a cloud project**, the subgraphs in your local metadata will automatically be generated in your
cloud project. **If you add subgraphs after initialization, you'll have to manually add the subgraphs to the cloud
project as well.** Learn more [here](project-configuration/subgraphs/create-a-subgraph.mdx#cloud).
:::
## Next steps
In the example above, you learned the steps to organize your project into multiple subgraphs for clearer ownership
between teams. To take this a step further, many teams prefer to implement multi-repository setups wherein a single
subgraph can be added to an existing or private team repository. Learn more
[here](/project-configuration/tutorials/work-with-multiple-repositories.mdx).
==============================
# work-with-multiple-repositories.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/tutorials/work-with-multiple-repositories
# Work with Multiple Repositories
## Introduction
Managing multiple repositories in Hasura DDN lets you distribute development across independent subgraphs located in
separate repositories while maintaining a unified supergraph. This approach provides flexibility for teams to work
autonomously in their respective data domains, while contributing to a single coordinated API.
By organizing your project into multiple repositories, you can iterate and deploy changes efficiently without impacting
other subgraphs, ensuring a smooth and collaborative development experience.
You'll learn:
- How to organize your project into subgraphs
- How to provision a "parent" repo
- How to set up your cloud project for multi-subgraph development
- How to create independent subgraph repositories
- How to create, test, and deploy your supergraph as subgraphs develop independently
- How to create relationships across independent subgraphs
This tutorial takes about thirty minutes.
:::warning DDN Advanced Plan required
In order to utilize multi-repository collaboration, you must have an active
[DDN Advanced Plan](https://hasura.io/pricing).
:::
## Create the initial project
Begin by creating a "parent project" that will serve as the coordinated supergraph for your independent subgraph
repositories. The person creating this will assume the role of **supergraph admin** and have full control over
provisioning subgraphs, inviting collaborators, and managing the API as a whole.
### Step 1. Initialize a new local project
```sh title="Create a new local project and initialize a git repository:"
ddn supergraph init parent-project && cd parent-project && git init
```
This will scaffold out the local configuration for a DDN project and initialize a git repository.
### Step 2. Create a cloud project
```sh title="From the local project directory, create a new cloud project:"
ddn project init
```
In `.hasura/context.yaml`, you'll see a new `project` key-value pair with the name of the project returned from the CLI.
### Step 3. Create a commit
```sh title="Create an initial commit with your local project mapped to the cloud project via context:"
git add . && git commit -m "Initial commit"
```
### Step 4. Provision subgraphs
We'll add two subgraphs to this supergraph: `customers` and `billing`.
```sh title="Create the subgraphs on the cloud project:"
ddn project subgraph create customers && ddn project subgraph create billing
```
### Step 5. Create a supergraph build
```sh title="Create an initial supergraph build:"
ddn supergraph build create
```
This will serve as the foundation for your first **subgraph** build to expand upon.
### Step 6. Invite collaborators
Head to the project's console at [console.hasura.io](https://console.hasura.io) and navigate to
`Setetings/Collaborators`. Then, invite collaborators based on their role:
| Role | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| Subgraph Admin | Users with permissions to create builds and deploy them to the parent project's endpoint. |
| Subgraph Developer | Developers responsible for implementing features and making iterative changes to the subgraph's functionality. |
:::info Learn more about collaborators
Learn more about collaborators and roles [here](/project-configuration/project-management/manage-collaborators.mdx).
:::
Each collaborator will receive an email inviting them to your project.
## Add independent subgraphs to the project
Each subgraph will live in its own repository. While this can be an existing repository, we're going to demonstrate
initializing a new repository below.
### Customers
#### Step 1. Create a new repo for the `customers` subgraph
```sh title="Initialize a new local project and git repo:"
ddn supergraph init customer-team --create-subgraph customers && cd customer-team && git init
```
This will scaffold out the necessary project structure along with initializing a git repository for version control.
#### Step 2. Map the local project to the existing cloud project
```sh title="Use the --with-project flag to map the local directory to the parent cloud project, replacing the name with your parent project's name:"
ddn project init --with-project
```
You'll see the `project` key-value pair updated in your `.hasura/context.yaml` file.
#### Step 3. Set the subgraph context
```sh title="Change the context to your subgraph's configuration file:"
ddn context set subgraph ./customers/subgraph.yaml
```
This will set your unique subgraph in your `.hasura/context.yaml` file and make the CLI commands below more concise.
#### Step 4. Add prefixing
```sh title="Next, open the customers/subgraph.yaml file and add the following:"
kind: Subgraph
version: v2
definition:
name: customers
generator:
rootPath: .
namingConvention: graphql
#highlight-start
graphqlRootFieldPrefix: customers_
graphqlTypeNamePrefix: customers_
#highlight-end
includePaths:
- metadata
```
These will prevent collisions of GraphQL root fields and GraphQL types when your supergraph is built.
#### Step 5. Add a data source and generate your first local build
In this tutorial, we'll use the PostgreSQL connector and our sample PostgreSQL database:
```plaintext
postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app
```
```sh title="In your project directory, run the following, choose hasura/postrges, and pass the connection URI above when prompted:"
ddn connector init customers_pg -i
```
##### Generate the Hasura metadata
```sh title="Next, use the CLI to introspect the PostgreSQL database:"
ddn connector introspect customers_pg
```
After running this, you should see a representation of your database's schema in the
`customers/connector/customers_pg/configuration.json` file; you can view this using `cat` or open the file in your
editor.
```sh title="Now, track the table from your PostgreSQL database as a model in your DDN metadata:"
ddn models add customers_pg users
```
Open the `customers/metadata` directory and you'll find a newly-generated file: `Users.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `users` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
##### Create a new local build and test the API
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
```sh title="Start your local Hasura DDN Engine and PostgreSQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_USERS {
customers_users {
id
name
}
}
```
```json title="You'll get the following response:"
{
"data": {
"customers_users": [
{
"id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb",
"name": "Sean"
},
{
"id": "82001336-65b7-11ed-b905-7fa26a16d198",
"name": "Rob"
},
{
"id": "86d5fba0-65b7-11ed-b906-afb985970e2e",
"name": "Marion"
},
{
"id": "8dea1160-65b7-11ed-b907-e3c5123cb650",
"name": "Sandeep"
},
{
"id": "9bd9d300-65b7-11ed-b908-571fef22d2ba",
"name": "Abby"
}
]
}
}
```
#### Step 6. Create a subgraph build
Now that we've tested our local API and are happy with its state, we can create a build of our independent subgraph on
our cloud project.
```sh title="Create a subgraph build on your DDN project:"
ddn subgraph build create
```
The CLI will return the subgraph's build version; you'll need this value in the next step.
#### Step 7. Create a supergraph build
```sh title="Begin by listing the available supergraph builds:"
ddn supergraph build get
```
Grab the most recent build's version and use it β along with the subgraph build version βΒ in the following command.
```sh title="Finally, create a supergraph build incorporating your latest subgraph build:"
ddn supergraph build create --subgraph-version customers: --base-supergraph-version
```
The CLI will return a build URL for the console so you can explore your build and test your subgraph changes with others
before applying the build to the supergraph. In order to apply the build, you must be a **subgraph admin** or higher:
```sh
ddn supergraph build apply
```
:::tip Streamline team collaboration
For more efficient development across multiple repositories, you can create and apply the supergraph build in a single command using
`ddn supergraph build create --subgraph-version customers: --base-supergraph-version --apply`
:::
:::warning Make sure to stop all running services
As a multi-repository setup is intended to be utilized by multiple persons across teams, you're not likely to have
multiple instances of your Hasura DDN Engine and other services running at the same time on your machine. Ensure you've
killed all active services before proceeding to the next step for the `billing` subgraph.
:::
### Billing
The steps below will allow you to incorporate your independent `billing` subgraph into your deployed supergraph API.
#### Step 1. Create a new repo for the `billing` subgraph
```sh title="Initialize a new local project and git repo:"
ddn supergraph init billing-team --create-subgraph billing && cd billing-team && git init
```
This will scaffold out the necessary project structure along with initializing a git repository for version control.
#### Step 2. Map the local project to the existing cloud project
```sh title="Use the --with-project flag to map the local directory to the parent cloud project, replacing the name with your parent project's name:"
ddn project init --with-project
```
You'll see the `project` key-value pair updated in your `.hasura/context.yaml` file.
#### Step 3. Set the subgraph context
```sh title="Change the context to your subgraph's configuration file:"
ddn context set subgraph ./billing/subgraph.yaml
```
This will set your unique subgraph in your `.hasura/context.yaml` file and make the CLI commands below more concise.
#### Step 4. Add prefixing
```sh title="Next, open the billing/subgraph.yaml file and add the following:"
kind: Subgraph
version: v2
definition:
name: billing
generator:
rootPath: .
namingConvention: graphql
#highlight-start
graphqlRootFieldPrefix: billing_
graphqlTypeNamePrefix: billing_
#highlight-end
includePaths:
- metadata
```
Like before, this will ensure the types an fields are namespaced to your subgraph.
#### Step 5. Add a data source and generate your first local build
As before, we'll use the PostgreSQL connector with our sample database:
```plaintext
postgresql://read_only_user:readonlyuser@35.236.11.122:5432/v3-docs-sample-app
```
```sh title="In your project directory, run:"
ddn connector init billing_pg -i
```
##### Generate the Hasura metadata
```sh title="Next, use the CLI to introspect the PostgreSQL database:"
ddn connector introspect billing_pg
```
After running this, you should see a representation of your database's schema in the
`billing/connector/billing_pg/configuration.json` file; you can view this using `cat` or open the file in your editor.
```sh title="Now, track the table from the PostgreSQL database as a model in your DDN metadata:"
ddn models add billing_pg orders
```
Open the `billing/metadata` directory and you'll find a newly-generated file: `Orders.hml`. The DDN CLI will use this
Hasura Metadata Language file to represent the `orders` table from PostgreSQL in your API as a
[model](/reference/metadata-reference/models.mdx).
##### Create a new local build and test the API
```sh title="To create a local build, run:"
ddn supergraph build local
```
The build is stored as a set of JSON files in `engine/build`.
```sh title="Start your local Hasura DDN Engine and PostgreSQL connector:"
ddn run docker-start
```
Your terminal will be taken over by logs for the different services.
```sh title="In a new terminal tab, open your local console:"
ddn console --local
```
```graphql title="In the GraphiQL explorer of the console, write this query:"
query GET_ORDERS {
billing_orders {
id
createdAt
status
}
}
```
```json title="You'll get the following response:"
{
"data": {
"billing_orders": [
{
"id": "c7406b75-6b24-41e4-9c5b-ff3feada9447",
"createdAt": "2023-10-29T17:02:50.889261+00:00",
"status": "processing"
},
{
"id": "7ff13435-b590-4d6b-957f-f7fd39d4528a",
"createdAt": "2023-10-29T17:02:50.958076+00:00",
"status": "complete"
},
{
"id": "98612470-1feb-4b91-88f7-9289d652ee87",
"createdAt": "2023-10-29T17:02:51.021317+00:00",
"status": "complete"
},
{
"id": "85581445-752a-4aef-9684-b648eb5d5f42",
"createdAt": "2023-10-29T17:02:51.085229+00:00",
"status": "complete"
},
{
"id": "9891596a-a732-4c1c-902c-1a112da48fec",
"createdAt": "2023-10-29T17:02:51.150084+00:00",
"status": "complete"
}
]
}
}
```
#### Step 6. Create a subgraph build
```sh title="Once your local metadata is in its desired state, create a subgraph build on your DDN project:"
ddn subgraph build create
```
The CLI will return the subgraph's build version; you'll need this value in the next step.
#### Step 7. Create a supergraph build
```sh title="Begin by listing the available supergraph builds:"
ddn supergraph build get
```
Grab the most-recent build's version and use it β along with the subgraph build version βΒ in the following command.
```sh title="Finally, create a supergraph build incorporating your latest subgraph build:"
ddn supergraph build create --subgraph-version billing: --base-supergraph-version
```
The CLI will return a build URL for the console so you can explore your build and test your subgraph changes with others
before applying the build to the supergraph. In order to apply the build, you must be a **subgraph admin** or higher:
```sh
ddn supergraph build apply
```
:::tip Streamline team collaboration
For more efficient development across multiple repositories, you can create and apply the supergraph build in a single command using
`ddn supergraph build create --subgraph-version billing: --base-supergraph-version --apply`
:::
:::warning Make sure to stop all running services
As a multi-repository setup is intended to be utilized by multiple persons across teams, you're not likely to have
multiple instances of your Hasura DDN Engine and other services running at the same time on your machine. Ensure you've
killed all active services before proceeding to the next step.
:::
## Add a cross-subgraph relationship
**We can create relationships across subgraphs that are testable on a Hasura Cloud project**. As your locally-running
engine will only have access to your local subgraphs and their accompanying metadata, you'll need to define
relationships using your knowledge of other subgraphs and then test them using a supergraph build in your hosted
environment.
```yaml title="Add the following relationship object to your Users.hml file in your customer-team repo:"
---
kind: Relationship
version: v1
definition:
name: orders
sourceType: Users
target:
model:
subgraph: billing
name: Orders
relationshipType: Array
mapping:
- source:
fieldPath:
- fieldName: id
target:
modelField:
- fieldName: userId
```
```sh title="Create a new subgraph build:"
ddn subgraph build create
```
```sh title="Get the list of supergraph builds:"
ddn supergraph build get
```
```sh title="Then, create a supergraph build incorporating your latest subgraph build:"
ddn supergraph build create --subgraph-version customers: --base-supergraph-version
```
```graphql title="Now, you can test your nested query across subgraphs:"
query GET_USERS_AND_ORDERS {
customers_users {
id
name
orders {
id
createdAt
status
}
}
}
```
```json title="With a repsonse like this:"
{
"data": {
"customers_users": [
{
"id": "7cf0a66c-65b7-11ed-b904-fb49f034fbbb",
"name": "Sean",
"orders": [
{
"id": "7ff13435-b590-4d6b-957f-f7fd39d4528a",
"createdAt": "2023-10-29T17:02:50.958076+00:00",
"status": "complete"
}
]
},
{
"id": "82001336-65b7-11ed-b905-7fa26a16d198",
"name": "Rob",
"orders": [
{
"id": "9891596a-a732-4c1c-902c-1a112da48fec",
"createdAt": "2023-10-29T17:02:51.150084+00:00",
"status": "complete"
}
]
},
{
"id": "86d5fba0-65b7-11ed-b906-afb985970e2e",
"name": "Marion",
"orders": [
{
"id": "85581445-752a-4aef-9684-b648eb5d5f42",
"createdAt": "2023-10-29T17:02:51.085229+00:00",
"status": "complete"
}
]
},
{
"id": "8dea1160-65b7-11ed-b907-e3c5123cb650",
"name": "Sandeep",
"orders": [
{
"id": "c7406b75-6b24-41e4-9c5b-ff3feada9447",
"createdAt": "2023-10-29T17:02:50.889261+00:00",
"status": "processing"
}
]
},
{
"id": "9bd9d300-65b7-11ed-b908-571fef22d2ba",
"name": "Abby",
"orders": [
{
"id": "98612470-1feb-4b91-88f7-9289d652ee87",
"createdAt": "2023-10-29T17:02:51.021317+00:00",
"status": "complete"
}
]
}
]
}
}
```
## Recap
By organizing your project into multiple repositories, you can create a flexible and collaborative workflow for subgraph
development in Hasura DDN. Starting with a parent project, you learned how to provision subgraphs, invite collaborators,
and manage builds to integrate subgraph changes into a unified supergraph. This structure ensures teams can work
independently while maintaining seamless integration and coordination across the entire API.
==============================
# supergraph.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/supergraph
# Supergraph
## Introduction
A supergraph is essentially the API for your project. It provides a unified interface for interacting with your data and
serves as the backbone for building applications that rely on multiple data sources.
## Components
The supergraph is made up of several key components that work together to deliver a seamless API experience:
- **Subgraphs**: These represent different data domains and can include multiple data connectors to bring in data from
various sources.
- **Builds**: A supergraph consists of immutable builds. At any given time, one build is applied, and it can be easily
rolled back to a previous state if necessary.
## How it all works
The supergraph is defined using Hasura Metadata Language, which the engine uses to generate and serve the API. This
metadata acts as the blueprint for everything, including:
- Defining roles and permissions to control access.
- Configuring authentication mechanisms.
- Defining relationships across and between data sources.
- Specifying the GraphQL fields and types exposed by the API.
By centralizing these configurations, the supergraph ensures consistency and simplifies collaboration across teams.
## Next steps
- [Learn more about subgraphs](/project-configuration/subgraphs/index.mdx)
==============================
# Allowlist Plugin
URL: https://hasura.io/docs/3.0/docs/plugins/allowlist/
# Allowlist Plugin
## Introduction
The [Allowlist Plugin](https://github.com/hasura/engine-plugin-allowlist) provides a mechanism to restrict access to
your supergraph by defining specific queries or mutations that are allowed. This adds an extra layer of security by
ensuring that only predefined operations can be executed.
The plugin integrates with Hasura DDN as a **pre-parse plugin** and can be deployed as an HTTP service using tools like
Cloudflare Workers or similar services.
Key benefits of the allowlist plugin include:
- **Enhanced security:** Restrict operations to a predefined set.
- **Flexibility:** Supports dynamic configuration through environment variables.
- **Integration:** Works as a pre-parse plugin, ensuring only allowed queries or mutations are processed.
## Next steps
To get started with configuring and deploying the Allowlist Plugin, refer to the [guide](/plugins/allowlist/how-to.mdx),
which walks you through the process of setting up, configuring, and deploying the plugin.
==============================
# create-a-subgraph.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/subgraphs/create-a-subgraph
# How to Create a Subgraph
## Introduction
You can easily create subgraphs using the CLI and β for cloud projects β the Hasura DDN console. By default, the CLI
will generate an `app` subgraph when a new project is initialized.
## In your local metadata {#local}
```sh title="Within a local project directory, run the following:"
ddn subgraph init
```
You can verify this by identifying the new subdirectory in your project with the subgraph's name.
```sh title="Then, add the subgraph to your supergraph's config file:"
ddn subgraph add --subgraph .//subgraph.yaml --target-supergraph ./supergraph.yaml
```
```sh title="This will add the new subgraph to your supergraph.yaml:"
kind: Supergraph
version: v2
definition:
subgraphs:
- globals/subgraph.yaml
- app/subgraph.yaml
# highlight-start
- /subgraph.yaml
# highlight-end
```
## On a cloud project {#cloud}
By default, when a cloud project is initialized, any subgraphs present in your supergraph configuration will be
initialized as well. However, to explicitly add a new subgraph to a cloud project β such as when iterating on an
existing project β follow these steps:
### Using the CLI
```sh title="The folowing will create a new subgraph on the current context's cloud project:"
ddn project subgraph create
```
### Using the console
Under `Settings` > `Subgraphs` click `+ Create New Subgraph`.
:::info Subgraph mapping
Be careful of mismatching subgraphs between your local metadata and a cloud project: if you create a subgraph locally
and add it to your supergraph configuration without also creating the subgraph on your cloud project, cloud builds will
fail.
To resolve this, ensure that when you create a local subgraph in your metadata, you also create the companion subgraph
on the cloud project.
:::
## Next steps
- [Check out an end-to-end tutorial for working with multiple subgraphs](/project-configuration/tutorials/work-with-multiple-subgraphs.mdx)
- [Learn how to work with multiple subgraphs in a project](/project-configuration/subgraphs/working-with-multiple-subgraphs.mdx)
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/subgraphs/
# Subgraphs
## Introduction
A subgraph is a focused component of your overall supergraph, typically organized around specific data domains or
business functions.
Subgraphs are often team-specific, aligning with the responsibilities and expertise of individual teams. This structure
not only enables efficient development but also makes it easier to onboard new teams as your unified supergraph evolves.
By defining clear boundaries and responsibilities, subgraphs allow new teams to integrate seamlessly without impacting
existing functionality, fostering a scalable and collaborative development environment.
## Organization
Subgraphs provide flexibility and modularity in API development. A single project can include one or more subgraphs,
depending on its complexity and the distribution of responsibilities among teams. Subgraphs are designed to be iterated
on and developed independently, allowing teams to work at their own pace and with their own preferred tools and
languages without interfering with the broader system.
In setups with multiple repositories, subgraphs also enhance governance by limiting access. Individual developers or
teams are only granted permissions to their specific subgraph, ensuring they cannot modify or disrupt other parts of the
API. This separation not only protects the integrity of the overall system but also enables streamlined collaboration
across teams by maintaining a well-defined scope of access.
## Next steps
- [Learn how to create a subgraph](/project-configuration/subgraphs/create-a-subgraph.mdx)
- [Learn how to establish relationships across subgraphs to unify your data](/project-configuration/subgraphs/working-with-multiple-subgraphs.mdx)
- [Learn how to split subgraphs across repositories to enable decentralized development](/project-configuration/subgraphs/working-with-multiple-repositories.mdx)
==============================
# How to Configure the Allowlist Plugin
URL: https://hasura.io/docs/3.0/docs/plugins/allowlist/how-to
# How to Configure the Allowlist Plugin
## Introduction
The [allowlist plugin](https://github.com/hasura/engine-plugin-allowlist) adds an allowlist layer on top of your
supergraph to restrict access to only specific queries or mutations.
:::info We're using Cloudflare Wrangler
In this example, we're using Cloudflare Wrangler to deploy our plugin as a Cloudflare Worker. However, you can use any
other tool or service that hosts HTTPS services you wish. You can get started with Wrangler
[here](https://developers.cloudflare.com/workers/wrangler/install-and-update/).
:::
## Step 1. Create a new Worker project
Create a new Cloudflare Worker project using the `create-cloudflare` command with the
[`allowlist` plugin template](https://github.com/hasura/engine-plugin-allowlist):
```bash
npm create cloudflare@latest allowlist-plugin -- --template https://github.com/hasura/engine-plugin-allowlist
```
## Step 2. Install the dependencies
Navigate to the new directory and install the dependencies.
```bash
cd allowlist-plugin
npm install
```
Also, start the local development server.
```bash
npm start
```
## Step 3. Add the plugin configuration
We'll let the engine know about the plugin and to execute it as a pre-parse plugin by creating a new metadata file. In
your `global` subgraph's metadata directory, create a new file named `allow-list.hml` and add the following
configuration.
```yaml
kind: LifecyclePluginHook
version: v1
definition:
name: cloudflare allowlist
url:
valueFromEnv: ALLOW_LIST_URL
pre: parse
config:
request:
headers:
additional:
hasura-m-auth:
valueFromEnv: M_AUTH_KEY
session: {}
rawRequest:
query: {}
variables: {}
```
:::info Using environment variables
We've used `valueFromEnv` so that we can dynamically and securely add values from our environment variables. You can add
these values to your root-level `.env` and then map them in the `globals` subgraph.yaml file. Alternatively, you can
include raw strings here using `value` instead of `valueFromEnv` and passing the keys.
:::
Next, update the `subgraph.yaml` file to include the metadata file and the environment variables.
```yaml
kind: Subgraph
version: v2
definition:
name: globals
...
includePaths:
...
- allowlist-plugin.hml
envMapping:
ALLOW_LIST_URL:
fromEnv: ALLOW_LIST_URL
M_AUTH_KEY:
fromEnv: M_AUTH_KEY
```
Finally, we need to add the environment variables to the `.env` file.
```bash
ALLOW_LIST_URL="http://local.hasura.dev:8787"
M_AUTH_KEY="your-strong-m-auth-key"
```
:::tip M-Auth Key
The `hasura-m-auth` header is a custom header that is used to authenticate the requests to the allowlist plugin. You can
use any strong key here to authenticate the plugin. DDN will automatically add this header to the requests to the
plugin. Also, make sure to update the `src/config.ts` file (in step 5) with the same key.
:::
## Step 4. Create a new build for local development
Create a new supergraph build.
```bash
ddn supergraph build local
```
Start the console for the local supergraph.
```bash
ddn console --local
```
You can now test the plugin by running queries or mutations that are not in the allowlist. The plugin will restrict
access to only the queries or mutations you've defined.
## Step 5. Update the plugin config
Update the `src/config.ts` file with the queries and mutations that you want to allow, using a strong m-auth key.
```typescript
export const Config = {
headers: {
"hasura-m-auth": "your-strong-m-auth-key",
},
allowlist: [
...,
"query MyQuery {\n getAuthorById(author_id: 10) {\n first_name\n id\n last_name\n }\n}",
],
};
```
:::info Hot reloading
The local wrangler development server will automatically reload the plugin when you make changes to the code.
:::
## Step 6. Configure the plugin variables
:::info Setup tracing
To enable tracing for the plugin, you need to update the `wrangler.toml` file with the required configurations. If you
don't want to enable tracing for the plugin, you can skip this step.
:::
In `allowlist-plugin` directory, update the `wrangler.toml` file with the required configurations.
```toml
...
[vars]
OTEL_EXPORTER_OTLP_ENDPOINT = "https://gateway.otlp.hasura.io:443/v1/traces"
OTEL_EXPORTER_PAT = ""
```
Replace `` with the Personal Access Token (PAT) for the Hasura Cloud account. You can generate this using the
`ddn auth print-access-token` command.
## Step 7. Deploy the plugin
For your plugin to be reachable by your hosted supergraph, we'll need to deploy using Cloudflare Wrangler. The `deploy`
command included in your plugin's `package.json` will do this automatically for you and return the hosted service's URL.
**Note**: Please also update the `wrangler.toml` with your cloud PAT for the tracing to work.
```bash
npm run deploy
```
This will deploy the plugin to Cloudflare Workers and return the URL of the hosted service. Next, update the .env.cloud
file with the URL.
```bash
ALLOW_LIST_URL="https://.workers.dev"
M_AUTH_KEY="your-strong-m-auth-key"
```
## Step 8. Create a new build
Create a new supergraph build.
```bash
ddn supergraph build create
```
The engine will execute the plugin before each request using the queries or mutations you defined.
==============================
# working-with-multiple-subgraphs.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/subgraphs/working-with-multiple-subgraphs
# How to Work with Multiple Subgraphs
## Introduction
In your daily development, you'll often encounter scenarios where your project requires multiple subgraphs to represent
distinct domains. Whether you're working in a single repository or planning to split subgraphs across multiple
repositories for team independence, understanding how to manage multiple subgraphs is crucial.
We'll walk you through the best practices for working with multiple subgraphs, setting their context, managing
namespacing, and establishing relationships across them to create a unified supergraph.
## Subgraph Namespacing
Each subgraph in your project operates in its own namespace, which means internal metadata objects (like models,
relationships, and permissions) cannot conflict with other subgraphs. However, the GraphQL API is where all subgraphs
come together, and conflicts can occur with root field and type names.
To prevent naming conflicts, you can:
1. Use subgraph prefixing (recommended for multi-team setups)
2. Manually ensure unique names across subgraphs
3. Use explicit type names in your schema
For more information about prefixing, see the
[subgraph prefixing guide](/project-configuration/subgraphs/subgraph-prefixing.mdx).
## Set subgraph context
If you're working in a project with multiple subgraphs, there will be the need to switch between subgraphs when
executing commands with the DDN CLI. While the CLI supports a `--subgraph` flag, it is much more convenient to set your
subgraph in context.
```sh title="Switch between context using the path to the subgraph's configuration file:"
ddn context set subgraph .//subgraph.yaml
```
```sh title="You can verify this by checking the current context:"
ddn context get subgraph
```
Which should return the path you entered. This will ensure any commands you run β such as adding a new data connector β
are run in the appropriate subgraph.
## Relationships across subgraphs
Relationships across subgraphs work in nearly the exact same way as relationships within a single subgraph; the only
caveat is to add the `subgraph` name of the target type.
```yaml title="In the example below, we're creating a simple relationship between models, with the target model belonging to the billing subgraph:" {8}
kind: Relationship
version: v1
definition:
name: orders
sourceType: Users
target:
model:
subgraph: billing
name: Orders
relationshipType: Array
mapping:
- source:
fieldPath:
- fieldName: id
target:
modelField:
- fieldName: userId
```
### Single-repo relationships
In a single-repo setup, relationships are straightforward to manage. All subgraphs are in the same repository and the
[Hasura VS Code extension](https://marketplace.visualstudio.com/items?itemName=HasuraHQ.hasura) can be used to assist
with authoring relationships, providing auto-complete and validation.
### Cross-repo relationships {#cross-repo-relationships}
:::info Advanced plan
You will need a project on the [DDN Advanced plan](https://hasura.io/pricing) to use multi-repo federation and
cross-repo relationships.
:::
In a [multi-repo setup](/project-configuration/subgraphs/working-with-multiple-repositories.mdx), while working with an
independent subgraph in its own repository, you may want to relate objects that reside in different repositories, some
of which you may not have access to.
In these cases the Hasura VS Code extension cannot validate the entirety of the `Relationship` object and you will
manually author cross-repo relationships and ensure that the field mappings are correct. However, upon creating a
supergraph build, all cross-subgraph metadata is validated to prevent mistakes from being deployed to the final API.
You can still easily use the Hasura DDN console to explore the supergraph and test relationships across subgraphs once
you have created a build.
If you perform a _local_ supergraph build using the CLI (ie. `ddn supergraph build local`), cross-repo relationships
will be ignored and will not be validated. If you run the build locally you will only see the subgraphs in that
repository, and any relationships to subgraphs from other repositories will be missing.
### Example
Let's say you have a supergraph with two subgraphs, each managed in different repositories: `users` and `products`.
The `users` subgraph in repo 'A' has a `User` type with a field called `user_favorite_product_id`.
The `products` subgraph in repo 'B' has a `Product` type with a field called `id`.
To create a relationship between these two types in different repositories, you would create a `Relationship` object in
the `users` subgraph metadata as normal.
The LSP is able to understand that the `Product` type is in a different subgraph to which it does not have access and
will not give a warning on the foreign type.
```yaml
kind: Relationship
version: v1
definition:
name: favorite_product
sourceType: User
target:
model:
name: Product
subgraph: products
relationshipType: Object
mapping:
- source:
fieldPath:
- fieldName: user_favorite_product_id
target:
modelField:
- fieldName: id
```
This `Relationship` object defines a relationship called `favorite_product` from the `User` type to the `Product` type.
The `mapping` field specifies how the `user_favorite_product_id` field in the `User` type maps to the `id` field in the
`Product` type.
After defining the cross-repo relationship, it's important to note that you won't be able to test this locally. To see
the relationship in action, you'll need to follow these steps:
1. Create a new supergraph build on DDN using the `ddn supergraph build create` command. (Subgraph builds do not get an
API, so supergraph builds are required to test.)
2. You can then use the Hasura DDN console to explore and test the relationship across subgraphs.
3. If you have admin permissions, you can apply the subgraph to the supergraph with the `ddn subgraph apply` command.
Remember, cross-repo relationships only come into effect when the subgraphs are combined in the DDN environment. Local
development and testing are limited to the scope of your current repository.
With this relationship defined, you can now query the `favorite_product` field on the `User` type to retrieve the
related `Product`.
```graphql
query {
users {
id
name
favorite_product {
id
name
}
}
}
```
## Next steps
- [Learn how to split subgraphs across repositories](/project-configuration/subgraphs/working-with-multiple-repositories.mdx)
:::info Multi-Repository Development
For larger teams, you can split subgraphs across multiple repositories to enable independent development lifecycles.
This advanced feature requires the DDN Advanced plan. Learn more in our guide about
[splitting subgraphs across repositories](/project-configuration/subgraphs/working-with-multiple-repositories.mdx).
:::
==============================
# working-with-multiple-repositories.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/subgraphs/working-with-multiple-repositories
# How to Split Subgraphs across Repositories
## Introduction
For larger teams and larger projects, it can make sense to split your subgraphs into multiple repositories contributing
to the supergraph in the "parent" project. This approach enables teams to have independent software development
lifecycles (SDLC) and CI/CD pipelines, providing better governance and control over their specific domains.
In this setup, one or more subgraphs are defined in each repository and depending on the role of the user on the cloud
project, can be built and applied to the supergraph independently. Each subgraph typically represents a distinct data
domain (e.g., users, orders, products) and can be managed by different teams.
:::tip Key Concepts
- Supergraph and subgraph builds are immutable and have unique IDs
- A subgraph must exist in the DDN project before inviting collaborators to it
- Each subgraph is namespaced and internal metadata objects cannot conflict with other subgraphs
- The GraphQL API is where subgraphs meet and conflicts can occur with root field and type names
- [Subgraph prefixing](/project-configuration/subgraphs/subgraph-prefixing.mdx) can automatically remedy naming
conflicts
:::
Deployment with a multi-repository setup is effectively the same as
[deploying from a single repository](/deployment/hasura-ddn/index.mdx), but with the considerations of managing your
context to reference the shared project, and also being aware of the differences in abilities of the users building and
deploying.
See the tables [below](#invite-collaborators) for the roles and permissions of users on a Hasura DDN project.
:::warning DDN Advanced Plan required
In order to utilize multi-repository collaboration, you must have an active
[DDN Advanced Plan](https://hasura.io/pricing).
:::
## How it works
### Parent Project:
The project `Owner` or `Admin` will need to:
1. Initialize a new local parent project
2. Initialize the corresponding cloud parent project
3. Initialize git for the parent project
4. Provision subgraphs on the cloud parent project
5. Create a base build of the cloud parent project
6. Invite collaborators to the cloud parent project
### Independent Subgraphs:
The invited user will need to:
1. Create a new supergraph with a new subgraph and initialize a new repository
2. Initialize their cloud project as the parent project and link the new subgraph
3. Set the subgraph context
4. Create a subgraph build
5. Integrate the subgraph build into the parent project
6. Apply the build to be the official supergraph API (`Owner`, `Admin`, `Subgraph Admin` roles)
## Create the Initial Parent Project
Begin by creating a "parent" project that will serve as the coordinated supergraph for your independent subgraph
repositories. This central project will manage provisioning subgraphs, inviting collaborators, and maintaining the
overall API.
### Step 1. Initialize a new local project
This will serve as the parent project.
```sh
ddn supergraph init && cd && git init
```
This will scaffold the local configuration for your DDN project and initialize a Git repository.
### Step 2. Initialize the cloud project
This is based on the local parent project.
```sh
ddn project init
```
In your configuration file (e.g., `.hasura/context.yaml`), you'll see a new `project` entry with the name of the project
returned by the CLI.
### Step 3. Create an initial commit
```sh
git add . && git commit -m "Initial commit"
```
Push this repository to your preferred hosting service to share it with collaborators.
### Step 4. Provision subgraphs on the cloud parent project
If you know the subgraphs to include, you can provision them using the DDN CLI. Replace `` with the
desired name:
```sh
ddn project subgraph create
```
You are also able to add a subgraph on the console in the `Share > Invite a user > Granular access` section.
:::info On-Demand Subgraphs
Subgraphs can be added as needed when collaborators are onboarded.
:::
### Step 5. Create a base build of the cloud parent project
```sh
ddn supergraph build create
```
This initial build serves as the foundation for future subgraph builds.
### Step 6. Invite collaborators to the cloud parent project {#invite-collaborators}
Navigate to your project's console and invite collaborators based on their roles:
| Role | Abilities |
| ------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Owner** | All Project abilities including deletion. At this time, project ownership is not transferable. |
| **Admin** | Same as a Project Owner, excluding deletion of the project. |
| **Read Only** | Only explore and visualize the supergraph. |
| **Subgraph Admin** \* | All subgraph development-related abilities: create subgraph build, apply subgraph build to endpoint. |
| **Subgraph Developer** \* | Same as a subgraph admin, excluding the ability to apply subgraph build to endpoint. |
\* Subgraph roles are only available on [DDN Advanced projects](https://hasura.io/pricing/ddn).
The following are the detailed permissions for the above roles:
| Permissions | Owner | Admin | Read Only | Subgraph Admin \* | Subgraph Developer \* |
| ------------------------------------------------------------------- | ----- | ----- | --------- | ---------------------- | -------------------------- |
| View Supergraph Explorer | β | β | β | β | β |
| Make GraphQL API requests | β | β | β | β | β |
| View project insights | β | β | β | β | β |
| Create supergraph builds - using all subgraphs' metadata | β | β | β | β | β |
| Create supergraph builds - using single subgraph's metadata \* | β | β | β | β | β |
| Apply supergraph builds to project endpoint | β | β | β | β | β |
| Create subgraph builds \* | β | β | β | β | β |
| Apply subgraph builds to project endpoint \* | β | β | β | β | β |
| Admin permissions on all subgraphs | β | β | β | β | β |
| Create / Delete subgraphs | β | β | β | β | β |
| Add / Remove collaborators | β | β | β | β | β |
| Manage project plans and billing | β | β | β | β | β |
| Delete project | β | β | β | β | β |
\* Only available on [DDN Advanced projects](https://hasura.io/pricing/ddn).
Each collaborator will receive an invitation to join the project and can proceed to add their subgraphs.
## Independent Subgraphs
Each independent subgraph contributing to the parent project resides in its own separate repository and has it's own
local supergraph. The repository could be an existing or newly initialized one.
### Step 1. Create a new supergraph with a new subgraph and initialize a repository
The invited user will need to create a new supergraph on their machine with a new subgraph and initialize a repository.
They will use the same name for the subgraph as was created in the parent project.
```sh
ddn supergraph init --create-subgraph && cd && git init
```
This scaffolds the necessary structure and initializes a Git repository.
### Step 2. Initialize the cloud project as the parent project and link the new subgraph
This will initialize the cloud project as the parent project.
```sh
ddn project init --with-project
```
Your configuration file will now link the local subgraph to the parent project.
:::info Local Development
You can add data sources and develop locally at this point. Check out relevant tutorials for adding sources.
:::
### Step 3. Set the subgraph context
```sh
ddn context set subgraph .//subgraph.yaml
```
This sets the subgraph as the default for future CLI commands.
### Step 4. Create a subgraph build
```sh
ddn subgraph build create
```
The CLI returns a build version for later integration into the parent supergraph.
### Step 5. Integrate the subgraph build into the parent project
```sh
ddn supergraph build get
```
Use the latest build version from the parent supergraph and the subgraph build version in the following command:
```sh
ddn supergraph build create --subgraph-version --base-supergraph-version
```
This integrates the subgraph changes into an existing build of the parent supergraph.
### Step 6. Apply the build
```sh
ddn supergraph build apply
```
This finalizes the integration, making the changes available to all collaborators. This command is only available to
`Owner` and `Admin` roles. A `Subgraph Admin` can run:
```sh title="Apply a single subgraph. Unavailable for Subgraph Developer role"
ddn subgraph build apply
```
:::tip Simplify multi-team workflows
For more efficient collaboration across repositories, you can create and apply the supergraph build in a single command using
`ddn supergraph build create --subgraph-version --base-supergraph-version --apply`
:::
## Build Summary
### Supergraph and all subgraphs
Even though all subgraphs are not in the same repo, the `ddn supergraph build create` command will build the supergraph
including all subgraphs which are on DDN cloud.
Once your [context is set to the shared project](/project-configuration/project-management/manage-environments.mdx), an
`Owner` or an `Admin` role can build and apply the supergraph including all subgraphs.
As always, `ddn supergraph build apply ` will make it the active supergraph API.
`Subgraph Admin` and `Subgraph Developer` cannot build or apply supergraphs.
### Supergraph and specific subgraphs
An `Owner` or an `Admin` role can build and apply new supergraph and specify subgraphs to be built and applied.
You can also list the available builds of subgraphs to use with:
```bash
ddn subgraph build get
```
```bash title="Build a supergraph based on a specific build and with mutiple specific subgraphs"
ddn supergraph build create --subgraph-version --subgraph-version --base-supergraph-version
```
See more about incremental builds [here](/deployment/hasura-ddn/incremental-builds.mdx).
### A single subgraph
An `Owner`, `Admin`, `Subgraph Admin` and `Subgraph Developer` role can build a single subgraph. All except
`Subgraph Developer` can apply a single subgraph.
```bash title="Build a single subgraph"
ddn subgraph build create --subgraph-version --base-supergraph-version
```
```bash title="Apply a single subgraph. Unavailable for Subgraph Developer role"
ddn subgraph build apply --subgraph-version
```
## Merging Existing Projects
If you have two independently developed projects on Hasura DDN that you want to merge into a single project with
independent subgraph development, follow these steps:
1. Choose which project will be the main project and which will be the subgraph project
2. In the main project, create a new subgraph placeholder:
```sh
ddn project subgraph create
```
3. Invite the subgraph project collaborators with appropriate permissions
4. Once collaborators accept the invitation, they should set their project context:
```sh
ddn context set project
```
5. Set up subgraph prefixes if needed to prevent naming conflicts
6. Create a subgraph build on the main project:
```sh
ddn subgraph build create
```
7. The main project owner/admin can then apply the subgraph build:
```sh
ddn subgraph build apply
```
## Next steps
- [Follow an end-to-end tutorial](/project-configuration/tutorials/work-with-multiple-repositories.mdx)
==============================
# subgraph-prefixing.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/subgraphs/subgraph-prefixing
# Subgraph GraphQL Root Field and Type Name Prefixing
Subgraphs are namespaced and metadata object names are independent from one another and cannot conflict. However, the
GraphQL API is where the subgraphs meet, potentially leading to naming collisions.
To avoid collisions between GraphQL root fields and type names across when federating subgraphs, you can optionally
customize the prefixes for each.
For example, if two subgraphs both have a `Users` type, you can apply different prefixes to distinguish one from the
other. This ensures that each subgraph remains unique and prevents any naming conflicts.
You can make these modifications in the `subgraph.yaml` file for a subgraph.
```yaml title="Add the highlighted lines:"
kind: Subgraph
version: v2
definition:
name: my_subgraph
generator:
rootPath: .
#highlight-start
graphqlRootFieldPrefix: my_subgraph_
graphqlTypeNamePrefix: My_subgraph_
#highlight-end
```
By default, the `subgraph.yaml` file is generated without any prefixes. You can read more about these fields
[here](reference/metadata-reference/build-configs.mdx#subgraph-subgraphgeneratorconfig).
## Renaming GraphQL root fields and GraphQL type name prefixes
This codemod will rename prefixes in already generated metadata. It can also be used to add or remove prefixes
altogether.
The `--from-graphql-root-field` prefix will be stripped if provided, and the new prefix, `--graphql-root-field`, will be
added. If the new prefix is already present, it will not be reapplied.
Examples:
```bash
# Add root field and type name prefixes to the subgraph set in the context
ddn codemod rename-graphql-prefixes --graphql-root-field 'app_' --graphql-type-name 'App_'
# Change the root field prefix for the specified subgraph
ddn codemod rename-graphql-prefixes --subgraph app/subgraph.yaml --from-graphql-root-field 'app_' --graphql-root-field 'new_'
```
==============================
# Caching Plugin
URL: https://hasura.io/docs/3.0/docs/plugins/caching/
# Caching Plugin
## Introduction
The [Caching Plugin](https://github.com/hasura/engine-plugin-caching) allows you to cache responses for specific GraphQL
queries, enhancing performance by reducing repeated computation for frequently executed queries.
The plugin integrates with Hasura DDN as both a **pre-parse plugin** and **pre-response plugin**. It uses Redis as the
caching backend, ensuring fast and reliable storage for cached query results.
Key benefits of the caching plugin include:
- **Improved performance:** Reduces load on your supergraph by caching frequently executed queries.
- **Customizable caching:** Define specific queries to cache and configure cache lifetimes.
- **Integration-ready:** Compatible with Hasura's lifecycle plugin hooks and OpenTelemetry for tracing.
## Next steps
To configure and deploy the Caching Plugin, refer to the [guide](/plugins/caching/how-to.mdx), which provides detailed
steps for setting up the plugin in a local or production environment.
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/project-management/
# Project Management
## Introduction
Broadly, projects can be managed using two tools: context and collaborators.
**Context** allows you to swap out local and cloud configuration files and values. This makes it easier to execute
commands via the CLI, switch between environments when testing your API, and executing automated CI/CD scripts.
**Collaborators** can be added to any [Hasura DDN Base or Hasura DDN Advanced project](https://hasura.io/pricing).
Depending on the project's plan, you can add collaborators with read-only access all the way to granular access,
enabling them to only contribute to certain [subgraphs](/project-configuration/subgraphs/index.mdx).
## Next steps
- [Learn how to manage context](/project-configuration/project-management/manage-contexts.mdx)
- [Learn how to invite collaborators](/project-configuration/project-management/manage-collaborators.mdx)
==============================
# manage-contexts.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/project-management/manage-contexts
# How to Manage Project Contexts
## Introduction
Contexts simplify your development workflow by making CLI commands more concise and ensuring easy transitions between
environments. They act as predefined configurations that the CLI uses to interact with specific sets of values, making
processes like deploying, testing, or managing your project in CI/CD pipelines easier.
You can manage your project's contexts using the `.hasura/context.yaml` file, which is generated in the root of your
project when the CLI initializes it.
## Contexts
By default, the CLI sets up your project with a `default` context. To add new contexts, use the
[`create-context` CLI command](/reference/cli/commands/ddn_context_create-context.mdx).
For a full list of context-related commands, refer to
[this category of commands](/reference/cli/commands/ddn_context.mdx). Although you can modify these values directly in
the file, we recommend using the CLI for consistency and ease. Examples are provided below.
### Project
The `project` key-value pair can be mapped to a Hasura DDN cloud project. This is used to dictate which cloud-hosted
project should be referenced by the CLI when cloud-based and project-related commands are executed.
```yaml title="When the default context is set, the great-ddn-1234 project will be used by the CLI:"
contexts:
default:
#highlight-start
project: great-ddn-1234
#highlight-end
supergraph: ../supergraph.yaml
subgraph: ../app/subgraph.yaml
localEnvFile: ../.env
cloudEnvFile: ../.env.cloud
```
When you initialize a new Hasura Cloud project, the `project` key-value will automatically be set by the CLI. You can
also override this manually using [the `ddn context set` command](/reference/cli/commands/ddn_context_set.mdx).
This key-value pair simplifies switching contexts across development stages. For instance, the `default` context could
be used for production builds, while a separate `staging` context could map to a dedicated project for API collaboration
and testing before moving to production.
### Supergraph
The `supergraph` key-value pair maps to a
[Supergraph metadata object](reference/metadata-reference/build-configs.mdx#supergraph-supergraph), which contains a
list of the subgraphs to include in any supergraph build.
```yaml title="When the default context is set, the root supergraph.yaml will be used by the CLI:"
contexts:
default:
project: great-ddn-1234
#highlight-start
supergraph: ../supergraph.yaml
#highlight-end
subgraph: ../app/subgraph.yaml
localEnvFile: ../.env
cloudEnvFile: ../.env.cloud
```
By default, this is set to the root `supergraph.yaml` when you initialize a local project. However, you can customize
this to use any valid Supergraph metadata object.
As with subgraphs below, setting this value makes any supergraph-related CLI commands more concise by eliminating the
need for extra flags.
### Subgraph
The `subgraph` key-value pair maps to a
[Subgraph metadata object](reference/metadata-reference/build-configs.mdx#subgraph-subgraph) which contains information
about which connectors are included in the subgraph and mappings for environment variables.
```yaml title="With the configuration below, if the current context is default, the app/subgraph.yaml configuration file will be used:"
contexts:
default:
project: great-ddn-1234
supergraph: ../supergraph.yaml
#highlight-start
subgraph: ../app/subgraph.yaml
#highlight-end
localEnvFile: ../.env
cloudEnvFile: ../.env.cloud
```
For projects with multiple subgraphs, it's handy to use this command to switch between subgraphs:
```sh
ddn context set subgraph
```
This ensures common actions β like adding data connectors and generating metadata after introspection β are executed by
the CLI in the correct subgraph, keeping your supergraph organized and resources with their appropriate owners.
### localEnvFile
Your project is initialized with a root-level `.env` file and this is automatically set in the `default` context.
Whenever a new environment variable is added β such as when initializing a new connector β the CLI will add the
appropriate key-value pairs to this file. It is used in local development such as when running your local services
(i.e., the engine and connectors).
```yaml title="When the default context is set, the root .env file will be used by the CLI for any local development commands:"
contexts:
default:
project: great-ddn-1234
supergraph: ../supergraph.yaml
subgraph: ../app/subgraph.yaml
#highlight-start
localEnvFile: ../.env
#highlight-end
cloudEnvFile: ../.env.cloud
```
Ideally, the values here will β such as connection strings to data sources β will be mapped to development instances,
whether that be locally or hosted elsewhere. This makes it easy to work with what will eventually become a production
build, but using local resources for quicker iteration and shorter feedback loops.
Any time you need to add additional environment variables to a connector, you can
[use the CLI](/reference/cli/commands/ddn_connector_env_add.mdx).
### cloudEnvFile
Your project includes a root-level `.env.cloud` file, which is automatically set in the current context whenever a new
cloud project is initialized. This file is specifically used for cloud-related operations, such as deploying or managing
your services in a cloud environment.
```yaml title="When the default context is set, the root .env.cloud file will be used by the CLI for any cloud-related commands:"
contexts:
default:
project: great-ddn-1234
supergraph: ../supergraph.yaml
subgraph: ../app/subgraph.yaml
localEnvFile: ../.env
#highlight-start
cloudEnvFile: ../.env.cloud
#highlight-end
```
:::tip A localEnvFile is required before setting the cloudEnvFile
A `localEnvFile` is required before setting the `cloudEnvFile`. Ensure your `localEnvFile` is set for the current
context **before** initializing a new cloud project to avoid any deployment issues.
:::
## Next steps
Now that you have a better idea of configuring your project for various contexts,
[learn how to collaborate with others](/project-configuration/project-management/manage-collaborators.mdx) by adding
collaborators and defining their roles.
==============================
# How to Configure the Caching Plugin
URL: https://hasura.io/docs/3.0/docs/plugins/caching/how-to
# How to Configure the Caching Plugin
## Introduction
The [caching plugin](https://github.com/hasura/engine-plugin-caching) adds the ability to specify a list of queries
whose responses should be cached.
## Setting up for local development
We can set up everything we need for local development through Docker. Add the following entries to your `compose.yaml`
file:
```yaml
redis:
image: redis:latest
ports:
- 6379:6379
caching:
build:
context: https://github.com/hasura/engine-plugin-caching.git
ports:
- 8787:8787
extra_hosts:
- local.hasura.dev=host-gateway
volumes:
- ./globals/plugins/caching-config.js:/app/src/config.js
```
Here, we've added a Redis instance to our Docker Compose project, as well as an instance of the caching plugin. The
caching plugin takes a config file (which we've said here is saved in `./globals/plugins/caching-config.js`, though the
path is up to you). Here's an example config file:
```javascript
export const Config = {
// Client header configuration
headers: {
// A secret that must be provided in incoming requests from the engine.
// Change this to whatever you'd like, though remember to update the
// references further on.
"hasura-m-auth": "zZkhKqFjqXR4g5MZCsJUZCnhCcoPyZ",
},
// A URL for redis. If you copied the docker-compose configuration for
// `redis` above, this doesn't need changing.
redis_url: "redis://redis:6379",
// OpenTelemetry configuration. The name of this environment variable will
// depend on your subgraph name - check your `.env` file to find the correct
// name. You can also specify any further headers that your telemetry
// collector may require.
otel_endpoint: process.env.GLOBALS_OTEL_EXPORTER_OTLP_ENDPOINT,
otel_headers: {},
// A list of queries that we want to cache. Note that these queries will be
// cached based on their parsed structures, so white space doesn't matter.
queries_to_cache: [
{
query: ` query MyQuery {
customers {
firstName
lastName
}
}
`,
// How long a cached response should live (in seconds).
time_to_live: 600,
},
],
};
```
The `queries_to_cache` list can be extended to contain all the queries you'd like to be cached. Note that the query
response is cached for each set of session variables and each role, as these may yield different outputs.
:::info Using environment variables
This example uses hard-coded values for the URLs and the request headers, though these can be dynamically and securely
injected using environment variables. Changing the `value` key to `valueFromEnv` allows us to specify the name of an
environment variable from which to get this information. Note that the variables are defined via the `envMapping` config
in `subgraph.yaml`, which states which environment variables should be inherited from the root `.env`.
:::
## Adding the plugin to your project
Once we've configured the plugin, running `ddn run docker-start` should work happily. Now, we just need to configure
Hasura to use the plugin. Add the following to one of your `subgraph.yaml` files:
```yaml
---
kind: LifecyclePluginHook
version: v1
definition:
pre: parse
name: cache_get_test
url:
valueFromEnv: HASURA_CACHING_PRE_PARSE_URL
config:
request:
headers:
additional:
hasura-m-auth:
value: zZkhKqFjqXR4g5MZCsJUZCnhCcoPyZ
rawRequest:
query: {}
variables: {}
---
kind: LifecyclePluginHook
version: v1
definition:
pre: response
name: cache_set_test
url:
valueFromEnv: HASURA_CACHING_PRE_RESPONSE_URL
config:
request:
headers:
additional:
hasura-m-auth:
value: zZkhKqFjqXR4g5MZCsJUZCnhCcoPyZ
rawRequest:
query: {}
variables: {}
```
:::info Using environment variables
We've used `valueFromEnv` so that we can dynamically and securely add values from our environment variables. You can add
these values to your root-level `.env` and then map them in the `globals` subgraph.yaml file. Alternatively, you can
include raw strings here using `value` instead of `valueFromEnv` and passing the keys.
For local development, `HASURA_CACHING_PRE_PARSE_URL` should be `http://local.hasura.dev:8787/pre-parse`, and
`HASURA_CACHING_PRE_RESPONSE_URL` should be `http://local.hasura.dev:8787/pre-response`.
:::
## Running the project
At this point, we can create a build of our project and start local development:
```bash
ddn supergraph build local
ddn run docker-start
```
Queries marked in the caching config as cacheable should now be cached. The caching plugin will output logs to indicate
which requests have and have not been cached, so `docker compose logs -f caching` will allow you to watch these logs as
they arise.
## Deploying the plugin
The connector can be deployed as a regular HTTP service, anywhere an Express server can be deployed. When deployed, make
sure to set the `HASURA_CACHING_PRE_PARSE_URL` and `HASURA_CACHING_PRE_RESPONSE_URL` to appropriate values in
`.cloud.env`. Note that the plugin must be visible from your Hasura deployment: if hosting in the Hasura Cloud, the
plugin must be publicly visible. In this instance, make sure to set the `hasura-m-auth` header to something other than
the example given in this guide to keep the plugin secure from malicious third-party users.
==============================
# manage-collaborators.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/project-management/manage-collaborators
# How to Manage Project Collaborators
## Introduction
Hasura DDN allows multiple users and teams to work together as collaborators on projects by assigning each user specific
roles and permissions.
You can [invite users](#invite-collaborators) to a project, or allow users to [request access](#request-access).
:::info Only available on DDN Base and higher
In order to add collaborators, your project must either be a
[DDN Base or DDN Advanced project](https://hasura.io/pricing).
:::
## Available roles {#roles}
| Role | Abilities |
| ------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Owner** | All Project abilities including deletion. At this time, project ownership is not transferable. |
| **Admin** | Same as a Project Owner, excluding deletion of the project. |
| **Read Only** | Only explore and visualize the supergraph. |
| **Subgraph Admin** \* | All subgraph development-related abilities: create subgraph build, apply subgraph build to endpoint. |
| **Subgraph Developer** \* | Same as a subgraph admin, excluding the ability to apply subgraph build to endpoint. |
\* Subgraph roles are only available on [DDN Advanced projects](https://hasura.io/pricing/ddn).
The following are the detailed permissions for the above roles:
| Permissions | Owner | Admin | Read Only | Subgraph Admin \* | Subgraph Developer \* |
| ------------------------------------------------------------------- | ----- | ----- | --------- | ---------------------- | -------------------------- |
| View Supergraph Explorer | β | β | β | β | β |
| Make GraphQL API requests | β | β | β | β | β |
| View project insights | β | β | β | β | β |
| Create supergraph builds - using all subgraphs' metadata | β | β | β | β | β |
| Create supergraph builds - using single subgraph's metadata \* | β | β | β | β | β |
| Apply supergraph builds to project endpoint | β | β | β | β | β |
| Create subgraph builds \* | β | β | β | β | β |
| Apply subgraph builds to project endpoint \* | β | β | β | β | β |
| Admin permissions on all subgraphs | β | β | β | β | β |
| Create / Delete subgraphs | β | β | β | β | β |
| Add / Remove collaborators | β | β | β | β | β |
| Manage project plans and billing | β | β | β | β | β |
| Delete project | β | β | β | β | β |
\* Only available on [DDN Advanced projects](https://hasura.io/pricing/ddn).
## How to invite collaborators {#invite-collaborators}
### Step 1. Navigate to the Collaborators section
Open your project's console at [https://console.hasura.io](https://console.hasura.io) and select it from the list of
available projects. Once the project is open, click `Settings` in the bottom-left corner and then select `Collaborators`
from the `Project Settings` menu:
### Step 2. Enter information
Click the `+ Invite Collaborator` button in the top-right corner of the `Collaborators` section and enter the
collaborator's email address, select the access level you'd like to assign them, and click `Invite`.
:::info Granular Access (Subgraph Collaborators) Only available on DDN Advanced
In order to add subgraph collaborators, your project must be a [DDN Advanced project](https://hasura.io/pricing/ddn).
:::
The invitee will receive an email with a link allowing them to accept the invite and join the project.
## How to accept a collaboration invite {#accept-invite}
### Step 1. Click the link in your email
From your email, click the `View invitation` button. This will send you to
[https://console.hasura.io](https://console.hasura.io) where you can accept it and then explore and contribute to the
project according your [role](#roles).
### Step 2. Explore the project
From your new project, you can explore the console by:
- [Running queries](/graphql-api/queries/) from the GraphiQL explorer.
- Visualizing the supergraph with the Explorer tab.
- Seeing other collaborators present in the project.
### Step 3. Learn how to develop locally
The owner of the project most likely has a Git repository with the project's contents available on a service such as
GitHub. To run the supergraph locally, and make contributions to the deployed supergraph,
[pick up here](/quickstart.mdx) in our getting started docs.
## Allow users to request access {#request-access}
You can adjust your project's settings to allow users to request access when navigating to your project's URL. To do
this, click the `Share` button in the top navigation of your project and select `Request Access`.
Each time a user requests access, you'll be able to approve or deny the request from this modal.
## More information
See more about Hasura DDN plans and pricing [here](/reference/pricing.mdx).
==============================
# manage-environments.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/project-management/manage-environments
# Alternative Configuration Files per Environment
## Introduction
Following on from setting up and
[managing multiple contexts](/project-configuration/project-management/manage-contexts.mdx), you can also specify
different configuration files per environment. This is useful if you want to use different setups for each.
## Example
For example, if in `development` you want to use `noAuth` mode but for `staging` and `production` you want to use JWT
mode, you can create a supergraph config file for each environment and then specify the correct file in the context.
In the following example we have a `supergraph-development.yaml` file which specifies a chain to the
`subgraph-development.yaml` to the `metadata_development` directory to include for the metadata which sets the `noAuth`
mode for development context.
```yaml title="supergraph-development.yaml"
kind: Supergraph
version: v2
definition:
subgraphs:
# highlight-start
- globals/subgraph-development.yaml
# highlight-end
- my_subgraph/subgraph.yaml
```
```yaml title="globals/subgraph-development.yaml"
kind: Subgraph
version: v2
definition:
name: globals
generator:
rootPath: .
# highlight-start
includePaths:
- metadata_development
# highlight-end
```
```yaml title="globals/metadata_development/auth-config.hml"
kind: AuthConfig
version: v4
definition:
mode:
noAuth:
role: admin
sessionVariables: {}
```
Then similarly, we would have the supergraph file for the other environments to use which specifies JWT mode in the
`auth-config.yaml` file. You can read more about [auth here](/auth/overview.mdx).
==============================
# service-accounts.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/project-management/service-accounts
# Service Accounts
## Introduction
A service account represents an application, service, or automated process rather than a human user. These accounts are
employed for running background jobs, connecting systems, or performing specific operations without requiring manual
intervention.
Project owners or administrators can create and delete service accounts as needed. These accounts can act as
collaborators on other projects, just like regular users, and can hold roles such as supergraph admin or subgraph admin.
Additionally, project owners or administrators can regenerate service account tokens, making them a secure and ideal
alternative to Personal Access Tokens (PAT) for use in CI/CD workflows.
:::info Only available on DDN Base and higher
In order to create service accounts, your project must either be a
[DDN Base or DDN Advanced project](https://hasura.io/pricing/ddn).
:::
## How to create service account
### Step 1: Navigate to the Service Accounts section
As an owner or administrator on the project, open your project's console at
[https://console.hasura.io](https://console.hasura.io) and click `Settings` in the bottom-left corner. Then select
`Service Accounts` from the `Project Settings` menu and click `New Service Account`:
### Step 2: Enter details
Enter service account name and click `Continue`:
### Step 3: Save service account token
After creating the service account, copy the service account token and store it securely. This token allows the CLI to
authenticate your service account You will not be able to view the token again after this step.
### Step 4: Setup service account permissions
After saving the service account token, select the project and access level you want to grant to the service account,
and click `Give Access`:
:::info Granular Access Only available on DDN Advanced
In order to add service account as a subgraph administrator, your project must be a
[DDN Advanced project](https://hasura.io/pricing/ddn).
:::
You can skip this step and assign permissions later by
[inviting the service account](/project-configuration/project-management/manage-collaborators.mdx) as a project
collaborator.
## How to use service account token
### Step 1. Login via CLI
```bash
ddn auth login --access-token
```
### Step 2. Create and apply supergraph build
```bash
# Create supergraph build
ddn supergraph build create [flags]
# Apply supergraph build
ddn supergraph build apply [flags]
```
:::tip Streamline CI/CD workflows
For more efficient automation with service accounts, you can create and apply the supergraph build in a single command using
`ddn supergraph build create [flags] --apply`
:::
## How to regenerate service account token
Navigate to the `Service Accounts` section and click the `Regenerate` button next to the service account for which you
want to regenerate the token:
## How to delete service account
### Step 1: Navigate to the Service Accounts section
Click the `Delete` button next to the service account you want to delete:
### Step 2: Verify and confirm deletion
## More information
See more about Hasura DDN plans and pricing [here](/reference/pricing.mdx).
==============================
# console-collaborator-comments.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/project-management/console-collaborator-comments
# Commenting on Metadata
## Introduction
Hasura DDN provides a commenting feature that allows your API producers and consumers to start conversations directly on
the API metadata. This feature enhances collaboration by closing the feedback loop and helps teams communicate more
effectively about their API design and implementation.
:::tip Getting Access
This feature is available for all users on [Base and Advanced plans](https://hasura.io/pricing).
:::
## How to comment
You can add comments on various objects from your metadata.
1. Navigate to any model page in your project via the `Explorer` tab.
2. Hover over the field or section you want to comment on.
3. Click on the comment icon that appears.
## Commenting areas
### Explorer Tab
The **Explorer Tab** is the primary interface where users interact with the API metadata, making it a crucial place for
collaboration. Comments here enable API producers and consumers to discuss design decisions, clarify data models, and
suggest improvements directly on specific metadata elements.
- **Supergraph Page** β Comment on the overall schema design, structure, and implementation details of the supergraph.
- **Subgraph Page** β Provide feedback on individual subgraphs, ensuring alignment with the larger supergraph design.
- **Models β General**
- **Description** β Clarify the modelβs purpose and usage for better documentation.
- **Signature** β Discuss function signatures, return types, and argument structures.
- **GraphQL Root Fields** β Suggest improvements or changes to the root field definitions.
- **Models β Fields & Relationships**
- **Output Fields** β Ask questions or provide insights on field usage and expected data.
- **Arguments** β Discuss argument types, required vs. optional parameters, and potential defaults.
- **Relationships** β Ensure relationships between models are well-defined and documented.
- **Models β Permissions**
- **Role** β Comment on role-based access control settings.
- **Read / Create / Update / Delete** β Discuss permission settings for different CRUD operations.
This feature is especially useful for teams working on large-scale API projects, as it ensures everyone stays aligned on
API structure and permissions.
---
### GraphiQL Tab
The **GraphiQL Tab** is where users can interactively query the API and test GraphQL operations. Commenting here allows
for real-time feedback on API responses, query structures, and potential performance optimizations.
- Discuss unexpected query results and suggest potential schema modifications.
- Collaborate on best practices for query structuring and field selection.
- Provide feedback on performance concerns, such as response times and pagination strategies.
- Leave notes on commonly used queries to improve team-wide API consistency.
By adding comments directly in GraphiQL, teams can streamline debugging and optimize API queries collaboratively.
---
### Insights Tab
The **Insights Tab** provides performance metrics, traces, and reports about API usage. Commenting here helps teams
analyze and discuss API behavior, identify bottlenecks, and track improvements over time.
- **Performance** β Leave feedback on latency, throughput, and error rates.
- **Platform Report** β Discuss API usage patterns and suggest improvements.
- **Traces** β Analyze request traces and collaborate on optimizing execution paths.
This feature is valuable for DevOps, backend engineers, and API consumers looking to enhance API efficiency.
---
### Builds Tab
The **Builds Tab** allows teams to track and validate schema changes across supergraph, subgraph, and connector builds.
Commenting in this area helps teams coordinate schema evolution and avoid breaking changes.
- **Supergraph Builds** β Discuss schema changes at the supergraph level and their impact on consumers.
- **Subgraph Builds** β Leave comments about specific subgraph updates and dependencies.
- **Connector Builds** β Provide feedback on connector integrations and compatibility.
- **Schema Diff** β Highlight breaking changes or inconsistencies between schema versions.
By facilitating discussions on builds, teams can ensure smooth API evolution and prevent unexpected failures.
## Notifications
In-app notifications show new comments made on your project.
The notification hub can be found in the top right corner of the console. On clicking the comments button, you will see
all the comments where you are tagged in one place. You can click on a particular comment (deep linking) and go to the
original thread on the console. You can also delete notifications from that menu.
{/* */}

:::info Invite collaborators
You can learn how to invite collaborators [here](/project-configuration/project-management/manage-collaborators.mdx).
:::
## Limitations
The feature is in early access and has known limitations, which are in our backlog. Let us know if you would like to
prioritize any specific functionality.
1. Tagging users on comments
2. Resolving comments
3. Email notifications
4. Ability to auto notify subgraph admin and developers.
5. History Tab for comments.
==============================
# RESTified Endpoints Plugin
URL: https://hasura.io/docs/3.0/docs/plugins/restified-endpoints/
# RESTified Endpoints Plugin
The [RESTified Endpoints Plugin](https://github.com/hasura/engine-plugin-restified-endpoint) allows you to add RESTified
GraphQL endpoints to DDN. This can be used to add custom REST endpoints to DDN that will execute a specified GraphQL
query on the DDN GraphQL API and return the response.
The plugin integrates with Hasura DDN as a **pre-route plugin** and can be deployed as an HTTP service using tools like
Cloudflare Workers or similar services.
Key benefits of using the RESTified Endpoints Plugin include:
- **Simplified API Design**: The plugin enables you to design and expose your GraphQL API as RESTful endpoints, making
it easier for clients to interact with your API using familiar HTTP methods and conventions.
- **Flexibility**: You can define custom REST endpoints that correspond to specific GraphQL queries, allowing you to
tailor your API to the needs of your clients.
- **Easy Integration**: The plugin seamlessly integrates with Hasura DDN, ensuring that your RESTified endpoints are
well-integrated with your existing GraphQL API, providing a single, unified interface for your clients.
## Next steps
To get started with configuring and deploying the RESTified Endpoints Plugin, refer to the
[guide](/plugins/restified-endpoints/how-to.mdx), which walks you through the process of setting up, configuring, and
deploying the plugin.
==============================
# how-to.mdx
URL: https://hasura.io/docs/3.0/docs/plugins/restified-endpoints/how-to
# How to Configure the RESTified Endpoints Plugin
## Introduction
The [RESTified GraphQL endpoints plugin](https://github.com/hasura/engine-plugin-restified-endpoint) allows you to add
RESTified GraphQL endpoints to DDN. This can be used to add custom REST endpoints to DDN that would execute specified
GraphQL query on the DDN GraphQL API and return the response.
This guide provides a step-by-step guide on how to configure and deploy the RESTified Endpoints Plugin for Hasura DDN
using either of the two methods:
1. [Using a Docker image](#using-a-docker-image)
2. [Using Cloudflare Wrangler](#using-cloudflare-wrangler)
3. [Using AWS Lambda](#using-aws-lambda)
## Using a Docker Image
In this example, we're going to use a Docker image to configure the RESTified Endpoints Plugin for Hasura DDN. You can
use similar configurations to deploy the plugin using Kubernetes or any other container orchestration tool.
### Step 1. Set up plugin for local development
Add the following service to your root-level `compose.yaml` file (this is the one located at
`/compose.yaml`). Add this new service at the same level as your existing `services` entries:
```yaml
restified-endpoints:
image: ghcr.io/hasura/engine-plugin-restified-endpoint:latest
ports:
- 8787:8787
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://local.hasura.dev:4317}
GRAPHQL_SERVER_URL: http://engine:3280
HASURA_DDN_PLUGIN_CONFIG_PATH: /app/plugin_config
volumes:
- ./restified-endpoints-config:/app/plugin_config
labels:
io.hasura.ddn.service-name: restified-endpoints
extra_hosts:
- local.hasura.dev:host-gateway
```
Here, we're setting up the `restified-endpoints` service from the `engine-plugin-restified-endpoint` image. We're also forwarding the
`plugin_config` directory to the container. This directory will contain the configuration for the plugin. Here is the
structure of the `plugin_config` directory:
```
plugin_config/
βββ configuration.json
```
The `configuration.json` file is used to configure the plugin. Here's an example configuration:
```json
{
"graphqlServer": {
"headers": {
"additional": {
"Content-Type": "application/json"
},
"forward": ["X-Hasura-Role", "Authorization", "x-hasura-ddn-token"]
}
},
"headers": {
"hasura-m-auth": ""
},
"restifiedEndpoints": [
{
"path": "/v1/api/rest/artistbyname/:name",
"methods": ["GET", "POST"],
"query": "query artistByName($name: String!) { artist(where: {name: {_eq: $name}}) { name }}"
},
{
"path": "/v1/api/rest/artists",
"methods": ["GET", "POST"],
"query": "query artists($limit: Int = 10, $offset: Int = 0) { artist(limit: $limit, offset: $offset) { name } }"
}
]
}
```
The RESTified endpoints configuration includes:
- `graphqlServer`: Configuration for the GraphQL server:
- `headers`: Configuration for the headers to be sent to the GraphQL server:
- `additional`: Additional headers to be sent to the GraphQL server.
- `forward`: Headers to be forwarded from the request to the GraphQL server.
- `headers.hasura-m-auth`: The `hasura-m-auth` header is a custom header that is used to authenticate the requests to
the plugin. You can use any strong key here to authenticate the plugin. DDN will automatically add this header to the
requests to the plugin. You can also use the `HASURA_M_AUTH` environment variable for this header.
- `restifiedEndpoints`: Configuration for the RESTified endpoints. Each endpoint includes:
- `path`: The path of the endpoint (this can include path parameters like `:name`, which is used in the query as
variables)
- `methods`: The HTTP methods supported by the endpoint.
- `query`: The GraphQL query to be executed when the endpoint is called.
### Step 2. Add the plugin configuration
We'll let the engine know about the plugin and to execute it as a pre-route plugin by creating a new metadata file. In
your `global` subgraph's metadata directory, create a new file named `restified-endpoints.hml` and add the following
configuration.
```yaml
kind: LifecyclePluginHook
version: v1
definition:
pre: route
name: restified_endpoints
url:
valueFromEnv: RESTIFIED_ENDPOINTS_URL
config:
matchPath: "/v1/api/rest/*"
matchMethods: ["GET", "POST"]
request:
method: POST
headers:
forward:
- Authorization
- x-hasura-role
- x-hasura-ddn-token
additional:
hasura-m-auth:
valueFromEnv: M_AUTH_KEY
rawRequest:
path: {}
query: {}
method: {}
body: {}
response:
headers:
additional:
content-type:
value: application/json
```
:::tip URL Match
The `matchPath` field is used to match the regex to the request path, while `matchMethods` specifies the HTTP methods to
match. In this example, we're matching all `GET` and `POST` requests to `/v1/api/rest/*`. You can modify these to match
any path and methods (GET/POST/PUT/PATCH/DELETE) you wish.
Also, note that we can not use the pre-defined DDN endpoints (like `/graphql`, `/v1/sql`, `/v1/jsonapi`, `/v1/explain`,
`/healthz` and `/metrics`) in the `matchPath` field.
Moreover, on DDN cloud, for security reasons, only the `/v1/api/rest/*` path is allowed for the pre-route plugin.
:::
:::info Using environment variables
We've used `valueFromEnv` so that we can dynamically and securely add values from our environment variables. You can add
these values to your root-level `.env` and then map them in the `globals/subgraph.yaml` file. Alternatively, you can
include raw strings here using `value` instead of `valueFromEnv` and passing the keys.
:::
Next, update the `subgraph.yaml` file to include the environment variables.
```yaml
kind: Subgraph
version: v2
definition:
name: globals
...
includePaths:
...
envMapping:
RESTIFIED_ENDPOINTS_URL:
fromEnv: RESTIFIED_ENDPOINTS_URL
M_AUTH_KEY:
fromEnv: M_AUTH_KEY
```
Finally, we need to add the environment variables to the `.env` file.
```bash
RESTIFIED_ENDPOINTS_URL="http://local.hasura.dev:8787"
M_AUTH_KEY="your-strong-m-auth-key"
```
:::tip M-Auth Key
The `hasura-m-auth` header is a custom header that is used to authenticate the requests to the allowlist plugin. You can
use any strong key here to authenticate the plugin. DDN will automatically add this header to the requests to the
plugin.
:::
### Step 3. Run everything
At this point, you can run the following command to build and start local development:
```bash
ddn supergraph build local
ddn run docker-start
```
You can now test the plugin by making a request to the RESTified GraphQL endpoints defined in the plugin's
configuration.
If you want to make changes to the plugin configuration, you can update the `configuration.json` file and restart the
docker services.
### Step 4. Deploy the plugin
The plugin can be deployed as a regular HTTP service using any container orchestration tool. When deployed, make sure to
set the `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_PAT`, `GRAPHQL_SERVER_URL` and `HASURA_DDN_PLUGIN_CONFIG_PATH` to
appropriate values in `.cloud.env`. Note that the plugin must be visible from your Hasura deployment: if you are hosting
for the Hasura Cloud, the plugin must be publicly visible.
:::info Your plugin must be reachable
Since the RESTified endpoints plugin is a pre-parse plugin, it must be reachable from the Hasura deployment. If you are
hosting for the Hasura Cloud, the plugin must be publicly visible.
:::
## Using Cloudflare Wrangler
In this example, we're using Cloudflare Wrangler to deploy our plugin as a Cloudflare Worker. You can get started with
Wrangler [here](https://developers.cloudflare.com/workers/wrangler/install-and-update/).
### Step 1. Create a new plugin project
Create a new Cloudflare Worker project using the `create-cloudflare` command with the
[`restified-endpoints` plugin template](https://github.com/hasura/engine-plugin-restified-endpoint):
```bash
npx create-cloudflare@latest restified-endpoints-plugin --template https://github.com/hasura/engine-plugin-restified-endpoint
```
This will create a new project with the required files and dependencies.
### Step 2. Install dependencies
Navigate to the new directory and install the dependencies.
```bash
cd restified-endpoints-plugin
npm install
```
Start the local development server.
```bash
npm start
```
### Step 3. Add the plugin configuration
Follow the steps in the [docker image plugin configuration](#step-2-add-the-plugin-configuration) section to add the
plugin configuration.
### Step 4. Create a new build for local development
Create a new supergraph build.
```bash
ddn supergraph build local
```
Start the console for the local supergraph.
```bash
ddn console --local
```
You can now test the plugin by making a request to the RESTified GraphQL endpoints defined in the plugin's
configuration.
### Step 5. Update the plugin config
Update the `src/config.ts` file with the RESTified GraphQL endpoints that you want to add.
```typescript
export const Config = {
graphqlServer: {
headers: {
additional: {
"Content-Type": "application/json",
},
// Please make sure to forward the authentication headers here
forward: ["hasura_cloud_pat", "x-hasura-ddn-token"],
},
},
headers: {
"hasura-m-auth": "your-strong-m-auth-key",
},
restifiedEndpoints: [
{
path: "/v1/api/rest/users",
methods: ["GET", "POST"],
query: `
query MyQuery($limit: Int = 10, $offset: Int = 0) {
Users(limit: $limit, offset: $offset) {
Name
}
}
`,
},
// Add more RESTified endpoints here
],
};
```
:::info Hot reloading
The local wrangler development server will automatically reload the plugin when you make changes to the code.
:::
### Step 6. Configure the plugin variables
:::info Setup tracing
To enable tracing for the plugin, you need to update the `wrangler.toml` file with the required configurations. If you
don't want to enable tracing for the plugin, you can skip this step.
:::
In `restified-endpoints-plugin` directory, update the `wrangler.toml` file with the required configurations.
```toml
...
[vars]
OTEL_EXPORTER_OTLP_ENDPOINT = "https://gateway.otlp.hasura.io:443/v1/traces"
OTEL_EXPORTER_PAT = ""
GRAPHQL_SERVER_URL = ""
```
Replace `` with the Personal Access Token (PAT) for the Hasura Cloud account. You can display this using the
`ddn auth print-access-token` command.
Replace `` with the URL of your DDN GraphQL server. You can find this in the DDN console under
the `Settings > Project Summary` section.
### Step 7. Deploy the plugin
For your plugin to be reachable by your hosted supergraph, we can deploy with any service that hosts HTTPS services.
Here we will use Cloudflare Wrangler to deploy the plugin. The `deploy` command included in your plugin's `package.json`
will do this automatically for you and return the hosted service's URL.
**Note**: Please also update the `wrangler.toml` with your cloud PAT for the tracing to work.
```bash
npm run deploy
```
This will deploy the plugin to Cloudflare Workers and return the URL of the hosted service. Next, update the .env.cloud
file with the URL.
```bash
RESTIFIED_ENDPOINTS_URL="https://.workers.dev"
M_AUTH_KEY="your-strong-m-auth-key"
```
### Step 8. Create a new build
Create a new supergraph build on Hasura DDN to check your work in the cloud.
```bash
ddn supergraph build create
```
:::info Cloud project required
In order for this command to succeed, you'll need a cloud project paired with your local metadata. If you don't already
have one, you can create one using the CLI.
```sh title="From the root of the project directory, run:"
ddn project init .
```
:::
### Step 9. Apply the build
Apply the build to make it the default one served by your Hasura DDN project endpoint. You can do this from the DDN
console by choosing the build from the `Builds` tab and clicking `Apply Build`.
The engine will execute the plugin for each requests to the RESTified GraphQL endpoints you defined.
```sh title="As in the earlier example, you can test this by navigating to the endpoint for any paths you've set up in your plugin's configuration:"
# E.g., https://profound-applet-5420.ddn.hasura.app/v1/api/rest/users
https:///v1/api/rest/
```
## Using AWS Lambda
In this example, we're using AWS CDK to deploy our plugin as a Lambda service. You can get started with
AWS CDK [here](https://docs.aws.amazon.com/lambda/latest/dg/lambda-cdk-tutorial.html).
### Step 1. Clone the source code
Clone [`restified-endpoints` plugin template](https://github.com/hasura/engine-plugin-restified-endpoint):
```bash
git clone https://github.com/hasura/engine-plugin-restified-endpoint.git
```
### Step 2. Install dependencies
Navigate to the new directory and install the dependencies.
```bash
cd engine-plugin-restified-endpoint
npm install
```
Start the local development server.
```bash
npm start
```
### Step 3. Add the plugin configuration
Follow the steps in the [docker image plugin configuration](#step-2-add-the-plugin-configuration) section to add the
plugin configuration.
### Step 4. Create a new build for local development
Create a new supergraph build.
```bash
ddn supergraph build local
```
Start the console for the local supergraph.
```bash
ddn console --local
```
You can now test the plugin by making a request to the RESTified GraphQL endpoints defined in the plugin's
configuration.
### Step 5. Update the plugin config
Update the `src/config.ts` file with the RESTified GraphQL endpoints that you want to add.
```typescript
export const Config = {
graphqlServer: {
headers: {
additional: {
"Content-Type": "application/json",
},
// Please make sure to forward the authentication headers here
forward: ["x-hasura-role", "authorization", "x-hasura-ddn-token"],
},
},
headers: {
"hasura-m-auth": "your-strong-m-auth-key",
},
restifiedEndpoints: [
{
path: "/v1/api/rest/users",
methods: ["GET", "POST"],
query: `
query MyQuery($limit: Int = 10, $offset: Int = 0) {
Users(limit: $limit, offset: $offset) {
Name
}
}
`,
},
// Add more RESTified endpoints here
],
};
```
### Step 6. Configure the plugin variables
In `restified-endpoints-plugin` directory, change the GraphQL URL in the `src/lambda.ts` file.
```ts
app.all(
"/",
routeHandler(
process.env.GRAPHQL_SERVER_URL || "http://localhost:3000/graphql",
config,
),
);
```
Change the AWS CDK deployment configurations in `bin/serverless-aws.ts` and `lib/serverless-aws-stack.ts` files.
### Step 7. Build the plugin
For your plugin to be reachable by your hosted supergraph, we can deploy with any service that hosts HTTPS services.
First, build the code and dependencies for AWS Lambda manifests.
**Linux / MacOS**
```bash
npm run build:lambda
```
**Windows**
- Run `npm run build`.
- Copy `package.lambda.json` to `dist/package.lambda.json`.
- Go to `dist` folder and install packages `npm install`.
### Step 8. Deploy the plugin
Install AWS CDK `npm install -g aws-cdk`.
Login or export environment variables of AWS account.
Run bootstrap for the first deployment.
```bash
cdk synth
cdk bootstrap
```
Deploy the stack.
```bash
cdk deploy
```
This will deploy the plugin to AWS Lambda and return the URL of the hosted service. Next, update the .env.cloud
file with the URL.
```bash
RESTIFIED_ENDPOINTS_URL="https://"
M_AUTH_KEY="your-strong-m-auth-key"
```
### Step 9. Create a new build
Create a new supergraph build on Hasura DDN to check your work in the cloud.
```bash
ddn supergraph build create
```
:::info Cloud project required
In order for this command to succeed, you'll need a cloud project paired with your local metadata. If you don't already
have one, you can create one using the CLI.
```sh title="From the root of the project directory, run:"
ddn project init .
```
:::
### Step 10. Apply the build
Apply the build to make it the default one served by your Hasura DDN project endpoint. You can do this from the DDN
console by choosing the build from the `Builds` tab and clicking `Apply Build`.
The engine will execute the plugin for each requests to the RESTified GraphQL endpoints you defined.
```sh title="As in the earlier example, you can test this by navigating to the endpoint for any paths you've set up in your plugin's configuration:"
# E.g., https://profound-applet-5420.ddn.hasura.app/v1/api/rest/users
https:///v1/api/rest/
```
==============================
# Rate Limit Plugin
URL: https://hasura.io/docs/3.0/docs/plugins/rate-limit/
# Rate Limit Plugin
## Introduction
The [Rate Limit Plugin](https://github.com/hasura/engine-plugin-rate-limit) allows you to add rate limiting to your
supergraph, ensuring that the supergraph is not overloaded with requests. This can be useful for preventing abuse or
denial-of-service attacks.
The plugin integrates with Hasura DDN as a **pre-parse plugin**. It uses Redis for keeping track of the number of
requests made to the supergraph.
Key benefits of the rate limit plugin include:
- **Enhanced security:** Prevent abuse or denial-of-service attacks.
- **Flexibility:** Supports dynamic configuration including using roles, headers (can be used for IP-based rate
limiting) and session variables.
- **Integration:** Works as a pre-parse plugin, ensuring easy integration with Hasura DDN.
## Next steps
To get started with configuring and deploying the Rate Limit Plugin, refer to the
[guide](/plugins/rate-limit/how-to.mdx), which walks you through the process of setting up, configuring, and deploying
the plugin.
==============================
# deprecated-metadata-upgrades.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/upgrading-project-config/deprecated-metadata-upgrades
[//]: # "Add codemods in the order the user is expected to run them."
[//]: # "This is a descending order list. Newer codemods appear at the top."
# Deprecated Metadata Upgrades
Codemods can upgrade your metadata to the latest version so you can take advantage of the latest engine features.
Codemods below are listed with the CLI version they were added in.
## `upgrade-object-boolean-expression-types` (v2.4.0)
Upgrades the deprecated
[`ObjectBooleanExpressionType`](/reference/metadata-reference/boolean-expressions.mdx#objectbooleanexpressiontype-objectbooleanexpressiontype)
to the new [`BooleanExpressionType`](/reference/metadata-reference/boolean-expressions.mdx).
This enables filtering Models on nested objects and arrays, and based on relationships.
```bash
ddn codemod upgrade-object-boolean-expression-types
```
By default the codemod will run against the supergraph.yaml from the context. Provide `--supergraph` or `--subgraph` to
run the codemod against a different supergraph or subgraph.
## `upgrade-graphqlconfig-aggregate` (v2.4.0)
Add aggregates support to metadata through [`AggregateExpression`](/reference/metadata-reference/aggregate-expressions).
Use aggregates (like sum, min, count, etc) in your GraphQL API.
```bash
ddn codemod upgrade-graphqlconfig-aggregate
```
By default the codemod will run against the supergraph.yaml from the context.
The `GraphqlConfig` metadata object is required to be upgraded to enable aggregates for all subgraphs.
Run against a specific supergraph.yaml or subgraph.yaml file
```bash
ddn codemod upgrade-graphqlconfig-aggregate --supergraph ./supergraph.cloud.yaml
ddn codemod upgrade-graphqlconfig-aggregate --subgraph ./app/subgraph.yaml
```
## `upgrade-graphqlconfig-subscriptions` (v2.14.0)
Learn more about upgrading your metadata to have subscriptions by visiting [this page](/graphql-api/subscriptions/).
==============================
# index.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/upgrading-project-config/
# Upgrading legacy configurations
## Introduction
A series of legacy configurations and upgrade guides exist to help you bring your project up-to-speed with the latest
and greatest features of Hasura DDN.
## Project config upgrades
- [Upgrade to project config v3](upgrade-project-v3)
- [Upgrade to project config v2](upgrade-project-v2)
## Supergraph config upgrades
- [Upgrade to supergraph config v2](upgrade-supergraph-v2)
## Context config upgrades
- [Upgrade to context config v3](upgrade-context-v3)
==============================
# upgrade-project-v2.mdx
URL: https://hasura.io/docs/3.0/docs/project-configuration/upgrading-project-config/upgrade-project-v2
# Upgrading to project config v2
## What has happened?
A new revision (**2024.06.06**) of the [DDN CLI](/reference/cli/installation.mdx) has been released. With this release,
local project directory structures created by CLI versions **\<= 2024.06.05** are now out-of-date and deprecated.
This page contains some information on what has changed and instructions to migrate your existing local projects.
However, for users to most efficiently familiarize themselves with the new CLI and project structure, we recommend
setting up a new project from scratch by following the new [getting started guide](/how-to-build-with-ddn/overview.mdx).
:::warning A newer CLI version has been released since!
A newer revision (**v1.x.x**) of the [DDN CLI](/reference/cli/installation.mdx) has been released. See the upgrade guide
[here](project/configuration/overview.mdx If your local project was created by CLI versions **\<= 2024.06.05**, you will
need to update your local project directory using this current guide first before upgrading to the above revision.
:::
## What has changed?
- The project config version defined in `hasura.yaml` is bumped from `v1` to `v2`.
- Local dev workflows using Docker have been introduced.
- The DDN CLI commands. See details on the new commands [here](/reference/cli/commands/ddn).
- The local project directory structure.
- `SupergraphManifest` and `ConnectorManifest` objects are now called `Supergraph` and `Connector` respectively.
- The `source` field for `Relationship` objects has been deprecated in favour of the `sourceType` field. Though
`Relationship` objects with the `source` field will continue to be supported.
We **strongly recommend** you go through our [new getting started guide](/how-to-build-with-ddn/overview) to learn about
all the changes and experience the new workflow to develop your API with DDN.
:::info No changes to existing DDN projects
Note that this a CLI only change and does not impact Hasura DDN projects. You can continue using existing DDN projects
and builds and also be able to create new builds using the new CLI revision (with the new local project structure).
:::
## Migrate an existing local project
Below are steps to convert a local project created with the DDN CLI versions `<= v2024.06.05` to the new project
structure.
### Step 1: Get the new CLI
Also, update your Hasura VS Code extension to `v0.6.x` if not done automatically.
:::info Need the previous CLI revision?
Note that the above will overwrite your current DDN CLI revision. You can re-install the previous revision by replacing
`/ddn/cli/v2/` with `/ddn/cli/v1/` in the above download command.
:::
### Step 2: Set up a fresh local project
#### Step 2.1: Initialize a new supergraph
Run:
```bash
ddn supergraph init --dir
```
Set the default supergraph config file to the CLI context to avoid having to repeat it in future commands. This file
contains the `Supergraph` object similar to the `SupergraphManifest` object in your existing project directory.
```bash
cd && ddn context set supergraph ./supergraph.local.yaml
```
#### Step 2.2: Copy over supergraph global objects
Copy the files containing the supergraph global objects `CompatibilityConfig`, `GraphqlConfig` and `AuthConfig` from
your existing project directory to the `supergraph_globals` directory in the new project directory. These should
typically be in the `supergraph` directory in your existing project directory.
Note that these objects are already created in the `supergraph_globals` directory of your new project directory. The
`AuthConfig` object created here is to be used for local development. To avoid overwriting it, while copying your
`auth-config.hml` file from the existing project directory rename it to `auth-config.cloud.hml` instead. The
`graphql-config.hml` and `compatibility-config.hml` files on the other hand should be overwritten.
### Step 3: Add your subgraphs
For each subgraph in your project:
Follow the below steps:
#### Step 3.1: Initialize the subgraph
Run:
```bash
ddn subgraph init
```
#### Step 3.2: Add an env file for deploying to DDN
A `.env.` file is created in the `` directory to be used during local development.
Create a new file `.env.cloud.` alongside it to be used for deployments to DDN. You can leave this file
empty for now.
### Step 4: Set up your data connectors
**Case 1:**
For self-deployed data connectors in your subgraphs, i.e. `ConnectorManifest` of type `endpoints`:
You can skip this step.
**Case 2:**
For each data connector deployed on Hasura DDN in your subgraphs, i.e. `ConnectorManifest` of type `cloud`:
Follow the steps below:
#### Step 4.1: Initialize data connector
Run:
```bash
ddn connector init --subgraph --hub-connector
```
Ensure you use the same connector name and type from your existing `ConnectorManifest`.
#### Step 4.2: Create connector configuration files for deploying to DDN
##### Step 4.2.1: Create a connector.cloud.yaml
Each connector utilizes a `connector.yaml` file for local development. This file contains the `Connector` object similar
to the `ConnectorManifest` object in your existing project.
To deploy your connector to DDN create a `connector.cloud.yaml` file alongside the connector's local `connector.yaml`.
You can simply copy the contents of the `connector.yaml` file and update the `envFile` key's value to `.env.cloud` which
we'll create now.
```yaml title="For example, /connector//connector.cloud.yaml"
kind: Connector
version: v1
definition:
name:
subgraph:
source: hasura/:
context: .
#highlight-start
envFile: .env.cloud
#highlight-end
```
##### Step 4.2.2: Create a .env.cloud for the connector
Create an environment variable file `.env.cloud` alongside the `.env.local` file for the connector. You can leave this
file empty for now.
#### Step 4.3: Add data connector configuration
##### Case 1: For hasura/postgres connectors
###### Step 4.3.1: Set database connection string
Add a new key `CONNECTION_URI` to the file `/connector//.env.cloud` and set your database
connection string as the value.
For example:
```env
CONNECTION_URI=
```
###### Step 4.3.2: Introspect the database schema
Run:
```bash
ddn connector introspect --connector /connector//connector.cloud.yaml
```
##### Case 2: For hasura/nodejs connectors
###### Step 4.3.1: Copy over functions
Copy the file(s) containing your TS functions from your existing project directory to the
`/connector/` directory in the new project directory. This file should typically be the
`functions.ts` file in the `//connector` directory in your existing project directory.
### Step 5: Bring over connector related metadata
For each data connector:
Follow the below steps:
#### Step 5.1: Copy over connector related metadata files
Copy the files containing metadata objects related to the connector from your existing project directory to the
`/metadata` directory in the new project directory (the directory might need to be created). These files
should typically be in the `/` directory in your existing project directory and be
suffixed as `.hml`.
- the file with the `DataConnectorLink` object. Typically `.hml`
- the file with the data connector type objects. Typically `-types.hml`
- the files with Models, Commands, Relationships, etc. Typically in `models`/`commands` directory.
Within the `/metadata` directory you can choose to arrange these files however you like.
#### Step 5.2: Update DataConnectorLink object
In the `DataConnectorLink` object replace the `url` section with:
```
url:
readWriteUrls:
read:
valueFromEnv: _READ_URL
write:
valueFromEnv: _WRITE_URL
```
We will set the values of these env vars in the following steps.
#### Step 5.3: (Optional) Update Relationship objects
As mentioned earlier in the what-has-changed section, the `source` field for `Relationship` objects has been deprecated
in favour of the `sourceType` field. Though `Relationship` objects with the `source` field will continue to be
supported, it is recommended to update them.
To update the relationship objects, rename the `source` key in your objects to `sourceType`.
For example:
```yaml
kind: Relationship
version: v1
definition:
name:
#highlight-start
sourceType:
#highlight-end
target: ...
```
### Step 6: Deploy to a DDN project
#### Step 6.1: Set the DDN project to deploy to
Run the following command to set your DDN project to the CLI context to avoid having to repeat it in future commands.
You can find your project name in the `hasura.yaml` file in your existing project directory.
```
ddn context set project
```
#### Step 6.2: Deploy the data connectors
For each data connector deployed on Hasura DDN in your subgraphs, i.e. `ConnectorManifest` of type `cloud`:
Run:
```bash
ddn connector build create --connector /connector//connector.cloud.yaml
```
On deployment completion, the read and write URLs for your deployed connector will be returned as a response. You will
need these in the next step.
#### Step 6.3: Set connector endpoints in env files
For each subgraph:
Update the file `/.env.cloud.` with the read and write URLs of the data connectors in the
subgraph.
For a self-deployed data connector in your subgraph, i.e. `ConnectorManifest` of type `endpoints`, use the connector URL
provided in the existing `ConnectorManifest` as both the read and write URLs.
For data connectors deployed on Hasura DDN in your subgraph, use the connector URLs returned in the previous deploy
step.
For example:
```env title="/.env.cloud.:"
_READ_URL=_WRITE_URL=_READ_URL=_WRITE_URL=
```
#### Step 6.4: Deploy the supergraph
##### Step 6.4.1: Create a supergraph config file for deploying to DDN
In the root of the new project directory, create a new file `supergraph.cloud.yaml` and copy the contents of the
existing `supergraph.yaml` file to it.
Update the following values in the new file:
- `./supergraph_globals/metadata/auth-config.hml` -> To the name of the AuthConfig file copied earlier. e.g.
`./supergraph_globals/metadata/auth-config.cloud.hml`
- For each subgraph: `envFile: /.env.` -> To the name of the cloud env files created
earlier. e.g. `envFile: /.env.cloud.`
For example:
```yaml title="supergraph.cloud.yaml"
kind: Supergraph
version: v1
definition:
supergraph_globals:
generator:
rootPath: ./supergraph_globals
envFile: ./supergraph_globals/.env.supergraph_globals
includePaths:
#highlight-start
- ./supergraph_globals/auth-config.cloud.hml
#highlight-end
- ./supergraph_globals/compatibility-config.hml
- ./supergraph_globals/graphql-config.hml
subgraphs:
:
generator:
rootPath: /metadata
#highlight-start
envFile: /.env.cloud.
#highlight-end
includePaths:
- /metadata
```
##### Step 6.4.2: Build and deploy your supergraph
Run:
```bash
ddn supergraph build create --supergraph ./supergraph.cloud.yaml
```
On build completion, the build version, API endpoint and console URLs will be returned as response.
#### Step 6.5: Verify migration
You can now head to your project console using the console URL returned in the previous step and verify the API
generated with the above build is the same as what you had earlier.
## Need help?
If you need help migrating your project or have any other questions please reach out to us on our
[Discord](https://hasura.io/discord).
## Legacy project structure
See the legacy project structure before this update below.
| File type | Description |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [`hasura.yaml`](#hasurayaml) | The main configuration file. |
| [`*.env.yaml`](#envyaml) | A file that stores environment variables. |
| [`*.supergraph.yaml`](#supergraph-manifests) | Build manifest file(s) for a project. |
| `*.hml` | Hasura metadata files for a project. |
| [`supergraph`](#supergraph) | A directory, containing all the necessary configuration and metadata files, for the supergraph-level scope of a project. |
| [``](#subgraphs) | A directory, containing all the necessary configuration and metadata files, for a corresponding subgraph within a project. |
#### hasura.yaml
This is the entrypoint to a Hasura project.
```yaml
version: v1
project:
subgraphs:
app:
path: ./app
defaultSupergraphManifest: base
```
The `version` section is used to specify the version of the configuration file. The `project` field is used to specify
the project name.
The `hasura.yaml` file also contains a `subgraphs` section. This section is used to specify the various
[subgraphs](#subgraphs) associated with the project.
#### \*.env.yaml
This file is used to store environment variables. The CLI generates a `base.env.yaml` for you by default. You can add
environment variables by adding key-value pairs under a particular subgraph:
```yaml
supergraph: {}
subgraphs:
#highlight-start
app:
APP_CONNECTOR_CONNECTION_URI: "CONNECTION_URI"
#highlight-end
```
And then referencing them in your metadata using `valueFromEnv`:
```yaml
kind: ConnectorManifest
version: v1
spec:
supergraphManifests:
- base
definition:
name: app_connector
type: cloud
connector:
type: hub
name:
deployments:
- context: .
env:
CONNECTION_URI:
#highlight-start
valueFromEnv: APP_CONNECTOR_CONNECTION_URI
#highlight-end
```
:::info Accessing environment variables
In the example above, our `supergraphManifests` array references our `base.supergraph.hml` file, which contains an
`envfile` key that maps to our `base.env.yaml`. Thus, any environment variables present in this YAML file will then be
accessible in our HML.
If these files contain sensitive information, you should add them to your gitignore so as to avoid accidentally
committing them to version control.
:::
#### Supergraph manifests {#supergraph-manifests}
Supergraph manifests tell Hasura DDN how to construct your supergraph. A manifest will contain information such as which
subgraphs to include and which resources to use for the build.
```yaml
kind: SupergraphManifest
version: v1
definition:
name: base
envfile: base.env.yaml
subgraphs:
- app
```
#### Supergraph
The `supergraph` directory contains the supergraph configuration files. The three included files are:
```bash
βββ auth-config.hml
βββ compatibility-config.hml
βββ graphql-config.hml
```
:::info Organizing files
The contents of these files include the necessary metadata objects to build your supergraph. We've separated them out
into files organized by their purpose, but you can organize them anyway you like so long as the metadata objects within
them are all included.
:::
Each of these files' contents are used to configure the supergraph.
The `auth-config.hml` file contains the authentication configuration for the supergraph, allowing you to utilize either
JWTs or webhooks.
The `compatibility-config.hml` file contains the compatibility configuration βΒ using a version number β for the
supergraph.
The `graphql-config.hml` file contains the GraphQL configuration for the supergraph, which allows you to customize the
available query and mutation capabilities along with the schema.
#### Subgraphs
Each subgraph is listed as a top-level directory in the root of the project. The CLI will init your project with an
`app` subgraph.
Each subgraph directory will contain the following folder structure when a new connector manifest is added for the
subgraph:
```bash
βββ
βΒ Β βββ -types.hml
βΒ Β βββ .hml
βΒ Β βββ connector
βΒ Β βΒ Β βββ .build.hml
βΒ Β βΒ Β βββ configuration.json
βΒ Β βΒ Β βββ schema.json
βΒ Β βββ models
```
| File/Folder | Description |
| --------------------------------------- | ---------------------------------------------------------------------------------- |
| `` | The directory containing the build artifacts for the connector. |
| `-types.html` | The metadata file for the connector's types. |
| `