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"]
}
}