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].graphql.[region].nhost.run/v1
When using the CLI the GraphQL API is available at https://local.graphql.nhost.run/v1
.
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 query is used to fetch data from the GraphQL API.
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.
- Request
- Response
query GetTodos {
todos {
title
body
isCompleted
}
}
{
"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:
- Request
- Response
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)
}
}
}
}
{
"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.
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:
- Request
- Response
mutation InsertTodo {
insert_todos(
objects: [{ title: "Delete Firebase account", body: "Migrate to Nhost", isCompleted: false }]
) {
returning {
id
title
body
isCompleted
}
}
}
{
"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:
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:
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:
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
:
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:
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.