Pipeline reference

# Code step

A Code step runs a short program on each record it processes.

The program's statements run in order, top to bottom. It reads the record's fields by name and writes fields that later steps and destinations see; assigning `null` removes a field. Every name has one type. This page gives worked examples of common tasks, each showing a record before and after the step. The [expression reference](/docs/code-reference) gives the rules its code follows, and lists the operators and functions it can use.

## Settings

It also takes the settings [every step](/docs/steps#settings) takes.

`body`  program required

The program run on each record the step processes.

Examples:

- `status = status as i64;`
- `if (status >= 500) { severity = "error"; } else { severity = "info"; }`

## Common tasks

### Set, rename and remove fields

Assign to set a field, and assign `null` to remove one:

Code

```
let label = "checkout";
service = label.toUpperCase();
debug = null;
```

Input

```
{
  "service": "cart",
  "debug": "trace-7",
  "status": 200
}
```

→

Output

```
{
  "service": "CHECKOUT",
  "status": 200
}
```

[`delete`](/docs/code-reference#statements) removes fields from the record, and `move` renames one. Inside an `if`, loop or `try`, `delete` cannot remove a field bound outside it; assign it `null` there instead.

Code

```
delete(password, debug);
move(msg, message);
```

Input

```
{
  "user": "ana",
  "password": "hunter2",
  "debug": true,
  "msg": "signed in"
}
```

→

Output

```
{
  "message": "signed in",
  "user": "ana"
}
```

### Choose a value

[`if`](/docs/code-reference#statements) and `else` choose which statements run for each record. For a short value selection, write `severity = status >= 500 ? "error" : "normal";`.

Code

```
if (status >= 500) {
  severity = "error";
} else {
  severity = "normal";
}
```

Input

```
{
  "status": 503
}
```

→

Output

```
{
  "status": 503,
  "severity": "error"
}
```

[`switch`](/docs/code-reference#statements) compares one value with each `case` in order. Cases with no statements between them share the next one's, and `break` ends the switch:

Code

```
switch (status) {
  case 200:
  case 204:
    outcome = "ok";
    break;
  case 404:
    outcome = "missing";
    break;
  default:
    outcome = "error";
}
```

Input

```
{ "status": 204 }
```

```
{ "status": 404 }
```

```
{ "status": 503 }
```

→

Output

```
{ "outcome": "ok", "status": 204 }
```

```
{ "outcome": "missing", "status": 404 }
```

```
{ "outcome": "error", "status": 503 }
```

### Filter records

[`drop()`](/docs/code-reference#statements) discards the record. It stands under an `if`, never inside a loop; a loop sets a flag, and an `if` after the loop drops on it. The step's filter is a condition. Where it is true, the body runs on the record. Where it is false or null, the body does not run, and the record continues with its existing values.

Code

```
if (path == "/health") drop();
```

Input

```
{ "path": "/health" }
```

```
{ "path": "/pay" }
```

→

Output

```
{ "path": "/pay" }
```

### Parse a field

The parse functions read text into an object, as a Parser step does, and `lookup` reads a lookup table, as a Lookup step does. A call stands where a statement writes its object: `Object.assign($, f(…))`, `$ = f(…)`, `x = f(…)` and `x: T = f(…)`, or a `let`. Smeltr reads everything but the text and the key when the pipeline compiles, so you write it as literals. Text the function cannot read marks the record with an `Unparsable` exception and writes nothing.

Code

```
Object.assign($, kv(line));
```

Input

```
{
  "line": "user=ana action=\"sign in\""
}
```

→

Output

```
{
  "action": "sign in",
  "line": "user=ana action=\"sign in\"",
  "user": "ana"
}
```

A parse function converts a field that a declared type names to it; every other field keeps the type the format gives, which is `str` for all but JSON. The declared type turns `status` into an `i64`:

Code

```
let hit: {status: i64, path: str} = grok(line, ["%{VERB:verb} %{URIPATH:path} %{INT:status}"], {patterns: {VERB: "[A-Z]+"}});
status = hit.status;
path = hit.path;
```

Input

```
{
  "line": "GET /pay 503"
}
```

→

Output

```
{
  "line": "GET /pay 503",
  "path": "/pay",
  "status": 503
}
```

The [parse functions](/docs/code-reference#parse) are `json`, `kv`, `cef`, `csv` and `grok`, each with its options.

### Look up a table

[`lookup`](/docs/code-reference#parse-lookup) reads the row of a lookup table that a key matches. `fields`: each output named for its column, `"column"`, or for its column and its default, `["column", "default"]`. A field with no match and no default is left out.

Lookup table users:

| user | team | level |
| --- | --- | --- |
| `alice` | `payments` | `3` |
| `bob` | `search` | `1` |

Code

```
Object.assign($, lookup("users", user, {column: "user", fields: {team: "team", level: ["level", "0"]}}));
```

Input

```
{ "user": "alice" }
```

```
{ "user": "zed" }
```

→

Output

```
{ "level": "3", "team": "payments", "user": "alice" }
```

```
{ "level": "0", "user": "zed" }
```

### Work with arrays

[`filter`](/docs/code-reference#filter) keeps the items of an array for which an arrow's expression is true. [`map`](/docs/code-reference#map) replaces each item with the arrow's value. The original array remains unless you assign the result back to it, and the calls chain: `values.filter(x => x > 2).map(x => x * 2)`.

Code

```
values = [1, 2, 3, 4];
kept = values.filter(x => x > 2);
doubled = kept.map(x => x * 2);
```

Input

```
{
  "id": 7
}
```

→

Output

```
{
  "id": 7,
  "values": [1,2,3,4],
  "kept": [3,4],
  "doubled": [6,8]
}
```

[`for...of`](/docs/code-reference#statements) visits each item in an array.

Code

```
let total = 0;
for (let value of [4, 5, 6]) {
  total = total + value;
}
sum = total;
```

Input

```
{
  "id": 7
}
```

→

Output

```
{
  "id": 7,
  "sum": 15
}
```

### Split a record

[`unroll`](/docs/code-reference#statements) replaces a record with one record per element of an array field. With `true` each new record keeps the other fields. It changes how many records there are, so it stands outside every `if`, loop and `try`.

Code

```
unroll(items, true);
```

Input

```
{ "order": "A1", "items": ["pen", "ink"] }
```

→

Output

```
{ "items": "pen", "order": "A1" }
```

```
{ "items": "ink", "order": "A1" }
```

### Reuse a calculation

A [local function](/docs/code-reference#statements) names a repeated calculation. The body's last expression, written without a semicolon, is its value, as is a `return`.

Code

```
function addTax(amount: f64): f64 {
  return amount * 1.2;
}
price = addTax(10);
```

Input

```
{
  "id": 7
}
```

→

Output

```
{
  "id": 7,
  "price": 12.0
}
```

### Handle bad data

Use [`items ?? []`](/docs/code-reference#nulls) when a missing array should default to an empty one. Only the selected branch can mark the record with an exception. For example, `d == 0 ? 0 : n / d` returns zero when `d` is zero, without a `DivideByZero`. A [`try`](/docs/code-reference#exceptions) answers what is left, one kind at a time:

Code

```
try {
  ratio = bytes / packets;
  count = parseInt(text);
} catch (e: DivideByZero | NaN) {
  ratio = 0.0;
} catch (e) {
  problem = e.kind;
}
```

Input

```
{
  "bytes": 1200,
  "packets": 0,
  "text": "many"
}
```

→

Output

```
{
  "bytes": 1200,
  "packets": 0,
  "ratio": 0.0,
  "text": "many"
}
```

Dividing by zero stops the `try` before `parseInt` runs, so the first catch takes the record. A record with packets but unreadable text would run the second catch.

## See also

-   [Expression reference](/docs/code-reference)
-   [Pipeline steps](/docs/steps)
-   [Exception answers](/docs/routing/exceptions#answers)
-   [Unroll step](/docs/steps/unroll)
