Working with NPM Registry from Java

image



NPM is a unique package repository from the JavaScript world. Basically here are the JS libraries that can be used in the frontend / in the browser, but there are also server-side libraries for use in node.js and not only. If you are a Java programmer and you need to integrate with the NPM repository, then most likely you have one of the following two cases:



  • You are writing a Web application in one of the Java frameworks and certain NPM packages are required for the client side
  • You have a Java application (for example, for Android), which needs to be able to request dependencies and resources / packages themselves from NPM


Let's see how this can be done in Java.



NPM resources for a web application



You have 2 options:



  • Pack the necessary NPM resources inside your WAR / JAR
  • Use CDN to load required resources at runtime


Packaging NPM Resources in WAR / JAR



First of all, you need to learn more about something like WebJars . It allows NPM (and not only) packages to be "reflected" into the Maven repository. This way you can work with NPM packages as you would with regular Java packages in Maven. For example, in order to include resources from the well-known Boostrap in your WAR, it is enough to add the following dependency to pom.xml:



<dependency>
    <groupId>org.webjars.npm</groupId>
    <artifactId>bootstrap</artifactId>
    <version>4.5.0</version>
</dependency>


WebJars reflects packages from NPM to Maven along with all the necessary dependencies, so that by connecting one JAR by dependencies, all other necessary packages will be connected.

WebJars also has a large set of libraries for different Java frameworks in order to make it easier to work with packed and connected resources. Read more in the documentation .



WebJars is a great tool for any Java Backend developer. But there are also lighter alternatives: packaging the required packages from NPM using Maven plugins. Here's a maybe not complete list:





For example, to include the vue and vuex packages of the required versions using jnpm-maven-plugin, add the following lines to the pom.xml:



<plugin>
    <groupId>org.orienteer.jnpm</groupId>
    <artifactId>jnpm-maven-plugin</artifactId>
    <version>1.0</version>
	<executions>
		<execution>
			<goals>
				<goal>install</goal>
			</goals>
			<configuration>
				<packages>
					<package>vue@2.6.11</package>
					<package>vuex@~3.4.0</package>
				</packages>
			</configuration>
		</execution>
	</executions>
</plugin>


You can use NPM notation to define the range of required versions:



  • Asterisk (* | X | x) - 1. * equivalent to> = 1.0.0 & <2.0.0
  • Tilde (~) - ~ 1.5 is equivalent to> = 1.5.0 & <1.6.0
  • Hyphen (-) - 1.0-2.0 is equivalent to> = 1.0.0 & <= 2.0.0
  • Caret (^) - ^ 0.2.3 is equivalent to> = 0.2.3 & <0.3.0
  • Partial range - 1 is equivalent to 1.X or> = 1.0.0 & <2.0.0
  • Negation -! (1.x) is equivalent to <1.0.0 &> = 2.0.0
  • Difficult - ~ 1.3 | (1.4. * &! = 1.4.5) | ~ 2


Also, you can specify which files to include from packages using includes and excludes. For example, usually an NPM package contains the "compiled" files in the / dist directory. Other files are source files and are unlikely to be needed or useful inside a Java Web application. To include only the contents of the dist / directory, just add the following to the section:



<includes>
  <include>dist/*</include>
</includes>


By default, jnpm-maven-plugin packs resources in exactly the same paths as WebJars. This allows the WebJars libraries mentioned above to be used across different frameworks to access resources. If you need any other specific packaging format, please refer to the documentation .



Using CDN



There are many publicly available CDNs with NPM resources. The most famous and used:





You can also use your own CDN (for example, raised through docker) or even embed the CDN functionality inside your Web-App. For example, add the following servlet to web.xml to enable the JNPM CDN. Edit as needed:



<servlet>
  <servlet-name>CDNServlet</servlet-name>
  <servlet-class>org.orienteer.jnpm.cdn.CDNServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>CDNServlet</servlet-name>
  <url-pattern>/cdn/*</url-pattern>
</servlet-mapping>


After downloading the NPM servlet, the resources will be available via a URL in the following format: http (s): // <domain>: <port> / <path to web application> / cdn / <NPM package> / <path to file>.

For instance:
localhost : 8080/cdn/vue@2.6.11/dist/vue.js




Working with NPM REST API from Java



You can of course use the NPM Registry REST API directly, say via Retrofit . The corresponding documentation will help you with this . But it is more convenient to use the JNPM library , which provides a Java wrapper for this REST API and more.



Include the JNPM Jar in the pom.xml:



<dependency>
    <groupId>org.orienteer.jnpm</groupId>
    <artifactId>jnpm</artifactId>
    <version>1.0</version>
</dependency>


Let's initialize the JNPM API:



JNPMService.configure(JNPMSettings.builder()
  .homeDirectory(Paths.get("/home/myuser/.jnpm")) //
  .downloadDirectory(Paths.get("/tmp")) //
  //   - . 
 	.build());


JNPM API provides 2 options: Synchronous API and Asynchronous API via RXJava. What exactly to use is up to you:



JNPMService jnpmService = JNPMService.instance(); //Synchronous Java API
RxJNPMService rxJnpmService = JNPMService.instance().getRxService() //RXJava API


Usage example:



//   NPM 
System.out.println(JNPMService.instance().getRegistryInfo());
//       VUE
System.out.println(JNPMService.instance().getPackageInfo("vue").getLatest());
//   vue@2.6.11
System.out.println(JNPMService.instance().getVersionInfo("vue", "2.6.11").getDescription());
//      
System.out.println(JNPMService.instance().bestMatch("vue@<2").getVersionAsString());
//   vue@2.6.11    
VersionInfo vueVersion = JNPMService.instance().getVersionInfo("vue", "2.6.11");
vueVersion.downloadTarball().blockingAwait();
System.out.println(vueVersion.getLocalTarball().getAbsolutePath());
// "vue"     
System.out.println(JNPMService.instance().search("vue").getObjects().get(0).getSearchPackage().getDescription());
//       dev    vue 
//            NPM (node_modules/vue  ..)
JNPMService.instance().getRxService()
   .traverse(TraverseDirection.WIDER, TraversalRule.DEV_DEPENDENCIES, "vue")
   .subscribe(t -> {System.out.println(t); t.install(Paths.get("target", "readme"), InstallationStrategy.NPM);});


If you have a specific case that was not described here - please let me know!



All Articles