Custom Functions

Custom functions let you define reusable jq logic and call it from eval: expressions anywhere in your configuration. Functions are written in plain jq def syntax and placed in a .jq file. That file is then loaded as a jq module via $extends or $includes.

For the full syntax of $extends and $includes see the Syntax Reference. For available builtin variables and functions see the Builtins page.

Defining a module

Create a file with a .jq extension. Each function is declared with the standard jq def keyword.

A parameterless function:

greet.jq
def hello_world:
  "Hello, world!";

A parameterized function (jq uses ; to separate parameters):

utils.jq
def port_of($service):
  refexpr(".ports.\($service)");

def url_of($service):
  refexpr(".urls.\($service)");

Functions in a .jq module have full access to jq++ builtins such as ref, refexpr, reftag, parent, and topathexpr. They do not run in a restricted "plain jq only" context, so they can resolve dynamic values directly instead of forcing every such value through explicit parameters.

Loading a module

Reference the .jq file in the $extends or $includes list of your configuration:

{
  "$extends": ["greet.jq"],
  "greeting": "eval:string:greet::hello_world"
}

The file is resolved using the standard search path (current directory first, then JF_PATH). See the Syntax Reference — Search Paths section for details.

Calling a function

Inside an eval: expression, call a custom function using module-qualified syntax:

modulename::functionname

The module name is the filename without the .jq extension. For example, greet.jq becomes the module greet, and utils.jq becomes utils.

input.json — parameterless call
{
  "$extends": ["greet.jq"],
  "greeting": "eval:string:greet::hello_world"
}
input.json — parameterized call
{
  "$extends": ["utils.jq"],
  "apiUrl": "eval:string:utils::url_of(\"api\")"
}

Using jq++ builtins inside functions

Custom functions can call any jq++ builtin. A function wrapping parent to return a path expression:

nav.jq
def current_path:
  parent;
input.json
{
  "$extends": ["nav.jq"],
  "info": {
    "nested": {
      "path": "eval:string:nav::current_path | topathexpr(.)"
    }
  }
}

The expression evaluates to the jq path of the nested object (e.g., .info.nested).

A function can also resolve values from the surrounding jq++ document directly:

funcs.jq
def port_of($service):
  refexpr(".ports.\($service)");

def tag_value:
  reftag("thetag");
input.json
{
  "$extends": ["funcs.jq"],
  "thetag": "tag-value",
  "ports": { "api": 8080 },
  "resolvedPort": "eval:number:funcs::port_of(\"api\")",
  "resolvedTag": "eval:string:funcs::tag_value"
}