> ## Documentation Index
> This page is part of the Image and Video APIs product. Fetch the complete documentation index for Image and Video APIs at: https://cloudinary.com/documentation/llms-image-and-video-apis.txt?referrer=docpage and then use it to discover all relevant pages before exploring further.
> If your task extends beyond this product, fetch the top-level index covering all Cloudinary products and topics at: https://cloudinary.com/documentation/llms.txt?referrer=docpage

# Custom functions


Cloudinary supports injecting a custom function into the image transformation pipeline. You can either use a remote function/lambda as your source, run a WebAssembly function from a compiled wasm file uploaded to your product environment, or use a jq filter to select an asset to deliver.

To specify a custom function to call, use the `custom_function` parameter (`fn` in URLs). The parameter accepts an object detailing the function to inject as follows:

Parameter | Description
---|---
`function_type` | The type of function to run, either 'remote', 'pre', 'select', 'refine', 'render' or 'wasm'.
`source` | The source of the custom function, either the public_id of the wasm file, the URL of the remote function, the video rendering parameters, or the jq filter to use.

> **TIP**: Once requested, derived images from custom functions are cached like any other derived images generated by Cloudinary: modifying your custom function will not generate a new image for the same dynamic URL as the URL itself has not changed. If you need to bypass this issue when you change a custom function, we recommend adding a [version](advanced_url_delivery_options#asset_versions) component to the Cloudinary URL.

## WebAssembly functions

Compiled `wasm` files may be uploaded as [raw](upload_parameters#uploading_non_media_files_as_raw_files) and [authenticated](upload_parameters#authenticated_assets) resources to your Cloudinary product environment and then referenced in a custom function. Use the `custom_function` parameter with the `function_type` set to "wasm" (`fn_wasm` in URLs), and the `source` parameter set to the public_id of your compiled wasm file. If the public ID includes slashes, replace the slashes with colons (e.g., `custom/example.wasm` becomes `custom:example.wasm`).

For example, to deliver an image after running the WebAssembly functions located in a compiled wasm file:

1. Upload your compiled `wasm` file to your product environment as an authenticated raw file:

    ```multi
    |ruby 
    Cloudinary::Uploader.upload("quantize.wasm", 
      use_filename: true,
      unique_filename: false,
      type: "authenticated",
      resource_type: "raw")
      
    |php_2
    $cloudinary->uploadApi()->upload("quantize.wasm", [
        "use_filename" => true,
        "unique_filename" => false,
        "type" => "authenticated",
        "resource_type" => "raw"]);

    |python
    cloudinary.uploader.upload("quantize.wasm", 
      use_filename = True,
      unique_filename = False,
      type = "authenticated",
      resource_type = "raw")

    |nodejs
    cloudinary.v2.uploader
    .upload("quantize.wasm", 
      { use_filename: true,
        unique_filename: false,
        type: "authenticated",
        resource_type: "raw" })
    .then(result=>console.log(result)); 
      
    |java
    cloudinary.uploader().upload("quantize.wasm", 
      ObjectUtils.asMap(
        "use_filename", "true",
        "unique_filename", "false",
        "type", "authenticated",
        "resource_type", "raw"));

    |csharp
    var uploadParams = new RawUploadParams(){  // by default, ResourceType is already set to "raw"
      UseFilename = true,
      UniqueFilename = false,
      Type = "authenticated",
      File = new FileDescription(@"quantize.wasm")};
    var uploadResult = cloudinary.Upload(uploadParams);  

    |go
    resp, err := cld.Upload.Upload(ctx, "quantize.wasm", uploader.UploadParams{
		  UseFilename:  api.Bool(true),
      UniqueFilename:  api.Bool(false),
		  Type:         "authenticated",
		  ResourceType: "raw"})

    |cli
    cld uploader upload quantize.wasm use_filename=true unique_filename=false type=authenticated resource_type=raw          
    ```

2. Deliver the `oldman_village_st` image after running the WebAssembly functions located in your now uploaded `quantize.wasm` file:

  ![Quantized image with a custom function](https://res.cloudinary.com/demo/image/upload/$colors_8/fn_wasm:quantize.wasm/oldman_village_st.jpg "thumb:c_scale,w_200")

```nodejs
cloudinary.image("oldman_village_st.jpg", {transformation: [
  {variables: [["$colors", "8"]]},
  {function: "wasm:quantize.wasm"}
  ]})
```

```react
import { set } from "@cloudinary/url-gen/actions/variable";
import { wasm } from "@cloudinary/url-gen/actions/customFunction";

new CloudinaryImage("oldman_village_st.jpg")
  .addVariable(set("colors", 8))
  .customFunction(wasm("quantize.wasm"));
```

```vue
import { set } from "@cloudinary/url-gen/actions/variable";
import { wasm } from "@cloudinary/url-gen/actions/customFunction";

new CloudinaryImage("oldman_village_st.jpg")
  .addVariable(set("colors", 8))
  .customFunction(wasm("quantize.wasm"));
```

```angular
import { set } from "@cloudinary/url-gen/actions/variable";
import { wasm } from "@cloudinary/url-gen/actions/customFunction";

new CloudinaryImage("oldman_village_st.jpg")
  .addVariable(set("colors", 8))
  .customFunction(wasm("quantize.wasm"));
```

```js
import { set } from "@cloudinary/url-gen/actions/variable";
import { wasm } from "@cloudinary/url-gen/actions/customFunction";

new CloudinaryImage("oldman_village_st.jpg")
  .addVariable(set("colors", 8))
  .customFunction(wasm("quantize.wasm"));
```

```python
CloudinaryImage("oldman_village_st.jpg").image(transformation=[
  {'variables': [["$colors", "8"]]},
  {'function': "wasm:quantize.wasm"}
  ])
```

```php
use Cloudinary\Transformation\Variable;
use Cloudinary\Transformation\CustomFunction;

(new ImageTag('oldman_village_st.jpg'))
	->addVariable(Variable::set("colors",8))
	->customFunction(CustomFunction::wasm("quantize.wasm"));
```

```java
cloudinary.url().transformation(new Transformation()
  .variables(variable("$colors","8")).chain()
  .function("wasm:quantize.wasm")).imageTag("oldman_village_st.jpg");
```

```ruby
cl_image_tag("oldman_village_st.jpg", transformation: [
  {variables: [["$colors", "8"]]},
  {function: "wasm:quantize.wasm"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Variables("$colors", 8).Chain()
  .Function("wasm:quantize.wasm")).BuildImageTag("oldman_village_st.jpg")
```

```dart
cloudinary.image('oldman_village_st.jpg').transformation(Transformation()
	.addVariable(Variable.set("colors",8))
	.customFunction(CustomFunction.wasm("quantize.wasm")));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .chain()
  .setFunction("wasm:quantize.wasm")).generate("oldman_village_st.jpg")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .variables(variable("$colors","8")).chain()
  .function("wasm:quantize.wasm")).generate("oldman_village_st.jpg");
```

```flutter
cloudinary.image('oldman_village_st.jpg').transformation(Transformation()
	.addVariable(Variable.set("colors",8))
	.customFunction(CustomFunction.wasm("quantize.wasm")));
```

```kotlin
cloudinary.image {
	publicId("oldman_village_st.jpg")
	 addVariable(Variable.set("colors",8))
	 customFunction(CustomFunction.wasm("quantize.wasm")) 
}.generate()
```

```jquery
$.cloudinary.image("oldman_village_st.jpg", {transformation: [
  {variables: [["$colors", "8"]]},
  {function: "wasm:quantize.wasm"}
  ]})
```

```react_native
import { set } from "@cloudinary/url-gen/actions/variable";
import { wasm } from "@cloudinary/url-gen/actions/customFunction";

new CloudinaryImage("oldman_village_st.jpg")
  .addVariable(set("colors", 8))
  .customFunction(wasm("quantize.wasm"));
```

### WebAssembly contract

> **NOTE**:
>
> The samples below are given in the **Rust** programming language which is then compiled to a wasm target - but this can be achieved with any language that compiles to `.wasm`.

Your wasm file should be compiled for a server environment (not JavaScript), and the WebAssembly functions in your compiled wasm file need to comply with a specific interface - you must provide the following 3 public functions:

#### 1. alloc

Your `alloc` method needs to allocate memory according to the size given and then return a pointer to the allocated memory.

```
    alloc(size: usize) -> *mut u8
```

#### 2. dealloc

Your `dealloc` method should deallocate memory at the given pointer, according to the size given.

```
    dealloc(ptr: *mut u8, cap: usize)
```

#### 3. transform

Your `transform` method should include the code you want to perform the actual transformation of the image. The method receives the image width, image height, a pointer to the pixel buffer (of size = width x height x 4) where the pixel scheme is guaranteed to be RGBA interleaved, a pointer to the metadata, and the metadata size.

Metadata is given as a JSON structure which contains:

```
{
  current_page: [Integer],
  variables: [Map of string keys to string values]
}
```

* **current_page** - The current page in a multi-paged asset.
* **variables** - Any [user-defined variables](user_defined_variables#user_defined_variables_overview) specified as part of the transformation.

You can pass files to WebAssembly functions as base64 encoded strings using [file reference variables](user_defined_variables#file_reference_variables), as shown in this [example](user_defined_variables#variable_reference).
  
The `transform` function must return a pointer to the output buffer that contains the following information: *width* as a 32 bit BigEndian, followed by the *height* as a 32 bit BigEndian, followed by the *image pixel buffer* (RGBA scheme again).

```
    transform(width: u32, height: u32, image_ptr: *mut u8, meta_ptr: *mut u8, meta_size: usize) -> u32
```

### WebAssembly example

The following example applies a blur effect to an image:

```
#![feature(exact_chunks)]

#[macro_use]
extern crate serde_derive;
extern crate image;
extern crate serde;
extern crate serde_json;
extern crate byteorder;

use std::mem;
use byteorder::{ WriteBytesExt, BigEndian};
use image::{RgbaImage, imageops};

#[derive(Deserialize, Debug)]
struct Metadata {
    variables: Option<std::collections::HashMap<String, i32>>,
}

#[no_mangle]
pub extern "C" fn alloc(size: usize) -> *mut u8 {
    let mut buf = Vec::<u8>::with_capacity(size);
    let ptr = buf.as_mut_ptr();
    mem::forget(buf);
    return ptr;
}

#[no_mangle]
pub extern "C" fn dealloc(ptr: *mut u8, cap: usize) {
    unsafe  {
      let _buf = Vec::from_raw_parts(ptr, 0, cap);
    }
}

#[no_mangle]
pub extern "C" fn transform(width: u32, height: u32, image_ptr: *mut u8, meta_ptr: *mut u8, meta_size: usize) -> u32 {
  let size = (width * height * 4) as usize;
  let bytes = unsafe {Vec::from_raw_parts(image_ptr, size, size)};
  let meta_bytes = unsafe {Vec::from_raw_parts(meta_ptr, meta_size, meta_size)};
  let metadata: Metadata = serde_json::from_slice(&meta_bytes).expect("Failed to deserialize metadata json");
  host_trace(format!("{:?}", metadata));
  let (out_w, out_h, mut out_buffer) = blur(width, height, bytes, metadata);
  let mut dims = vec![];
  let _ = dims.write_u32::<BigEndian>(out_w);
  let _ = dims.write_u32::<BigEndian>(out_h);
  dims.append(&mut out_buffer);
  let out_buffer = dims;
  let out_ptr = out_buffer.as_ptr() as u32;
  mem::forget(out_buffer);
  out_ptr  
}
fn host_trace(x: String) {
  let buf = x.into_bytes();
  let length = buf.len();
  let ptr = buf.as_ptr();
  unsafe { trace(ptr as u32, length as u32) }
}
extern "C" {
  pub fn trace(x: u32, length: u32);
}
fn blur(width: u32, height: u32, bytes: Vec<u8>, _metadata: Metadata) -> (u32, u32, Vec<u8>) {
  let ref img = RgbaImage::from_raw(width, height, bytes).unwrap();
  let subimg = imageops::blur(img, 2.5);
  let out_w = subimg.width();
  let out_h = subimg.height();
  let out_buffer = subimg.into_raw();
  (out_w, out_h, out_buffer)
}
```

## Remote functions

You can call a remote function/lambda as part of the transformation chain. The remote function receives an input image file (PNG) along with metadata, and must return an image file (optionally along with metadata also). Use the `custom_function` parameter with the `function_type` set to "remote" (`fn_remote` in URLs), and the `source` parameter set to the URL of the custom function. The delivery URL also needs to be [signed](control_access_to_media#enforcement_mechanism_signed_delivery_urls), which means also adding the `sign_url` parameter set to "true" to the SDK method. 

> **NOTE**: The image is sent in the PNG format to the remote function, even if the uploaded image is in a different format.

For example, to deliver the 'sample' image after running the remote function located at 'https://my.example.custom/function':

```multi
|ruby
cl_image_tag("sample.jpg", 
  sign_url: true,
  custom_function: {
    function_type: "remote", 
    source: "https://my.example.custom/function"})

|php_2
ImageTag::fromParams("sample.jpg", [
  "sign_url" => true,
  "custom_function" => [
    "function_type" => "remote", 
    "source" => "https://my.example.custom/function"]])

|python
CloudinaryImage("sample.jpg").image(
  sign_url = True,
  custom_function = {
    "function_type" : "remote", 
    "source" : "https://my.example.custom/function"})

|nodejs
cloudinary.image("sample.jpg", {
  sign_url: true,
  custom_function:{
    function_type: "remote", 
    source: "https://my.example.custom/function"}})

|java
cloudinary.url().transformation(new Transformation()
    .signed(true)
    .customFunction(remote("https://my.example.custom/function")))
  .imageTag("sample.jpg");

|csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
    .Signed(true)
    .CustomFunction(CustomFunction.Remote("https://my.example.custom/function")))
  .BuildImageTag("sample.jpg")

|go
myWasm, err := cld.Media("sample.jpg")
myWasm.Transformation = "fn_remote:aHR0cHM6Ly9teS5leGFtcGxlLmN1c3RvbS9mdW5jdGlvbg=="
myWasm.Config.URL.SignURL = true 
url, err := myWasm.String()
```

The code generates a URL similar to:

```html
https://res.cloudinary.com/demo/image/upload/s--89bc7b34--/fn_remote:aHR0cHM6Ly9teS5leGFtcGxlLmN1c3RvbS9mdW5jdGlvbg==/sample.jpg
```

> **NOTE**: The Cloudinary SDKs automatically generate a base64 encoded URL from the source parameter: you will need to supply the source URL in base64 with padding if you generate the delivery URL in your own code.

### Request structure

The URL of your remote function receives the following information in the HTTP POST request from Cloudinary:

**Request headers**:  

* `X-Cld-Timestamp` - an integer value representing the time the request was sent in Unix time.  
* `X-Cld-Signature` - a [signature](signatures) string for verification: the string is an SHA512 Hex-Digest of the timestamp + your API secret. 

**Request body**:  

* `file` - the (binary) image file.  
* `metadata` - a JSON structure that includes the current_page, tags, coordinates (each key represents a coordinate source) and [variables](user_defined_variables#user_defined_variables_overview) (key-value pairs representing variable names and values). For example:

    ```json
    {
      "current_page": 1,
      "tags": [],
      "coordinates": {
        "eyedea": {
          "coords": [],
          "kind": "eyedea",
          "on_original": true,
          "failed": false
        }
      },
      "variables": {
        "z": 5,
        "foo": 10
      }
    }
    ```

### Response structure

Your response should include the image Content-Type in the header and the image data in the body. For example, if using API Gateway to host your remote function then the response would be similar to:

```json
{
  "statusCode": 200,
  "headers": {
    "Content-Type": "image/jpeg",
    "Content-Length": "result.length"
  },
  "isBase64Encoded": true,             // base64 is restriction imposed by API Gateway
  "body": "result.toString('base64')"   // image data encoded as base64 with padding
}
```

The `body` of your response can contain just the image data or you can include new metadata as follows:

1. Start the response body with the string 'CLDB'.
2. Add 4 bytes (BigEndian) describing the byte length of the result image buffer.
3. Add the image buffer.
4. Add 4 bytes (BigEndian) describing the byte length of the result metadata JSON buffer.
5. Add the metadata buffer.

```
CLDB + length_of_image_buffer + image_buffer + length_of_metadata buffer + metadata_buffer
```

### Sample remote function

The following code is an example of a remote function that is hosted on AWS lambda behind an API gateway and is written in JavaScript. The function resizes an image to a width of 314px and adds a text overlay of the current date (subsequent requests will retrieve this image from the CDN cache and the date will not change). It also returns new metadata:

```js
const im = require('imagemagick');
const multipart = require('aws-lambda-multipart-parser');
const fs = require('fs');
const fail = (message) => {
    console.log(message);
    throw new Error(message);
};
const perform = (operation, args) => new Promise((resolve, reject) => im[operation](args, (err, res) => {
    if (err) {
        console.log(`${operation} operation failed:`, err);
        reject(err);
    } else {
        console.log(`${operation} completed successfully`);
        resolve(res);
    }
}));
const postProcessResource = (resource, fn) => {
    let ret = null;
    if (resource) {
        if (fn) {
            ret = fn(resource);
        }
        try {
            fs.unlinkSync(resource);
        } catch (err) {
            // Ignore
        }
    }
    return ret;
};
const transform = async (file) => {
    // current time as string
    const date = (new Date()).toDateString();
    // transformation in imagemagick: resize to 314px, overlay text at x=5px, y=20px.
    const customArgs = ['-resize', '314x', '-fill', 'blue', '-draw', `text 5,20 '${date}'`];
    // prepare input and output files
    let inputFile = null;
    let outputFile = null;
    inputFile = `/tmp/inputFile`;
    fs.writeFileSync(inputFile, file);
    customArgs.unshift(inputFile);
    outputFile = `/tmp/outputFile.jpg`;
    customArgs.push(outputFile);
    // actual conversion
    const output = await perform('convert', customArgs);
    postProcessResource(inputFile);
    if (outputFile) {
        return postProcessResource(outputFile, (file) => new Buffer(fs.readFileSync(file)));
    }
    // Return the command line output as a debugging aid
    return output;
};
exports.handler = async (event, context, callback) => {
    const parsedRequest = multipart.parse(event, false);
    const file = parsedRequest.file.content;
    return transform(file).then((result) => {
      // return the image and new metadata.   
      if (event.queryStringParameters && event.queryStringParameters.cldb) {
          const bodyLengthBuf = new Buffer(4);
          const bodyLength = result.length;
          bodyLengthBuf.writeUInt32BE(bodyLength);
          const metadata = Buffer.from(JSON.stringify({"coordinates": {"custom": [[45,57,100,120]]}}));
          const metadataLengthBuf = new Buffer(4);
          const metadataLength = metadata.length;
          metadataLengthBuf.writeUInt32BE(metadataLength);
          result = Buffer.concat([Buffer.from('CLDB'), bodyLengthBuf, result, metadataLengthBuf, metadata], 3*4 + metadataLength + bodyLength);
      }
      callback(null, {
        statusCode: 200,
        headers: { 'Content-Type': 'image/jpeg', 'Content-Length': result.length},
        isBase64Encoded: true,
        body: result.toString('base64')
      });
    }).catch((error) => {
      callback(null, {
        statusCode: 502,
        headers:    { 'Content-Type': 'application/json' },
        body:    `{"error": "Error manipulating image ${error}"}`
      });
    });
};
```


## Preprocessing custom functions (for remote functions)

You can insert your custom function earlier in the transformation chain, before Cloudinary does any processing whatsoever on the image. Whereas the remote function option described above is sent an image in PNG format, a preprocessing remote function is sent the original image file, as uploaded to Cloudinary. For example, you can upload images in a format Cloudinary does not support for transformations and use a custom function to return an image format that Cloudinary does support. 

Only **Remote Functions** are supported for preprocessing as described above, except for the following differences:

1. Use the `custom_pre_function` parameter (`fn_pre` in URLs) to call the custom function to preprocess. The parameter accepts the same type of object as the `custom_function` parameter, detailing the `function_type` ('remote') and `source`.
2. The preprocessing function is sent the **original unaltered image**, plus any defined [variables](user_defined_variables#user_defined_variables_overview), and must return an image in a format that Cloudinary [supports for transformations](image_format_support).
3. Any other Cloudinary transformations given are applied to the result of the preprocessing function: the `custom_pre_function` parameter is applied first, no matter its location in the transformation chain.

For example, to deliver the 'sample' image after preprocessing the remote function located at 'https://my.preprocess.custom/function':

```multi
|ruby
cl_image_tag("sample.jpg", 
  sign_url: true,
  custom_pre_function: {
    function_type: "remote", 
    source: "https://my.preprocess.custom/function"})

|php_2
ImageTag::fromParams("sample.jpg", [
  "sign_url" => true,
  "custom_pre_function" => [
    "function_type" => "remote", 
    "source" => "https://my.preprocess.custom/function"]])

|python
CloudinaryImage("sample.jpg").image(
  sign_url = True,
  custom_pre_function = {
    "function_type" : "remote", 
    "source" : "https://my.preprocess.custom/function"})

|nodejs
cloudinary.image("sample.jpg", {
  sign_url: true,
  custom_pre_function:{
    function_type: "remote", 
    source: "https://my.preprocess.custom/function"}})

|java
cloudinary.url().transformation(new Transformation()
    .signed(true)
    .customPreFunction(remote("https://my.preprocess.custom/function")))
  .imageTag("sample.jpg");

|csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
    .Signed(true)
    .customPreFunction(CustomFunction.Remote("https://my.preprocess.custom/function")))
  .BuildImageTag("sample.jpg")
```

The code generates a URL similar to:

```html
https://res.cloudinary.com/demo/image/upload/s--994c2b72--/fn_pre:remote:aHR0cHM6Ly9teS5wcmVwcm9jZXNzLmN1c3RvbS9mdW5jdGlvbg==/sample.jpg
```

## Select custom functions 

> **NOTE**: Currently only URL syntax is supported - not supported by the Cloudinary SDKs.

The `select` custom function (`fn_select` in URLs) can be used to deliver assets based on filters using tags and structured metadata.  This feature can be used for eCommerce use cases such as:

* Assigning tags to all assets required for a 'product ID' and delivering the main image or video (aka the Hero Image) on Product listing pages (PLPs).
* Displaying images based on expected position in a product gallery on Product Detail Pages (PDPs).  Here, the expected position can be assigned using SMD.
* Displaying alternate images (or videos) on PLPs  when the user hovers on a product tile.
* Delivering assets using complex logic based on the existence of Structured Metadata.   

The general URL syntax takes the form:

```
https://res.cloudinary.com/{cloud}/{resource_type}/list/fn_select:jq:{jq_filter}/{transformations}/{tag}.json

```

Where:

* `cloud` - your Cloudinary product environment's cloud name.
* `resource_type` - the type of resource to deliver, `image` or `video`.
* `jq_filter` - the jq filter to use for selecting the asset.
* `transformations` - (optional) any transformations to apply to the selected asset.
* `tag` - the tag for generating the list of assets.

Under the hood, the jq filter is applied to the JSON response from a client-side call to generate a [list of all assets](list_assets#client_side_asset_lists) with the same tag. The jq filter would most commonly select a single public_id based on the values of [structured metadata](structured_metadata) saved with the assets, but the filter can select based on any of the resource properties returned in the response.

An example of a JSON response snippet, listing assets with the same tag ('samples'):

```json
// example partial response generated from: 
// https://res.cloudinary.com/demo/image/list/samples.json
{
  "resources": [
    {
      "public_id": "cld-sample",
      ...
      "metadata": [
        {
          "external_id": "product-gallery-position",
          "label": "product-gallery-position",
          "type": "integer",
          "value": 1
        },
        {
          "external_id": "product-hero-image",
          "label": "product-hero-image",
          "type": "string",
          "value": "yes"
        }
      ]
    },
    {
      "public_id": "cld-sample-5",
      ...
      "metadata": [
        {
          "external_id": "product-gallery-position",
          "label": "product-gallery-position",
          "type": "integer",
          "value": 2
        },
        {
          "external_id": "product-hero-image",
          "label": "product-hero-image",
          "type": "string",
          "value": "no"
        }
      ]
    }
  ],
}
```

### jq filters

You select the asset by using a [jq](https://stedolan.github.io/jq/) filter to return a single public ID which will then be delivered to the browser, along with any necessary transformations.  jq is a popular command line parser for JSON, and you can create complex filters to fit your needs.

An example **jq filter** to select an asset where the value of its 'product-hero-image' metadata field is 'yes':

```
first(.[]|if has("metadata") then select(any(.metadata[]; .external_id == "product-hero-image" and .value == "yes")) else empty end)
```

The filter must also be URL safe, so we need to convert it using a 3rd party library or tools such as [URL Encode and Decode - Online](https://www.urlencoder.org/) to get:

```
first%28.%5B%5D%7Cif%20has%28%22metadata%22%29%20then%20select%28any%28.metadata%5B%5D%3B%20.external_id%20%3D%3D%20%22product-hero-image%22%20and%20.value%20%3D%3D%20%22yes%22%29%29%20else%20empty%20end%29
```

Putting all the above in a single URL:

```
https://res.cloudinary.com/demo/image/list/fn_select:jq:first%28.%5B%5D%7Cif%20has%28%22metadata%22%29%20then%20select%28any%28.metadata%5B%5D%3B%20.external_id%20%3D%3D%20%22product-hero-image%22%20and%20.value%20%3D%3D%20%22yes%22%29%29%20else%20empty%20end%29/samples.json
```

![Select function used to return an image](https://res.cloudinary.com/demo/image/list/fn_select:jq:first%28.%5B%5D%7Cif%20has%28%22metadata%22%29%20then%20select%28any%28.metadata%5B%5D%3B%20.external_id%20%3D%3D%20%22product-hero-image%22%20and%20.value%20%3D%3D%20%22yes%22%29%29%20else%20empty%20end%29/c_scale,h_200/samples.json "with_code:false, with_url:false")

Note that running the jq filter above on the example response, returns the asset with a public_id of `cld-sample`.

#### Select base64 alternative

Instead of specifying the jq filter as a URL safe string, the select custom function also supports specifying the filter in base64 (url-safe variant), that not only makes the filter URL safe, but also pretty since there won't be any special characters.  The syntax is similar to the above, except for using the parameter as `fn_select:jqb64` to indicate that it's a base64 encoded filter. 

For example:  

![Select base64 function](https://res.cloudinary.com/demo/image/list/fn_select:jqb64:Zmlyc3QoLltdfGlmIGhhcygibWV0YWRhdGEiKSB0aGVuIHNlbGVjdChhbnkoLm1ldGFkYXRhW107IC5leHRlcm5hbF9pZCA9PSAicHJvZHVjdC1oZXJvLWltYWdlIiBhbmQgLnZhbHVlID09ICJ5ZXMiKSkgZWxzZSBlbXB0eSBlbmQp/samples.json "thumb:c_scale,h_200, with_code:false")

### JavaScript filters

You can use a JavaScript filter to select the asset to deliver, along with any necessary transformations. The filter is applied to the JSON response from a client-side call to generate a [list of all assets](list_assets#client_side_asset_lists) with the same tag. The filter would most commonly select a single asset based on the values of [structured metadata](structured_metadata) saved with the assets, but the filter can select based on any of the resource properties returned in the response.

The JavaScript filter should return details of the asset to deliver as an object, for example:

```js
{ "public_id": "sample", "resource_type": "image", "type": "upload" }
```

JavaScript files may be uploaded as [raw](upload_parameters#uploading_non_media_files_as_raw_files) [authenticated](upload_parameters#authenticated_assets) assets to your Cloudinary product environment and then referenced in a custom function. Use the `custom_function` parameter with the `function_type` set to `select:js` (`fn_select:js` in URLs), and the `source` parameter set to the public ID of your JavaScript file. If the public ID includes slashes, replace the slashes with colons (e.g., `custom/example.js` becomes `custom:example.js`).

For example, to deliver an image based on the result of a filter located in a JavaScript file that runs on the JSON response from a list of assets with the 'samples' tag:

1. Upload your JavaScript file to your product environment as an authenticated raw file:

    ```multi
    |ruby 
    Cloudinary::Uploader.upload("my_example.js", 
      use_filename: true,
      unique_filename: false,
      type: "authenticated",
      resource_type: "raw")
      
    |php_2
    $cloudinary->uploadApi()->upload("my_example.js", [
        "use_filename" => true,
        "unique_filename" => false,
        "type" => "authenticated",
        "resource_type" => "raw"]);

    |python
    cloudinary.uploader.upload("my_example.js", 
      use_filename = True,
      unique_filename = False,
      type = "authenticated",
      resource_type = "raw")

    |nodejs
    cloudinary.v2.uploader
    .upload("my_example.js", 
      { use_filename: true,
        unique_filename: false,
        type: "authenticated",
        resource_type: "raw" })
    .then(result=>console.log(result)); 
      
    |java
    cloudinary.uploader().upload("my_example.js", 
      ObjectUtils.asMap(
        "use_filename", "true",
        "unique_filename", "false",
        "type", "authenticated",
        "resource_type", "raw"));

    |csharp
    var uploadParams = new RawUploadParams(){  // by default, ResourceType is already set to "raw"
      UseFilename = true,
      UniqueFilename = false,
      Type = "authenticated",
      File = new FileDescription(@"my_example.js")};
    var uploadResult = cloudinary.Upload(uploadParams);  

    |go
    resp, err := cld.Upload.Upload(ctx, "my_example.js", uploader.UploadParams{
		  UseFilename:  api.Bool(true),
      UniqueFilename:  api.Bool(false),
		  Type:         "authenticated",
		  ResourceType: "raw"})

    |cli
    cld uploader upload my_example.js use_filename=true unique_filename=false type=authenticated resource_type=raw          
    ```

2. Deliver the image after running the select filter located in your now uploaded `my_example.js` file on the JSON response from a list of assets with the 'samples' tag:
  
    ![Selected sample file](https://res.cloudinary.com/demo/image/list/fn_select:js:my_example.js/samples.json)

```nodejs
cloudinary.image("samples.json", {function: "select:js:my_example.js", type: "list"})
```

```react
new CloudinaryMedia("samples.json")
  .addTransformation("fn_select:js:my_example.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```vue
new CloudinaryMedia("samples.json")
  .addTransformation("fn_select:js:my_example.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```angular
new CloudinaryMedia("samples.json")
  .addTransformation("fn_select:js:my_example.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```js
new CloudinaryMedia("samples.json")
  .addTransformation("fn_select:js:my_example.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```python
CloudinaryImage("samples.json").image(function="select:js:my_example.js", type="list")
```

```php
(new Media('samples.json'))
	->addTransformation("fn_select:js:my_example.js")
	->deliveryType("list")
	->assetType("image");
```

```java
cloudinary.url().transformation(new Transformation().function("select:js:my_example.js")).type("list").imageTag("samples.json");
```

```ruby
cl_image_tag("samples.json", function: "select:js:my_example.js", type: "list")
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().Function("select:js:my_example.js")).Action("list").BuildImageTag("samples.json")
```

```dart
cloudinary.media('samples.json').transformation(Transformation()
	.addTransformation("fn_select:js:my_example.js")
	.setDeliveryType("list")
	.setAssetType("image"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setType( "list").setTransformation(CLDTransformation().setFunction("select:js:my_example.js")).generate("samples.json")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().function("select:js:my_example.js")).type("list").generate("samples.json");
```

```flutter
cloudinary.media('samples.json').transformation(Transformation()
	.addTransformation("fn_select:js:my_example.js")
	.setDeliveryType("list")
	.setAssetType("image"));
```

```kotlin
cloudinary.media {
	publicId("samples.json")
	 addTransformation("fn_select:js:my_example.js")
	 deliveryType("list")
	 assetType("image") 
}.generate()
```

```jquery
$.cloudinary.image("samples.json", {function: "select:js:my_example.js", type: "list"})
```

```react_native
new CloudinaryMedia("samples.json")
  .addTransformation("fn_select:js:my_example.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

> **NOTE**: There is a [special transformation count](transformation_counts#custom_functions) for usage of `fn_select:js`.

#### JavaScript file contents

The JavaScript file must contain a single `main` function that then returns the asset to be delivered. Additionally, the following calls can be made within your code to access relevant data:

* `getDocument()` - returns the response of a call to [list all assets](list_assets#client_side_asset_lists) with the same tag, or the contents of a [custom data file](#custom_data_file) (see below).
* `getContext()` - returns an object listing all the transformation parameters used in the dynamic URL as well as the public ID. For example, for the URL, `https://res.cloudinary.com/demo/image/list/fn_select:js:match-resource.js/c_scale,w_150/samples.json`:
    * `getContext()["transformation"]` returns "fn_select:js:match-resource.js/c_scale,w_150/json".
    * `getContext()["resource"]` returns "samples".

For example, to select the asset where the value of its "product-hero-image" metadata field is "yes":

match-resource.js

```js
function main() {
    var content = JSON.parse(getDocument());
    var matchingResource = content.find(resource => {
        return resource.metadata && resource.metadata.some(meta => meta.external_id === "product-hero-image" && meta.value === "yes");
    });

    return matchingResource;
}
```

You can also add a transformation into the code, for example to scale the image to 150 pixels in width (this is the JavaScript in **my_example.js** from above):

my_example.js

```js
function main() {
    var content = JSON.parse(getDocument());
    var matchingResource = content.find(resource => {
        return resource.metadata && resource.metadata.some(meta => meta.external_id === "product-hero-image" && meta.value === "yes");
    });

    if (matchingResource) {
        matchingResource.transformation = "c_scale,w_150";
    }

    return matchingResource;
}
```

Alternatively, you can add a transformation into the URL itself:

Selected sample file](https://res.cloudinary.com/demo/image/list/fn_select:js:match-resource.js/c_scale,w_150/samples.json)

If you want to set a transformation in the JavaScript in addition to in the URL, you can use the `getContext()` function to get the URL transformation, extract the transformation after the `fn_select` component, and combine them:

combine-transformations.js

```js
function extractTransformation(inputString) {

    var pattern = /fn_select:js:[^\/]+\/((?:[^\/]+\/)+)json/;
    var match = inputString.match(pattern);

    if (match && match.length > 1) {
        var transformation = match[1];
        return transformation;
    }

    // Return null if no match is found
    return null;
}

function main() {
    var content = JSON.parse(getDocument());
    var matchingResource = content.find(resource => {
        return resource.metadata && resource.metadata.some(meta => meta.external_id === "product-hero-image" && meta.value === "yes");
    });

    var urlTransformation = extractTransformation(getContext()["transformation"]);

    if (matchingResource) {
        matchingResource.transformation = "c_scale,w_150";

        if (urlTransformation) {
            matchingResource.transformation = "c_scale,w_150/" + urlTransformation;
        }
    }   

    return matchingResource;
}
```

For the following example, `getContext()["transformation"]` (from the code above) returns `fn_select:js:combine-tx.js/e_grayscale/a_30/f_png/json`, so `extractTransformation` is used to return only `e_grayscale/a_30/f_png`.  This is then added to the `c_scale,w_150` transformation.

![Selected sample file](https://res.cloudinary.com/demo/image/list/fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png/samples.json)

```nodejs
cloudinary.image("samples.json", {type: "list", transformation: [
  {function: "select:js:combine-transformations.js"},
  {effect: "grayscale"},
  {angle: 30},
  {fetch_format: "png"}
  ]})
```

```react
new CloudinaryMedia("samples.json")
  .addTransformation(
    "fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png"
  )
  .setDeliveryType("list")
  .setAssetType("image");
```

```vue
new CloudinaryMedia("samples.json")
  .addTransformation(
    "fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png"
  )
  .setDeliveryType("list")
  .setAssetType("image");
```

```angular
new CloudinaryMedia("samples.json")
  .addTransformation(
    "fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png"
  )
  .setDeliveryType("list")
  .setAssetType("image");
```

```js
new CloudinaryMedia("samples.json")
  .addTransformation(
    "fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png"
  )
  .setDeliveryType("list")
  .setAssetType("image");
```

```python
CloudinaryImage("samples.json").image(type="list", transformation=[
  {'function': "select:js:combine-transformations.js"},
  {'effect': "grayscale"},
  {'angle': 30},
  {'fetch_format': "png"}
  ])
```

```php
(new Media('samples.json'))
	->addTransformation("fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png")
	->deliveryType("list")
	->assetType("image");
```

```java
cloudinary.url().transformation(new Transformation()
  .function("select:js:combine-transformations.js").chain()
  .effect("grayscale").chain()
  .angle(30).chain()
  .fetchFormat("png")).type("list").imageTag("samples.json");
```

```ruby
cl_image_tag("samples.json", type: "list", transformation: [
  {function: "select:js:combine-transformations.js"},
  {effect: "grayscale"},
  {angle: 30},
  {fetch_format: "png"}
  ])
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Function("select:js:combine-transformations.js").Chain()
  .Effect("grayscale").Chain()
  .Angle(30).Chain()
  .FetchFormat("png")).Action("list").BuildImageTag("samples.json")
```

```dart
cloudinary.media('samples.json').transformation(Transformation()
	.addTransformation("fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png")
	.setDeliveryType("list")
	.setAssetType("image"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setType( "list").setTransformation(CLDTransformation()
  .setFunction("select:js:combine-transformations.js").chain()
  .setEffect("grayscale").chain()
  .setAngle(30).chain()
  .setFetchFormat("png")).generate("samples.json")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation()
  .function("select:js:combine-transformations.js").chain()
  .effect("grayscale").chain()
  .angle(30).chain()
  .fetchFormat("png")).type("list").generate("samples.json");
```

```flutter
cloudinary.media('samples.json').transformation(Transformation()
	.addTransformation("fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png")
	.setDeliveryType("list")
	.setAssetType("image"));
```

```kotlin
cloudinary.media {
	publicId("samples.json")
	 addTransformation("fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png")
	 deliveryType("list")
	 assetType("image") 
}.generate()
```

```jquery
$.cloudinary.image("samples.json", {type: "list", transformation: [
  {function: "select:js:combine-transformations.js"},
  {effect: "grayscale"},
  {angle: 30},
  {fetch_format: "png"}
  ]})
```

```react_native
new CloudinaryMedia("samples.json")
  .addTransformation(
    "fn_select:js:combine-transformations.js/e_grayscale/a_30/f_png"
  )
  .setDeliveryType("list")
  .setAssetType("image");
```

#### Custom data file

The JavaScript filter can be applied to a custom data file instead of applying the filter to the JSON response listing all assets with the same tag.

You first upload the custom data file as a [raw](upload_parameters#uploading_non_media_files_as_raw_files) asset to your Cloudinary product environment and then the JavaScript filter is applied in the delivery URL for that raw custom file.

The general URL syntax for applying a JavaScript filter to a raw custom file takes the form:

```
https://res.cloudinary.com/{cloud}/raw/{type}/fn_select:{js_file}/{transformations}/{data_file}
```

Where:

* `cloud` - your Cloudinary product environment's cloud name.
* `type` - the type of the uploaded raw file, either `upload`, `authenticated` or `private`.
* `js_file` - the public_id of the JavaScript filter uploaded as a separate raw file.
* `transformations` - (optional) any transformations to apply to the selected asset.
* `data_file` - the public_id of the raw custom data file.

The JavaScript filter still needs to return the asset to deliver, but the filter can now be applied to any information you supply in the custom data file.

For example, to apply the JavaScript filter uploaded as a raw file called "my_example.js" to the source data file uploaded as "sample-db.txt":

```
https://res.cloudinary.com/demo/raw/upload/fn_select:my_example.js/sample-db.txt
```

## Refine custom functions

You can use the `refine` custom function (`fn_refine` in URLs) to filter the list of assets returned when using the `list` delivery type (see [Client-side asset lists](list_assets#client_side_asset_lists) for syntax details). 

You can use this, for example, to hide confidential metadata that you don't want to be made publicly available on the client side.

Specify the filter as JavaScript code, uploaded to your product environment as an authenticated raw file.

1. Create your JavaScript file. In this example, the JavaScript code filters the response from the `list` call to return details of assets that are of format `jpg` and smaller than 500 pixels in height, excluding the `metadata` field from each asset's response:

    
    format-height-no-metadata.js
    

    ```js
    function main(){
        var content = getDocument();
        content.resources = content.resources.filter(e => { return e.format == "jpg" && e.height < 500; });
        content.resources.forEach((resource) => delete resource.metadata);
        return content;
    }
    ```

    > **NOTES**: 1. The code must contain a `main()` function.
    1. `getDocument()` is a function built into Cloudinary, which returns the response of the `list` API call.
1. Upload your JavaScript file to your product environment as an authenticated raw file (here, we're also setting the public ID to `docs/format-height-no-metadata`):

    ```multi
    |ruby 
    Cloudinary::Uploader.upload("format-height-no-metadata.js", 
      public_id: "docs/format-height-no-metadata.js",
      type: "authenticated",
      resource_type: "raw")
      
    |php_2
    $cloudinary->uploadApi()->upload("format-height-no-metadata.js", [
        "public_id" => "docs/format-height-no-metadata.js",
        "type" => "authenticated",
        "resource_type" => "raw"]);

    |python
    cloudinary.uploader.upload("format-height-no-metadata.js", 
      public_id = "docs/format-height-no-metadata.js",
      type = "authenticated",
      resource_type = "raw")

    |nodejs
    cloudinary.v2.uploader
    .upload("format-height-no-metadata.js", 
      { public_id: "docs/format-height-no-metadata.js",
        type: "authenticated",
        resource_type: "raw" })
    .then(result=>console.log(result)); 
      
    |java
    cloudinary.uploader().upload("format-height-no-metadata.js", 
      ObjectUtils.asMap(
        "public_id", "docs/format-height-no-metadata.js",
        "type", "authenticated",
        "resource_type", "raw"));

    |csharp
    var uploadParams = new RawUploadParams(){  // by default, ResourceType is already set to "raw"
      PublicId = "docs/format-height-no-metadata.js",
      UniqueFilename = false,
      Type = "authenticated",
      File = new FileDescription(@"format-height-no-metadata.js")};
    var uploadResult = cloudinary.Upload(uploadParams);  

    |go
    resp, err := cld.Upload.Upload(ctx, "format-height-no-metadata.js", uploader.UploadParams{
      PublicId:  "docs/format-height-no-metadata.js",
      Type:         "authenticated",
      ResourceType: "raw"})

    |cli
    cld uploader upload format-height-no-metadata.js public_id="docs/format-height-no-metadata.js" type=authenticated resource_type=raw          
    ```
1. Deliver a JSON file containing the refined list response for assets tagged `amazing`, based on the code in the JavaScript file (note that any slashes (`/`) in the public ID are replace with colons (`:`)):
    ![Filtered list](https://res.cloudinary.com/demo/image/list/fn_refine:docs:format-height-no-metadata.js/amazing.json "with_image: false")

```nodejs
cloudinary.image("amazing.json", {function: "refine:docs:format-height-no-metadata.js", type: "list"})
```

```react
new CloudinaryMedia("amazing.json")
  .addTransformation("fn_refine:docs:format-height-no-metadata.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```vue
new CloudinaryMedia("amazing.json")
  .addTransformation("fn_refine:docs:format-height-no-metadata.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```angular
new CloudinaryMedia("amazing.json")
  .addTransformation("fn_refine:docs:format-height-no-metadata.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```js
new CloudinaryMedia("amazing.json")
  .addTransformation("fn_refine:docs:format-height-no-metadata.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

```python
CloudinaryImage("amazing.json").image(function="refine:docs:format-height-no-metadata.js", type="list")
```

```php
(new Media('amazing.json'))
	->addTransformation("fn_refine:docs:format-height-no-metadata.js")
	->deliveryType("list")
	->assetType("image");
```

```java
cloudinary.url().transformation(new Transformation().function("refine:docs:format-height-no-metadata.js")).type("list").imageTag("amazing.json");
```

```ruby
cl_image_tag("amazing.json", function: "refine:docs:format-height-no-metadata.js", type: "list")
```

```csharp
cloudinary.Api.UrlImgUp.Transform(new Transformation().Function("refine:docs:format-height-no-metadata.js")).Action("list").BuildImageTag("amazing.json")
```

```dart
cloudinary.media('amazing.json').transformation(Transformation()
	.addTransformation("fn_refine:docs:format-height-no-metadata.js")
	.setDeliveryType("list")
	.setAssetType("image"));
```

```swift
imageView.cldSetImage(cloudinary.createUrl().setType( "list").setTransformation(CLDTransformation().setFunction("refine:docs:format-height-no-metadata.js")).generate("amazing.json")!, cloudinary: cloudinary)
```

```android
MediaManager.get().url().transformation(new Transformation().function("refine:docs:format-height-no-metadata.js")).type("list").generate("amazing.json");
```

```flutter
cloudinary.media('amazing.json').transformation(Transformation()
	.addTransformation("fn_refine:docs:format-height-no-metadata.js")
	.setDeliveryType("list")
	.setAssetType("image"));
```

```kotlin
cloudinary.media {
	publicId("amazing.json")
	 addTransformation("fn_refine:docs:format-height-no-metadata.js")
	 deliveryType("list")
	 assetType("image") 
}.generate()
```

```jquery
$.cloudinary.image("amazing.json", {function: "refine:docs:format-height-no-metadata.js", type: "list"})
```

```react_native
new CloudinaryMedia("amazing.json")
  .addTransformation("fn_refine:docs:format-height-no-metadata.js")
  .setDeliveryType("list")
  .setAssetType("image");
```

    This is the returned JSON:

    ```json
    {
      "resources": [
        {
          "public_id": "docs/escalator-200",
          "version": 1713527389,
          "format": "jpg",
          "width": 200,
          "height": 132,
          "type": "upload",
          "created_at": "2024-04-19T11:48:26Z"
        },
        {
          "public_id": "docs/hallway",
          "version": 1713526845,
          "format": "jpg",
          "width": 302,
          "height": 201,
          "type": "upload",
          "created_at": "2024-04-19T11:40:45Z"
        },
        {
          "public_id": "docs/denim_640",
          "version": 1713255161,
          "format": "jpg",
          "width": 640,
          "height": 427,
          "type": "upload",
          "created_at": "2024-04-16T08:12:41Z"
        }
      ],
      "updated_at": "2024-05-13T08:19:07Z"
    }
    ```

    > **TIP**: Compare this with the unrefined list response.

