Skip to content

Angular Example

Demo WebsiteGitHub Source Code

Overview

This example demonstrates how to integrate ApiSorcery with Angular projects. Angular is a comprehensive platform for building web applications with TypeScript, providing powerful features like dependency injection, RxJS observables, and a robust CLI.

Features

  • Type safety: Full TypeScript support with generated interfaces and types
  • RxJS integration: Seamless integration with Angular's reactive programming patterns
  • Dependency injection: Angular services for clean architecture
  • Axios integration: Built-in support for Axios HTTP client with interceptors
  • IntelliSense support: Full IDE support with auto-completion and type checking
  • Tree shaking: Optimized bundle size with ES modules support

Quick Setup

1. Install ApiSorcery

bash
npm install -g autoapi

2. Initialize Configuration

bash
autoapi init -l ts

This creates a .autoapirc.json configuration file:

json
{
  "application": {
    "language": "ts",
    "outputDir": "./src/app/api/auto"
  },
  "services": [
    {
      "code": "demo",
      "token": "72735b33815c4e5c9c2a924a8f4907ef",
      "version": 3,
      "enabled": true,
      "source": "https://your-api.com/swagger.json"
    }
  ]
}

3. Install Dependencies

bash
npm install axios
npm install -D @types/node

4. Generate API Client

bash
autoapi generate

5. Use in Angular

typescript
import { Component, OnInit } from '@angular/core';
import * as ApiUser from '@/app/api/auto/demo/ApiUser';
import type { User } from '@/app/api/auto/demo/model';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
  users: User[] = [];
  loading = false;

  ngOnInit() {
    this.fetchUsers();
  }

  async fetchUsers() {
    this.loading = true;
    try {
      const res = await ApiUser.getUserPaged({
        pagination: {
          page: 1,
          limit: 10,
        },
      });
      this.users = res.results || [];
    } catch (error) {
      console.error('Failed to fetch users:', error);
    } finally {
      this.loading = false;
    }
  }
}

Angular Integration Patterns

Creating an Angular Service

typescript
// services/user.service.ts
import { Injectable } from '@angular/core';
import { Observable, from, BehaviorSubject } from 'rxjs';
import { map, catchError, tap } from 'rxjs/operators';
import * as ApiMain from '@/app/api/auto/demo/ApiMain';
import type { User } from '@/app/api/auto/demo/model';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private usersSubject = new BehaviorSubject<User[]>([]);
  private loadingSubject = new BehaviorSubject<boolean>(false);

  users$ = this.usersSubject.asObservable();
  loading$ = this.loadingSubject.asObservable();

  getUsers(): Observable<User[]> {
    this.loadingSubject.next(true);
    
    return from(ApiMain.getUsers()).pipe(
      map(response => response.data || []),
      tap(users => {
        this.usersSubject.next(users);
        this.loadingSubject.next(false);
      }),
      catchError(error => {
        this.loadingSubject.next(false);
        console.error('Failed to fetch users:', error);
        throw error;
      })
    );
  }

  getUserById(id: string): Observable<User> {
    return from(ApiMain.getUserById(id)).pipe(
      map(response => response.data)
    );
  }

  createUser(userData: Partial<User>): Observable<User> {
    return from(ApiMain.createUser(userData)).pipe(
      map(response => response.data),
      tap(() => this.getUsers().subscribe())
    );
  }
}

Using Service in Component

typescript
// components/user-list/user-list.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { UserService } from '@/app/services/user.service';
import type { User } from '@/app/api/auto/main/model';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit, OnDestroy {
  users: User[] = [];
  loading = false;
  private destroy$ = new Subject<void>();

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.userService.users$
      .pipe(takeUntil(this.destroy$))
      .subscribe(users => this.users = users);

    this.userService.loading$
      .pipe(takeUntil(this.destroy$))
      .subscribe(loading => this.loading = loading);

    this.userService.getUsers().subscribe();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  refresh() {
    this.userService.getUsers().subscribe();
  }
}

Component Template

html
<!-- user-list.component.html -->
<div class="user-list">
  <div *ngIf="loading" class="loading">
    <p>Loading...</p>
  </div>
  
  <ul *ngIf="!loading" class="users">
    <li *ngFor="let user of users; trackBy: trackByUserId">
      {{ user.name }}
    </li>
  </ul>
  
  <button (click)="refresh()" [disabled]="loading">
    Refresh
  </button>
</div>

Using with Angular Signals (Angular 16+)

typescript
import { Component, OnInit, signal, computed } from '@angular/core';
import * as ApiMain from '@/app/api/auto/demo/ApiMain';
import type { User } from '@/app/api/auto/demo/model';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  standalone: true
})
export class UserListComponent implements OnInit {
  users = signal<User[]>([]);
  loading = signal(false);
  error = signal<string | null>(null);
  
  userCount = computed(() => this.users().length);

  ngOnInit() {
    this.fetchUsers();
  }

  async fetchUsers() {
    this.loading.set(true);
    this.error.set(null);
    
    try {
      const response = await ApiMain.getUsers();
      this.users.set(response.data || []);
    } catch (err) {
      this.error.set(err instanceof Error ? err.message : 'Failed to fetch users');
    } finally {
      this.loading.set(false);
    }
  }
}

Best Practices

  1. Type Definitions: Leverage generated TypeScript interfaces for better type safety
  2. Angular Services: Encapsulate API calls in injectable services for reusability
  3. RxJS Operators: Use RxJS operators for data transformation and error handling
  4. Dependency Injection: Utilize Angular's DI system for clean architecture
  5. Unsubscribe Pattern: Always unsubscribe from observables to prevent memory leaks
  6. Error Handling: Implement proper error handling with Angular's error interceptors
  7. Environment Configuration: Use Angular environment files for different API endpoints
  8. Lazy Loading: Implement lazy loading for modules to optimize bundle size