Let's create a simple todo-app using Nhost. With this app, a user will be able to create and list its own todos and not have anyone else see them.
To implement this behavior with Nhost, we'll briefly cover the following topics:
Creating a new app on Nhost
Defining a database schema
Inserting data
Setting permissions
Querying data via the GraphQL API
By the end of this tutorial, you will better understand what Nhost is and what it does for you.
Go to app.nhost.io and sign up for a new account if you don't have one already.
Press the New App button on the console's home page. Choose a name and pick the region closest to your users.
You'll be all set with the Default Workspace and the Free plan for now.
Creating a new app takes around 20 seconds or so. During this time, Nhost sets up your app's entire backend and infrastructure.
Once the setup completes, you'll automatically see the app dashboard, and you're ready to define your app's database schema.
Now, you will write a small JavaScript client to interact and retrieve todos from your Nhost app.
Nhost is framework-agnostic and works with any frontend you might build. You can also connect to Nhost from your server-side if you wish.
In this guide, we'll keep the example simple. We're not using a frontend framework. In a real-life scenario, you'd probably build a frontend client with a framework such as React, Vue, Svelte, or React Native.
Make sure you have Node.js and npm or Yarn installed.
Create a new folder called nhost-todos
, initialize a new JavaScript app there, and install the Nhost JavaScript SDK:
npm init -y
npm install @nhost/nhost-js graphql
or with Yarn
yarn init -y
yarn add @nhost/nhost-js graphql
You might have to edit the package.json
file and add/change the type
object to module
("type": "module"
).
In the new directory, create a file called index.js
.
Enter the following code into this file. It will initialize a new JavaScript client that will interact with your backend:
import { NhostClient } from '@nhost/nhost-js';
const nhost = new NhostClient({
backendUrl: 'https://[app-subdomain].nhost.run', // replace this with the backend URL of your app
});
console.log(nhost.graphql.getUrl());
Run the code in your terminal. You should see your app's GraphQL endpoint URL:
➜ node index.js
https://[app-subdomain].nhost.run/v1/graphql
If you now add the following GraphQL query to the client, let's see what happens when you run the updated version:
import { NhostClient } from '@nhost/nhost-js';
const nhost = new NhostClient({
backendUrl: 'https://[app-subdomain].nhost.run',
})(async () => {
// nhost.graphql.request returns a promise, so we use await here
const todos = await nhost.graphql.request(`
query {
todos {
id
created_at
name
is_completed
}
}
`);
// Print todos to console
console.log(JSON.stringify(todos.data, null, 2));
})();
➜ node index.js
null
null
is printed. Why is that? Let's find out.
While using the Hasura Console, you could fetch the todos because the admin role is enabled by default but when building your applications with a client, you want to define permissions using roles that your users can assume when making requests.
Hasura supports role-based access control. You create rules for each role, table, and operation (select, insert, update and delete) that can check dynamic session variables, like user ID.
Use the public
role in permissions when you want some data to be accessed by anyone without being signed in. The public
role is the default role in all unauthenticated requests.
Generally speaking, the public
role should not have permission to insert, update or delete data.
public
permissionsIn Hasura Console, go to the Data tab, click on the todos table, then click Permissions. Add a new role called public
and click on select. The permission options for the select operation show up below.
Add the following permissions:
Rerun the program. Now you see all todos.
➜ node index.js
{
"todos": [
{
"id": "558b9754-bb18-4abd-83d9-e9056934e812",
"created_at": "2021-12-01T17:05:09.311362+00:00",
"name": "write docs",
"is_completed": false
},
{
"id": "480369c8-6f57-4061-bfdf-9ead647e10d3",
"created_at": "2021-12-01T17:05:20.5693+00:00",
"name": "cook dinner",
"is_completed": true
}
]
}
There are two reasons why the request succeeded:
Nhost sets the public
role for every unauthenticated GraphQL request.
You explicitly defined permissions for the public
role.
It is essential to understand that Hasura has an allow nothing by default policy to ensure that only roles and permissions you define explicitly have access to the GraphQL API.
To implement an app for managing a todo list, let's ensure we have database tables for storing todos and users.
Hasura generates real-time GraphQL APIs, but it also provides a web console for manipulating the schema and data of your database.
Go to the Data tab on your app's dashboard and select Open Hasura. Remember to copy the admin secret.
The Hasura Console of your app's dedicated Hasura instance will open in a new tab. You can use Hasura Console to manage your app's schema, data, permissions, and event triggers.
You should see all your database tables on the left-hand side of the screen. You should see multiple different schemas displayed as folders:
public
schema for your app's custom tables
auth
and storage
schemas for Nhost's user management and file storage
If you open the auth
schema, you'll see that your app already has a users
table, so you don't have to create one.
In Hasura Console, go to the data tab, then click Create Table. Name this table todos
.
id
and created_at
columns are standard and can be added with two clicks. Click Frequently used columns and create them:
id
(UUID)
created_at
(timestamp)
Using frequently used columns ensures the columns get the right name, type, and default value.
Add two more columns manually:
name
(text)
is_completed
(boolean)
Make sure to set the default value of is_completed
to false
.
This is all we need! A new table will be created when you click Add Table.
Go to the Insert Row tab to add some data to your database.
Now that we have data in our database, we can retrieve it via a GraphQL API. Go to the API tab in the main menu. You can use this view to make GraphQL requests that query or mutate data in your database.
Paste the following GraphQL query into the form and press the Play button:
query {
todos {
id
created_at
name
is_completed
}
}
You should see the todos you just inserted show up as output on the right-hand side.
All requests in the Hasura Console use the admin
role by default. This role has access to all tables and permissions.
In the previous section, you defined select
permissions for the public
role. You will now add insert
and select
permissions for authenticated users to secure your app's GraphQL API with authentication.
Nhost's authentication service lets you deliver frictionless registration and login experiences to your users. We support most social providers and different methods such as email & password and passwordless (magic link).
Manually create a user by going to your app's Users tab (top menu) and clicking on Add User.
You will now use that newly created user. We'll use this newly created user to make authenticated requests to the GraphQL API.
Add the following code to sign in the new user and request the list of todos again:
import { NhostClient } from '@nhost/nhost-js';
const nhost = new NhostClient({
backendUrl: 'https://[app-subdomain].nhost.run',
})(async () => {
// Sign in user
const signInResponse = await nhost.auth.signIn({
email: 'joe@example.com',
password: 'securepassword',
});
// Handle sign-in error
if (signInResponse.error) {
throw signInResponse.error;
}
// Get todos
const todos = await nhost.graphql.request(`
query {
todos {
id
created_at
name
is_completed
}
}
`);
console.log(JSON.stringify(todos.data, null, 2));
})();
Why is the return value null
? Because when making GraphQL requests as an authenticated user, the user
role is assumed.
For authenticated requests, there is always the option to override the default
user
role with any other valid role.
To prepare our database and GraphQL API to work for signed-in users we need to do two things:
Add a user_id
column to the todos
table, so we know what todo belongs to which user.
Use the user
role instead of the public
role for permissions.
user_id
columnBefore adding the user_id
column, let's delete all existing todos.
Then add the user_id
column as a UUID
type. Make sure that nullable
is not checked. This will ensure that all todos must have a user_id
value.
At last, we'll create a connection between the todos
table and the users
table. For that, we need to do yet another two things:
Create a Foreign Key (FK) between todos
and auth.users.id
.
Let Hasura track the relationship between the two tables.
Create a FK between the auth.users.id
column and the public.todos.user_id
column. See video above.
Click on the public
schema and track the untracked foreign key relationship. Then click on the auth
schema and track the relationship again. See video above.
We track these relationships to create the GrpahQL relationships between the todos
table to the users
table and the users
table to the todos
table.
Ok, our user_id
column is added and connected correctly. Let's continue with setting permissions for signed-in users.
Let us organize the permissions so it works for signed in users too.
We won't use the public
role anymore, so let's remove all permission for that role.
Now we'll add permissions for the user
role.
Signed-in users use the
user
role by default
First, we'll set the Insert permission.
A user can only insert name
because all other columns will be set automatically. More specifically, user_id
is set to the user's id making the request (x-hasura-user-id
) and is configured in the Column presets
section. See the image below.
For Select permission, set a custom check so users can only select todos where user_id
is the same as their user id. In other words: users are only allowed to select their todos. See the image below.
Now rerun the app. New todos are inserted, and only todos for the user are fetched and displayed. Your backend is successfully secured!
We use cookies to provide our services and for analytics and marketing. By continuing to browse our website, you agree to our use of cookies.
To find out more about our use of cookies, please see our Privacy Policy and Cookies Policy.