# How the RealWorld Feed Algorithm Retrieves Articles from Followed Users

> Discover the RealWorld feed algorithm logic: query followed users, sort by newest, and get paginated articles with author data.

- Repository: [Thinkster/realworld](https://github.com/gothinkster/realworld)
- Tags: internals
- Published: 2026-02-28

---

**The RealWorld feed algorithm queries the database for articles authored by users the authenticated client follows, orders them by newest first, and returns paginated results with computed favorited states and author metadata.**

The **RealWorld** codebase (`gothinkster/realworld`) implements a Medium‑clone API specification that includes a personalized feed endpoint. The core logic for this RealWorld feed algorithm resides in [`apps/api/server/routes/api/articles/feed.get.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/articles/feed.get.ts) and leverages Prisma ORM to execute efficient relational queries across the article and user tables.

## How the Feed Endpoint Filters Articles by Followed Authors

The feed endpoint authenticates the request and applies a relational filter to retrieve only content from authors the current user tracks.

### Authentication and Query Parameter Extraction

The handler first extracts the authenticated user context and pagination parameters. The `auth` object containing the signed‑in user’s ID is provided by private event middleware, while `offset` and `limit` values are parsed from the request query string.

```typescript
const query = getQuery(event);

```

### Counting Matching Articles

Before fetching the data, the algorithm executes a fast `COUNT` query to determine the total number of available articles. The Prisma `where` clause filters for articles whose **author** has a `followedBy` relation containing the current user’s ID.

```typescript
const articlesCount = await usePrisma().article.count({
  where: { author: { followedBy: { some: { id: auth.id } } } },
});

```

This count enables the client to render pagination controls or infinite‑scroll indicators accurately.

## Pagination and Sorting Strategy

The RealWorld feed algorithm implements offset‑based pagination and strict chronological ordering to ensure consistent feed delivery.

### Offset-Based Pagination Implementation

The `findMany` query uses Prisma’s `skip` and `take` options to handle pagination. The `skip` value corresponds to the user‑provided `offset`, while `take` defaults to 10 items unless overridden by the `limit` parameter.

```typescript
skip: Number(query.offset) || 0,
take: Number(query.limit) || 10,

```

### Chronological Ordering

Results are ordered by creation date in descending order to display the newest articles first.

```typescript
orderBy: { createdAt: 'desc' },

```

## Data Projection and Relation Loading

To optimize performance, the algorithm omits unused fields and eagerly loads necessary relations in a single database round‑trip.

### Omitting Unnecessary Fields

The query explicitly excludes the article `body` and `updatedAt` fields using Prisma’s `omit` option. These fields are unnecessary for feed display and reducing payload size improves response times.

```typescript
omit: { body: true, updatedAt: true },

```

### Eager Loading Related Data

The `include` clause fetches the article’s tags, author details, favorited‑by users, and a computed favorites count within the same query. This approach eliminates N+1 query issues when rendering feed items.

```typescript
include: {
  tagList: true,
  author: { include: { followedBy: true } },
  favoritedBy: true,
  _count: { select: { favoritedBy: true } },
}

```

## Mapping Prisma Results to the API Contract

Raw database results undergo transformation to match the Conduit API specification before serialization.

### Article Transformation Logic

The `articleMapper` function in **[`apps/api/server/utils/article.mapper.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/utils/article.mapper.ts)** converts Prisma article objects into the public API shape. This mapping includes transforming the `tagList` relation into an array of tag names and computing the boolean `favorited` flag by checking if the current user’s ID appears within the article’s `favoritedBy` array.

```typescript
articles.map((article) => articleMapper(article, auth.id))

```

### Author Profile Enrichment

The author object is further processed by **[`apps/api/server/utils/author.mapper.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/utils/author.mapper.ts)** to inject the `following` boolean. This status is derived from the `followedBy` relation loaded during the initial query, confirming that the authenticated user indeed follows the author as defined in **[`apps/api/server/models/profile.model.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/models/profile.model.ts)**.

## Summary

- The RealWorld feed algorithm lives in [`apps/api/server/routes/api/articles/feed.get.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/articles/feed.get.ts) and uses Prisma to query the **`article`** table.
- It filters articles using a nested `where` clause that checks if the author’s `followedBy` relation includes the authenticated user’s ID, as defined in **[`apps/api/server/models/article.model.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/models/article.model.ts)**.
- Pagination is handled via `skip` and `take` parameters with a default limit of 10 articles, sorted by `createdAt` in descending order.
- The query omits heavy fields (`body`, `updatedAt`) and eager‑loads tags, author data, and favorite counts to minimize database round‑trips.
- Results are mapped through `articleMapper` and `authorMapper` to conform to the Conduit API response format, including computed `favorited` and `following` states.

## Frequently Asked Questions

### How does the RealWorld feed algorithm determine which articles to show?

The algorithm queries for articles where the author’s `followedBy` relation contains the authenticated user’s ID. This relational check is performed in both the count query and the main data fetch within [`apps/api/server/routes/api/articles/feed.get.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/articles/feed.get.ts).

### What database queries does the feed endpoint execute?

The endpoint executes two sequential Prisma queries: a `count()` operation to determine total available articles, and a `findMany()` operation that retrieves the actual article rows along with their related tags, author profiles, and favorite counts via SQL joins.

### How does the feed handle pagination?

The feed implements offset‑based pagination using `skip` (offset) and `take` (limit) parameters. The client specifies these values in the query string, defaulting to 0 and 10 respectively, allowing for standard paginated or infinite‑scroll interfaces.

### Where is the feed logic implemented in the codebase?

The primary implementation resides in **[`apps/api/server/routes/api/articles/feed.get.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/articles/feed.get.ts)**. Supporting transformation logic is found in **[`apps/api/server/utils/article.mapper.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/utils/article.mapper.ts)** and **[`apps/api/server/utils/author.mapper.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/utils/author.mapper.ts)**, while the data models are defined in **[`apps/api/server/models/article.model.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/models/article.model.ts)** and **[`apps/api/server/models/profile.model.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/models/profile.model.ts)**.