Have you considered using XQRS ?, you can create custom REST endpoints with ease using intuitive Function Annotations much like one would do with JAX-RS or Java Spring REST Services. It can be installed and used with or with-out Gradle.
You can keep your custom logic either in these REST functions, or you could write your custom logic in JavaScript code that you then import/invoke from these functions. XQRS provides you with total flexibility. You can make beautiful REST APIs with Human-Friendly URLs that sit directly on MarkLogic, taking complete control of the URL Path. It's solid as a rock and unit tested to death. It scales with MarkLogic - i.e. you add more MarkLogic e-nodes and you automatically scale your REST servers, it's totally free and open source. The project is actively maintained and support is available on GitHub. If you've got a Swagger/OpenAPI interface file, you can generate a MarkLogic Stub too which could save you a lot of time.
Functions simply become available as REST Services based on the Annotations you place on them - and you can pass query/form parameters, cookies and the request body via the Annotations. Check out this snippet for a sample flavour.
declare
%rest:path("/factory/warehouse/wheel/{$wheel-id}")
%rest:GET
function get-wheel($wheel-id as xs:string) {
fn:doc($wheel-id)
};
declare
%rest:path("/factory/warehouse/wheel/{$wheel-id}")
%rest:PUT("{$doc}")
%xdmp:update
function put-wheel($wheel-id as xs:string, $doc as document-node(element())) {
xdmp:document-insert($wheel-id, $doc, map:entry("collections", "wheels"))
};
declare
%rest:path("/factory/warehouse/wheel")
function list-wheels() {
<wheels>{
fn:collection("wheels")
}</wheels>
};