Pick your app

The examples below will be updated with your app ID.

Platform features

Schema-as-code

The schema definition file: instant.schema.ts

This file lives in the root of your project and will be consumed by the Instant CLI. You can apply your schema to the production database with npx instant-cli push schema.

The default export of instant.schema.ts should always be the result of a call to i.schema.

// instant.schema.ts

import { i } from '@instantdb/core';

const _schema = i.schema({
  entities: entitiesMap, // a map of `i.entity` definitions, see "Defining entities" below
  links: linksMap, // a description of links between your app's entities, see "Defining links" below
  rooms: roomsMap // If you use presence or cursors, you can define your schema for them here
);

// This helps Typescript display nicer intellisense
type _AppSchema = typeof _schema;
interface AppSchema extends _AppSchema {}
const schema: AppSchema = _schema;

export { type AppSchema };
export default schema;

Defining entities

The entities paramemter in i.schema is a dictionary of entities, where the key represents the entities name, and the value is a call to i.entity with a dictionary of attributes.

{
  profile: i.entity({
    name: i.string(),
    email: i.string().unique().indexed(),
    age: i.number().optional(),
  }),
  // more definitions...
}

Defining an attribute

Entity definitions accept a map of attribute definitions, where the key represents the attribute name and the value contains configuration for the attribute.

First we specify the expected type of the attribute: i.string(), i.number(), i.boolean(), i.date(), i.json(), or i.any().

i.date() accepts dates as either a numeric timestamp (in milliseconds) or an ISO 8601 string. JSON.stringify(new Date()) will return an ISO 8601 string.

We can then chain modifiers: .optional(), .unique() and .indexed().

When adding a type to an existing attribute, push schema will kick off a job to check the existing data for the attribute before setting the type on the attribute. If you prefer not to enforce the type, you can run push schema with the --skip-check-types flag.

Here are some examples:

// strings
i.string();

// numbers
i.number();

// booleans
i.boolean();

// complex JSON values
i.json<ValueShape>();

// any type
i.any();

// optional
i.string().optional();

// indexed values
i.string().indexed();

// unique
i.string().unique();

// chaining
i.string().unique().indexed();

Link definitions are used to express relationships in your app's data model.

Links are bidirectional, and you can specify a name and cardinality for both the forward and reverse direction.

{
  authorPosts: {
    forward: {
      on: 'authors',  // corresponds to an entity name
      has: 'many', // the cardinality of the authors -> posts link, i.e. "authors have many posts"
      label: 'posts', // the name of the field when performing queries with InstaQL
    },
    reverse: {
      on: 'posts', // corresponds to an entity name
      has: 'one', // i.e. the cardinality of the posts -> authors link, "posts have one author"
      label: 'author', // the name of the field when performing queries with InstaQL
    },
  },
  // more links...
}

Defining rooms

The rooms key let you define a schema for presence, cursors, and other ephemeral features. Here's how this looks:

{
  // `chat` is the `roomType`
  chat: {
    // You can define presence state here
    presence: i.entity({
      nickname: i.string(),
    }),
    topics: {
      // You can define payloads for different topics here
      sendEmoji: i.entity({
        emoji: i.string(),
      })
    }
  }
}

An example schema file

Below we demonstrate a data model for a blog. First we define our core entities: authors, posts and tags.

Then we define links. Note how each direction specifies its own label (posts.author instead of posts.authors) and cardinality (has: 'one' and has: 'many').

Make sure to set the graph object as your file's default export to that it can be picked up by the CLI.

import { i } from '@instantdb/core';

const _schema = i.schema({
  entities: {
    $users: i.entity({
      email: i.string().unique().indexed(),
    }),
    posts: i.entity({
      title: i.string(),
      content: i.string(),
    }),
    tags: i.entity({
      label: i.string(),
    }),
  },
  links: {
    postsAuthor: {
      forward: {
        on: 'posts',
        has: 'one',
        label: 'author',
      },
      reverse: {
        on: '$users',
        has: 'many',
        label: 'authoredPosts',
      },
    },
    postsTags: {
      forward: {
        on: 'posts',
        has: 'many',
        label: 'tags',
      },
      reverse: {
        on: 'tags',
        has: 'many',
        label: 'posts',
      },
    },
  },
  rooms: {
    chat: {
      presence: i.entity({
        nickname: i.string(),
      }),
      topics: {
        sendEmoji: i.entity({
          emoji: i.string(),
        }),
      },
    },
  },
});

// This helps Typescript display nicer intellisense
type _AppSchema = typeof _schema;
interface AppSchema extends _AppSchema {}
const schema: AppSchema = _schema;

export { type AppSchema };
export default schema;