Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/how-to/resource-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,64 @@ export function action(_: Route.ActionArgs) {
});
}
```

## Return Types

Resource Routes are flexible when it comes to the return type - you can return [`Response`][Response] instances or [`data()`][data] objects. A good general rule of thumb when deciding which type to use is:

- If you're using resource routes intended for external consumption, return `Response` instances
- Keeps the resulting response encoding explicit in your code rather than having to wonder how React Router might convert `data() -> Response` under the hood
- If you're accessing resource routes from [fetchers][fetcher] or [`<Form>`][form] submissions, return `data()`
- Keeps things consistent with the loaders/actions in your UI routes
- Allows you to stream promises down to your UI through `data()`/[`Await`][await]

While we don't recommend it, you _can_ return plain JS objects from a resource route and they will be converted into an appropriate `Response` based on to the HTTP Request (a single-fetch response for `.data` requests or a JSON response for other external HTTP requests). We don't prefer this pattern because `new Response()`/`data()` calls keep the code explicit for API-only/non-UI routes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has enough "don't do this" language that I almost wonder if it's worth just removing/not mentioning. If you feel it needs to be explained though, that's fine. I think I would just flip it around a little:

It is recommended that you return `new Response()`/`data()` calls from resource routes keep the code explicit for API-only/non-UI routes. While you _can_ return a plain JS objects from a resource route, they will be converted into different a `Response` based on to the HTTP Request (a single-fetch response for `.data` requests or a JSON response for other external HTTP requests).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I went back and forth on that too - let's just remove it


## Error Handling

Throwing an `Error` from Resource route (or anything other than a `Response`/`data()`) will trigger [`handleError`][handleError] and result in a 500 HTTP Response:

```tsx
export function action() {
let db = await getDb();
if (!db) {
// Fatal error - return a 500 response and trigger `handleError`
throw new Error("Could not connect to DB");
}
// ...
}
```

If a resource route generates a `Response` (via `new Response()` or `data()`), it is considered a successful execution and will not trigger `handleError` because the API has successfully produced a Response for the HTTP request. This applies to thrown responses as well as returned responses with a 4xx/5xx status code. This behavior aligns with `fetch()` which does not return a rejected promise on 4xx/5xx Responses.

```tsx
export function action() {
// Non-fatal error - don't trigger `handleError`:
throw new Response(
{ error: "Unauthorized" },
{ status: 401 },
);

// These 3 are equivalent to the above
return new Response(
{ error: "Unauthorized" },
{ status: 401 },
);

throw data({ error: "Unauthorized" }, { status: 401 });

return data({ error: "Unauthorized" }, { status: 401 });
}
```

### Error Boundaries

[Error Boundaries][error-boundary] are only applicable when a resource route is accessed from a UI, such as from a [`fetcher`][fetcher] call or a [`<Form>`][form] submission. If you `throw` from your resource route in these cases, it will bubble to the nearest `ErrorBoundary` in the UI.

[handleError]: ../api/framework-conventions/entry.server.tsx#handleerror
[data]: ../api/utils/data
[Response]: https://developer.mozilla.org/en-US/docs/Web/API/Response
[fetcher]: ../api/hooks/useFetcher
[form]: ../api/components/Form
[await]: ../api/components/Await
[error-boundary]: ../start/framework/route-module#errorboundary