Schemas

Schemas

Schemaful vs Schemaless

Providing a schema to Triplit is optional, but it is recommended in order to take advantage of all the features provided by Triplit.

Limitations of schemaless mode include:

  • You are limited to exclusively using storing value types that are supported by JSON: string, number, boolean, objects, null.
  • If you use Typescript, you will not get type checking for your queries and results.
  • Access control rules are defined in schemas, and thus are not supported in schemaless mode.

Defining your schema

A schema object defines your collections and the attributes and relationships on those collections. Schemas are defined in Javascript like so:

import { Schema as S } from '@triplit/db';
import { TriplitClient } from '@triplit/client';
 
const schema = {
  todos: {
    schema: S.Schema({
      id: S.Id(),
      text: S.String(),
      complete: S.Boolean(),
      created_at: S.Date(),
      tags: S.Set(S.String()),
    }),
  },
  users: {
    schema: S.Schema({
      id: S.Id(),
      name: S.string(),
      address: S.Record({
        street: S.string(),
        city: S.string(),
        state: S.string(),
        zip: S.string(),
      }),
    }),
  },
};
 
const client = new TriplitClient({
  schema,
});

Passing a schema to the client constructor will override any schema currently stored in your cache. You may also manage your schema with migrations, as explained in the Migrations section. Using migrations is recommended for production applications that persist state locally or use syncing.

id

Every collection in Triplit must define an id field in its schema. The S.Id() data type will generate a random id by default upon insertion. If you want to specify the id for each entity, you may pass it as a string in to the insert method as shown below.

// assigning the id automatically
await client.insert('todos', {
  text: 'get tortillas',
  complete: false,
  created_at: new Date(),
  tags: new Set([groceries]),
})
 
// assigning the id manually
await client.insert('todos', {
  id: 'tortillas'
  text: 'get tortillas',
  complete: false,
  created_at: new Date(),
  tags: new Set([groceries]),
})

Data types

When using a schema you have a few datatypes at your disposal:

Value types

Value types are basic primitive types for the database.

String

The string data type is used to store text.

import { Schema as S } from '@triplit/db';
const stringType = S.String();

Strings support =, !=, like and nlike operators in where statements.

You can use the like operator in a where clause to do simple filtering with string similarity. A like expression is true if the supplied attribute matches the supplied filter pattern.

An underscore (_) in a pattern stands for (matches) any single character; a percent sign (%) matches any sequence of zero or more characters.

For example:

['triplit', 'like', 'triplit']    true
['triplit', 'like', 'tri%']       true
['triplit', 'like', 'tr_pl_t']    true
['triplit', 'like', 'trip']       false

Number

The number data type is used to store integer or float numbers.

import { Schema as S } from '@triplit/db';
const numberType = S.Number();

Numbers support =, !=, >, >=, <, <= operators in where statements.

Boolean

The boolean data type is used to store true or false values.

import { Schema as S } from '@triplit/db';
const booleanType = S.Boolean();

Booleans support =, != operators in where statements.

Date

The date data type is used to store date and time values.

import { Schema as S } from '@triplit/db';
const dateType = S.Date();

Dates support =, !=, >, >=, <, <= operators in where statements.

Options

Value types have a few options that can be passed to their constructor.

nullable

You can indicate an attribute is nullable by passing the { nullable: true } option to its constructor.

import { Schema as S } from '@triplit/db';
import { TriplitClient } from '@triplit/client';
const schema = {
  test: {
    schema: S.Schema({
      id: S.Id(),
      nullableString: S.String({ nullable: true }),
    }),
  },
};
 
const client = new TriplitClient({
  schema,
});
 
await client.insert('test', {
  nullableString: null,
});
default

You can provide defaults values or functions for an attribute. Triplit currently support literal values and the following functions:

  • uuid()
  • now()

The below schema has literal and function default values.

import { Schema as S } from '@triplit/db';
import { TriplitClient } from '@triplit/client';
const schema = {
  collections: {
    messages: {
      schema: S.Schema({
        id: S.Id(),
        defaultValue: S.String({ default: 'hello' }),
        defaultFunction: S.Date({ default: S.Default.now() }),
      }),
    },
  },
};
await client.insert('test', {});
// { id: <uuid>, defaultValue: 'hello', defaultFunction: '2021-03-01T00:00:00.000Z' }

Set

Set types are used to store a collection of non nullable value types. Sets are unordered and do not allow duplicate values.

ℹ️

Lists, which support ordering and duplicate values, are on the roadmap (opens in a new tab).

import { Schema as S } from '@triplit/db';
const stringSet = S.Set(S.String());

Sets support =, != operators in where statements, which check if the set contains the value.

Record

Record types support nested information.

import { Schema as S } from '@triplit/db';
const recordType = S.Record({
  street: S.String(),
  city: S.String(),
  state: S.String(),
  zip: S.String(),
});

Relationships

⚠️

Relationships are only partially supported in Triplit. Specifically you may filter data based on a relationship, however related data is not selectable. Full support for relationships is on the roadmap (opens in a new tab).

To define a relationship between two collections, you define a subquery that describes the relationship with Query(). Within the where clause, you can reference the current collection's attributes with $.

import { Schema as S } from '@triplit/db';
const schema = {
  collections: {
    departments: {
      schema: S.Schema({
        id: S.Id(),
        name: S.String(),
        classes: S.Query({
          collectionName: 'classes',
          where: [['department_id', '=', '$id']],
        }),
      }),
    },
    classes: {
      schema: S.Schema({
        id: S.Id(),
        name: S.String(),
        level: S.Number(),
        building: S.String(),
        department_id: S.String(),
        department: S.Query({
          collectionName: 'departments',
          where: [['id', '=', '$department_id']],
        }),
      }),
    },
  },
};
 
const query = client
  .query('departments')
  .where([['classes.building', '=', 'Voter']])
  .build();