Typed REST Resources
$rest wraps a REST backend with a small typed resource client. By default it
uses $http; pass a custom backend when a resource should read from another
data source or compose network and cache behavior.
Exact method signatures live in TypeDoc:
Configure shared defaults
Configure defaults before the application starts when every resource should share the same request behavior.
angular.createModule('demo', []).config({
$rest: {
defaults: {
timeout: 5000,
withCredentials: true,
},
},
});
Create a resource at runtime
Inject $rest anywhere you need a resource client. This keeps controllers and
services focused on the workflow instead of repeating request setup.
class UserRepository {
static $inject = ['$rest'];
private users: ng.RestService<User, number>;
constructor($rest: ng.RestFactory) {
this.users = $rest<User, number>('/api/users', User);
}
listAdmins() {
return this.users.list({ role: 'admin' });
}
getUser(id: number) {
return this.users.get(id);
}
}
Use URI templates
$rest expands RFC 6570 templates before sending a request. Template variables
are taken from the params object you pass to list() or get().
const issues = $rest<Issue>(
'/api/repos/{owner}/{repo}/issues{?labels*}',
Issue,
);
const openBugs = await issues.list({
owner: 'angular-wave',
repo: 'angular.ts',
labels: ['bug', 'ui'],
});
Params that are not consumed by the template are forwarded to $http as query
params.
Map server data to classes
Pass an entity class when the raw response needs normalization, computed properties, or methods.
class Article {
id: number;
title: string;
publishedAt: Date;
constructor(data: any) {
this.id = data.id;
this.title = data.title;
this.publishedAt = new Date(data.published_at);
}
get isPublished() {
return this.publishedAt.getTime() <= Date.now();
}
}
const articles = $rest<Article, number>('/api/articles', Article);
const article = await articles.get(42);
if (article?.isPublished) {
// article is a real Article instance.
}
If you omit the entity class, $rest returns the parsed response data as-is.
Handle writes
create() sends POST, update() sends PUT, and delete() sends DELETE.
The methods intentionally stay close to HTTP semantics so errors and
interceptors still flow through $http.
class ArticleController {
static $inject = ['$rest'];
private articles: ng.RestService<Article, number>;
items: Article[] = [];
constructor($rest: ng.RestFactory) {
this.articles = $rest<Article, number>('/api/articles', Article);
}
async publish(draft: Partial<Article>) {
const created = await this.articles.create(draft as Article);
this.items.unshift(created as Article);
}
async rename(id: number, title: string) {
const updated = await this.articles.update(id, { title });
if (updated) {
this.items = this.items.map((item) =>
item.id === id ? (updated as Article) : item,
);
}
}
async remove(id: number) {
await this.articles.delete(id);
this.items = this.items.filter((item) => item.id !== id);
}
}
get(), create(), and update() return null for an empty response.
delete() resolves with no value after the backend succeeds. Backend failures
reject for every operation, allowing one application error boundary or policy to
handle them consistently.
$rest does not perform framework-property cleanup itself. With the default
HTTP backend, $http deproxies scope payloads before JSON serialization, so
proxy helpers such as _target, _handler, and _proxy do not reach the
server. Generated repeat identity is stored as internal metadata rather than on
your model object, so it is not included in request bodies. Explicit
application-owned properties remain part of the payload.
Use a cached backend
CachedRestBackend wraps a
network backend and an async cache store. The default HTTP backend remains
available through
HttpRestBackend, while cache
storage can be memory, IndexedDB, the Cache API, or any object that implements
RestCacheStore.
const backend = new CachedRestBackend({
network: new HttpRestBackend($http),
cache: appCache,
policy: ({ url }) =>
url.startsWith('/api/catalog') ? 'stale-while-revalidate' : 'network-first',
});
const articles = $rest<Article, number>('/api/articles', Article, { backend });
Supported policy results are cache-first, network-first, and
stale-while-revalidate. Use the strategy option when every request should
use one static result. Writes always go to the network backend first; successful
writes invalidate cached collection and entity keys for the resource.
Cache keys are generated by
CachedRestBackend. A
RestCacheStore receives the
final key string in get(), set(), delete(), and deletePrefix() and
should treat that key as opaque. createRestCacheKey() is an internal REST
module helper, not a top-level namespace API.
Write a custom backend
A custom backend implements
RestBackend. It receives
normalized requests and returns raw response data for
RestService to map.
class IndexedDbRestBackend implements ng.RestBackend {
async request<T>(request: ng.RestRequest): Promise<ng.RestResponse<T>> {
if (request.method === 'GET') {
return { data: (await readFromDb(request.url)) as T };
}
throw new Error(`Unsupported method: ${request.method}`);
}
}
const articles = $rest<Article, number>('/api/articles', Article, {
backend: new IndexedDbRestBackend(),
});
Use HttpRestBackend when the
backend should delegate to $http, and wrap it with
CachedRestBackend when
reads should use one of the cache strategies.
CRUD demo
The demo at /src/services/rest/rest-crud-demo.html uses the Go demo backend
through /api/tasks. It shows list(), get(), create(), update(), and
delete() against a real HTTP endpoint, renders rows with ng-repeat, and
includes a cache strategy toggle for network-first, cache-first, and
stale-while-revalidate.
Related
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.