Server-Side Logic
Vincent Depassier · August 30, 2026
Most products that let you build a UI leave you stranded the moment you need something to happen on a server. Automations are Praxsuite's answer to that: they are the backend, and there is no server for you to run, scale or patch.
This page is about using them that way — as the trusted half of your system.
Why anything needs a server at all
A browser is a hostile place to keep a secret. Anything your page holds, a visitor holds: keys, tokens, business rules, the lot. And anything your page decides, a visitor can decide differently by editing the request.
So the split is not about convenience, it is about trust:
In the browser | On the server |
Displaying data the caller is allowed to see | Holding credentials |
Collecting input | Deciding whether something is allowed |
Calling your own endpoint | Talking to a paid third-party API |
| Anything that must happen exactly once |
An automation as an HTTP API
The Endpoint trigger turns an automation into an API your app can call. The request comes in, your flow runs, and a Response node defines exactly what goes back — status code, headers, body.
Endpoint trigger ──▶ validate ──▶ do the work ──▶ Response (200, { ok: true })Three rules the platform enforces, so you find out when you save rather than in production:
An endpoint automation must have a Response node.
It may have exactly one. Not zero, not two.
An automation without an endpoint trigger may not have one at all.
That last rule surprises people. A Response node is meaningless in a scheduled job — there is nobody waiting for an answer — so the validator refuses it rather than letting it sit there doing nothing.
Compare with the Webhook trigger, which answers immediately and runs afterwards. Use webhooks for "another system is telling me something happened". Use endpoints for "my app is asking me a question and needs the answer".
The Script node
When the built-in nodes do not express what you need, the Script node runs your own code — JavaScript or Python — in an isolated sandbox.
|
|
Code size | Up to 64 KB |
Timeout | 1 to 30 seconds; 10 by default |
Inputs | Named values you declare, resolved from the flow's context |
Outputs | Validated against the output schema you declare |
Console | Captured into the run log |
It is a genuine escape hatch, not a toy: parse an awkward payload, compute something the formula node cannot express, reshape a response before it goes back to a caller.
Files in and out
A script can declare a blob to load before it runs, arriving either as a base64 string or as an object with the data, content type and filename. And it can return base64 in an output key, which gets uploaded for you and replaced with the stored file's id, URL, filename, content type and size.
Between the two, "read the uploaded CSV, transform it, hand back an XLSX" is a single node.
Three traps that cost an afternoon
Inputs need their braces. An input is a template: {{context.steps.query.rows}}. A bare field name is a literal string, and it will not error — it will just be wrong.
Do not parse what is already parsed. Inputs arrive as real values. Calling JSON.parse on one that is already an object throws, in a way that reads like a platform fault.
Column names lose their spaces. Rows from a Query Rows node reach a script keyed by underscore — Player_Name, not Player Name. Reading the spaced name returns nothing, silently. The gateway does not do this; it only bites inside automations.
Secrets
Credentials belong in the Vault, not in a node's configuration. Read a secret with the Vault node and it becomes available to the nodes downstream; it never appears in the automation's definition, and it is not something a person browsing the flow can read off the screen.
There is a write side too, for rotating a secret from a flow — the honest way to handle a credential that expires on a schedule.
Talking to the outside world
The HTTP Request node calls any internal or external endpoint and captures the response. Combined with the Vault, that is the whole pattern for a third-party integration: read the key, make the call, use the answer — with the key never leaving the server.
Control flow that survives contact with reality
Node | Use it for |
If / Else | A decision with a true and a false path |
Try / Catch | Work that can fail, with a path for the failure and one that always runs |
For Each | Iterating a list from a previous step |
For Table | Iterating every row of a table, with filters |
Delay | Waiting before continuing |
Formula | Arithmetic without dropping into code |
Call Automation | Handing work to another flow, fire-and-forget |
Try/Catch is the one people reach for too late. An HTTP call to someone else's API will fail eventually — their outage, a timeout, a change they did not announce. Without a catch path, the run stops there and whatever came before is left half-done.
Call Automation is fire-and-forget. It starts the other flow and moves on; it does not wait, and it does not bring back a result. When you need the answer, that is a different design — the work belongs inline, or behind an endpoint you call.
Keeping it fast
Every node costs something, so fewer, larger steps beat many small ones. Two habits pay for themselves:
Filter in the query, not after it. Reading rows and then discarding most of them in a script does the expensive part anyway. Row reads are also capped per call, so a filter is often the difference between correct and quietly truncated.
Do not put a loop where a filter would do. A For Table over everything, with an If inside, is the same work as one filtered query — except it runs a node per row.
Next
Trigger nodes — the five ways in
Logic nodes — branching, loops, error handling, Response
Runs and debugging — reading what actually happened
API Gateway — the other half: what clients may do directly