Reader Stacks

How to Create and Use a Service in Angular

A service is where shared logic and state belong instead of a component — Angular's dependency injection handles creating and sharing a single instance across every place that needs it.

A service is where shared logic, state, or API access belongs instead of a component — Angular's dependency injection system handles creating (and by default, sharing) a single instance across every component that asks for it.

Generating a service with the CLI

ng generate service services/product
// shorthand
ng g s services/product

A basic service

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class ProductService {
    constructor(private http: HttpClient) {}

    getProducts(): Observable {
        return this.http.get('/api/products');
    }

    getProduct(id: number): Observable {
        return this.http.get(`/api/products/${id}`);
    }
}

Using the service in a component

export class ProductListComponent implements OnInit {
    products: Product[] = [];

    constructor(private productService: ProductService) {}

    ngOnInit() {
        this.productService.getProducts().subscribe(products => {
            this.products = products;
        });
    }
}

Angular's dependency injection resolves ProductService automatically just by declaring it as a constructor parameter type — no manual instantiation with new, and no manual wiring needed beyond the @Injectable decorator on the service itself.

Why providedIn: 'root' matters

providedIn: 'root' registers the service with the application's root injector, making it a singleton shared across the entire app — every component that injects ProductService gets the exact same instance, which is what allows a service to hold shared state (like a cached list of products) that stays consistent across every component using it.

A service holding shared, reactive state

@Injectable({ providedIn: 'root' })
export class CartService {
    private itemsSubject = new BehaviorSubject([]);
    items$ = this.itemsSubject.asObservable();

    addItem(item: CartItem): void {
        const current = this.itemsSubject.value;
        this.itemsSubject.next([...current, item]);
    }

    get itemCount(): number {
        return this.itemsSubject.value.length;
    }
}
// in any component, anywhere in the app
this.cartService.items$.subscribe(items => this.cartItems = items);

A BehaviorSubject-backed service like this is a common lightweight pattern for sharing reactive state (a shopping cart, the current logged-in user) across unrelated components, without needing a full state management library for simpler cases.

Scoping a service to a specific module or component instead of the whole app

@Component({
    selector: 'app-product-editor',
    providers: [DraftService], // a fresh instance created just for this component
})
export class ProductEditorComponent {}

Listing a service in a component's own providers array (instead of using providedIn: 'root') creates a new instance scoped just to that component and its children — the right choice for state that genuinely shouldn't be shared app-wide, like a form's in-progress draft data that should reset each time the component is created fresh.