Skip to content
Twilight React Playground
ثيم رائدaren

orThrow, orUnauthorized

functionAdvancedserverbrowserlive demo

Wrap an API call in a loader: orThrow turns any failure into a not-found error, orUnauthorized turns a 401 or 403 into the unauthorized flow.

import { orThrow, orUnauthorized } from '@salla.sa/twilight-theme-engine/providers';

In plain words

API calls fail: a product was deleted, the server had a bad moment, a customer's session expired. Wrapping the call decides what the visitor sees instead of a crash.

await orThrow(product.find(id)) gives you the product, or a clean not-found page. await orUnauthorized(order.find(id)) gives you the order, or the unauthorized page when the API says the customer is not signed in; any other failure is left for you to handle.

Signature

function orThrow<T>(promise: Promise<T>): Promise<T>
// on any rejection: console.debug('[loader error]', error), then throw new NotFoundError()

function orUnauthorized<T>(promise: Promise<T>): Promise<T>
// rejection whose error.response.status is 401 or 403: throw unauthorized()
// any other rejection is rethrown unchanged

Try it live

Both wrappers pass a successful result through untouched; they differ in what a failure becomes.Try this: with orThrow, every failure becomes NotFoundError. Switch to orUnauthorized: only 401 and 403 change, the rest are rethrown as they were.
Storefront canvas · en · LTR

await orThrow(request that fails: HTTP 500) → running…

Controls
What a theme writes
import { orThrow } from '@salla.sa/twilight-theme-engine/providers';
import { product } from '@salla.sa/twilight-theme-engine/api/product';

// Any failure (missing, 500, offline) becomes a 404 page instead of a crash.
export async function loader({ params }: { params: { id: string } }) {
  return { product: await orThrow(product.find(params.id)) };
}

Example

app/routes/order-receipt.$id.tsx (loader)
import { orUnauthorized } from '@salla.sa/twilight-theme-engine/providers';
import { order } from '@salla.sa/twilight-theme-engine/api/order';

export async function loader({ params }: { params: { id: string } }) {
  return { order: await orUnauthorized(order.find(params.id)) };
}

How it behaves

  • The engine uses both: product.findOrThrow is orThrow(product.find(id)), order.findOrThrow is orUnauthorized(order.find(id)), and the orders, wallet, wishlist and notifications loaders wrap their calls in orUnauthorized.

  • orUnauthorized reads error.response.status, the shape of the engine API client's HTTP errors.

Gotchas

  • orThrow throws new NotFoundError() directly, not the adapted notFound(): under TanStack Router it reaches the route's error component (the default one renders a 404 ErrorPage for it), not the not-found component. A custom errorComponent must recognise NotFoundError itself.

  • orThrow reports every failure, a network error or a 500 included, as "not found", keeping the original only in a console.debug line. Use it where "this does not exist" is the honest answer.

  • A rejection without response, such as a plain fetch network error, passes through orUnauthorized unchanged.

Related

Source and docs