# Client Actions vs Server Actions: When to Use Which in OutSystems

- Tool: OutSystems
- Difficulty: Beginner
- Time required: 20-30 min
- Compatibility: OutSystems 11 and ODC
- Last updated: March 2026

## TL;DR

Client Actions run in the browser and can access screen widgets and Client Variables but not the database. Server Actions run on the server and can access the database, Site Properties, and external APIs but not the screen. Always use Server Actions for data operations and Client Actions for UI logic. Call Server Actions from Client Actions when you need both.

## Client Actions vs Server Actions in OutSystems

The single most common certification question and beginner confusion point in OutSystems is: 'Where should I put this logic?' The answer depends entirely on where the code needs to run. Client Actions run in the browser (JavaScript), Server Actions run on the .NET server. They live in different worlds and have different capabilities. Getting this right prevents security holes, unnecessary server round-trips, and TrueChange errors.

## Before you start

- Service Studio installed with a Reactive Web App open
- Basic familiarity with the Logic tab and how to create actions

## Step-by-step guide

### 1. Understand the two execution contexts

Before writing any code, internalize this mental model:

Client Actions = JavaScript running in the user's browser.
- Can see: screen widgets and their properties, Local Variables on the current screen, Client Variables (localStorage), navigate between screens, call JavaScript nodes, call other Client Actions.
- Cannot see: database tables, Site Properties, Session Variables (O11 Traditional only), external APIs (CORS blocks direct browser calls).

Server Actions = .NET code running on OutSystems Platform Server.
- Can see: all entities (database), Site Properties, server-side sessions, external REST/SOAP APIs, email sending, file I/O.
- Cannot see: screen widgets, Client Variables, browser state.

This boundary is enforced by OutSystems — TrueChange will show a red error if you try to access a database entity from a Client Action.

**Expected result:** You can classify any operation into 'needs Client Action' or 'needs Server Action' before writing a single line of logic.

### 2. Create a Client Action for form validation and UI control

Client Actions are the right home for logic that reads or writes to screen widgets, validates user input before sending to the server, toggles visibility, or navigates.

To create: right-click the screen name in the Widget Tree → 'Add Client Action'. Name it 'ValidateAndSubmitForm'.

Or: right-click a Button widget → 'Add OnClick Client Action' — Service Studio creates it automatically.

Example Client Action flow for a form submit:
Start → If: Length(Trim(FullName)) < 2 → [True] Assign FullNameInput.Valid=False → End → [False continues] → If: Form.Valid → [True] calls SaveEmployee Server Action → [False] End

Note: 'SaveEmployee' is a Server Action call node — you drag it from Logic tab → Server Actions into the Client Action flow.

```
/* Client Action: ValidateAndSubmitForm */
Start
  --> If: Length(Trim(FullName)) < 2
       [True] --> Assign: FullNameInput.Valid = False
                          FullNameInput.ValidationMessage = "Name too short"
               --> End
  --> If: Form.Valid
       [True] --> SaveEmployee  /* Server Action call */
                    Input: FullName, DepartmentId
               --> Message: "Saved!" (Success)
               --> End
       [False]--> End
```

**Expected result:** A Client Action node appears under your screen in the Interface tab. Clicking the submit button runs the validation logic entirely in the browser before making any server call.

### 3. Create a Server Action for database operations

Server Actions belong to the module-level logic layer. They run on the server and can call entity actions (Create, Update, Delete, Get) and Aggregates.

To create: Logic tab → Server Actions → right-click → 'Add Server Action'. Name it 'SaveEmployee'.

Add Input Parameters: right-click the Server Action → Add Input Parameter:
- FullName: Text
- DepartmentId: Department Identifier

Add Output Parameter:
- EmployeeId: Employee Identifier

Action flow:
Start → CreateEmployee: Employee.FullName=FullName, Employee.DepartmentId=DepartmentId, Employee.CreatedAt=CurrDateTime() → Assign: EmployeeId = CreateEmployee.Id → End

The CreateEmployee entity action is auto-generated when the Employee entity exists. Drag it from the Logic tab or Toolbox.

```
/* Server Action: SaveEmployee
   Input:  FullName (Text), DepartmentId (Department Identifier)
   Output: EmployeeId (Employee Identifier) */
Start
  --> CreateEmployee:
        Employee.FullName     = FullName
        Employee.DepartmentId = DepartmentId
        Employee.CreatedAt    = CurrDateTime()
  --> Assign: EmployeeId = CreateEmployee.Id
  --> End
```

**Expected result:** SaveEmployee is available in the Logic tab under Server Actions. It can be dragged into Client Action flows as a call node.

### 4. Call a Server Action from a Client Action and handle the response

To connect Client and Server Actions, drag the Server Action into the Client Action flow. Service Studio creates a call node with input and output mapping.

In ValidateAndSubmitForm (Client Action), after the Form.Valid True branch:
- Drag SaveEmployee from Logic tab → Server Actions onto the flow
- The call node shows input mappings: FullName → FullName, DepartmentId → DepartmentId
- To use the output: after the call node, add an Assign: NewEmployeeId = SaveEmployee.EmployeeId
- Then navigate: Destination node → EmployeeDetailScreen (pass NewEmployeeId as input)

Every Server Action call crosses the network — minimize them by batching operations and avoiding calls inside loops.

```
/* Client Action: ValidateAndSubmitForm - full flow */
Start
  --> (validation logic from Step 2)
  --> If: Form.Valid
       [True] --> SaveEmployee: FullName=FullName, DepartmentId=DepartmentId
               --> Assign: NewEmployeeId = SaveEmployee.EmployeeId
               --> Destination: EmployeeDetailScreen (EmployeeId=NewEmployeeId)
       [False]--> End
```

**Expected result:** Submitting the form validates client-side, calls SaveEmployee once, gets back the new ID, and navigates to the detail screen.

### 5. Understand Data Actions and when to prefer them

Reactive Web and Mobile apps have a third action type: Data Actions. A Data Action is a server-side action that runs asynchronously when the screen loads, separate from the main render cycle.

Create: right-click screen → 'Fetch data from Other Sources' → creates a Data Action.

Data Actions are best for:
- Fetching data that does not come from a single Aggregate (e.g., calling an API, running multiple aggregates and combining results)
- Loading data that depends on complex server-side logic

Data Actions are NOT for:
- Simple database queries — use a Screen Aggregate instead (lighter weight)
- Anything triggered by user interaction — use a Client Action that calls a Server Action

Key behavior: Data Actions run in parallel with other screen data fetches and do not block the screen render. They provide a .Data output you bind to widgets.

**Expected result:** You can identify whether a given data-loading scenario calls for a Screen Aggregate, a Data Action, or a Client Action calling a Server Action on demand.

## Complete code example

File: `ClientServerAction_decision_guide.txt`

```outsystems-pseudocode
/* DECISION GUIDE: Client vs Server Action

   USE CLIENT ACTION when:
   - Setting widget.Valid, widget.ValidationMessage
   - Toggling Visible property of a widget
   - Navigating to another screen (Destination node)
   - Running JavaScript (JavaScript node)
   - Calling a Server Action and handling its output
   - Updating Local Variables that bind to screen widgets

   USE SERVER ACTION when:
   - Reading from database (Aggregate or SQL node)
   - Writing to database (Create/Update/Delete entity actions)
   - Reading Site Properties
   - Calling REST/SOAP APIs
   - Sending emails
   - Processing files (Binary Data)
   - Any operation requiring server-side security

   USE DATA ACTION when:
   - Loading data on screen open that requires complex server logic
   - Combining results from multiple sources before binding to screen
*/

/* EXAMPLE: Correct pattern for form submit */

/* Client Action: SubmitForm */
Start
  [Client-side validation]
  --> SaveRecord  (Server Action call -- crosses network once)
  --> Destination: NextScreen
  --> End

/* Server Action: SaveRecord */
Start
  --> GetRelatedData  (Aggregate -- DB read)
  --> CreateRecord    (Entity action -- DB write)
  --> CallExternalAPI (REST method -- network call)
  --> End

/* ANTI-PATTERN: Do NOT call Server Actions in a loop */
/* BAD: */
For Each Product in ProductList
  --> SaveProduct (Server Action)  /* N+1: 1 call per row = slow */
End

/* GOOD: batch the operation into one Server Action call */
SaveAllProducts (Server Action, passes entire ProductList as input)
```

## Common mistakes

- **Trying to access a database entity or run an Aggregate inside a Client Action** — undefined Fix: Entities and Aggregates are server-side. Move the data access to a Server Action with appropriate output parameters, then call it from the Client Action.
- **Calling a Server Action inside a For Each loop in a Client Action** — undefined Fix: This causes N+1 server calls (one per loop iteration). Move the batch logic into a single Server Action that accepts a list input and processes all items in one server round-trip.
- **Using a Client Variable to pass data 'into' a Server Action** — undefined Fix: Server Actions cannot read Client Variables. Pass data explicitly as input parameters when calling the Server Action from a Client Action.
- **Placing navigation (Destination node) inside a Server Action** — undefined Fix: Navigation is a client-side operation. Move the Destination node to the Client Action after the Server Action call completes and returns control.

## Best practices

- Never put database access logic (Aggregates, entity actions) in a Client Action — TrueChange will block publishing with a red error.
- Never put widget property assignments (InputName.Valid, widget.Visible) in a Server Action — Server Actions run on the server and cannot see screen state.
- Minimize Server Action calls — each call is a network round-trip. Batch multiple operations into one Server Action rather than chaining multiple calls.
- Pass only the data a Server Action needs as input parameters — never pass entire record lists when an Id will do.
- Use Data Actions (asynchronous) over Server Actions called in OnInitialize (synchronous) for non-blocking screen data loading.
- Mark Server Actions as Functions (returning a value) when they are pure computations with no side effects — this enables use in expressions.

## Frequently asked questions

### Can I call a Client Action from a Server Action?

No. A Server Action runs on the server and has no way to invoke browser-side logic. The call direction is always Client Action → Server Action. If your Server Action needs to update the UI, return values as output parameters and let the Client Action handle the UI update after the Server Action completes.

### What is a Service Action and how does it differ from a Server Action?

A Service Action is a Server Action that is explicitly exposed for use by other modules. It is created in Logic tab → Service Actions (not Server Actions). Regular Server Actions are module-private. Service Actions are the OutSystems equivalent of a public API between modules. In ODC, all inter-app calls use Service Actions.

### Is there a performance cost to calling a Server Action from a Client Action?

Yes — every Server Action call is an HTTP round-trip from the browser to the server. Keep Server Action calls to a minimum. For simple UI operations like toggling visibility or clearing a form, use Client Actions only. For data operations, batch work into as few Server Action calls as possible.

### Can I make a Server Action run asynchronously?

Not natively in a single call. The standard Server Action call is synchronous (the Client Action waits for the result). For background processing, use Timers (Processes tab → Timers) which run as scheduled jobs on the server, or ODC Workflows for more complex async patterns.

---

Source: https://www.rapidevelopers.com/outsystems-tutorial/outsystems-client-server-actions
© RapidDev — https://www.rapidevelopers.com/outsystems-tutorial/outsystems-client-server-actions
