APIs

Remix routes handle both backend code and UI code in the same file. Loaders fetch data for rendering, and actions handle form submissions and mutations. Both run exclusively on the server.

Loaders and actions

A loader runs on GET requests and provides data to the component. An action runs on POST/PUT/PATCH/DELETE requests and handles mutations. Both are tightly coupled to their route's UI.

export async function loader({ request }: LoaderFunctionArgs) {
  const user = await requireUser(request)
  return json({ user })
}

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData()
  // handle the mutation
  return redirect('/success')
}

Resource routes

Routes that export only a loader or action (no default component) are resource routes. They let you create REST endpoints, generate PDFs, serve images, stream media, and more.

LaunchFast includes resource routes for image serving, health checks, theme switching, and cache management in app/routes/resources+/.

Full-stack components

Some components manage both their UI and their associated backend logic in a single file. This pattern keeps related code colocated — the component knows how to fetch its own data and handle its own mutations through its resource route.

Building REST APIs

You can use resource routes to build APIs for third-party clients or mobile apps. Create a route file with only loader and/or action exports, and return JSON responses:

// app/routes/api+/users.ts
export async function loader({ request }: LoaderFunctionArgs) {
  const users = await prisma.user.findMany()
  return json({ users })
}