5.0 KiB
5.0 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Implement Smart Route Model Caching | MEDIUM-HIGH | Reduce redundant API calls and improve UX | routes, caching, performance, model |
Implement Smart Route Model Caching
Implement intelligent model caching strategies to reduce redundant API calls and improve user experience.
Incorrect (always fetches fresh data):
// app/routes/post.gjs
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class PostRoute extends Route {
@service store;
model(params) {
// Always makes API call, even if we just loaded this post
return this.store.request({ url: `/posts/${params.post_id}` });
}
<template>
<article>
<h1>{{@model.title}}</h1>
<div>{{@model.content}}</div>
</article>
{{outlet}}
</template>
}
Correct (with smart caching):
// app/routes/post.gjs
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class PostRoute extends Route {
@service store;
model(params) {
// Check cache first
const cached = this.store.cache.peek({
type: 'post',
id: params.post_id,
});
// Return cached if fresh (less than 5 minutes old)
if (cached && this.isCacheFresh(cached)) {
return cached;
}
// Fetch fresh data
return this.store.request({
url: `/posts/${params.post_id}`,
options: { reload: true },
});
}
isCacheFresh(record) {
const cacheTime = record.meta?.cachedAt || 0;
const fiveMinutes = 5 * 60 * 1000;
return Date.now() - cacheTime < fiveMinutes;
}
<template>
<article>
<h1>{{@model.title}}</h1>
<div>{{@model.content}}</div>
</article>
{{outlet}}
</template>
}
Service-based caching layer:
// app/services/post-cache.js
import Service from '@ember/service';
import { service } from '@ember/service';
import { TrackedMap } from 'tracked-built-ins';
export default class PostCacheService extends Service {
@service store;
cache = new TrackedMap();
cacheTimes = new Map();
cacheTimeout = 5 * 60 * 1000; // 5 minutes
async getPost(id, { forceRefresh = false } = {}) {
const now = Date.now();
const cacheTime = this.cacheTimes.get(id) || 0;
const isFresh = now - cacheTime < this.cacheTimeout;
if (!forceRefresh && isFresh && this.cache.has(id)) {
return this.cache.get(id);
}
const post = await this.store.request({ url: `/posts/${id}` });
this.cache.set(id, post);
this.cacheTimes.set(id, now);
return post;
}
invalidate(id) {
this.cache.delete(id);
this.cacheTimes.delete(id);
}
invalidateAll() {
this.cache.clear();
this.cacheTimes.clear();
}
}
// app/routes/post.gjs
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class PostRoute extends Route {
@service postCache;
model(params) {
return this.postCache.getPost(params.post_id);
}
// Refresh data when returning to route
async activate() {
super.activate(...arguments);
const params = this.paramsFor('post');
await this.postCache.getPost(params.post_id, { forceRefresh: true });
}
<template>
<article>
<h1>{{@model.title}}</h1>
<div>{{@model.content}}</div>
</article>
{{outlet}}
</template>
}
Using query params for cache control:
// app/routes/posts.gjs
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class PostsRoute extends Route {
@service store;
queryParams = {
refresh: { refreshModel: true },
};
model(params) {
const options = params.refresh ? { reload: true } : { backgroundReload: true };
return this.store.request({
url: '/posts',
options,
});
}
<template>
<div class="posts">
<button {{on "click" (fn this.refresh)}}>
Refresh
</button>
<ul>
{{#each @model as |post|}}
<li>{{post.title}}</li>
{{/each}}
</ul>
</div>
{{outlet}}
</template>
}
Background refresh pattern:
// app/routes/dashboard.gjs
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class DashboardRoute extends Route {
@service store;
async model() {
// Return cached data immediately
const cached = this.store.cache.peek({ type: 'dashboard' });
// Refresh in background
this.store.request({
url: '/dashboard',
options: { backgroundReload: true },
});
return cached || this.store.request({ url: '/dashboard' });
}
<template>
<div class="dashboard">
<h1>Dashboard</h1>
<div>Stats: {{@model.stats}}</div>
</div>
{{outlet}}
</template>
}
Smart caching reduces server load, improves perceived performance, and provides better offline support while keeping data fresh.
Reference: WarpDrive Caching