Node and Express are the runtime and HTTP framework for your API. Topics here cover bootstrapping the app, parsing JSON bodies, and structured logging—everything that runs before a route handler executes. Master this layer first so later routers and controllers plug into a consistent foundation.

Async and await in Express

Matches Async and await in Express: Promises, async controllers, and awaiting Knex in services.

3 topic(s) on this page.

How Promises work in backend code — Basic

A Promise represents async work that will finish later (database, file, network). Knex queries return promises; Express handlers use async/await to read results cleanly.
Learn: Before async/await, Promises were the standard way to handle async results in Node. Knex still returns a Promise from every query builder chain. Understanding then/catch helps you read older examples; writing new code with async/await is usually clearer in controllers and services.

Supplementary examples

Promise.all for parallel queries

const [users, pastes] = await Promise.all([
  knex("users").select("*"),
  knex("pastes").select("*"),
]);

Course example

// Knex returns a Promise
const promise = knex("users").select("*");

promise
  .then((rows) => console.log(rows))
  .catch((err) => console.error(err));

// Same query with async/await (preferred in controllers/services)
async function listUsers() {
  const rows = await knex("users").select("*");
  return rows;
}

Additional references & examples

How to write async route handlers — Intermediate

Mark controller functions async and await service/Knex calls. Always forward failures with next(err) so the error middleware can respond.
Learn: Async handlers must forward rejections with `next(err)`—otherwise Express may not invoke your error middleware. Wrapping async functions or using a small `asyncHandler` utility removes repetitive try/catch blocks.

Supplementary examples

asyncHandler wrapper

function asyncHandler(fn) {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

router.get("/", asyncHandler(async (req, res) => {
  const rows = await service.list();
  res.json({ data: rows });
}));

Course example

async function create(req, res, next) {
  try {
    const created = await service.create(req.body);
    res.status(201).json({ data: created });
  } catch (err) {
    next(err);
  }
}

Additional references & examples

How to return Promises from services — Intermediate

Service functions return promises (or async functions that return data). Controllers await them; do not mix callback-style Knex with async/await in the same path.
Learn: Keep Promise-returning database code in services. The controller awaits one or more service calls and maps results to HTTP. If you return knex(...) directly from a service without await inside, the controller still awaits the same Promise—both styles work if errors propagate to next(err).

Supplementary examples

async service function

async function remove(pasteId) {
  const deleted = await knex("pastes").where({ paste_id: pasteId }).del();
  if (!deleted) {
    const err = new Error("Paste not found");
    err.status = 404;
    throw err;
  }
}

Course example

// service — return the Knex promise (or use async/await inside)
function listByUser(userId) {
  return knex("pastes")
    .where({ user_id: userId })
    .orderBy("created_at", "desc");
}

// controller
async function list(req, res, next) {
  try {
    const rows = await service.listByUser(req.params.userId);
    res.json({ data: rows });
  } catch (err) {
    next(err);
  }
}

Additional references & examples