This project introduces prototype APIs for handling data fetching and mutations in Angular applications using a reactive approach. These utilities bridge RxJS with Angular Signals to provide a clean, declarative pattern for managing asynchronous operations.
rxResource
is a utility for handling data fetching operations with built-in status tracking.
// Simple Fetching
const customers = rxResource({
loader: () => customersService.getAll(),
initialValue: [], // undefined by default
});
// Access State
console.log(customerDetails.value()); // current value
console.log(customerDetails.status()); // idle, pending, fulfilled, or error
console.log(customerDetails.isPending()); // true if loading
console.log(customerDetails.isFulfilled()); // true if successfully loaded
console.log(customerDetails.error()); // error object if failed
// Reload Data
customers.reload();
// Fetch on State Changes
const query = signal('');
const limit = signal(10);
const customers = rxResource({
source: () => ({ query: query(), limit: limit() }), // computation fn or observable
loader: (filter) => customersService.getByFilter(filter),
initialValue: [],
});
// Fetch on Trigger
const trigger$ = new Subject<void>();
const query = signal('');
const customers = rxResource({
source: trigger$, // fetches only when trigger$ emits a value
loader: () => customersService.getByQuery(query()),
});
// Fetch Data
trigger$.next();
// Auto-Polling
const customers = rxResource({
source: timer(0, 30_000), // re-fetch every 30 seconds
loader: () => customersService.getAll(),
initialValue: [],
});
// Change Default Fetching Behavior
const customers = rxResource({
loader: () => customersService.getAll(),
operator: exhaustMap, // switchMap by default
});
// Disable Auto-Fetching
const enabled = signal(false);
const customers = rxResource({
loader: () => customersService.getAll(),
enabled, // boolean or computation fn; true by default
});
// Start Fetching
enabled.set(true);
rxMutation
is a utility for data mutations (create, update, delete) with built-in status tracking.
// Simple Mutation
const deleteCustomer = rxMutation((id: number) => customerService.delete(id));
// Trigger Mutation
deleteCustomer.execute(123);
// Mutation Status
console.log(addToCart.status()); // idle, pending, fulfilled, or error
console.log(addToCart.isPending()); // true if operation is in progress
console.log(addToCart.isFulfilled()); // true if operation completed successfully
console.log(addToCart.value()); // value returned by the mutation
console.log(addToCart.error()); // error object if mutation failed
// Change Default Mutating Behavior
const deleteCustomer = rxMutation({
executor: (id: number) => customerService.delete(id),
operator: concatMap, // mergeMap by default to enable parallel mutations
});
// Handle Success and Error Responses
const updateCustomer = rxMutation({
executor: (customer: Customer) => customerService.update(customer),
onSuccess: ({ value }) => {
toastr.success(`Customer ${value.name} has been successfully updated.`);
router.navigateByUrl('/customers');
},
onError: ({ input, error }) => {
toastr.error(`Customer ${input.name} cannot be updated.`, error);
},
});
This project uses Angular 19 for the frontend and json-server for a mock REST API.
- Node.js (v20)
- pnpm (v10)
git clone https://github.com/yourusername/rx-resource-proto.git
cd rx-resource-proto
pnpm install
The start script runs both the Angular development server and the json-server API:
pnpm start
This will:
- Start the Angular dev server at http://localhost:4200
- Start json-server at http://localhost:3000 with a 1.5s delay to simulate network latency
# Start both servers
pnpm start
# Start only Angular dev server
pnpm start:app
# Start only mock API server
pnpm start:server
# Build production bundle
pnpm build
# Format code with Prettier
pnpm format
The application uses json-server to provide a mock REST API.
The data is stored in db.json
and automatically persisted between restarts.