-
Notifications
You must be signed in to change notification settings - Fork 395
Blog post: announce the quarkus-quickjs4j extension #2353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
cescoffier
merged 2 commits into
quarkusio:main
from
EricWittmann:blog/quarkus-quickjs4j
Jul 15, 2025
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,326 @@ | ||
| --- | ||
| layout: post | ||
| title: 'Introducing Quarkus quickjs4j: Seamless JavaScript Integration in Your Quarkus Applications' | ||
| date: 2025-07-14 | ||
| tags: javascript, integration, cdi, build-time | ||
| synopsis: A new Quarkiverse extension that brings JavaScript execution to your Java applications with compile-time code generation and full CDI integration. | ||
| author: ewittman | ||
| --- | ||
| :imagesdir: /assets/images/posts/quickjs4j | ||
| ifdef::env-github,env-browser,env-vscode[:imagesdir: ../assets/images/posts/quickjs4j] | ||
|
|
||
| == Introduction | ||
|
|
||
| We're excited to announce the release of the Quarkus quickjs4j extension, a powerful new addition to the | ||
| Quarkus ecosystem that enables seamless execution of JavaScript code within your Java applications. Built | ||
| on top of the https://github.com/roastedroot/quickjs4j[quickjs4j library], this extension brings the | ||
| lightweight QuickJS JavaScript engine to Quarkus with full CDI integration and compile-time optimizations. | ||
|
|
||
| Whether you need to execute dynamic business logic, implement configurable rules engines, or integrate with | ||
| JavaScript-based algorithms, the Quarkus quickjs4j extension provides a type-safe, performant solution that | ||
| leverages Quarkus's build-time processing capabilities. | ||
|
|
||
| == Why JavaScript in Java Applications? | ||
|
|
||
| Quarkus applications often need to execute dynamic logic that can be modified without recompiling the entire | ||
| application. JavaScript provides an excellent solution for this use case, offering: | ||
|
|
||
| - **Dynamic Configuration**: Update business rules and logic without application restarts | ||
| - **Scripting Capabilities**: Enable power users to customize application behavior | ||
| - **Algorithm Integration**: Leverage existing JavaScript libraries and algorithms | ||
| - **Rapid Prototyping**: Quickly test and iterate on complex logic | ||
|
|
||
| The Quarkus quickjs4j extension makes this integration seamless while maintaining the performance and developer | ||
| experience you expect from Quarkus. | ||
|
|
||
| == Key Features | ||
|
|
||
| === Compile-time Code Generation | ||
| The extension automatically generates CDI beans and proxy classes for your JavaScript interfaces during build | ||
| time, ensuring optimal performance and early error detection. | ||
|
|
||
| === Full CDI Integration | ||
| JavaScript interfaces are first-class citizens in your Quarkus application, injectable like any other CDI bean. | ||
|
|
||
| === Flexible Script Loading | ||
| Load JavaScript files from multiple sources: | ||
| - Classpath resources (recommended for packaged scripts) | ||
cescoffier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - Filesystem paths (for dynamic script loading) | ||
| - URLs (for remote script execution) | ||
| - Anywhere else (using the optional Factory pattern) | ||
|
|
||
| === Context Support | ||
| Pass Java objects as context to JavaScript execution, enabling bidirectional communication between Java and | ||
| JavaScript code. | ||
|
|
||
| === Sandboxed Execution | ||
| QuickJs4J provides a secure and efficient way to execute JavaScript within Java. By running code in a sandbox, | ||
| it ensures: | ||
|
|
||
| - Memory safety – JavaScript runs in isolation, protecting your application from crashes or memory leaks. | ||
| - No system access by default – JavaScript cannot access the filesystem, network, or other sensitive resources unless explicitly allowed. | ||
| - Portability – Being pure Java bytecode, it runs wherever the JVM does. | ||
| - Native-image friendly – Compatible with GraalVM's native-image for fast, lightweight deployments. | ||
|
|
||
| Whether you're embedding scripting capabilities or isolating untrusted code, QuickJs4J is designed for safe and | ||
| seamless integration. | ||
|
|
||
| == Getting Started | ||
|
|
||
| Adding the extension to your Quarkus application is straightforward. First, add the dependency to your `pom.xml`: | ||
|
|
||
| [source,xml] | ||
| ---- | ||
| <dependency> | ||
| <groupId>io.quarkiverse.quickjs4j</groupId> | ||
| <artifactId>quarkus-quickjs4j</artifactId> | ||
| <version>${quarkus-quickjs4j.version}</version> | ||
| </dependency> | ||
| ---- | ||
|
|
||
| You'll also need to enable the annotation processor for code generation: | ||
|
|
||
| [source,xml] | ||
| ---- | ||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-compiler-plugin</artifactId> | ||
| <configuration> | ||
| <annotationProcessorPaths> | ||
| <path> | ||
| <groupId>io.quarkiverse.quickjs4j</groupId> | ||
| <artifactId>quarkus-quickjs4j</artifactId> | ||
| <version>${quarkus-quickjs4j.version}</version> | ||
| </path> | ||
| </annotationProcessorPaths> | ||
| </configuration> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| ---- | ||
|
|
||
| == Simple Example: JavaScript Calculator | ||
|
|
||
| Let's create a simple calculator to demonstrate the extension's capabilities. | ||
|
|
||
| First, define a Java interface annotated with `@ScriptInterface` and `@ScriptImplementation`: | ||
|
|
||
| [source,java] | ||
| ---- | ||
| package com.example; | ||
|
|
||
| import io.roastedroot.quickjs4j.annotations.ScriptInterface; | ||
| import io.quarkiverse.quickjs4j.annotations.ScriptImplementation; | ||
|
|
||
| @ScriptInterface | ||
| @ScriptImplementation(location = "calculator.js") | ||
| public interface Calculator { | ||
| int add(int a, int b); | ||
| int multiply(int a, int b); | ||
| } | ||
| ---- | ||
|
|
||
| Next, create the JavaScript implementation in `src/main/resources/calculator.js`: | ||
|
|
||
| [source,javascript] | ||
| ---- | ||
| function add(a, b) { | ||
| return a + b; | ||
| } | ||
|
|
||
| function multiply(a, b) { | ||
| return a * b; | ||
| } | ||
|
|
||
| export { add, multiply }; | ||
| ---- | ||
|
|
||
| Finally, inject and use the calculator in your Quarkus application: | ||
|
|
||
| [source,java] | ||
| ---- | ||
| package com.example; | ||
|
|
||
| import jakarta.enterprise.context.ApplicationScoped; | ||
| import jakarta.inject.Inject; | ||
|
|
||
| @ApplicationScoped | ||
| public class MathService { | ||
|
|
||
| @Inject | ||
| Calculator calculator; | ||
|
|
||
| public int performCalculation() { | ||
| int sum = calculator.add(5, 3); // Returns 8 | ||
| int product = calculator.multiply(4, 7); // Returns 28 | ||
| double quotient = calculator.divide(10.0, 2.0); // Returns 5.0 | ||
|
|
||
| return sum + product + (int) quotient; | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| That's it! The extension handles all the complexity of JavaScript execution, type conversion, and CDI | ||
| integration behind the scenes. | ||
|
|
||
| == Advanced Features | ||
|
|
||
| === Context Objects for Bidirectional Communication | ||
|
|
||
| A powerful feature of quickjs4j is the ability to provide Java context objects that JavaScript code | ||
| can invoke: | ||
|
|
||
| [source,java] | ||
| ---- | ||
| @ScriptInterface(context = CalculatorContext.class) | ||
| @ScriptImplementation(location = "calculator.js") | ||
| public interface Calculator { | ||
| int add(int a, int b); | ||
| int multiply(int a, int b); | ||
| } | ||
|
|
||
| @ApplicationScoped | ||
| public class CalculatorContext { | ||
EricWittmann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| public void log(String message) { | ||
| System.out.println("Calc>> " + message); | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| Your JavaScript code can then call these Java methods: | ||
|
|
||
| [source,javascript] | ||
| ---- | ||
| function add(a, b) { | ||
| Calculator_Builtins.log(`Adding ${a} + ${b}`); | ||
cescoffier marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return a + b; | ||
| } | ||
|
|
||
| function multiply(a, b) { | ||
| Calculator_Builtins.log(`Multiplying ${a} * ${b}`); | ||
| return a * b; | ||
| } | ||
|
|
||
| export { add, multiply }; | ||
| ---- | ||
|
|
||
| === Factory Pattern for Dynamic Scripts | ||
|
|
||
| For scenarios where you need to load scripts dynamically at runtime, use the factory pattern: | ||
|
|
||
| [source,java] | ||
| ---- | ||
| @ApplicationScoped | ||
| public class DynamicMathService { | ||
|
|
||
| @Inject | ||
| CalculatorContext context; | ||
|
|
||
| @Inject | ||
| ScriptInterfaceFactory<Calculator, CalculatorContext> calculatorFactory; | ||
|
|
||
| public void executeCustomScript() { | ||
| // Load your javascript from some dynamic source | ||
| String scriptContent = loadDynamicScriptContent(); | ||
|
|
||
| // Create calculator instance with dynamic script | ||
| Calculator calculator = calculatorFactory.create(scriptContent, context); | ||
|
|
||
| // Use the calculator | ||
| int result = calculator.add(10, 20); | ||
| System.out.println("Result: " + result); | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| This approach is perfect for applications that need to execute user-provided scripts or | ||
| load scripts from external sources. Note that the execution of the script is fully sandboxed. | ||
| Only the methods exposed by the Context can be invoked from within the script. | ||
|
|
||
| == Error Handling and Debugging | ||
|
|
||
| JavaScript errors are propagated as Java exceptions, making debugging straightforward: | ||
|
|
||
| [source,java] | ||
| ---- | ||
| try { | ||
| double result = calculator.divide(10, 0); | ||
| } catch (RuntimeException e) { | ||
| logger.error("JavaScript execution failed: {}", e.getMessage(), e); | ||
| // Handle the error appropriately | ||
| } | ||
| ---- | ||
|
|
||
| == Build-time Magic | ||
|
|
||
| Behind the scenes, the extension performs build-time code generation, creating: | ||
|
|
||
| 1. **CDI Bean Classes**: `{InterfaceName}_CDI` - Injectable CDI beans | ||
| 2. **Factory Classes**: `{InterfaceName}_Factory` - Injectable factory beans | ||
| 3. **Proxy Classes**: `{InterfaceName}_Proxy` - Generated by quickjs4j | ||
| 4. **Context Builtins**: `{ContextName}_Builtins` - JavaScript-accessible Java methods | ||
|
|
||
| This build-time approach ensures minimal runtime overhead while providing full IDE | ||
| support with code completion and type checking. | ||
|
|
||
| == Performance Considerations | ||
|
|
||
| The QuickJS engine is designed for lightweight, fast JavaScript execution. Combined with | ||
| Quarkus's build-time optimizations, the extension provides: | ||
|
|
||
| - **Fast Startup**: Minimal impact on application startup time | ||
| - **Low Memory Footprint**: Efficient memory usage for JavaScript execution | ||
| - **Native Image Support**: Full compatibility with GraalVM native images | ||
| - **Build-time Validation**: Early detection of interface mismatches and errors | ||
|
|
||
| == Use Cases | ||
|
|
||
| The Quarkus quickjs4j extension is perfect for: | ||
|
|
||
| - **Business Rules Engines**: Implement configurable business logic | ||
| - **Template Processing**: Generate dynamic content with JavaScript templates | ||
| - **Algorithm Integration**: Leverage existing JavaScript algorithms and libraries | ||
| - **User Scripting**: Allow power users to customize application behavior | ||
| - **Configuration Logic**: Implement complex configuration scenarios | ||
|
|
||
| == Current Status and Future Plans | ||
|
|
||
| The extension is currently in **experimental status**, meaning APIs may evolve based on | ||
| community feedback. We're actively working on: | ||
|
|
||
| - Enhanced error reporting and debugging capabilities | ||
| - Performance optimizations | ||
| - Additional JavaScript engine options | ||
| - Improved IDE integration and tooling | ||
|
|
||
| == Getting Involved | ||
|
|
||
| The Quarkus quickjs4j extension is part of the Quarkiverse ecosystem and welcomes community | ||
| contributions. Whether you're interested in: | ||
|
|
||
| - Reporting bugs or requesting features | ||
| - Contributing code improvements | ||
| - Sharing use cases and examples | ||
| - Improving documentation | ||
|
|
||
| Visit our https://github.com/quarkiverse/quarkus-quickjs4j[GitHub repository] to get involved! | ||
|
|
||
| == Conclusion | ||
|
|
||
| The Quarkus quickjs4j extension opens up exciting possibilities for Java developers who need | ||
| to integrate JavaScript execution into their applications. With its compile-time code generation, | ||
| full CDI integration, and flexible script loading options, it provides a powerful yet easy-to-use | ||
| solution for dynamic code execution. | ||
|
|
||
| Try it out and let us know what you think! We're excited to see what the community builds with this | ||
| capability. | ||
|
|
||
| == Links and Resources | ||
|
|
||
| - https://github.com/quarkiverse/quarkus-quickjs4j[Quarkus quickjs4j GitHub Repository] | ||
| - https://github.com/roastedroot/quickjs4j[quickjs4j Library] | ||
| - https://bellard.org/quickjs/[QuickJS JavaScript Engine] | ||
| - https://quarkus.io/extensions/[Quarkus Extensions] | ||
| - https://github.com/quarkiverse[Quarkiverse Hub] | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.