Skip to content

Export Tiers

When you write a function in Rust, Python, or Java, Transit can find it automatically. You do not need to register functions manually or write special configuration. Transit scans your code and discovers functions based on simple rules.

Think of tiers like levels of permission:

  • Tier 1 (default): Transit finds functions that are already public in your language. No changes needed.
  • Tier 2 (file-level): You put a comment at the top of a file to export everything in that file.
  • Tier 3 (function-level): You put a comment above a specific function to export it, even if it is private.

Most people only need Tier 1.

Tier 1: Public Functions (No Changes Needed)

Section titled “Tier 1: Public Functions (No Changes Needed)”

Transit finds functions that are public by your language’s own rules:

Any function with pub fn is found automatically:

// Transit finds this — it is a pub fn
pub fn process_job(job: FileJob) -> ProcessResult {
// your code here
}
// Transit does NOT find this — it is not pub
fn internal_helper(data: &[u8]) -> String {
format!("Internal: {} bytes", data.len())
}

Important: For Transit to call your Rust function from JavaScript, it must also be annotated with #[napi]. Here is a complete example:

use napi_derive::napi;
#[napi]
pub fn process_job(job: String) -> String {
format!("Processed: {}", job)
}

And your Cargo.toml must include:

[lib]
crate-type = ["cdylib"]
[dependencies]
napi = "2"
napi-derive = "2"

Any function defined at the top level of a file (not inside a class) is found:

# Transit finds this — it is a top-level def
def process_data(data):
return transform(data)
# Transit also finds class methods
class DataProcessor:
def process(self, data): # Found as "DataProcessor.process"
return transform(data)
# Transit does NOT find this — it starts with underscore
def _private_helper(data):
return optimized_path(data)

Important: Your Python function receives a JSON string, not a parsed dictionary. You must call json.loads():

import json
def process_data(args_json):
args = json.loads(args_json) # Now args is a dictionary
items = args.get("items", [])
return json.dumps({"count": len(items)})

Any public method is found:

// Transit finds this — it is a public method
public String processJob(String argsJson) {
return "{\"result\": \"done\"}";
}
// Transit does NOT find this — it is private
private String internalHelper(String data) {
return data.toUpperCase();
}

Any function whose signature is declared via the generated glue header is found automatically:

transit_c_glue.gen.h
void process_chunk(const char* args_json, char** result_json);
transit_cpp_glue.gen.h
void fast_compute(const char* args_json, char** result_json);

Transit detects these signatures from the generated headers and exports the corresponding implementations.

Any exported function is found:

// Transit finds this — it is exported
export function processJob(data) {
return { result: "done" };
}
// Transit does NOT find this — it is not exported
function internalHelper(data) {
return data.toUpperCase();
}

If you want to export all functions in a file (including private ones), add a comment at the very top:

// transit:file
// All public functions in this file are exported
pub fn helper_a() -> String { ... }
pub fn helper_b() -> String { ... }
# transit:file
def process(data):
return transform(data)
// transit:file
public class Utils {
public static String format(String input) { ... }
public static String parse(String input) { ... }
}

If you want to export a specific private function, add a comment directly above it:

// transit:function
fn internal_helper(data: &[u8]) -> String {
format!("Internal: {} bytes", data.len())
}
# transit:function
def _private_transform(data):
return optimized_path(data)
// transit:function
function computeHash(buffer) {
// ...
}

Key point: Tier 3 overrides the language’s privacy rules. A function that would normally be hidden becomes callable from other languages.

If two files export functions with the same name, use the file name to pick which one:

// If lib.rs and utils.rs both export "process":
await rs["lib"]["process"](data) // calls lib.rs's process
await rs["utils"]["process"](data) // calls utils.rs's process
// Dot notation also works:
await rs.lib.process(data)

When Transit scans your code, it creates a list of discovered functions:

{
"language": "rust",
"sourceFile": "/path/to/lib.rs",
"functionName": "process_job",
"signature": "pub fn process_job(job: FileJob) -> ProcessResult",
"exportTier": 1
}

This list is used by Transit to know which functions are available and how to call them.

Transit automatically handles naming differences between languages:

  • Rust uses snake_case (like process_general)
  • JavaScript uses camelCase (like processGeneral)
  • Java uses camelCase (like processSpecialized)

You can call functions using either style:

// Both work for a Rust function named process_general:
await rs.process_general(data)
await rs.processGeneral(data)