Using Injection-Js with Express and Typescript

Michael Michailidis
4 min readJan 3, 2020

--

Source Files on GitHub

I wrote this tutorial while using ‘injection-js’ on a Node project. The reason was that this framework was part of the original Angular project, and all the documentation I could find online assumed an Angular (rather than a Node) application.

For this demonstration we will build a simple IMDB search engine using the Google API. I will be skipping most of the UI and Google API parts, such as registering for an API key, to focus on Dependency Injection (DI )using the ‘injection-js’ framework (you can always read the codebase directly for the rest).

To simplify this tutorial even further and keep it focused on DI, we’ll start with a basic project described in detail in this excellent tutorial. Create an npm project and add the basic dependencies

npm init --yes
npm i express ts-node typescript tslib @types/express @types/node

Then include the ‘injection-js’ library itself:

npm i injection-js

Typescript needs a config file (named tsconfig.json) in order to compile:

{
"compilerOptions": {
"experimentalDecorators": true,
"sourceMap": true,
"target": "es6",
"module": "commonjs",
"outDir": "./dist",
"baseUrl": "./src"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}

Notice the experimentalDecorators variable? Injection-js uses decorators to add classes in the DI container. Then, in package.json, add the scripts as:

"scripts": {
"build": "tsc -p .",
"dev": "ts-node src/server.ts",
"start": "npm build && ts-node dist/server.js"
}

The “dev” script will run typescript directly while the build will compile it to javascript and place it in a dist/ folder. For the most part, and since this is tutorial, we’ll be using “dev.”

To start an express server, create a server.ts file and add the basic instructions:

import * as express from 'express';const port = process.env.PORT || 3000;
const app = express();
// Serve the homepage
app.get('/', (req, res) => {
res.sendFile('home.html', { root: __dirname });
});
// Start
app.listen(port, () => {
console.log(`App listening on the http://localhost:${port}`);
});

We will create a basic HTML with a search bar. I included Bootstrap and jQuery, both of which are loaded through a CDN so you don’t need to install anything. I won’t post it here so please look into the project. Now, for the “meat.”

Dependency Injection

We will create a service that makes the request to Google and returns the search results. The service will need the API_TOKEN obtained by Google, and since we might choose to pass this as an environmental variable in needs to be passed as a parameter, so why not inject it? The framework can inject classes without much trouble because it assumes that a class will be instantiated. Our token however is a string, so we must tell the framework how to resolve it. For this we create a token in a separate file called injection-tokens.ts.

import { InjectionToken } from "injection-js";export const API_TOKEN = new InjectionToken<any>('API_TOKEN');

In the SearchService class we can now inject the token in the constructor.

import { Inject } from "injection-js";
import { API_TOKEN } from "./injection-tokens";
class SearchService {
constructor(
@Inject(API_TOKEN) private api_token) {
}
search(term: string) {
// look into the repo for the implementation
}
}

All we need now is to provide for this token so that ‘inject-js’ knows what to do when encountering it.

const api_token = process.env.API_TOKEN || 'AIzaS...';const providers = [
{ provide: API_TOKEN, useValue: api_token },
];
const injector = ReflectiveInjector.resolveAndCreate(providers);
const service = injector.get(SearchService);

What we now want is to create a controller class that is going to use the service. The service therefore needs to be an injectable itself.

import { Injectable } from "injection-js";@Injectable()
class SearchService { ...

We will add a new token to the injection-tokens.ts file.

export const SEARCH_SRV = new InjectionToken<any>('SEARCH_SRV');

and the token to the list of provides, this time using the useClass:

const providers = [
{ provide: API_TOKEN, useValue: api_token },
{ provide: SEARCH_SRV, useClass: SearchService },
];

This suggests to the framework that a new instance of the SearchService must be created every-time the token is injected (rather that a constant value served as in the api_token above).

import { Inject } from "injection-js";
import { SEARCH_SRV } from "./injection-tokens";
class HomeController {
constructor(
@Inject(SEARCH_SRV) private service: SearchService) {
}
search(term: string) {
return this.service.search(term)
}
}

The HomeController will not be injected, and so it can be added to the list of providers directly as a class. The framework will assume that this is equivalent to a ‘useClass’ provider object. The complete providers for this project are:

import { Provider, ReflectiveInjector } from "injection-js";const providers = [
{ provide: API_TOKEN, useValue: api_token },
{ provide: SEARCH_SRV, useClass: SearchService },
HomeController
] as Provider[];
const injector = ReflectiveInjector.resolveAndCreate(providers);
const controller = injector.get(HomeController);

We should have a controller that has a service that has a token, all wired-up for us automatically via DI.

--

--

Michael Michailidis
Michael Michailidis

Written by Michael Michailidis

I am a software engineer working in Node.js + iOS with an eye for blockchain applications!

No responses yet