@braidlabs/angular-data
Normalized entity store, tag-based query invalidation, and durable mutation outbox for Angular. Built on @braidlabs/data and @braidlabs/skew.
Installation
bash
npm install @braidlabs/angular-data @braidlabs/data @braidlabs/skewKey Features
- Normalized Entity Store: Shared identity across components and fragments without duplicate network queries.
- Tag-Based Invalidation: Queries declare tags (e.g.
['funds', 'fund:123']); mutations invalidate tags to trigger precise background refetches. - Durable Mutation Outbox: Mutations are recorded to IndexedDB before making network requests. If the network drops or the browser crashes, mutations resume automatically on reconnect.
- Optimistic Updates: Immediate UI updates with automatic rollback or conflict resolution on server mismatch.
Usage
1. Register Data Providers
ts
import { ApplicationConfig } from '@angular/core';
import { provideData } from '@braidlabs/angular-data';
export const appConfig: ApplicationConfig = {
providers: [
provideData({
dbName: 'acme-data',
partition: 'tenant_main',
}),
],
};2. Querying & Tag Invalidation
ts
import { Component, inject } from '@angular/core';
import { EntityStore, query } from '@braidlabs/angular-data';
import { FundSchema } from './fund.schema';
@Component({
standalone: true,
template: `
@for (fund of funds.data(); track fund.id) {
<div class="fund-card">
<h3>{{ fund.name }}</h3>
<p>NAV: {{ fund.nav }}</p>
</div>
}
`,
})
export class FundListComponent {
private store = inject(EntityStore);
funds = query(this.store, {
key: 'fund-list',
schema: FundSchema,
tags: ['funds'],
fetch: () => fetch('/api/funds').then((r) => r.json()),
});
}3. Durable Optimistic Mutations
ts
import { Component, inject } from '@angular/core';
import { OutboxService } from '@braidlabs/angular-data';
import { FundSchema } from './fund.schema';
@Component({ ... })
export class FundEditorComponent {
private outbox = inject(OutboxService);
async updateFund(id: string, newNav: number) {
await this.outbox.enqueue({
id: `mut_${Date.now()}`,
endpoint: `/api/funds/${id}`,
method: 'PATCH',
payload: { nav: newNav },
invalidatesTags: ['funds', `fund:${id}`],
optimistic: {
collection: 'funds',
key: id,
schema: FundSchema,
update: (current) => ({ ...current, nav: newNav }),
},
});
}
}