Solution Engineer Technical Guide

Build on Fuuz like you built it

Understanding the stack under Fuuz — MongoDB, GraphQL APIs, the Data Flow Engine, Fuuz JSONata bindings, and SaaS on AWS — isn't background knowledge. It's how you write better screens, faster flows, cleaner integrations, and diagnose problems in minutes instead of hours.

MongoDB‍ ‍GraphQL APIs‍ ‍Fuuz JSONata ‍ ‍AWS SaaS ‍ ‍WebSockets

THE STACK

What's Running Under
Everything You Build

Every screen, data flow, integration, and data model you create in Fuuz runs on this stack. Knowing how each layer works and how they talk to each other changes how you build and how fast you debug.

The Builder's Insight

When something isn't working, the stack gives you a clear isolation strategy. Data wrong? Check the document shape via a GraphQL query. Query returning unexpected results? Check predicates and filters. Transform producing bad output? Check the JSONata or JS expression in the transform console. Screen not rendering? Inspect the element binding. Each layer has a distinct responsibility and a distinct debugging approach.

Real-time: WebSockets, Not GraphQL Subscriptions

Real-time updates in Fuuz — live screen refresh, push events, shop floor alerts — are delivered via a dedicated WebSocket subscription service, separate from the GraphQL APIs. The end result is the same (live data without polling), but it is a distinct service, not a GraphQL subscription.

MongoDB is your data layer

Documents, not rows. Schema lives in the Fuuz data model designer. Document structure determines how simple or complex your queries, flows, and bindings will be.

GraphQL APIs are your data contract

Multiple typed APIs serve different parts of the platform. Filtering and aggregation happen here via predicates and _aggregate queries — not in JSONata transforms.

Data Flow Engine is your workflow layer

The low-code / pro-code engine that orchestrates logic — API calls, transforms, conditionals, error handling, integrations. Sits above GraphQL in the stack.

Fuuz JSONata is your expression language

Fuuz extends standard JSONata with platform-specific bindings: $components, $metadata, $state, join functions, and more. Standard JSONata is just the foundation.

Client Layer Screen Designer · Mobile · External Apps · IoT Devices


GraphQL queries / mutations

WebSocket events (real-time)

Data Flow Engine Low-code / Pro-code workflow engine · Web, Backend & Gateway flows


JSONata / JavaScript transforms

Transformation Layer Fuuz JSONata Bindings · JavaScript · $state · $components · $metadata


typed queries / mutations / predicates

GraphQL APIs Multiple typed APIs · Schema-validated · Real-time via separate WebSocket service


BSON document reads / writes

GraphQL APIs Multiple typed APIs · Schema-validated · Real-time via separate WebSocket service

managed infrastructure


AWS SaaS Infrastructure Auto-scale · CDN · IAM · High availability · Managed operations

MONGODB

Think in Documents, Not Tables

The biggest conceptual shift for engineers from SQL-backed environments. MongoDB stores JSON documents, not rows. How you design documents affects query performance, screen binding complexity, and flow logic.

Concept SQL World MongoDB / Fuuz
Data unit Row in a table JSON document in a collection
Relationships Foreign keys + JOINs Nested sub-documents or ID references
Schema changes ALTER TABLE + migration + risk Add field in schema designer → live instantly, zero downtime
Optional data Nullable column on every row Field simply absent if not applicable
Variable shapes EAV tables or wide nullable rows Each document carries its own field set
Filtering / aggregation SQL WHERE / GROUP BY GraphQL predicates and _aggregate queries at the API layer
Record ID Primary key (varies) Always id in Fuuz GraphQL — no underscore prefix
SQL — Relational Traditional MES/ERP
-- Reconstructing ONE work order -- requires 4 tables + 3 JOINs SELECT wo.id, wo.number, wo.status, p.name, p.revision, op.seq, op.description FROM work_orders wo JOIN parts p ON p.id = wo.part_id JOIN wo_operations op ON op.wo_id = wo.id WHERE wo.id = 'WO-1042'; -- Add "customer_po" field? -- ALTER TABLE + migration + deployment window
MongoDB — Document (Fuuz) Fuuz Platform
// ONE document — full object in one read { "id": "WO-1042", // "id" not "_id" "number": "WO-1042", "status": "In Progress", "part": { "id": "part-ref-id", "name": "Bracket Assy", "revision": "C" }, "operations": [...], "customer_po": "PO-8812" // new field // schema designer → live. Zero downtime. }

On Embedding: Conceptually Sound, Practically Evolving

The embed-vs-reference pattern is the right mental model for document databases. In Fuuz, full embedded type support is actively in development. Apply the principles in your data model design now — as platform support deepens, models designed with this thinking will be well-positioned. Discuss with your team lead which patterns are best supported in your current release.

The #1 Mistake from SQL Backgrounds

Over-normalizing the data model. Creating a separate Fuuz model for every sub-entity because "that's how you do it in SQL." Before splitting, ask: will this data ever be queried independently, or does it always travel with its parent? If it always travels with its parent — it's a candidate for embedding. Fewer models often means simpler queries, cleaner flows, and easier screen bindings.

GRAPHQL APIs

GraphQL: Where
Filtering and Fetching Live

Fuuz exposes multiple GraphQL APIs. This is where you declare what data you want, apply filters and predicates, and run aggregations. Filtering and grouping happen here at the API layer — not in JSONata transforms.

Filtering Belongs in GraphQL, Not JSONata

A common pattern from SQL backgrounds is to fetch a large result set then filter it in a JSONata transform. Don't do this. Use GraphQL predicates to filter at the query layer. Use _aggregate queries for counts, sums, and grouping. JSONata then handles reshaping the already-filtered result.

Declare exactly what you need

Shape the query to match exactly what the screen or flow needs. No over-fetching a 40-field record to display 3 fields on a table row.

Mutations write data

Creates, updates, and deletes all go through GraphQL mutations. Always return id in the mutation selection set to confirm the write and enable chained operations.

Filter via predicates

GraphQL predicates are your WHERE clause. Filter by field values, date ranges, and relationships at the API layer before data ever reaches your screen or flow.

Traverse relationships in one call

Walk from Work Center → Work Orders → Operations in a single query. One roundtrip returns the full graph — no chained REST-style calls.

GraphQL — Query with Predicates Filter at the API Layer
# Filter HERE in the query — not in JSONata query GetOpenWOs($wcId: ID!) { workOrders( filter: { workCenter: { id: { _eq: $wcId } } status: { _eq: "Open" } } sort: { scheduledStart: ASC } ) { id // "id" — not "_id" in Fuuz number status scheduledStart part { name revision } } }
GraphQL — Mutation (Write) Flow / ERP Write-back
mutation UpdateWOStatus( $id: ID! $status: String! $completedAt: DateTime ) { updateWorkOrder( id: $id data: { status: $status completedAt: $completedAt } ) { id // always return id to confirm status completedAt } }

Aggregate at the query layer

Use _aggregate queries for counts, sums, and grouping. Runs server-side — far more efficient than fetching all records and counting in JSONata.

Schema-validated in real time

The GraphQL input in Fuuz autocompletes and validates against the schema as you type. Field name errors and type mismatches are caught immediately, before runtime.

GraphQL — Aggregate Query Count/Sum at API Layer
# Count WOs by status — server-side efficient query WOStatusCounts($wcId: ID!) { workOrders_aggregate( filter: { workCenter: { id: { _eq: $wcId } } } groupBy: ["status"] ) { status count } } # Returns: [{ status: "Open", count: 12 }, # { status: "Complete", count: 47 }] # No JSONata $count() loop needed.
Anti-pattern — Don't Do This Common SQL-background Mistake
// ❌ WRONG: Fetch all, filter in JSONata query { workOrders { // no filter — returns all id status number } } // JSONata transform node: $[status = "Open"] // wrong layer // ✅ RIGHT: Filter in the query query { workOrders(filter: { status: { _eq: "Open" } }) { id status number } }

DATA FLOW ENGINE

The Low-code / Pro-code
Workflow Engine

The Fuuz Data Flow Engine sits above the GraphQL layer — it orchestrates logic, calls APIs, handles errors, and applies transforms. Think of it as your application's business logic layer, built visually with the option to drop into JavaScript for complex operations.

Web Flows

Triggered by user interactions on screens. Handle button actions, form submissions, navigation, and reactive UI logic. Run client-side in the browser context.

Backend Flows

Server-side logic triggered by events, schedules, or API calls. Used for integration logic, data processing, ERP write-backs, and operations that should not run on the client.

Gateway Flows

Expose Fuuz logic to external systems as callable endpoints. Used when external systems need to push data in or trigger Fuuz operations programmatically.

Stack Position: DFE is Above GraphQL

The Data Flow Engine sits above the GraphQL API layer. Flows call GraphQL APIs to read and write data, then use the Transformation Layer to process results. Understanding this ordering matters when tracing what triggered what during a debug session.

Debugging Flows: Use the Console and Flow Logs

When a flow isn't behaving as expected, your primary tools are the Flow Console and execution logs. Every transform node exposes its input and output in the console — inspect the actual data at each step without adding separate debug nodes. Check the console of the relevant transform section before assuming the problem is upstream.

FUUZ JSONATA BINDINGS

Not Just JSONata —
Fuuz JSONata

Standard JSONata is the foundation, but Fuuz extends it substantially with platform-specific bindings. These give you access to screen state, user identity, flow context, join operations, and more. When you're building in Fuuz, you're using Fuuz JSONata — significantly more powerful than the open-source baseline.

Two Execution Contexts — Know Which One You're In

Screen expressions run client-side. You have access to $components, $metadata, $card, data, and $$.

Data Flow expressions run server-side. You have access to $state, $state.context, and $state.claims. Screen bindings like $components are not available here.

$components- Interact with Screen Element

Expression What It Does
$components.Form1.fn.save() Save the form
$components.Form1.fn.setValue("field", val) Set a single field value programmatically
$components.Form1.formState.dirty true when unsaved changes exist
$components.Form1.formState.valid true when all validations pass
$components.Form1.data.fieldName Read a field value directly
$components.Table1.fn.search() Trigger table search / refresh
$components.Table1.selectedRows Array of all selected rows
$components.Table1.selectedRows[0].id First selected row's ID
$components.Screen.fn.setContextValue("k", v) Set a single screen context key
$components.Screen.fn.mergeContext({...}) Shallow-merge values into screen context
$components.Screen.context.myKey Read a screen context value

$metadata — URL, User, Tenant

Expression What It Does
$metadata.urlParameters.id URL path parameter (e.g. /record/:id)
$metadata.querystring.tab Query string parameter (?tab=details)
$metadata.user.id Logged-in user ID
$metadata.user.email Logged-in user email
$metadata.user.roles Array of user roles
$metadata.tenant.id Current tenant ID
$metadata.tenant.name Current tenant name
$metadata.settings.timezone User timezone
$metadata.settings.dateFormat User date format string

$state — Data Flow Context

Expression What It Does
$ Current node payload — shorthand for $state.payload
$state.payload Full current node input payload
$state.context Shared flow context (set via Set/Merge Context nodes)
$state.context.myKey Read a specific context value
$state.claims.userId Triggering user ID from auth
$state.claims.tenantId Tenant ID from auth claims
$state.claims.roles User roles from auth claims
$state.metadata.flowId Current flow ID
$state.lastError Error from last caught error node

data, $card & $$ — Element-level Bindings

Expression What It Does
data.fieldName Read a field value inside a Form element
data.active ? "Yes" : "No" Ternary on a boolean form field
$card.data.id Card record ID — use inside Cards elements (not data.id)
$card.data.status Card record field value
$$.newValue New value in an onChange handler
$$.oldValue Previous value in an onChange handler
$isNotNilOrEmpty($$.newValue) and $not($$.newValue = $$.oldValue) Guard: only fire when non-empty and actually changed

Fuuz Join Functions & Utility Bindings

Fuuz JSONata — Join Functions Beyond Standard JSONata
// $innerJoin — match records from both sides $innerJoin( workOrders, // left array parts, // right array "partId", // left join key "id" // right join key ) // $leftOuterJoin — keep all left, match right $leftOuterJoin( workOrders, scheduledShifts, "shiftId", "id" )
Fuuz JSONata — Utility Bindings Screen & Flow Context
// $query — run a GraphQL query inline $query({ "api": "system", "statement": "query { ... }", "variables": { "id": "123" } }) // $numeral — number formatting $numeral(12345).format("0,0") // "12,345" $numeral(0.75).format("0%") // "75%" $numeral(1234.5).format("$0,0.00") // "$1,234.50" $now // ISO 8601 timestamp $appConfig.someKey // tenant app config

Critical JSONata Syntax Gotchas

Gotcha Correct Usage
No ! operator Use $not(expr) instead of !expr
String concat is & "a" & " " & "b" — not +
Equality is = x = "val" — not == or ===
Membership check x in [1,2,3] — not .includes()
No null coalescing ?? $exists(val) ? val : "default"
Cards vs Form data Inside Cards: $card.data.x — not data.x
Context is $ $ = entire current input. Confirm shape in the console before writing transforms.

MENTAL MODEL

Coming from SQL?
Reframe These 5 Things

These are the specific mental model shifts that trip up experienced engineers when they first build on Fuuz. These are the closest useful parallels — not a 1:1 mapping, the platforms are genuinely different.

SQL / Relational Normalize everything. Every sub-entity gets its own table. Reconstruct the object at query time with JOINs. MongoDB / Fuuz Design for your access pattern. Data that always travels together belongs together. Think in objects first, storage second.
SQL / Relational Schema changes are risky: ALTER TABLE, migration scripts, deployment windows, potential table locks. MongoDB / Fuuz Add a field in the Fuuz schema designer — live instantly, zero downtime. Schema field changes are low-risk, fast tasks.
SQL WHERE / GROUP BY Filter and aggregate in the database query engine using SQL clauses. GraphQL Predicates / _aggregate Filter at the GraphQL API layer. Use _aggregate for counts and sums. JSONata reshapes the result — it does not filter it.
SQL JOINs Combine data from multiple tables using INNER JOIN, LEFT JOIN, computed by the DB engine. Fuuz JSONata $innerJoin / $leftOuterJoin Fuuz provides join functions to combine result sets from multiple GraphQL queries in a flow transform.
On-prem / Hosted SQL Infrastructure is your team's problem. Backups, upgrades, scale events, DR planning — all on you. AWS SaaS / Fuuz All infrastructure is managed. Focus entirely on application delivery. Application performance on large datasets is still your design responsibility.