Skip to main content

Using RxDB with TypeScript

In this tutorial you will learn how to use RxDB with TypeScript. We will create a basic database with one collection and several ORM-methods, fully typed!

RxDB directly comes with its typings and you do not have to install anything else, however the latest version of RxDB (v9+) requires that you are using Typescript v3.8 or higher. Our way to go is

  • First define what the documents look like
  • Then define what the collections look like
  • Then define what the database looks like

Declare the types

First you import the types from RxDB.

import {
createRxDatabase,
RxDatabase,
RxCollection,
RxJsonSchema,
RxDocument,
} from 'rxdb';

Create the base document type

First we have to define the TypeScript type of the documents of a collection:

Option A: Create the document type from the schema

import {
toTypedRxJsonSchema,
ExtractDocumentTypeFromTypedRxJsonSchema,
RxJsonSchema
} from 'rxdb';
export const heroSchemaLiteral = {
title: 'hero schema',
description: 'describes a human being',
version: 0,
keyCompression: true,
primaryKey: 'passportId',
type: 'object',
properties: {
passportId: {
type: 'string',
maxLength: 100 // <- the primary key must have set maxLength
},
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
type: 'integer'
}
},
required: ['firstName', 'lastName', 'passportId'],
indexes: ['firstName']
} as const; // <- It is important to set 'as const' to preserve the literal type
const schemaTyped = toTypedRxJsonSchema(heroSchemaLiteral);

// aggregate the document type from the schema
export type HeroDocType = ExtractDocumentTypeFromTypedRxJsonSchema<typeof schemaTyped>;

// create the typed RxJsonSchema from the literal typed object.
export const heroSchema: RxJsonSchema<HeroDocType> = heroSchemaLiteral;

Option B: Manually type the document type

export type HeroDocType = {
passportId: string;
firstName: string;
lastName: string;
age?: number; // optional
};

Option C: Generate the document type from schema during build time

If your schema is in a .json file or generated from somewhere else, you might generate the typings with the json-schema-to-typescript module.

Types for the ORM methods

We also add some ORM-methods for the document.

export type HeroDocMethods = {
scream: (v: string) => string;
};

We can merge these into our HeroDocument.

export type HeroDocument = RxDocument<HeroDocType, HeroDocMethods>;

Now we can define type for the collection which contains the documents.


// we declare one static ORM-method for the collection
export type HeroCollectionMethods = {
countAllDocuments: () => Promise<number>;
}

// and then merge all our types
export type HeroCollection = RxCollection<HeroDocType, HeroDocMethods, HeroCollectionMethods>;

Before we can define the database, we make a helper-type which contains all collections of it.

export type MyDatabaseCollections = {
heroes: HeroCollection
}

Now the database.

export type MyDatabase = RxDatabase<MyDatabaseCollections>;

Using the types

Now that we have declare all our types, we can use them.


/**
* create database and collections
*/
const myDatabase: MyDatabase = await createRxDatabase<MyDatabaseCollections>({
name: 'mydb',
storage: getRxStorageDexie()
});

const heroSchema: RxJsonSchema<HeroDocType> = {
title: 'human schema',
description: 'describes a human being',
version: 0,
keyCompression: true,
primaryKey: 'passportId',
type: 'object',
properties: {
passportId: {
type: 'string'
},
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
type: 'integer'
}
},
required: ['passportId', 'firstName', 'lastName']
};

const heroDocMethods: HeroDocMethods = {
scream: function(this: HeroDocument, what: string) {
return this.firstName + ' screams: ' + what.toUpperCase();
}
};

const heroCollectionMethods: HeroCollectionMethods = {
countAllDocuments: async function(this: HeroCollection) {
const allDocs = await this.find().exec();
return allDocs.length;
}
};

await myDatabase.addCollections({
heroes: {
schema: heroSchema,
methods: heroDocMethods,
statics: heroCollectionMethods
}
});

// add a postInsert-hook
myDatabase.heroes.postInsert(
function myPostInsertHook(
this: HeroCollection, // own collection is bound to the scope
docData: HeroDocType, // documents data
doc: HeroDocument // RxDocument
) {
console.log('insert to ' + this.name + '-collection: ' + doc.firstName);
},
false // not async
);

/**
* use the database
*/

// insert a document
const hero: HeroDocument = await myDatabase.heroes.insert({
passportId: 'myId',
firstName: 'piotr',
lastName: 'potter',
age: 5
});

// access a property
console.log(hero.firstName);

// use a orm method
hero.scream('AAH!');

// use a static orm method from the collection
const amount: number = await myDatabase.heroes.countAllDocuments();
console.log(amount);


/**
* clean up
*/
myDatabase.destroy();

Known Problems

RxDB uses the WeakRef API. If your typescript bundler throws the error TS2304: Cannot find name 'WeakRef', you have to add ES2021.WeakRef to compilerOptions.lib in your tsconfig.json.

{
"compilerOptions": {
"lib": ["ES2020", "ES2021.WeakRef"]
}
}