Skip to content

Persist extention hydration #343

@noveogroup-amorgunov

Description

@noveogroup-amorgunov

Hi! First of all, thank you for releasing the new version of the library.
I have two questions about how to properly use the persist extension.

Schema example
import { Collection } from '@msw/data'
import { persist } from '@msw/data/extensions/persist'
import { sync } from '@msw/data/extensions/sync'
import { z } from 'zod'

const userSchema = z.object({
  id: z.number(),
  email: z.email(),
  password: z.string(),
})

const wishlistSchema = z.object({
  id: z.number(),
  get user() {
    return userSchema
  },
  productIds: z.array(z.number()),
})

export async function createDb() {
  const users = new Collection({
    schema: userSchema,
    extensions: [
      persist(),
      sync(),
    ],
  })
  
  const wishlists = new Collection({
    schema: wishlistSchema,
    extensions: [persist(), sync()],
  })
  
  wishlists.defineRelations(({ one }) => ({
    user: one(users),
  }))

  return {
    users,
    wishlists,
  }
}

////

createDb().then(
  db => startDatabaseMigration(db)
)

 1 – Asynchronous hydration

If I call a function to seed fixtures from the global scope, queries against the collections always return empty results, since the data is hydrated asynchronously.

export async function startDatabaseMigration() {
  const users = db.users.findMany(q => q.where({ id: value => Boolean(value) }))

  // users is always an empty array

To work around this, I can add a small delay — but this isn’t obvious from the docs and feels a bit hacky (it might also break logic that expects the collections to be ready synchronously):

export async function startDatabaseMigration() {
+  await new Promise(resolve => setTimeout(resolve, 10))

  const users = db.users.findMany(q => q.where({ id: value => Boolean(value) }))

  // users is correctly restored from localStorage

Is this the correct approach to handle the issue, or am I misusing the extension?

2 – Hydrating related entities

When I am trying to hydrate a related entity, I get an error saying that the user entity doesn’t exist yet at the time of hydration:

collection-BJduKbwC-….js?v=aa5ed28a:2330 Uncaught (in promise) OperationError$1: Failed to create a new record with initial values: does not match the schema. Please see the schema validation errors above.
    at async Promise.all (:5174/index 0)
await in create		
(anonymous)	@	db.ts:28

With errors like this:

Image

What I’ve discovered is that when closing the browser tab, the user inside wishlists record is not saved. So during the next hydration, the errors above occur:

Image

You can see in the screenshot that there’s no user field.

Here’s how I create the entities:

  const user = await db.users.create({ ...userData, id: generateId('user') })
  await wait(10)

  await db.wishlists.create({
    id: generateId('wishlist'),
    user,
    productIds: [3, 4, 5, 6, 7],
  })

On the first run (without reloading the page), everything works correctly:

Image

Is this a bug, or am I doing something wrong?

If needed, I can create a CodePen reproduction for both issues.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions