
Angular Http
Standardize Angular HttpClient and httpResource patterns—services, caching, pagination, uploads, and cancellation—in agent-assisted app builds.
Overview
angular-http is an agent skill for the Build phase that teaches Angular HttpClient service layers, httpResource reactivity, and production HTTP patterns including caching, pagination, uploads, and cancellation.
Install
npx skills add https://github.com/analogjs/angular-skills --skill angular-httpWhat is this skill?
- Service-layer pattern with inject(HttpClient) and typed CRUD helpers
- httpResource signals for reactive fetch when a selected ID changes
- Dedicated sections: caching, pagination, file upload, request cancellation
- Testing HTTP section for mocking and verifying client behavior
- Six documented topic areas in the table of contents
- 6 topic areas in the table of contents (service layer, caching, pagination, file upload, cancellation, testing HTTP)
Adoption & trust: 5.1k installs on skills.sh; 592 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Angular app scatters raw HTTP calls across components with no shared caching, cancellation, or test strategy.
Who is it for?
Solo builders adding or refactoring REST integration in Angular 17+ apps who want agent-generated code to match established service-layer conventions.
Skip if: Teams on non-Angular stacks, GraphQL-only clients without HttpClient, or backends where no browser client is being built.
When should I use this skill?
Scaffolding or refactoring Angular API services, httpResource usage, uploads, pagination, caching, or HTTP tests.
What do I get? / Deliverables
After applying the skill, API access lives in injectable services with documented patterns for pagination, uploads, and testable HTTP boundaries.
- Injectable API service classes with typed models
- httpResource or Observable-based data access with cancellation hooks
- HTTP test examples aligned with the testing section
Recommended Skills
Journey fit
HTTP client architecture is implemented while wiring the Angular UI to backend APIs during product construction. Encapsulated service layers and reactive resources belong on the frontend build shelf where API consumption meets components.
How it compares
Angular-specific HttpClient recipes rather than generic fetch snippets or OpenAPI-only server generation.
Common Questions / FAQ
Who is angular-http for?
Indie developers and small teams using Claude Code, Cursor, or Codex to build Angular SPAs or hybrid mobile shells that consume REST APIs.
When should I use angular-http?
During Build (frontend) when creating user services, wiring httpResource to route params, adding file uploads, or writing HTTP unit tests—especially before shipping features that paginate or cache list data.
Is angular-http safe to install?
Review the Security Audits panel on this Prism page and inspect the skill package in your repo before granting agent filesystem or network access to your API code.
SKILL.md
READMESKILL.md - Angular Http
# Angular HTTP Patterns ## Table of Contents - [Service Layer Pattern](#service-layer-pattern) - [Caching Strategies](#caching-strategies) - [Pagination](#pagination) - [File Upload](#file-upload) - [Request Cancellation](#request-cancellation) - [Testing HTTP](#testing-http) ## Service Layer Pattern Encapsulate HTTP logic in services: ```typescript import { Injectable, inject, signal, computed } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { httpResource } from '@angular/common/http'; export interface User { id: string; name: string; email: string; } @Injectable({ providedIn: 'root' }) export class User { private http = inject(HttpClient); private baseUrl = '/api/users'; // Current user ID for reactive fetching private currentUserId = signal<string | null>(null); // Reactive resource that updates when currentUserId changes currentUser = httpResource<User>(() => { const id = this.currentUserId(); return id ? `${this.baseUrl}/${id}` : undefined; }); // Set current user to fetch selectUser(id: string) { this.currentUserId.set(id); } // CRUD operations getAll() { return this.http.get<User[]>(this.baseUrl); } getById(id: string) { return this.http.get<User>(`${this.baseUrl}/${id}`); } create(user: Omit<User, 'id'>) { return this.http.post<User>(this.baseUrl, user); } update(id: string, user: Partial<User>) { return this.http.patch<User>(`${this.baseUrl}/${id}`, user); } delete(id: string) { return this.http.delete<void>(`${this.baseUrl}/${id}`); } } ``` ## Caching Strategies ### Simple In-Memory Cache ```typescript @Injectable({ providedIn: 'root' }) export class CachedUser { private http = inject(HttpClient); private cache = new Map<string, { data: User; timestamp: number }>(); private cacheDuration = 5 * 60 * 1000; // 5 minutes getUser(id: string): Observable<User> { const cached = this.cache.get(id); if (cached && Date.now() - cached.timestamp < this.cacheDuration) { return of(cached.data); } return this.http.get<User>(`/api/users/${id}`).pipe( tap(user => { this.cache.set(id, { data: user, timestamp: Date.now() }); }) ); } invalidateCache(id?: string) { if (id) { this.cache.delete(id); } else { this.cache.clear(); } } } ``` ### Signal-Based Cache ```typescript @Injectable({ providedIn: 'root' }) export class UserCache { private http = inject(HttpClient); // Cache as signal private usersCache = signal<Map<string, User>>(new Map()); // Computed for easy access users = computed(() => Array.from(this.usersCache().values())); getUser(id: string): User | undefined { return this.usersCache().get(id); } async fetchUser(id: string): Promise<User> { const cached = this.getUser(id); if (cached) return cached; const user = await firstValueFrom( this.http.get<User>(`/api/users/${id}`) ); this.usersCache.update(cache => { const newCache = new Map(cache); newCache.set(id, user); return newCache; }); return user; } } ``` ## Pagination ### Paginated Resource ```typescript interface PaginatedResponse<T> { data: T[]; total: number; page: number; pageSize: number; totalPages: number; } @Component({ template: ` @if (usersResource.isLoading()) { <app-spinner /> } @else if (usersResource.hasValue()) { <ul> @for (user of usersResource.value().data; track user.id) { <li>{{ user.name }}</li> } </ul> <div class="pagination"> <button (click)="prevPage()" [disabled]="page() === 1" >Previous</button> <span>Page {{ page() }} of {{ usersResource.value().totalPages }}</span> <button (click)="nextPage()" [disabled]="page() >= usersResource.valu