Normalized Data Store

The data store gives you a normalized, reactive entity layer — define models with decorators, store them once, and query them reactively across your app.

Defining models

A model is a class with @Model, an @Id field, and @Prop fields:

@Model
class User {
  @Id id!: string;
  @Prop name!: string;
  @Prop email!: string;
}

@Model
class Article {
  @Id id!: string;
  @Prop title!: string;
  @Prop authorId!: string;
}

Each @Prop field becomes a reactive signal internally. When you update user.name, anything reading it re-renders automatically.

Storing and querying

Use EntityStore to store and retrieve entities:

@Service
class UserService {
  @Inject(EntityStore) store!: EntityStore;

  addUsers(users: User[]) {
    this.store.set(User, users);
  }

  getActiveUsers() {
    return this.store.get(User, u => u.isActive);
  }

  removeUser(id: string) {
    this.store.delete(User, id);
  }
}

Entities are normalized by ID — each entity is stored once regardless of how many places reference it. Updating a user in the store updates it everywhere.

Consuming APIs

@Consume connects your API methods to the store automatically. It converts JSON responses to model instances and stores them:

@Service
class UserRepository {
  @Consume(User)
  async findAll() {
    const res = await fetch('/api/users');
    return await res.json();
  }

  @Consume(User)
  async findById(id: string) {
    const res = await fetch(`/api/users/${id}`);
    return await res.json();
  }
}

After calling findAll(), you get back User instances (not JSON) that are already in the store and fully reactive.