Defining a Service

Generate, Extend Service, Add Tracked State

Defining a Service

Running ember generate service scaffolds a singleton class. Extend the Service base class and add tracked state.

4 min read Level 2/5 #ember#services#singletons
What you'll learn
  • Generate a service with the ember-cli generator
  • Extend the Service base class
  • Add reactive state with the tracked decorator

A service is a singleton class owned by the application. It’s the canonical place to put cross-cutting state and behavior — auth, websockets, feature flags, a cart, a router wrapper.

Generate

ember generate service auth

This creates app/services/auth.js:

import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';

export default class AuthService extends Service {
  @tracked currentUser = null;

  async login(credentials) {
    const res = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials),
    });
    this.currentUser = await res.json();
  }

  logout() {
    this.currentUser = null;
  }
}

Reactive By Default

Because currentUser is @tracked, anything reading auth.currentUser — templates, getters, other services — updates automatically when it changes.

Lifecycle

Services are instantiated lazily on first lookup and live for the lifetime of the app instance. In tests, each test gets a fresh app instance, so services are reset between tests.

Override willDestroy if you need to clean up (close sockets, unsubscribe, etc.):

willDestroy() {
  this.socket?.close();
  super.willDestroy();
}

Where Services Live

Every service file lives at app/services/<kebab-case-name>.js. The filename matches the lookup name — auth.js'auth'.

Dependency Injection With @service →