npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@timdreamer/dataloader

v1.0.0

Published

A lightweight, framework-agnostic batch processing utility for optimizing asynchronous data loading

Readme

中文 | English

@timdreamer/dataloader

輕量級、框架無關的批次處理和去重工具,用於優化 JavaScript/TypeScript 應用程式中的非同步資料載入。

npm version License: MIT

功能特色

  • 自動批次處理 - 將多個請求合併為單一批次操作
  • 請求去重 - 同一批次中相同的請求共享同一個 Promise
  • 批次隔離 - 每個批次完全獨立,防止快取污染
  • 零依賴 - 純 TypeScript 實作,無外部依賴
  • 框架無關 - 可在任何 JavaScript/TypeScript 環境中使用
  • 型別安全 - 完整的 TypeScript 支援與泛型
  • 輕量級 - 核心實作僅約 60 行程式碼

安裝

npm install @timdreamer/dataloader
yarn add @timdreamer/dataloader
pnpm add @timdreamer/dataloader

快速開始

import { Dataloader } from '@timdreamer/dataloader';

// 定義批次載入函式
const batchLoadUsers = async (userIds: number[]) => {
  // 在一次 API 呼叫中取得多個使用者
  const users = await fetch('/api/users/batch', {
    method: 'POST',
    body: JSON.stringify({ ids: userIds })
  }).then(res => res.json());

  // 以相同的順序回傳結果
  return userIds.map(id => users.find(u => u.id === id));
};

// 建立 DataLoader 實例
const userLoader = new Dataloader(batchLoadUsers);

// 載入資料 - 多次呼叫會自動批次處理
const user1 = await userLoader.load(1);
const user2 = await userLoader.load(2);
const user3 = await userLoader.load(3);

框架整合範例

React

// hooks/useDataLoader.ts
import { useMemo } from 'react';
import { Dataloader } from '@timdreamer/dataloader';

export function useUserLoader() {
  const loader = useMemo(
    () => new Dataloader<number, User>(async (userIds) => {
      const response = await fetch('/api/users/batch', {
        method: 'POST',
        body: JSON.stringify({ ids: userIds }),
      });
      return response.json();
    }),
    []
  );

  return loader;
}

// components/UserProfile.tsx
function UserProfile({ userId }: { userId: number }) {
  const userLoader = useUserLoader();
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    userLoader.load(userId).then(setUser);
  }, [userId]);

  return user ? <div>{user.name}</div> : <div>載入中...</div>;
}

Vue 3

// composables/useDataLoader.ts
import { Dataloader } from '@timdreamer/dataloader';

export function useUserLoader() {
  const loader = new Dataloader<number, User>(async (userIds) => {
    const response = await fetch('/api/users/batch', {
      method: 'POST',
      body: JSON.stringify({ ids: userIds }),
    });
    return response.json();
  });

  return {
    loadUser: (id: number) => loader.load(id),
  };
}

// components/UserProfile.vue
<script setup lang="ts">
const props = defineProps<{ userId: number }>();
const { loadUser } = useUserLoader();
const user = ref<User | null>(null);

onMounted(async () => {
  user.value = await loadUser(props.userId);
});
</script>

Next.js / Nuxt.js (SSR)

⚠️ 重要:在 SSR 環境中,避免使用模組層級的單例模式,以防止跨請求資料污染。

// Nuxt 3 - composables/useDataLoader.ts
import { Dataloader } from '@timdreamer/dataloader';

export function useUserLoader() {
  // useState 確保 SSR 中的請求隔離
  const loader = useState(
    'userLoader',
    () => new Dataloader<number, User>(async (userIds) => {
      const response = await $fetch('/api/users/batch', {
        method: 'POST',
        body: { ids: userIds },
      });
      return response;
    })
  );

  return {
    loadUser: (id: number) => loader.value.load(id),
  };
}

Node.js / Express

// middleware/dataloader.ts
import { Dataloader } from '@timdreamer/dataloader';

// 為每個請求建立新的 DataLoader 實例
export function createLoaders() {
  return {
    userLoader: new Dataloader(async (userIds: number[]) => {
      const users = await db.users.findMany({
        where: { id: { in: userIds } },
      });
      return userIds.map(id => users.find(u => u.id === id));
    }),
  };
}

// 附加到請求上下文
app.use((req, res, next) => {
  req.loaders = createLoaders();
  next();
});

API

new Dataloader<T, R>(loaderFn)

建立新的 DataLoader 實例。

  • loaderFn: (args: T[]) => Promise<R[]> | R[] 接受鍵值陣列並回傳值陣列(或 Promise)的函式。 回傳的陣列必須與輸入陣列具有相同的長度和順序。

loader.load(key: T): Promise<R>

載入單一值。同一批次中的多次呼叫會自動批次處理。

實際應用案例

// 多個元件請求相同的資料
function ProductCard({ productId }: { productId: number }) {
  const product = await productLoader.load(productId);
  // ...
}

function ProductPrice({ productId }: { productId: number }) {
  const product = await productLoader.load(productId);
  // ...
}

function ProductReviews({ productId }: { productId: number }) {
  const product = await productLoader.load(productId);
  // ...
}

// 三個元件都請求 productId: 123
// DataLoader 自動:
// 1. 將所有請求批次為單一 API 呼叫
// 2. 去重複的 productId
// 3. 回傳相同結果給三個元件

文件

執行範例

執行範例程式:

# 基礎範例
npm run demo:basic

# 進階範例
npm run demo:advanced

貢獻

歡迎貢獻!請隨時提交 Pull Request。

授權

MIT © TimDreamer

致謝