Services — Long-Lived Singletons

Inject Once, Use Everywhere

Services — Long-Lived Singletons

Services are singleton classes that hold cross-cutting state — auth, router, store, feature flags — and are injected via the `@service` decorator.

4 min read Level 2/5 #ember#services#di
What you'll learn
  • Generate a service with `ember g service`
  • Inject it with the `@service` decorator
  • Use a service from components, routes, and other services

A service is a singleton living on Ember’s dependency-injection container. It is the right place for authentication state, feature flags, the current shopping cart, or any data shared across routes and components.

Define a Service

// app/services/auth.js
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';

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

  get isLoggedIn() {
    return this.user !== null;
  }

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

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

@tracked user means any template reading this.auth.user re-renders when login or logout runs.

Inject It

// app/components/nav-bar.js
import Component from '@glimmer/component';
import { service } from '@ember/service';

export default class NavBar extends Component {
  @service auth;
  @service router;
}

The field name (auth) must match the service file name. Inject under a different name with @service('auth') currentUser.

Built-in Services

Ember ships several services you can inject immediately:

  • @service router — programmatic navigation and URL info
  • @service store — Ember Data store
  • @service session — when using ember-simple-auth
  • @service intl — when using ember-intl
Ember Octane — What's New →