Pick your app

The examples below will be updated with your app ID.

Platform features

Managing users

See users in your app

You can manage users in your app using the $users namespace. This namespace is automatically created when you enable it from the Admin tab.

Once enabled, you'll see the $users namespace in the Explorer tab with all the users in your app!

Querying users

The $users namespace can be queried like any normal namespace. However, we've set some default permissions so that only a logged-in user can view their own data.

export default {
  "$users": {
    "allow": {
      "view": "auth.id == data.id",
      "create": "false",
      "delete": "false",
      "update": "false"
    }
  },
}

Right now $users is a read-only namespace. You can override the view permission to whatever you like, but create, delete, and update are restricted.

Linking users

Although you cannot directly add properties to the $users namespace, you can create links to other namespaces.

Below is an example of a schema for a todo app that has users, roles, profiles, and todos.

We create three links todoOwner, userRoles, and userProfiles to link the $users namespace to the todos, roles, and profiles namespaces respectively.

Notice that the $users namespace is in the reverse direction for all links. If you try to create a link with $users in the forward direction, you'll get an error.

Notice also that the profiles namespace has a nickname property. You may be wondering why we didn't add this directly to the $users namespace. This is because the $users namespace is read-only and we cannot add properties to it. If you want to add additional properties to a user, you'll need to create a new namespace and link it to $users.

// Use the Instant CLI tool to create an app with this schema!
import { i } from "@instantdb/react";

const graph = i.graph(
  {
    $users: i.entity({
      email: i.string().unique(),
    }),
    profiles: i.entity({
      nickname: i.string(), // We can't add this directly to `$users`
      userId: i.string().unique(),
    }),
    roles: i.entity({
      type: i.string().unique(), // We couldn't add this directly to `$users` either
    }),
    todos: i.entity({
      text: i.string(),
      userId: i.string(),
      completed: i.boolean(),
    }),
  },
  {
    // `$users` is in the reverse direction for all these links!
    todoOwner: {
      reverse: {
        on: "$users",
        has: "many",
        label: "todos"
      },
      forward: {
        on: "todos",
        has: "one",
        label: "owner"
      }
    },
    userRoles: {
      reverse: {
        on: "$users",
        has: "one",
        label: "role"
      },
      forward: {
        on: "roles",
        has: "many",
        label: "users"
      },
    }
    userProfiles: {
      reverse: {
        on: "$users",
        has: "one",
        label: "profile"
      },
      forward: {
        on: "profiles",
        has: "one",
        label: "user"
      },
    }
  }
);

export default graph;

You can then create links between users on the client side like so:

// Creates a todo and links the current user as an owner
const addTodo = (newTodo, currentUser) => {
  const newId = id();
  db.transact(
    tx.todos[newId].update({ text: newTodo, userId: currentUser.id, completed: false })
      // Link the todo to the user with the `owner` label we defined in the schema
      .link({ owner: currentUser.id }));
};

// Creates or updates a user profile with a nickname and links it to the
// current user
const updateNick = (newNick, currentUser) => {
  const profileId = lookup('email', currentUser.email);
  db.transact([
    tx.profiles[profileId].update({ userId: currentUser.id, nickname: newNick })
      // Link the profile to the user with the `user` label we defined in the schema
      .link({ user: currentUser.id }),
  ]);
}

If attr creation on the client is enabled, you can also create new links without having to define them in the schema. In this case you can only link to $users and not from $users.

// Comments is a new namespace! We haven't defined it in the schema.

// ✅ This works!
const commentId = id()
db.transact(
  tx.comments[commentId].update({ text: 'Hello world', userId: currentUser.id })
    .link({ $user: currentUser.id }));

// ❌ This will not work! Cannot create a forward link on the fly
const commentId = id()
db.transact([
  tx.comments[id()].update({ text: 'Hello world', userId: currentUser.id }),
  tx.$users[currentUser.id].link({ comment: commentId }))]);

// ❌ This will also not work! Cannot create new properties on `$users`
db.transact(tx.$users[currentUser.id].update({ nickname: "Alyssa" }))

User permissions

You can reference the $users namespace in your permission rules just like a normal namespace. For example, you can restrict a user to only update their own todos like so:

export default {
  // users perms...
  "todos": {
    "allow": {
      // owner is the label from the todos namespace to the $users namespace
      "update": "auth.id in data.ref('owner.id')",
    }
  }
};

You can also traverse the $users namespace directly from the auth object via auth.ref. When using auth.ref the arg must start with $user. Here's the equivalent rule to the one above using auth.ref:

export default {
  // users perms...
  "todos": {
    "allow": {
      // We traverse the users links directly from the auth object
      "update": "data.id in auth.ref('$user.todos.id')",
    }
  }
};

By creating links to $users and leveraging auth.ref, you can expressively build more complex permission rules.

export default {
  // users perms...
  "todos": {
    "bind" : [
      "isAdmin", "'admin' in auth.ref('$user.role.type')",
      "isOwner", "data.id in auth.ref('$user.todos.id')"
    ]
    "allow": {
      // We traverse the users links directly from the auth object
      "update": "isAdmin || isOwner",
    }
  }
};
Previous
Clerk