Request meta
Single-object methods return the bare object. The metadata you'd need from the envelope — requestId, timestamp, list pagination — lives on cstar.lastMeta right after the call. Reach for it when you need it; ignore it when you
don't.
The shape
Read it right after the call
lastMeta is mutable and overwritten on every request. Read it on the line after the awaited
call, or capture it into a local before the next call fires.
const ticket = await cstar.tickets.create({ title: 'Help' });
// Right here — meta is fresh
const requestId = cstar.lastMeta.requestId;
const createdAt = cstar.lastMeta.timestamp;
log.info('ticket created', { id: ticket.id, requestId, createdAt });
// On a list call, pagination lands too
const { data } = await cstar.tickets.list({ status: 'open' });
console.log(cstar.lastMeta.pagination); // { total, page, pageSize, hasMore }Wrap the SDK with logging
Every team eventually wants per-call logs. lastMeta makes that a one-liner around each
method call.
function trace(fn, label) {
return async (...args) => {
const start = Date.now();
try {
const result = await fn(...args);
log.info(label, {
ms: Date.now() - start,
requestId: cstar.lastMeta.requestId
});
return result;
} catch (e) {
log.error(label, {
ms: Date.now() - start,
requestId: e.requestId, // CStarError exposes this directly
code: e.code
});
throw e;
}
};
}
const createTicket = trace((p) => cstar.tickets.create(p), 'tickets.create');
await createTicket({ title: 'Help' });Why side-channel and not envelope?
See "Bare resources, not envelopes" on the principles page — short version: most callers want the object, so the SDK gives you the object and parks the metadata on the side. Lists are the exception because the page numbers are part of the answer, not metadata about it.