npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

app-service-container

v3.3.1

Published

A smart container for registering and resolving services with a single page application.

Readme

App Service Container

This package solves the problem of code-splitting large libraries in a single page application. It's a design pattern that involves a bit more structure for how services are "loaded" into your app, but once they have been registered, it becomes trivial to access them without having to think which webpack chunk they belong to. It will help you potentially improve your PWA scores by reducing the initial size of your app by separating your core packages from your other packages.

Getting started

To start using the container, add this package to your project:

yarn add app-service-container

Then we simply create a new instance of the container like so:

import Container from 'app-service-container';

const app = new Container();

export default app;

Once we have the container, we go about registering our services. Let's start with an example of a large package such as Firebase that we only want to use in our "admin" routes.

import Container from 'app-service-container';
import firebase from 'firebase';

const app = new Container();

app.register('firebase', async () => {
  // This is webpack codesplitting, read more here: https://webpack.js.org/guides/code-splitting/
  const firebase = await import('firebase');

  firebase.initializeApp({
    // ...
  });

  return firebase;
});

export default app;

To access our service, we would make a request for it from the container:

import app from './app';

// this resolves the import and registers it in the container.
app.resolve('firebase').then(firebase => {
  // ...
});

// you can also resolve it with a shorter syntax
app('firebase').then(firebase => {
  // ...
});

Once the service has been resolved, it's available statically from the container:

import app from './app';

app('firebase').then(firebase => {
  // it's now simple to access
  console.log(app.firebase);
});

It's a good idea to register all of your services in the container, even if they aren't split.

const router = new VueRouter({
  /* ... */
});
const store = new Vuex.Store({
  /* ... */
});
const vue = new Vue({
  router,
  store,
});

app.register('vue', async () => vue);
app.register('router', async () => router);
app.register('store', async () => store);

Usually you will want to connect a service with other parts of your application. You can register how it should connect in the following way:

import Vue from 'vue';

// Executes the callback when the service is resolved.
app.resolved('firebase', (container) => {
  Vue.prototype.$firebase = container.firebase;
});

The next step would be to separate the registration of services into individual files for better organization. So for our firebase example, we would have the following structure:

src/
  app/
    bootstrap/
      firebase.js
    index.js
  components/
  routes/
  store/
/* src/app/bootstrap/firebase.js */

import firebase from 'firebase';
import Vue from 'vue';

/**
 * This function allows you to register how a service is loaded
 * into the application, and what setup should be done when
 * that service is requested for the first time.
 *
 * @param {Function} register
 * @param {Function} resolved
 */
export default function({ register, resolved }) {
  /**
   * Register your service into the container. This is where
   * you might declare a service to be loaded asynchronously
   * into the container.
   */
  register('firebase', async () => {
    const firebase = await import('firebase');

    firebase.initializeApp({
      // ...credentials
    });

    return firebase;
  });

  /**
   * A service is resolved the first time it is requested
   * from the container. You can register a callback to
   * connect your service with other services once it's
   * resolved.
   */
  resolved('firebase', container => {
    Object.defineProperty(Vue.prototype, '$firebase', {
      get: () => container.firebase,
    });
  });
}
/* src/app/index/js */

import Container from 'app-service-container';
import firebase from './bootstrap/firebase';

const app = new Container();

app.bootstrap(firebase);

export app;

Now our code is easier to reason about where and how things are bootstrapped. Now let's look at why this type of design pattern would be effective in a large Single Page Application.

Example Vue Solution

Imagine our application has a simple group of public pages that we want to load quickly, and only require our core services to function. We also have an admin part of our app, and when visiting these pages, we want to make sure our services are loaded and usable.

The following example shows you how to register a service, register callbacks on resolving that service, and how to use the service once registered.

/* app.js */

import Container from 'app-service-container';
import Vue from 'vue';
import VueRouter from 'vue-router';
import Vuex from 'vuex';
import firebase from 'firebase';

const app = new Container();

const router = new VueRouter({
  /* ... */
});

const store = new Vuex.Store({
  /* ... */
});

const vue = new Vue({
  el: '#app',
  router,
  store,
});

app.register({
  vue: async () => vue,
  router: async () => router,
  store: async () => store,
});

app.register('firebase', async () => {
  const firebase = await import(/* webpackChunkName: "admin" */ 'firebase');

  firebase.initializeApp({
    // ...
  });

  return firebase;
});

app.resolved('firebase', (container) => {
  Object.defineProperty(Vue.prototype, '$firebase', {
    get: () => container.firebase,
  });
});

export default app;
/* routes.js */
import app from './app';

export default [
  /**
   * This is an example route where we want to make
   * sure our firebase service is resolved before
   * a user tries to log in.
   */
  {
    path: '/login',
    beforeEnter(to, from, next) {
      app.resolve('firebase').then(next);
    },
  },
];
/* login-component.js */

export default {
  methods: {
    /**
     * Imagine this is a method on our LoginComponent for
     * signing a user in. Because we know our service is
     * resolved, and we registered how firebase should connect
     * to Vue when resolved, we can use it within our component.
     *
     * @param {String} email
     * @param {String} password
     */
    attemptLogin(email, password) {
      this.$firebase
        .auth()
        .signInWithEmailAndPassword(email, password)
        .then(() => {
          this.$router.push('/admin/dashboard');
        });
    },
  },
};

So to recap the steps involved, we registered our service in the container (including which code chunk it would belong to), registered a callback for how the service should connect with our other packages when resolved, registered that we wanted to resolve our service when hitting a route where the service is needed, and then used the service directly in our component.

Once you register all of your application's core services, you can stop thinking about code splitting and just use them as you are building out features.

License

MIT.