Hi everyone. In this post i'm going to talk about how to use inversifyjs in your project with NodeJS, inversifyjs is an IoC (inversion of control), but inversifyjs has other package called inversify-express-utils this tool is very useful for your project because allow you to use routes through annotations (@httpPost, @httpGet and all http methods) also has annotations that indicate to your file that is a controller (@controller) and more useful annotations.
First step
Download with npm inversifyjs
npm i inversify
and download inversify-express-util
npm i inversify-express-utils
Second step
Create a file preferably into a folder "controllers" and set name "post.controller.ts" you're free to set name you want. I'll give you an example of code below.
import * as express from 'express';
import { interfaces, controller, httpGet, httpPost, request, response } from "inversify-express-utils";
@controller("/posts")
export class PostController implements interfaces.Controller {
@httpGet("/")
public async index (@request() req: express.Request, @response() res: express.Response) {
try {
const posts = await this.postRepository.findAll();
res.status(200).json(posts);
} catch(error) {
res.status(400).json(error);
}
}
}
You see that this class implements from interfaces.Controller this implementation is from the package inversify-express-utils and it is used to the implementation of a controller in the IoC. The annotation @controller('/posts') indicates that our class is a controller and receive a parameter that's the route for that controller.
We typically have a function with 2 or 3 parameters in our routes (request, response, next). Now let's see we have a method "index" with the same structure (req, res) we aren't using next, above our method we use @httpGet('/') this annotation indicates the http verb "GET" and through parameters indicate the endpoint. And our method has the annotations @request and @response in the parameters, these annotations provide us inversify-express-util and binds a method parameter to the request or response object.
Now you think "hey where are you injecting postRepository". Now we're going to do the class for PostRepository.
Third step
import { injectable } from "inversify";
@injectable()
export class PostRepositoryImpl {
findAll() {
//
}
create(post: Post) {
//
}
findById(id: number) {
//
}
update(id: number, post: Post) {
//
}
delete(id: number) {
//
}
}
With inversifyjs you can use the IoC and inject your dependencies. In this class "PostRepository" i recommend you use an interface and implement it. We have simple methods without logic. We use @injectable() and you can inject it where you want, it's important that you use @injectable() above your class.
Now if you want inject your class "PostRepository" in your controller, you can do it of the next form (Advice: it's a good practice pass the dependencies through constructor), add the next constructor and property in your controller.
Fourth step
private postRepository: PostRepositoryImpl;
constructor(@inject(TYPES.PostRepositoryImpl) postRepository: PostRepositoryImpl) {
this.postRepository = postRepository;
}
We inject the dependency using @inject() and in the parameters we pass the symbol that we'll create after.
Now it's necessary to work the IoC of InversifyJS, you need to create two files, the first: types.ts, second: inversify.config.ts.
File type.ts
Fifth step
const TYPES = {
PostRepositoryImpl: Symbol('PostRepositoryImpl'),
};
export default TYPES;
With this file we're creating a symbol for our abstractions. I said that i recommed you use an interface, it's best to the abstraction not only a class.
File inversify.config.ts
Sixth step
import TYPES from './types';
import {Container} from 'inversify';
import { interfaces, TYPE } from 'inversify-express-utils';
import { PostRepositoryImpl } from './repository/Post/PostRepositoryImpl';
const container = new Container();
container.bind<PostRepositoryImpl>(TYPES.PostRepositoryImpl ).to(PostRepositoryImpl).inSingletonScope();
export default container;
In this file we'are saying that for our class PostRepositoryImpl we want to create an instance from the same scope with the pattern Singleton. Using the symbol that we created and we export the instance of container.
And finally we create the app.ts where we'll create an InversifyExpressSeerver/
File app.ts
Seventh step
import "reflect-metadata";
import * as express from 'express';
import container from "./inversify.config";
import { InversifyExpressServer, interfaces, TYPE } from "inversify-express-utils";
import './controllers/PostController';
const app = express();
let server = new InversifyExpressServer(container, null, { rootPath: "/api" }, app);
let appConfigured = server.build();
let serve = appConfigured.listen(process.env.PORT || 3000, () => `App running on ${serve.address().port}`);
With this automatically Inversify create the IoC when we create the instance of "InversifyExpressServer", passing in the parameters our container created and the rootPath for our routes (you can set null) and the app express.
In the last 3 lines we built the server and set on listen the server.
And that's all, i recommed you use an IoC and use interfaces for your code and you can use abstraction. I think it's good practice use those things because may be you want that your code be maintainable and in the future be scalable.
Thank you for reading this post.
Top comments (1)
Github url ?