In short
async/await lets you write async code that reads like a normal top-to-bottom recipe. Almost every modern Node backend is written this way, so get very comfortable with it.
Syntactic sugar over Promises that lets asynchronous backend code read top-to-bottom like synchronous code, using `try`/`catch` for error handling.

Think of it like this
College admission day
First the fee counter, then document check, then the ID card desk. You can't skip ahead, so you wait at each one. But the office isn't frozen: clerks keep serving other students while you wait. await pauses your function, not the whole server.
Watch it happen
GET /users/7/orders with await
GET /users/7/orders
1/6A request comes in for a user's orders.
The big ideas
What you need to know
async returns a promise
An async function always returns a promise, even if you write return 5. Callers await it or use .then.
await pauses one function
Only the current function waits. The event loop keeps running everything else.
try/catch works again
A rejected promise becomes a thrown error at the await line, so normal try/catch handles it.
Express 5 catches for you
In Express 5, an error thrown in an async handler goes straight to your error middleware.
See it in code
Walk through the code
1import express from "express";2const app = express();3 4app.get("/users/:id/orders", async (req, res) => {5 const user = await db.findUser(req.params.id);6 if (!user) return res.status(404).json({ error: "User not found" });7 8 const orders = await db.findOrders(user.id);9 res.json({ user, orders });10});11 12// Express 5 sends rejected promises from handlers here automatically13app.use((err, req, res, next) => {14 console.error(err);15 res.status(500).json({ error: "Something went wrong" });16});1/3Await the user, and stop early with 404 if there isn't one.
db is a stand-in for your database layer. In Express 4 you'd need try/catch or a wrapper to forward async errors. Express 5 does it for you.
Save yourself the pain
Common mistakes
Oops
Forgetting await, so user is a Promise and user.id is undefined.
Do this instead
Await every async call whose result you use. TypeScript and the no-floating-promises lint rule help catch this.
Oops
Using await inside forEach and expecting it to wait.
Do this instead
forEach ignores the promises it gets back. Use for...of to go one by one, or Promise.all with map for parallel.
Your mission
Rewrite with async/await
Quick check
What does an async function return if you write return 5 inside it?
Go deeper
- 1Rewrite a `.then()` chain (e.g. a DB query then a response) using async/await.
- 2Practice wrapping async route handlers in try/catch to avoid unhandled rejections.
- 3Learn how to run independent async calls in parallel with `Promise.all` instead of sequential awaits.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.