Skip to content

.retrieve() calls should accept the object as first argument and allow reusing existing expansions #2305

@thexeos

Description

@thexeos

Is your feature request related to a problem? Please describe.

It is common that objects being passed around come expanded (e.g., webhook handler had to expand the object for routing purposes), but that can't be narrowed down with types, so boilerplate (or helper functions) are inevitable. For example:

async function getChargePaymentIntent(charge: Stripe.Charge): Promise<Stripe.PaymentIntent | null> {
  return typeof charge.payment_intent === 'string'
    ? await stripe.paymentIntents.retrieve(charge.payment_intent)
    : charge.payment_intent
}

Describe the solution you'd like

It would be great if all retrieve() methods could accept the object (we'll skip over null - developer can handle that before making SDK calls), read id property on it and perform the fetch:

async function getChargePaymentIntent(charge: Stripe.Charge): Promise<Stripe.PaymentIntent> {
  return await stripe.paymentIntents.retrieve(charge.payment_intent)
}

Of course, if the object is already expanded we may be making an extra API call when data is already available, but most of my code is doing something like this:

async function getChargeSubscription(charge: Stripe.Charge): Promise<Stripe.Subscription | null> {
  return (await stripe.paymentIntents.retrieve(charge.payment_intent, {
    expand: ['invoice.subscription'],
  }))?.invoice?.subscription ?? null
}

So API will be called regardless. To avoid needless API calls, maybe retrieve could (optionally) return the object if it already matches the requested "shape" (aka with everything in expand already expanded:

async function getChargeInvoiceWithSubscription(charge: Stripe.Charge): Promise<Stripe.Invoice | null> {
  const paymentIntent = await stripe.paymentIntents.retrieve(charge.payment_intent, {
    expand: ['invoice.subscription'],
  }) // 1 API call is made, assuming `charge` has nothing expanded, yet

  if (!paymentIntent?.invoice) {
    return null
  }

  return await stripe.invoices.retrieve(paymentIntent.invoice, {
    expand: ['subscription'],
    staleOkay: true, // new parameter
  }) // 0 API calls are made, since `subscription` was expanded before and is already available
}

Additional context

#1793 brings up a related pain point, where reading IDs means writing boilerplate code (or using helper functions).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions