Back to Blog
2025-12-05 7 min read

Your Script Parameters Are Brittle (And You Know It)

Your Script Parameters Are Brittle (And You Know It)

Let's be brutally honest. Your current parameter passing methods are a ticking time bomb. You know that uneasy feeling when you change a script parameter, and a seemingly unrelated script breaks? That's the symptom of a deeper problem.

The 'Pipe-Delimited' Nightmare is a classic. You've got a parameter string like "ID_Contact|Action_Delete|Source_Mobile". It works, until someone (maybe you!) decides to insert a new parameter, say, Confirm_Prompt, right in the middle. Now your receiving script, which was meticulously parsing GetValue(Get(ScriptParameter); 2) for Action_Delete, is suddenly getting Confirm_Prompt. You've just introduced a silent bug that could take hours to track down. It's a disaster waiting to happen.

The 'Let Notation' Trap is just as bad. For a few parameters, it seems okay: Let([p_id = GetValue(Get(ScriptParameter);1); p_action = GetValue(Get(ScriptParameter);2)], // do stuff). But what happens when you need 5, 8, or 10 parameters? Your Let statement explodes into an unreadable mess, sprawling across your calculation window. It's verbose, hard to maintain, and still suffers from that fundamental flaw.

The core problem with both these legacy methods is simple: they rely on parameter order, not name. You have to meticulously remember the position of each piece of data. This reliance on implicit order is the primary source of those silent, hard-to-debug errors that eat up your development time and fray your nerves.

Building Your First JSON Parameter: A Practical Example

Enough with the pain. Let's build something better. JSON is the answer, and it's surprisingly simple to implement.

You'll use the JSONSetElement function to construct your parameter object. Imagine you need to pass a contactID and a status to a script that updates a contact record.

JSONSetElement (
  "{}" ;
  [ "contactID" ; 123 ; JSONNumber ] ;
  [ "status" ; "active" ; JSONString ]
)

This calculation generates a clean JSON string: {"contactID":123,"status":"active"}. Notice the JSONNumber and JSONString types---these are crucial for ensuring your data is correctly interpreted.

Passing the object is ridiculously simple. In your Perform Script step, this JSON string becomes the sole script parameter. That's right, just one.

Inside your receiving script, parsing is equally straightforward. You'll use JSONGetElement to pull out the named values.

Set Variable [ $contactID ; Value: JSONGetElement ( Get ( ScriptParameter ) ; "contactID" ) ]
Set Variable [ $status ; Value: JSONGetElement ( Get ( ScriptParameter ) ; "status" ) ]

Notice something critical here: you're asking for "contactID" and "status" by name, not by position. This means you can add, remove, or reorder elements in your JSON object without ever breaking the receiving script. That's robustness you can build on.

Scaling Up: Handling Complex Data and Sub-Objects

JSON truly shines when you move beyond simple key-value pairs. Its hierarchical nature makes it perfect for complex data structures.

Imagine you're creating an invoice. You need the invoice ID, customer ID, and crucially, a list of line items. You can achieve this by nesting data for an invoice within your JSON parameter:

JSONSetElement (
  "{}" ;
  [ "invoiceID" ; 1001 ; JSONNumber ] ;
  [ "customerID" ; 500 ; JSONNumber ] ;
  [ "lineItems" ; "[{"product":"GadgetA","qty":2},{"product":"WidgetB","qty":5}]" ; JSONArray ]
)

Here, lineItems is an array of objects, each representing a product and its quantity. This structure is incredibly powerful and easily parsed.

You can also use JSON to pass found sets of records. Instead of passing a pipe-delimited list of IDs, embed them in a JSONArray. For example, you might use a custom function or List() to gather primary keys from a found set, then pass them like this:

JSONSetElement (
  "{}" ;
  "recordIDs" ;
  "["UUID1", "UUID2", "UUID3"]" ;
  JSONArray
)

This method is far more reliable than trying to parse a long string of IDs, especially if those IDs might contain your delimiter character.

You should also standardize script results by returning a JSON object from your scripts. Instead of just a "1" for success or "0" for failure, return a structured object.

JSONSetElement (
  "{}" ;
  [ "status" ; "success" ; JSONString ] ;
  [ "data" ; "newRecordUUID" ; JSONString ] ; // or an object with more data
  [ "errorMessage" ; "" ; JSONString ]
)

This approach gives you consistent error handling across your entire solution, making debugging and integration significantly easier. You can reliably check JSONGetElement(Get(ScriptResult); "status") every single time.

Counter-Intuitive: Stop Using JSONFormatElements for Parameters

This is where many developers get it wrong. There's a common misconception that JSON should always be "human-readable," and JSONFormatElements is often suggested for parameters. This is a colossal waste of processing power.

FileMaker scripts are machines talking to machines. They don't care about pretty indentation or line breaks. When you use JSONFormatElements before passing a parameter, you're adding unnecessary overhead. Imagine a script that needs to pass parameters 1000 times in a loop. Adding JSONFormatElements to each call could easily slow execution by 15-20%, adding hundreds of milliseconds to an operation that should fly. Think Big(O). We shouldn't make it work more than it needs to.

The correct workflow is simple: keep JSON compact for machine-to-machine communication. Only use JSONFormatElements when you're logging data to a field for human review, or when you're inspecting the JSON in the Data Viewer during debugging. For actual script-to-script communication, strip out the formatting. Your users will thank you for the snappier performance.

The Single-Parameter Rule: Your New Standard

If you take one thing away from this, let it be this: always pass a single JSON object as your script parameter. This isn't just a suggestion; it's a fundamental shift that will dramatically improve your FileMaker development.

JSON is self-documenting because keys describe values. It's order-independent, meaning you can change the internal structure without breaking callers. And it's infinitely scalable without ever requiring you to rewrite parsing logic in existing scripts. This approach significantly reduces the risk of silent, order-based bugs and can dramatically decrease debugging time.

Let's look at a quick refactor example.

Before (GetValue-based):

// Calling script
Perform Script [ "ProcessOrder" ; Parameter: OrderID & "|" & CustomerID & "|" & "Pending" ]

// Receiving script "ProcessOrder"
Set Variable [ $orderID ; GetValue(Get(ScriptParameter); 1) ]
Set Variable [ $customerID ; GetValue(Get(ScriptParameter); 2) ]
Set Variable [ $status ; GetValue(Get(ScriptParameter); 3) ]

After (JSON object):

// Calling script
Perform Script [ "ProcessOrder" ; Parameter:
  JSONSetElement (
    "{}" ;
    [ "orderID" ; Order::ID ; JSONNumber ] ;
    [ "customerID" ; Customer::ID ; JSONNumber ] ;
    [ "status" ; "Pending" ; JSONString ]
  )
]

// Receiving script "ProcessOrder"
Set Variable [ $orderID ; JSONGetElement(Get(ScriptParameter); "orderID") ]
Set Variable [ $customerID ; JSONGetElement(Get(ScriptParameter); "customerID") ]
Set Variable [ $status ; JSONGetElement(Get(ScriptParameter); "status") ]

The "after" example is clear, robust, and future-proof. You can add a deliveryDate parameter tomorrow, and your ProcessOrder script won't even flinch.

Make sure you use essential tooling. For complex JSON structures, don't build them blind in FileMaker. Use an online JSON validator (like jsonlint.com) or a dedicated text editor like VS Code with JSON extensions. These tools will highlight syntax errors instantly, saving you immense frustration when debugging. Build your JSON there, then paste the validated string into your FileMaker calculation. It's a small step that prevents huge headaches.

Your Script Parameters Are Brittle (And You Know It) | Jeffrey Henry