# Computed Fields

Computed fields let you define **dynamic, on-the-fly numeric fields** that are calculated during a request.
They are created using the `computed_fields[<alias>]` query parameter and can be used immediately in **filters** and **sorting**.

Computed fields work together with the standard filtering rules described in the
➡️ **[Filtering Documentation](/filtering)**

## What Are Computed Fields?

A computed field:

* Is defined per request
* Performs an **aggregate** on a relation
* Becomes available as a **numeric field** for filtering and sorting
* Exists only for the current request


Supported aggregates:

* `count()`
* `sum(<column>)`
* `avg(<column>)`
* `min(<column>)`
* `max(<column>)`


You declare them using query parameters:

```
computed_fields[alias]=<expression>
```

# Syntax

### Count

```
relation.count()
relation.count(<where>)
```

### Aggregates (sum, avg, min, max)

```
relation.sum(<column>)
relation.sum(<column>, <where>)
relation.avg(<column>)
relation.min(<column>)
relation.max(<column>)
```

### Where clause (`<where>`)

* Uses the same **DSL filter syntax** as the main `filter` parameter
* Is evaluated in the context of the **related model**


# Examples

## Count related tasks with status `"Completed"`

```
computed_fields[tasks_done]=tasks.count(status eq "Completed")
filter=tasks_done gt 0
```

## Sum of billable minutes on `timeUnits`

```
computed_fields[billable_minutes]=timeUnits.sum(minutes, status eq "billable")
filter=billable_minutes gte 120
```

## Average estimate across tasks

```
computed_fields[avg_estimate]=tasks.avg(estimated_hours)
filter=avg_estimate lt 6
```

## URL-encoded example

```
?computed_fields[tasks_done]=tasks.count(status%20eq%20%22Completed%22)&filter=tasks_done%20gt%200
```

# Using Computed Fields in Filters

Computed fields are treated as **numeric fields**.

Allowed operators:

* `eq`, `ne` / `neq`
* `gt`, `gte`
* `lt`, `lte`


Examples:

```
filter=tasks_done gt 0
filter=billable_minutes gte 120
filter=avg_estimate lt 6
```

### Types

* `count()` → integer
* `sum()`, `avg()`, `min()`, `max()` → number


# Sorting With Computed Fields

You can sort by any computed field as long as it is declared:

```
?computed_fields[tasks_done]=tasks.count()&sort=-tasks_done
```

Works regardless of whether the computed field is used in a filter.

# Validation Rules

The API validates computed field expressions. Errors occur when:

* The relation name is unknown
* The column does not exist
* The where clause uses invalid fields or operators
* The syntax is invalid


A 400 Bad Request is returned with details.

Example error response:

```json
{
  "message": "Invalid computed field expression",
  "errors": [
    {"alias": "billable_minutes", "issue": "Unknown column 'minutesx'"}
  ]
}
```

# Best Practices

* Use **short, descriptive aliases** (e.g., `tasks_done`, `avg_estimate`)
* Combine multiple computed fields for powerful analytics-style queries
* When passing DSL expressions, remember to URL-encode them
* Use computed fields when:
  * You need aggregated information
  * You need to filter or sort based on aggregated values
  * You want analytics without building a custom endpoint


# Related Documentation

See the **Filtering page** for:

* DSL filter syntax
* Operators
* Relation filters (`any`, `all`, `none`)
* Data types and validation


➡️ **[Filtering Documentation](/filtering)**