🚀 New batches open: Advanced Excel • Power BI • SQL • AI for Analytics — Book a free demo

Day 4 — Expressions - the syntax that trips everyone up

This is the day Power Automate stops being drag-and-drop and starts being a skill.

Two ways to write an expression

WrittenMeansGives back
@expressionThe whole value is the expressionThe real type - a number stays a number
text @{expression} textDrop the value into a stringText
@triggerBody()?['Amount']
@{triggerBody()?['Amount']}
The amount is @{triggerBody()?['Amount']} rupees
@add(triggerBody()?['Amount'], 1000)
Total: @{add(triggerBody()?['Amount'], 1000)}

Each line is evaluated on its own. Edit them, add your own, press Run.

Which to use Feeding a number into something that wants a number - use @.
Building a sentence, a subject line, a file name - use @{ } inside the text.
Everything inside @{ } becomes text, so @{add(1,2)} gives the string "3". Usually that is fine. When it is not, you will get a type error downstream and this is the reason.

Single quotes. Never double.

Text inside an expression is in single quotes concat('Hi ', 'there') is right. concat("Hi ", "there") is wrong, and the error you get does not say so clearly. The reason is that the whole flow is JSON, and JSON already uses double quotes - so they would end the string early.
To put a real apostrophe inside, double it: 'it''s here'.
@concat('Data', ' ', 'Analytics')
@concat('it''s ', 'fine')
@toUpper('north')
@trim('   spaces   ')

The question mark

?['Field'] instead of ['Field']. That one character is the difference between a flow that copes and a flow that dies at 3am.

@triggerBody()?['Name']
@triggerBody()?['Phone']
@triggerBody()['Phone']

The second line gives null and carries on. The third one fails, and it tells you which fields do exist - which is a useful message when you have mistyped a field name.

Use ?[ ] everywhere There is essentially no reason to use the plain brackets. A field can be missing because a form question was optional, because a system changed, or because somebody left it blank. ? turns a crash into a null you can handle with coalesce() or an if().

Handling the null

@coalesce(triggerBody()?['Phone'], 'No phone given')
@if(empty(triggerBody()?['Phone']), 'Missing', triggerBody()?['Phone'])
@empty(triggerBody()?['Phone'])
@empty(triggerBody()?['Name'])

coalesce() takes the first thing that is not empty. empty() is true for null, for an empty string, for an empty array and for an empty object - which makes it the safest test there is.

The string functions

@length(triggerBody()?['Name'])
@toUpper(triggerBody()?['Name'])
@split(triggerBody()?['Name'], ' ')
@first(split(triggerBody()?['Name'], ' '))
@last(split(triggerBody()?['Name'], ' '))
@substring(triggerBody()?['Code'], 4, 4)
@indexOf(triggerBody()?['Email'], '@@')
@replace(triggerBody()?['Code'], '-', '/')
@startsWith(triggerBody()?['Code'], 'ORD')
@join(split(triggerBody()?['Name'], ' '), '_')
Two gotchas in that list substring counts from 0. substring('ORD-2026-0042', 4, 4) gives 2026 - character 0 is the O.
To search for a literal @ you type @@. A single @ starts an expression, so it has to be escaped. This bites everyone who splits an email address.

Capitalising a name properly

There is no "proper case" function. This is the standard way, and it is a good test of whether the syntax has clicked.

@concat(toUpper(substring(triggerBody()?['Name'], 0, 1)), substring(triggerBody()?['Name'], 1))

Read it inside out: take the first character, upper-case it, then glue on everything from character 1 onwards.

Numbers

@add(triggerBody()?['Fee'], 500)
@mul(triggerBody()?['Fee'], triggerBody()?['Qty'])
@div(triggerBody()?['Fee'], 3)
@int(div(10, 3))
@mod(10, 3)
@formatNumber(1234567.891, 'N2')
There is no + - * / in Power Automate You write add(), sub(), mul() and div(). Nested arithmetic gets ugly fast: "fee plus 18 per cent" is add(fee, mul(fee, 0.18)). Build it up in Compose actions rather than writing one enormous expression - your future self will thank you.

Reading a long expression

Work from the inside out. Take this:

@toUpper(first(split(triggerBody()?['Name'], ' ')))
  1. triggerBody()?['Name'] gives "anita sharma"
  2. split(..., ' ') gives ["anita", "sharma"]
  3. first(...) gives "anita"
  4. toUpper(...) gives "ANITA"
@triggerBody()?['Name']
@split(triggerBody()?['Name'], ' ')
@first(split(triggerBody()?['Name'], ' '))
@toUpper(first(split(triggerBody()?['Name'], ' ')))
Build it a layer at a time That is not just how you read one - it is how you write one. Get the innermost part working in a Compose, then wrap the next function around it, run again. Writing the whole thing and then debugging it is how people lose an afternoon.

Try these yourself

  1. Get the domain out of an email address using split and last.
  2. Write an expression that gives the first letter of the region in capitals.
  3. Add 18 per cent GST to a fee, and round it down to a whole number.
  4. Use coalesce to supply "Not given" whenever a phone field is empty.
  5. Explain what the ? does in ?[\x27Field\x27] and why you should always use it.