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 🙏

© 2024 – Pkg Stats / Ryan Hefner

jservice

v0.3.10

Published

A small and powerful pure javascript DI container

Downloads

37

Readme

JService

Build Status codecov

A small and powerful pure javascript DI container that is non-opinionated, no automatic dependency resolution, and with scoping such as Singleton, Scoped and Transient. Can optionally integrate with any Node web frameworks that supports middleware, like Express, Koa, Fasify, etc.

Manual Resolution

Does NOT require to configure which dependencies to resolve automatically into the function or class, but instead, only a single provider object is passed (that can resolve services). It's like manual resolution, but with cleaner parameter signature.

Integrates with Web Frameworks

Directly infuse web frameworks with dependency injection without changing the middleware's function signature.

Scoping

  • Singleton - services are the same across providers
  • Scoped - services are the same within the provider but different on another instance of provider
  • Transient - services are always different even within the same provider

Declaration & Configuration

Isolate dependency declaration and configuration into a single file. Giving more control and ensure no implicit dependencies are leaked in.

Install

npm install jservice

Basic Usage

Using the core container without a web framework.

var JService = require('jservice')
var DbService = require('./services/database')
var UserService = require('./services/user')

// Create a container
var jservice = JService.create()
// or var jservice = new JService()

// Add add services to container
function registry(services) {
  // DbService is a class or function constructor
  services.singleton(DbService, 'db')
  services.transient(UserService, 'user')
}

// Build services
jservice.build(registry)

// Resolve service via provider
var db = jservice.provider.service('db')
db.Books.find(123).then(book => console.log(book))

Basic Integration Usage

Easily integrate with any web framework using middleware.

var JService = require('jservice')
var express = require('express')
var app = express()
var registry = require('./registry')
var { IncomingMessage } = require('http')

// Create container with registry function
var jservice = JService.create(registry)

// Infuse jservice to express through middleware
app.use(jservice.init(IncomingMessage.prototype))

// Signup endpoint
app.post('/signup', (req, res) => {

  // Get the user service we registered in registry
  var user = req.service('user')

  // Using the service to create new user
  user.createNewUser({
    name: req.body.name,
    password: req.body.password
  }).then(() => res.json({ success: 'Ok' }))

})

// Start server when jservice is ready
jservice.start().then(() => app.listen(3000))

Creating Services

A service can be a class, function, or concrete object. You basically don't need to do or set anything in the service. However, there are some options you can take, like explicitly putting the name and hook to startup events.

Basic ES5 service user.js, with no name and hooks using functional programming (vanilla service).

function User(provider) {
  // Injecting database service
  var db = provider.service('db')
  // Exposed methods
  return {
    createNewUser: function(data) {
      return db.User.create(data)
    }
  }
}
module.exports = User

Basic ES6 service database.js with name and hooks using class.

import mongoose from 'mongoose'

class Database {
  constructor(provider, config) {
    this.URL = config.url
    this.User = mongoose.model(
      'User',
      { name: String, password: String }
    )
  }
  connect(provider) {
    return mongoose.connect(this.URL || 'mongodb://localhost:27017/test')
  }
}

Registering Services

Create a file registry.js and add all the services.

var Database = require('./services/database')
var User = require('./services/user')
// more service ...

module.exports = services => {

  services.singleton(Database, 'db')
  services.transient(User, 'user')
  services.scoped(Foo)
  services.add(Bar) // alias for singleton

  // We can also add a service multiple times but need to set different name
  services.add(Config, 'user-config')
  services.add(Config, 'database-config')
  // more services...

  // Some services can be configured, like this
  services.configure(Database, provider => {
    // Here we get host and port from config
    var { url } = provider.get('database-config')
    // Or, you can also set it here
    // var url = '...'
    return { url }
  })
}

There are several ways to add registry

// 1st option
var jservice = new JService(registry)
// 2nd option
var jservice = JService.create(registry)
// 3rd option
var jservice = new JService()
jservice.build(registry)
// 4th option
var jservice = JService.create()
jservice.build(registry)
jservice.build(moreRegistry)

Service Scoping & Samples

In sample folder, you'll find various web framework integration samples and scope testing.

Notice the output ids generated when getting services. Same ids means the same instance.

  • Singleton services have same id across providers
  • Scoped services have same id within the request but different on subsequent request
  • Transient services are always different (new instance)
GLOBAL SERVICE PROVIDER
Singleton |  plgcGPuZu

SCOPED PROVIDER A (1st request)
Singleton |  plgcGPuZu
Singleton |  plgcGPuZu
Scoped    |  8YG0POVDjR
Scoped    |  8YG0POVDjR
Transient |  ZAoy3AJooc
Transient |  ok1MP0cYir

SCOPED PROVIDER B (2nd request)
Singleton |  plgcGPuZu
Singleton |  plgcGPuZu
Scoped    |  TQPz6T_Suk
Scoped    |  TQPz6T_Suk
Transient |  sPsB8hnC3U
Transient |  8iz9mz8bC9

You check out the sample and run it yourself.

git clone <repo>
npm install
npm run sample

API

Container (JService)

const JService = require('jservice')
const container = JService.create() // Or "new JService()"

Properties

.collection Collection - service storage (add or disable services) .provider Provider - service injector (get services) .isReady Boolean - when start method is done

Methods

.build(registry)

Build up the container. Requires registry function that accepts the services Collection.

container.build(services => {
  // `services` is instance of Collection
  services.add(MyService)
  // ...
})

.init(prototype, opt = {}) | returns middleware

Binds JService to any web frameworks that supports middleware. Check out the samples folder for supported frameworks and sample usage.

const express = require('express')
const { IncomingMessage } = require('http')
const app = express()
app.use(jservice.init(IncomingMessage.prototype))
jservice.start().then(() => app.listen(3000))

.start()

Starts the container, triggering all services that hooks to start event.

.createContainer() | returns new Container

Creates a sub-container that inherits all services from parent container.

const container = JService.create()
const subContainer = container.createContainer()

.createProvider() | return new Provider

Create a scoped provider.

Collection

.singleton(service, name = null, deps = null)

Alias: .add(...)

Adds a singleton service to collection. Can omit name (string) if the service contains static property service = <name>. Can also filter or customize dependencies available for the service using deps (function)

services.singleton(ZooService, 'zoo', provider => {
  const customProvider = provider.create({
    other: provider.get('other'),
    custom: { foo: 'bar' }
  })
  return ZooService(customProvider)
})

// Custom 3rd party vanilla services
servics.add(express, 'express', provider => {
  return express()
})

.transient(service, name = null, deps = null)

Adds a transient mode service.

.scoped(service, name = null, deps = null)

Adds a scoped mode service.

.configure(name, config)

Configure a service. The name can be either string or function service, to identify which service to configure. The config is a function.

services.configure(Database, provider => {
  // Get data from config service
  const config = provider.get('config')
  return {
    host: config.host,
    port: config.port
  }
})

.enable(name, yes = true)

Enable/disable a service.

Provider

.create(deps)

Creates a sub (filtered) provider that only contains dependencies in deps

.service(name)

Alias: .get(name)

Strictly get a service. Throws error if service is not found.

.serviceOrNull(name)

Alias: .getOrNull(name)

Softly get a service. Will NOT throw error, but instead returns null.

Mocking

const { mock } = require('jservice')
// Import service to mock
const User = require('./user.js')
function FakeDatabase() { }

// Mock a provider with fake dependencies
const { provider } = mock(
  [FakeDatabase, 'db']
  // more ...
)

// Create instance of user service to test
new User(provider)

License

MIT License Copyright (c) 2019 RhaldKhein