How toCall an action from a loader in Remix

Let's say you have a route with an action, and now you want a route loader to call that action. The simplest way would be to move the code in the action to a different function and call it in both places:

app/routes/something.ts
async function doSomething() { /* code */ } export async function loader(args: LoaderFunctionArgs) { // some code await doSomething() // more code } export async function action(args: ActionFunctionArgs) { // some code await doSomething() // more code }

This way, the doSomething function can receive exactly the arguments it needs and return values instead of responses which gives you more flexibility when using it.

Another option is to call the action:

app/routes/something.ts
export async function action(args: ActionFunctionArgs) { /* some code */ } export async function loader({ request, params, context }: LoaderFunctionArgs) { let body = new FormData() // add values to body here let actionResponse = await action({ request: new Request(request.url, { method: "post", headers: request.headers, body }), params, context, }) let actionData = await actionResponse.json() // more code using actionData here }

And finally another option is to do a fetch to the action:

app/routes/something.ts
export async function action(args: ActionFunctionArgs) { /* some code */ } export async function loader({ request, params, context }: LoaderFunctionArgs) { let actionResponse = await fetch(request.url, { method: "POST", headers }) // more code using actionResponse here }

In all cases where I used request.url that's supposing the action is on the same URL, but if it's in a different URL you can do.

let url = new URL("/some/pathname", request.url);

And this way change the pathname while retaining the hostname and protocol.