Serving Data with Remix
Introduction: Serving Data with Remix API Routes
Welcome back! In the last lesson, you learned how to read query parameters from incoming requests to make your API more dynamic. Now, let’s take the next step: serving a list of data from your Remix API route. This is a common task in web development — whether you’re building a social media app, an online store, or a dashboard, you’ll often need to send lists of users, products, or other items to the frontend.
In this lesson, you will learn how to serve a list of users from a mock database, filter that list using query parameters, and return a well-structured JSON response. By the end, you’ll be able to create an API route that returns useful data in a format that’s easy to work with.
Quick Recap: Mock Data and Project Structure
Before we dive in, let’s quickly remind ourselves about the mock data setup. So far, you’ve returned simple static or dynamic responses. In this lesson, you’ll work with a small dataset and learn how to send real data from your API. For this lesson, we’ll use a file called app/lib/data.ts that acts as our mock database. Here’s what it looks like:
You can import this data into your API route using:
This setup allows you to work with a list of users as if you were using a real database, but everything stays simple and local for now.
Creating the Users API Route
Let’s create an API route that serves the full list of users. In Remix, you place your API route in the app/routes/api.users.tsx file. Here’s how you can set up a basic handler for GET requests:
Explanation:
- We import the
jsonhelper from Remix to send JSON responses. - We import our
usersarray from the mock data file. - The
loaderfunction handles GET requests to/api/users. - We return the list of users as a JSON object.
🧠 This is an example of a read-only route, where your server simply sends back data and doesn’t modify anything. In this setup,
usersis the complete dataset, so we don’t need to check for parameters yet. The result is deterministic: the same every time unless the code changes.
Sample Output:
When you visit /api/users, you’ll get a response like:
Filtering Data with Query Parameters
Now, let’s make our API more flexible by allowing users to filter the list. For example, you might want to see only users with the role "admin" or only those who are active.
Here’s how you can read query parameters and filter the data in Remix:
Explanation:
- We use the
URLobject to parse the request URL and get query parameters. - If a
roleparameter is present, we filter users by their role. Thefilter()method goes through every user and checks if theirrolematches the value from the URL. To make sure it’s case-insensitive (so "Admin" and "admin" both work), we convert both sides to lowercase before comparing. - If an
activeparameter is present, we filter users by their active status. This filter works a bit differently. In the mock data, each user has a boolean fieldisActive, which is eithertrueorfalse. But query parameters are always strings, so if a client sends?active=true, we manually convert that string to a boolean. - The filtered list is returned as JSON.
For this introductory demo, examples expect active to be either "true" or "false". Full input validation for invalid values such as active=banana is intentionally deferred to a later course.
Here's what's happening behind the scenes:
- The client sends a request like
/api/users?role=admin - You extract that role from the URL
- You loop over the original list of users and only keep those that match the requested role
Think of it like using a search bar that says, “Show me all users where role === 'admin'.”
Sample Output:
Requesting /api/users?role=admin&active=true might return:
Building a Useful API Response
A good API response often includes more than just the data. It can also include metadata, such as the total number of results, the filters used, and a timestamp. This makes it easier for the frontend to display information and for developers to debug.
Here’s how you can structure your response:
Explanation:
- We add a
metadataobject with the total number of users, the current timestamp, and the filters used. - We also include the endpoint, method, and status for clarity.
- This structure helps anyone using your API understand what data they’re getting and why.
Sample Output:
A response like this is often called a "wrapped" response because your data is enclosed inside a larger object that includes context (metadata, status, etc.). This format makes the API more frontend-friendly. For example, the frontend can:
- Display the number of filtered users without re-counting
- Show which filters were applied
- Debug unexpected outputs using timestamps or endpoint metadata
What is metadata?
Metadata means “data about data.” It’s extra information that describes or gives context to your main data (in this case, the filtered list of users).
For example:
totalshows how many users matched the filterstimestamptells when the data was servedfiltersshow what criteria were applied (if any)
This helps developers or frontend apps understand:
- What was returned (user list)
- Why that list looks the way it does (filters used)
- When it was created (timestamp)
Instead of returning just the list, adding metadata makes the API response more transparent, debuggable, and easier to work with on the frontend.
Summary and Practice Preview
In this lesson, you learned how to serve a list of users from a mock database using a Remix API route. You saw how to filter the data using query parameters and how to build a clear, useful API response with metadata. These are essential skills for building real-world APIs that are both flexible and easy to use.
You’ll use this “filter and respond” pattern often — not just for users, but for things like products (/api/products?category=shoes), blog posts (/api/posts?author=john), or even search results. It’s a powerful pattern that lets your frontend ask specific questions — and your backend give focused answers.
Next, you’ll get a chance to practice what you’ve learned. The upcoming exercises will help you reinforce these concepts by having you build and test your own API routes. This hands-on practice will prepare you for more advanced backend development tasks with Remix. Keep up the great work!
