Skip to main content

GraphQL API

A GraphQL API is automatically and instantly available based on the tables and columns in your database.

The GraphQL API has support for inserting, selecting, updating, and deleting data, which usually accounts for 80% of all API operations you need.

It's the Hasura GraphQL engine that powers the GraphQL API which means that all documentation about queries, mutations, and subscriptions from Hasura's documentation is applicable.

What is GraphQL

GraphQL is a query language for APIs that prioritize developer experience. The GraphQL API can be used to both fetch (query) and modify (mutation) data. GraphQL is especially powerful for frontend developers who want to build products fast.

GraphQL has grown rapidly in popularity in the last years and has been adopted by almost all major tech companies such as Facebook, GitHub, and Stripe.

Building your GraphQL API is a lot of work, but with Nhost it's easy because every table and column is instantly available in your GraphQL API.

Endpoint

The GraphQL API is available at https://[subdomain].nhost.run/v1/graphql When using the CLI the GraphQL API is available at http://localhost:1337/v1/graphql.

GraphQL Clients for JavaScript

The Nhost JavaScript client comes with a simple GraphQL client that works well for the backend or simple applications.

When building more complex frontend applications, we recommend using a more advanced GraphQL client such as:

Queries

A GraphQL query is used to fetch data from the database.

tip

The Queries documentation from Hasura is applicable since we're using Hasura's GraphQL Engine for your project.

Example: A GraphQL query to select title, body, and isCompleted for every row in the todos table.

GraphQL
query GetTodos {
todos {
title
body
isCompleted
}
}

Response:

Response
{
"data": {
"todos": [
{
"title": "Delete Firebase account",
"body": "Migrate to Nhost",
"isCompleted": true
}
]
}
}

Filtering and sorting

More complex queries utilize filters, limits, sorting, and aggregation.

Here's an example of a more complex GraphQL query that selects all items in the todos table that are not completed, with the total number of comments and the last five comments:

GraphQL
query GetTodosWithLatestComments {
todos(where: { isCompleted: { _eq: false } }) {
title
body
comments(limit: 5, order_by: { createdAt: desc }) {
comment
createdAt
author
}
comments_aggregate {
aggregate {
count(columns: id)
}
}
}
}
Response
{
"data": {
"todos": [
{
"title": "Delete Firebase account",
"body": "Migrate to Nhost",
"comments": [
{
"comment": "Let's do this",
"created_at": "2019-10-31T08:34:25.621167+00:00",
"author": "John"
},
{
"comment": "🎉",
"created_at": "2019-10-31T08:33:18.465623+00:00",
"author": "Charlie"
}
],
"comments_aggregate": {
"aggregate": {
"count": 2
}
}
}
]
}
}

Mutations

A GraphQL mutation is used to insert, upsert, update, or delete data.

tip

The Mutations documentation from Hasura is applicable since we're using Hasura's GraphQL Engine for your project.

Insert Data

Example: A GraphQL mutation to insert data:

GraphQL
mutation InsertTodo {
insert_todos(
objects: [{ title: "Delete Firebase account", body: "Migrate to Nhost", isCompleted: false }]
) {
returning {
id
title
body
isCompleted
}
}
}
Reponse
{
"data": {
"insert_todos": [
{
"id": "bf4b01ec-8eb6-451b-afac-81f5058ce852",
"title": "Delete Firebase account",
"body": "Migrate to Nhost",
"isCompleted": true
}
]
}
}

Insert Multiple Rows

Use an array of objects to insert multiple rows at the same time.

Example: Insert multiple Todos at the same time:

GraphQL
mutation InsertMultipleTodos {
insert_todos(
objects: [
{ title: "Build the front end", body: "Mobile app or website first?", isCompleted: false }
{ title: "Launch 🚀", body: "That was easy", isCompleted: false }
]
) {
returning {
id
title
body
isCompleted
}
}
}

Update Data

You can update existing data with an update mutation. You can update multiple rows at once.

Example: A GraphQL mutation to mark a atodo item as completed:

GraphQL
mutation UpdateTodoStatus($id: uuid, $isCompleted: Boolean) {
update_todos(_set: { isCompleted: $isCompleted }, where: { id: { _eq: $id } }) {
returning {
body
isCompleted
title
}
}
}

Notice how we are using variables as the id and isDone variables, which lets us mark any todo as completed or not completed with the same mutation.

Upsert Data

When you're not sure if a piece of data already exists, use an upsert mutation. It will either insert an object into the database if it doesn't exist or update the fields of an existing object.

Unlike for update mutations, you must pass all columns to an upsert mutation.

To convert your insert mutation to an upsert, you need to add an on_conflict property for the GraphQL API to know which fields it should use to find duplicates.

The on_conflict key must be a unique key in your database:

GraphQL
mutation UpsertTodo {
insert_todos(
objects: { title: "Delete Firebase account", body: "...", isCompleted: false }
on_conflict: { constraint: todos_title_key, update_columns: [title, isCompleted] }
) {
returning {
id
title
body
isCompleted
}
}
}

This will update body and done of the todos with the title "Delete Firebase account".

Conditional upsert

Inserts a new object into a table, or if the primary key already exists, updates columns if the where condition is met.

For example, you may want to only update an existing todo if it is not done:

mutation UpsertTodo {
insert_todos(
objects: { title: "Delete Firebase account", body: "...", done: false }
on_conflict: {
constraint: todos_title_key
update_columns: [body, done]
where: { done: { _eq: false } }
}
) {
returning {
body
done
id
title
}
}
}

Ignore mutation on conflict

If update_columns is empty, the mutation will be ignored if the object already exists.

Here we have set the title to a unique key, to prevent multiple tasks with the same name. We want to avoid overwriting existing todos, so the update_columns array is empty:

mutation InsertTodo {
insert_todos(
objects: { title: "Delete Firebase account", body: "...", done: false }
on_conflict: { constraint: todos_title_key, update_columns: [] }
) {
returning {
body
done
id
title
}
}
}

In this case, the insert mutation is ignored because a todo with the title "Delete Firebase account" already exists, and update_columns is empty.

Delete Data

To delete your data, use a delete mutation. This mutation will delete all todos where done is true:

GraphQL mutation
mutation DeleteDoneTodos {
delete_todos(where: { done: { _eq: true } }) {
affected_rows
}
}

If you have set up foreign keys which will restrict a delete violation, you will get an error and will not be able to delete the data until all violations are solved. The simplest way to solve this is by set On Delete Violation to CASCADE when you set up a foreign Key.


Subscriptions

GraphQL subscriptions are queries that use WebSockets to keep the data up to date in your app in real-time. You only have to change query to subscription when constructing the GraphQL document:

GraphQL subscription
subscription GetTodos {
todos {
title
body
done
}
}

Your data is always in sync when using subscriptions. It does not matter if the data changes through GraphQL or directly in the database. The data is always syncing in real-time using GraphQL subscriptions.