Database integration patterns help you connect your Remix app to a database easily and safely. They make sure your app can save and get data without problems.
Database integration patterns in Remix
import { json } from '@remix-run/node'; import { useLoaderData } from '@remix-run/react'; import { db } from '~/utils/db.server'; export async function loader() { const items = await db.item.findMany(); return json({ items }); } export default function Items() { const { items } = useLoaderData(); return ( <ul> {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> ); }
Use loader to fetch data from the database before the page loads.
Use db from your database client to run queries.
import { json } from '@remix-run/node'; export async function loader() { const users = await db.user.findMany(); return json({ users }); }
export async function action({ request }) { const formData = await request.formData(); const name = formData.get('name'); await db.item.create({ data: { name } }); return null; }
import { json } from '@remix-run/node'; export async function loader() { try { const posts = await db.post.findMany(); return json({ posts }); } catch (error) { throw new Response('Database error', { status: 500 }); } }
This Remix component loads a list of products from the database and shows them on the page. It uses the loader function to get data before rendering.
import { json } from '@remix-run/node'; import { useLoaderData } from '@remix-run/react'; import { db } from '~/utils/db.server'; export async function loader() { const products = await db.product.findMany(); return json({ products }); } export default function Products() { const { products } = useLoaderData(); return ( <main> <h1>Product List</h1> <ul> {products.map(product => ( <li key={product.id}>{product.name} - ${product.price}</li> ))} </ul> </main> ); }
Always keep your database credentials safe and do not expose them in client code.
Use Remix loaders and actions to separate data fetching and data updating logic.
Test your database queries to avoid runtime errors.
Database integration patterns help connect Remix apps to databases smoothly.
Use loaders to fetch data and actions to update data.
Handle errors and keep your data access organized for better apps.
Practice
Solution
Step 1: Understand Remix data fetching
Remix usesloaderfunctions to fetch data on the server before rendering.Step 2: Differentiate from actions and React lifecycle
actionhandles data updates,useEffectandcomponentDidMountare React client hooks, not Remix server loaders.Final Answer:
loader-> Option DQuick Check:
Data fetching =loader[OK]
loader, update with action [OK]- Confusing
loaderwithaction - Using React hooks for server data fetching
- Thinking
componentDidMountruns on server
Solution
Step 1: Identify correct export style
Remix expects named exports for loaders, commonly as async arrow functions.Step 2: Check each option
A uses default export, incorrect for Remix loaders; B declares function then exports as default, also incorrect; C exports named async arrow function, correct Remix pattern; D lacks export keyword.Final Answer:
export const loader = async () => { return fetchData(); } -> Option AQuick Check:
Named async export = export const loader = async () => { return fetchData(); } [OK]
- Using default export instead of named export
- Forgetting async keyword for async data fetching
- Not exporting the loader function
export const loader = async () => {
const users = await db.user.findMany({ where: { active: true } });
return { users };
};Solution
Step 1: Understand the query filter
The query useswhere: { active: true }to get only active users.Step 2: Analyze the returned data structure
The loader returns an object with keyusersholding the array of filtered user records.Final Answer:
An object with a key 'users' containing an array of active user records -> Option BQuick Check:
Filtered query returns object with users array [OK]
where limit results; output matches filtered data [OK]- Assuming all users are returned without filter
- Confusing returned object with just array
- Thinking
findManyis invalid
export const action = async ({ request }) => {
const formData = await request.formData();
const name = formData.get('name');
await db.user.create({ data: { name } });
return redirect('/users');
};Solution
Step 1: Check Remix imports
Theredirectfunction must be imported from Remix to be used.Step 2: Validate other code parts
formData.getis correct to get form values;redirectis synchronous;db.user.createis valid Prisma method.Final Answer:
Missing import ofredirectfrom Remix -> Option AQuick Check:
Always importredirectbefore use [OK]
redirect [OK]- Forgetting to import
redirect - Confusing formData methods
- Misnaming Prisma methods
Solution
Step 1: Understand Prisma include syntax
To fetch related data, useincludewith the related model name set totrue.Step 2: Evaluate each option
B correctly includes author data; A only selects authorId, not full author; C misuseswherefor inclusion; D tries to include a field, not a relation.Final Answer:
await db.post.findMany({ include: { author: true } }) -> Option CQuick Check:
Useincludeto fetch related models [OK]
include with relation name to fetch related data [OK]- Using
whereinstead ofincludefor relations - Selecting foreign keys instead of related objects
- Including fields instead of relations
Ready to prove it?
Pass the test and this pattern moves unknown
