Skip to content

Commit 4b27520

Browse files
committed
Sync MD files from dexie-web - Applied patches for: docs/roadmap/dexie5.0.md (new file) - Source commit: 53a9976c7108a014fd3cdbeb9a5dcb57d617eab5
1 parent 9845a79 commit 4b27520

1 file changed

Lines changed: 248 additions & 0 deletions

File tree

docs/roadmap/dexie5.0.md

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
---
2+
layout: docs
3+
title: 'Road Map: Dexie 5.0'
4+
---
5+
6+
Some of the features presented here were moved from the road map for dexie@4 in January 2024 when dexie@4 went into release candidate.
7+
8+
The goal for Dexie 5.0 will be a better experience for web developers to declare and query their data. What we'll be focusing on will be to query richness, paging and performance using some RAM-sparse caching. The goal is an intuitive and easy-to-use db API that performs well in large apps without sacrificing device resource usage.
9+
10+
We don't have a any dedicated time schedule of when dexie 5 will be in alpha, beta or feature complete. This road map may also be updated and modified along the way.
11+
12+
# Type Safe Declaration
13+
14+
Schema definition and typings can be declared in a single expression. Instead of having to declare .version().stores() after instanciating db, the db instanciation and the schema declaration can be done in a single expression.
15+
16+
#### Dexie's classical schema style:
17+
18+
```ts
19+
// 1. Declare db
20+
export const db = new Dexie('friendsDB') as Dexie & {
21+
friends: Table<Friend, number>
22+
}
23+
24+
// 2. Specify version(s) and schema(s)
25+
db.version(1).stores({
26+
friends: `
27+
++id,
28+
name,
29+
age
30+
`
31+
});
32+
```
33+
34+
#### Dexie@5 type+schma declaration in single expression:
35+
36+
```ts
37+
export const db = new Dexie('friendsDB').stores({
38+
friends: Table<Friend>`
39+
++id
40+
name # Comments allowed!
41+
age # (comma is optional!)
42+
`
43+
});
44+
45+
export interface Friend {
46+
id: number
47+
name: string
48+
age: number
49+
picture?: Blob
50+
}
51+
52+
```
53+
54+
# Representing Classes instead of Interfaces
55+
56+
In dexie@4 and earlier, we've always had the [Table.mapToClass()](/docs/Table/Table.mapToClass()) method to connect a table to its model class.
57+
58+
In dexie@5 this will be done simply by declaring the schema with `Table(MyClass)` instead of `Table<MyInterface>`:
59+
60+
```ts
61+
export const db = new Dexie('friendsDB').stores({
62+
friends: Table(Friend)` # Table(Class) instead of Table<Type>
63+
++id
64+
name
65+
age
66+
`
67+
});
68+
69+
export class Friend {
70+
id = 0;
71+
name = "";
72+
age = -1;
73+
picture?: Blob
74+
75+
birthday() {
76+
return db.friends.update(this.id, { age: add(1) });
77+
}
78+
}
79+
80+
```
81+
82+
83+
#### Breaking Changes?
84+
85+
Ever since Dexie version 1 came out, we've been very strict with backward compability and almost never introduced any breaking changes.
86+
87+
To continue this approach, dexie schema declaration will stay backward compatible in dexie@5, so the old declaration style will continue to work. It will be an opt-in possibility to take advantage of the benefits with the new declaration style in dexie 5:
88+
89+
- One single declaration for both schema and typings
90+
- No version number needed
91+
- The class is automatically mapped, just like mapToClass() did work in earlier versions.
92+
93+
### Sub-classing Dexie
94+
95+
```ts
96+
export class AppDB extends Dexie {
97+
friends = Table(Friend)`
98+
++id
99+
name
100+
age
101+
`
102+
}
103+
104+
const db = new AppDB('appDB');
105+
```
106+
107+
The sub-classed version above is equivalent to:
108+
109+
```ts
110+
const db = new Dexie('appDB').stores({
111+
friends: Table(Friend)`
112+
++id
113+
name
114+
age
115+
`
116+
});
117+
```
118+
119+
Subclassing Dexie isn't required anymore for typings but it is still useful the declared class extends the `Entity` helper because it will have the properties `db` and `table` so that methods can perform operations on the database:
120+
121+
```ts
122+
class Friend extends Model<AppDB> {
123+
id!: string;
124+
name!: string;
125+
age!: number;
126+
127+
// methods can access this.db because we're subclassing Entity<AppDB>
128+
async birthDay() {
129+
return this.db.friends.update(this.id, { age: add(1) });
130+
}
131+
}
132+
```
133+
134+
Notice that versions aren't needed for schema changes anymore. Here we diverge from native IndexedDB that require this. As already introduced in dexie@4, we work around it letting the declared version and the native version diverge. And when they do, we store the virtual version in a meta table on the database. This table will only be created on-demand, if a schema upgrade on same given version was needed. Basically, we continue working like before, unless the db has the $meta table - in which case the info there will be respected instead of the native one.
135+
136+
Also, any methods in the type will be omitted from the insert type so that if you have a class with methods that backs the model of your table, you will continously be able to add items using plain objects (with methods omitted).
137+
138+
## Migrations
139+
140+
We've changed the view of migrations and version handling. Before the version was directly related to changes in the schema such as added tables or indexes. This was natural and corresponds to how IndexedDB works natively.
141+
142+
The only situations where you need a new version number in dexie@5 will be in one of the following situations:
143+
144+
- You want to rename a table or property
145+
- You've refactored your model and need to move data around to comply with the new model
146+
147+
### New Migration Methods for Rename
148+
149+
Three new methods exists that can be used in migrations instead of update(). These are declarative and revertable, which is much better canary use cases where you might have to downgrade the database without deleting it.
150+
151+
This new bidirectional framework is also compatible with Dexie Cloud since it allows for multiple clients sharing the same data of in different versions and still be able to sync it.
152+
153+
- renameTable()
154+
- renameProperty()
155+
- refactor()
156+
157+
#### Example: You want to rename a table or property or both:
158+
159+
You want to rename table "friends" to "contacts". You also want to rename a property on that model from "name" to "displayName":
160+
161+
```ts
162+
const db = new Dexie('dbName').version(2).stores({
163+
contacts: Table<Contact>`
164+
++id
165+
displayName
166+
age
167+
`
168+
}).renameTable({friends: 'contacts'})
169+
.renameProperty({contacts: {name: 'displayName'}}); // renaming prop "name" to "displayName"
170+
```
171+
172+
#### object-wise upgrade()
173+
174+
We add a new object-wise upgrade. In contrast to the generic `upgrade()` callback, object-wise upgrades can incrementally upgrade individual objects which makes it perfect for distributed synced databases where some clients may still be on the old version.
175+
176+
If you are on Dexie Cloud, only object-wise upgrades are permitted.
177+
178+
```ts
179+
const db = new Dexie('dbName').version(3).stores({
180+
contacts: Table<Contact>`
181+
++id
182+
[lastName+firstName]
183+
age
184+
`
185+
}).upgrade({
186+
contacts: (contactV2: ContactV2) => {
187+
// Split displayName into firstName and lastName:
188+
const [firstName, ...lastNames] = contactV2.displayName?.split(' ') ?? [];
189+
const contact: Contact = {
190+
...contactV2,
191+
firstName,
192+
lastName: lastNames?.join(' ')
193+
};
194+
return contact;
195+
}
196+
});
197+
198+
199+
// Keep the refactoring history of earlier versions in separate expression declared later:
200+
db.version(2)
201+
.renameTable({friends: 'contacts'})
202+
.renameProperty({contacts: {name: 'displayName'}});
203+
```
204+
205+
# Richer Queries
206+
207+
Dexie will support combinations of criterias within the same collection and support a subset of mongo-style queries. Dexie has before only supported queries that are fully utilizing at least one index. Where-clauses only allow fields that can be resolved using a plain or compound index. And `orderBy()` requires an index for ordering, making it impossible to combine with a critera, unless the criteria uses the same index. Currently, combining index-based and 'manual' filtering is possible using filter(), but it puts the burden onto the developer to determine which parts of the query that should utilize index and which parts should not. Dexie 5.0 will move away from this limitation and allow any combination of criterias within the same query. Resolving which parts to utilize index will be decided within the engine.
208+
209+
`orderBy()` will be available on Collection regardless of whether the current query already 'occupies' an index or not. It will support multiple fields including non-indexed ones, and allow to specify collation.
210+
211+
```ts
212+
await db.friends
213+
.where({
214+
name: 'foo',
215+
age: { between: [18, 65] },
216+
'address.city': { startsWith: 'S' }
217+
})
218+
.orderBy(['age', 'name'])
219+
.offset(50)
220+
.limit(25)
221+
.toArray();
222+
```
223+
224+
# Improved Paging
225+
226+
The cache will assist in improving paging. The caller will keep using offset()/limit() to do its paging. The difference will be that the engine can optimize an offset()-based query in case it starts close to an earlier query with the same criteria and order, so the caller will not need to use a new paging API
227+
228+
# Encryption
229+
230+
We will provide a new encryption addon, similar to the 3rd part [dexie-encrypted](https://github.com/mark43/dexie-encrypted) and [dexie-easy-encrypt](https://github.com/jaetask/dexie-easy-encrypt) addons. These addons will continue to work with dexie@5 but due to the lack of maintainance of we believe there's a need to have a maintained addon for such an important feature.
231+
232+
The syntax for initializing encryption is not yet decided on, but might not correspond to those of the current 3rd part addons.
233+
234+
# Support SQLite as backing DB
235+
236+
We also aim to make it possible to use Dexie and Dexie Cloud in react-native, nativescript, Node, Bun, Deno or in the browser with SQLite's webassembly build. Running Dexie on Node is actually already possible using [IndexedDBShim](https://www.npmjs.com/package/indexeddbshim) but the idea is to support it natively to improve performance and stability.
237+
238+
# Breaking Changes
239+
240+
## No default export
241+
242+
We will stop exporting Dexie as a default export as it is easier to prohibit [dual package hazard](https://github.com/GeoffreyBooth/dual-package-hazard) when not exporting both named and default exports. Named exports have the upside of enabling tree shaking so we prefer using that only.
243+
244+
Already since Dexie 3.0, it has been possible to import { Dexie } as a named export. To prepare for Dexie 5.0, it can be wise to change your imports from `import Dexie from 'dexie'` to `import { Dexie } from 'dexie'` already today.
245+
246+
## More to come
247+
248+
Dexie has been pretty much backward compatible between major versions so far and the plan is to continue being backward compatible as much as possible. But there might be additional breaking changes to come and they will be listed here. This is a living document. Subscribe to our [github discussions](https://github.com/dexie/Dexie.js) or to the [blog](https://medium.com/dexie-js) if you want to get notified on updates.

0 commit comments

Comments
 (0)