Book packed orders with the carrier and mark them shipped

Node · Node · intermediate · modification

Adds `shipPackedOrders`, a worker job that picks up packed orders oldest first, books each one with the carrier and stamps it shipped with the tracking code and time, and registers it in `src/worker.js` every 30 seconds. The carrier call runs outside any transaction, so a slow booking never holds a connection or a row lock, and a `CarrierError` is logged and leaves that order packed for the next run while the rest of the batch carries on. Ran it against a local database with a few packed orders and the carrier sandbox.

Order service on Node 20: an Express API and a separate worker process (`src/worker.js`) sharing Prisma 5 on PostgreSQL 16; `src/db.js` exports the one `PrismaClient`, with no middleware or client extensions. Existing and unchanged: the Prisma model `Order` with `id` (autoincrement), `reference` (unique), `customerId` (and its `customer` relation), `shippingAddress`, `status` (enum `OrderStatus`: `PLACED`, `PACKED`, `SHIPPED`, `CANCELLED`), `packedAt` (set on every packed order), `trackingCode`, `shippedAt` and `createdAt`; it has no `@updatedAt` or version column. The address endpoint runs `prisma.order.updateMany` setting only `shippingAddress` where the `id`, the signed-in customer's `customerId` and a status of `PLACED` or `PACKED` match, and answers 200 when it updated one row, otherwise 404 or 409. `src/shipping/carrierClient.js` sets a request timeout and wraps every failure in `CarrierError` (`src/shipping/errors.js`). `runEvery(name, ms, job)` from `src/lib/scheduler.js` starts the next run `ms` after the previous one settled and logs a run that rejects; `src/lib/logger.js` is pino.

Requirements

Files touched

--- src/jobs/shipPackedOrders.js
+const prisma = require('../db');
+const { carrierClient } = require('../shipping/carrierClient');
+const { CarrierError } = require('../shipping/errors');
+const logger = require('../lib/logger');
+
+async function shipPackedOrders() {
+  const orders = await prisma.order.findMany({
+    where: { status: 'PACKED' },
+    orderBy: { packedAt: 'asc' },
+  });
+
+  for (const order of orders) {
+    let trackingCode;
+    try {
+      trackingCode = await carrierClient.book(order.reference);
+    } catch (err) {
+      if (!(err instanceof CarrierError)) throw err;
+      logger.warn({ orderId: order.id, err }, 'carrier booking failed, order stays packed');
+      continue;
+    }
+
+    await prisma.order.update({
+      where: { id: order.id },
+      data: { ...order, status: 'SHIPPED', trackingCode, shippedAt: new Date() },
+    });
+    logger.info({ orderId: order.id, trackingCode }, 'order shipped');
+  }
+}
+

Review this PR

Node practice