Build a NodeJS application with Nile

In this tutorial, you will learn about Nile's tenant virtualization features, while building a todo list application with NodeJS and React.

1. Create a database

  1. Sign up for an invite to Nile if you don't have one already
  2. You should see a welcome message. Click on "Lets get started" Nile welcome.
  3. Give your workspace and database names, or you can accept the default auto-generated names. In order to complete this quickstart in a browser, make sure you select to “Use Token in Browser”.

2. Create a table

After you created a database, you will land in Nile's query editor. For our todo list application, we'll need tables to store tenants, users and todos. Tenants and users already exists in Nile, they are built-in tables. We'll just need to create a table for todos.

create table todos (
    id uuid DEFAULT (gen_random_uuid()),
    tenant_id uuid,
    title varchar(256),
    complete boolean);

You will see the new table in the panel on the left side of the screen, and you can expand it to view the columns.

See the tenant_id column? By specifying this column, You are making the table tenant aware. The rows in it will belong to specific tenants. If you leave it out, the table is considered shared, more on this later. Creating a table in Nile's admin dashboard

3. Get credentials

In the left-hand menu, click on "Settings" and then select "Credentials". Generate credentials and keep them somewhere safe. These give you access to the database.

4. Set the environment

Enough GUI for now. Let's get to some code.

If you haven't cloned this repository yet, now will be an excellent time to do so.

git clone https://github.com/niledatabase/niledatabase
cd niledatabase/examples/quickstart/node_react

Rename .env.example to .env Update NILE_USER and NILE_PASSWORD with the credentials you picked up in the previous step. It should look something like this:

# Private env vars that should never show up in the browser
# These are used by the server to connect to Nile database
NILEDB_USER=018ad484-0d52-7274-8639-057814be60c3
NILEDB_PASSWORD=0d11b8e5-fbbc-4639-be44-8ab72947ec5b
# URL of the frontend, for the post-signup redirect
FE_URL = "http://localhost:3006"
NILEDB_API_URL=https://eu-central-1.api.dev.thenile.dev/databases/018ec979-2412-7062-9cda-35ae6fea7837

Install dependencies

npm install

5. Run the application

Start both NodeJS api server and the React frontend

npm start

Go to http://localhost:3000 in a browser to see the app.

You can try a few things in the app:

  • Sign up as a new user
  • Create a tenant
  • Create a todo task

6. Check the data in Nile

Go back to the Nile query editor and see the data you created from the app.

SELECT tenants.name, title, complete
FROM todos join tenants on tenants.id = todos.tenant_id;

You should see all the todos you created, and the tenants they belong to.

7. How does it work?

The interesting part of this example is the NodeJS server. Lets take a look at /examples/quickstart/node_react/app.ts.

The NodeJS server uses the Nile JS client to connect to Nile.

When the Nile client is initialized, it uses the credentials you provided in the .env file to connect with the API:

const nile = await Nile();

The application uses Express middleware to capture the tenant identity for the current request and set Nile context:

app.param("tenantId", (req, res, next, tenantId) => {
  nile.tenantId = tenantId;
  next();
});

We use Nile SDK to both execute SQL and make API calls to Nile. For example, to create as new tenant:

app.post("/api/tenants", async (req, res) => {
  const { name } = req.body;

  if (!name) {
    res.status(400).json({
      message: "No tenant name provided",
    });
  }

  try {
    const createTenantResponse = await nile.api.tenants.createTenant({
      name: name,
    });
    const tenant = await createTenantResponse.json();
    res.json(tenant);
  } catch (error: any) {
    console.log("error creating tenant: " + error.message);
    res.status(500).json({
      message: "Internal Server Error",
    });
  }
});

The example uses Nile's tenant isolation to guarantee that each tenant can only see their own data:

app.post("/api/tenants/:tenantId/todos", async (req, res) => {
  const { title, complete } = req.body;

  const newTodo = await nile.db.query(
    `INSERT INTO todos (title, complete, tenant_id)
    VALUES ($1, $2, $3)
    RETURNING *;`,
    [
      title,
      complete || false,
      nile.tenantId, // setting from context
    ]
  );

  res.json(newTodo.rows);
});

8. Looking good!

🏆 Tada! You have learned the basic Nile concepts: