Let's say you're building a product which is spread across multiple micro-services. Generally the notion of micro-services is to develop independently, still there might be some parts of code which you'll tend to use in different micro-services as it is.
A naive approach would be writing a piece of code in a particular micro-service and then replicating it into all the others. However this would lead to an inconsistency which is caused be using different versions of the code across different micro-services. In other words, maintaining such a code base would be difficult.
Git Submodules?
Using git submodules, you can set up a directory within your codebase with links up to another git repository. You can write the reusable parts of the code in a standalone git repository and use it as a submodule in all your micr-services.
However, many developer just do not prefer using git submodules. There are various reasons for it, starting from poor development experience to struggles while deploying.
Packages are small libraries of code which we install and import in our project to make our lives easier! We do not code everything from scratch. For some stuffs, we prefer the abstraction that a package offers.
A popular package manager and registry among Javascript developers is npm
Public packages help us develop our services. However, we can also create our own packages. We can package our reusable code as a library and use it in any of our micro-services easily.
In this post, I would be demonstrating how to package an ExpressJS middleware and use it in a micro-service
Lets get started!
Like Linus Torvalds said:
Talk is cheap. Show me the code.
Not a fan of following through? Head to the GitHub Repository straightway:
Let's say you're building a product which is spread across multiple micro-services. Generally the notion of micro-services is to develop independently, still there might be some parts of code which you'll tend to use in different micro-services as it is.
A naive approach would be writing a piece of code in a particular micro-service and then replicating it into all the others. However this would lead to an inconsistency which is caused be using different versions of the code across different micro-services. In other words, maintaining such a code base would be difficult.
Git Submodules?
Using git submodules, you can set up a directory within your codebase with links up to another git repository. You can write the reusable parts of the code in a standalone git repository and use it as a submodule…
Bootstrap a NodeJS project as you want and install the dependencies required.
Herein is a basic authentication middleware that verifies a JWT token, decodes it and set's the _id in the request:
// authentication.jsconstjwt=require("jsonwebtoken");module.exports=(req,res,next)=>{if(req.headers.authorization&&req.headers.authorization.startsWith("Bearer")){try{consttoken=req.headers.authorization.split("")[1];constdecoded=jwt.verify(token,"myjwtsecretthatnobodyknows");req.user_id=decoded._id;next();}catch(err){returnres.status(401).json({error:"Token expired or couldn't be verified!"});}}else{returnres.status(401).json({error:"Unauthorized: No token found"});}};
Also adding an index.js file that would help managing multiple middlewares if we plan to extend them!
You'll see that this package doesn't officially have any entry point or an express server initialized. We didn't even install or use the express here. This is because we're just focusing and developing the middlewares as a standalone package and not the entire server here
Finally the package.json file to complete the project:
A few things to look out in the package.json file:
The name field: See how we prefixed the name with @prc. This is done to scope a package. Generally this is set to the name of your organization. Read in details about scoped packages here
The pack script: Used to package the pack the npm library into a tarball. Feel free to set the pack-destination to anywhere in your local system, however take note of it as we'll need that later!
For sake of simplicity, I'll use this script to generate a tarball and install from it. In a professional setting, packages are generally published to a registry (Read more about publishing below)!
Run the following command from the project directory to generate the tarball for the package:
npm run pack
You'll see that the tarball is generated as: prc-middlewares-1.0.0.tgz.
ExpressJS server (Micro-service)
Bootstrap a ExpressJS project. Install express and other required dependencies.
Add the middlewares package as dependencies in the package.json file as shown here:
{"name":"user","version":"1.0.0","description":"","main":"server.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"keywords":[],"author":"","license":"ISC","dependencies":{"@prc/middlewares":"file:../packages/prc-middlewares-2.0.0.tgz","express":"^4.18.1","jsonwebtoken":"^8.5.1"}}
The dependency @prc/middlewares is set as the file path of the packaged tarball file (which was noted in the previous step). This should be a relative path and prefixed with file:
Note: When using a published package, you might need to specify the package version, git repository or package registry details!
Make sure you run install after this:
npm install
Finally the server should look something like this:
// server.jsconstexpress=require("express");const{authentication}=require("@prc/middlewares");constjwt=require("jsonwebtoken");constapp=express();app.use(express.json());app.post("/login",(req,res)=>{const{username,password}=req.body;if(username==="kinjal"&&password==="123456"){consttoken=jwt.sign({_id:4},"myjwtsecretthatnobodyknows");returnres.json({token});}else{returnres.status(401).json({msg:"incorrect credentials!"});}});app.get("/user",authentication,(req,res)=>{returnres.json({msg:"Hello there!",user_id:req.user_id,});});constPORT=process.env.PORT||8080;app.listen(PORT,console.log(`Server is running on port ${PORT} 🚀`));
Note how we require the @prc/middlewares like any other package and use the authentication middleware in the /user route!
Publishing
While developing a bunch of micro-services with multiple developers, packaging libraries locally doesn't solve anything. Instead, developers often prefer versioning and publishing the packages. Packages are generally published to registries as private packages using npm, GitHub or sometimes behind an organization's firewall (like AWS CodeArtifact)