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

oh-tanstack-router

v0.1.0

Published

[![npm version](https://img.shields.io/npm/v/oh-tanstack-router)](https://www.npmjs.com/package/oh-tanstack-router) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Readme

oh-tanstack-router

npm version License: MIT

为 TanStack Router 打造的基于类的中间件系统与类型安全守卫。

oh-tanstack-router@tanstack/react-router 的一个轻量级封装。它引入了类似 Koa/NestJS 的中间件 (Middleware) 机制,自动处理路由元数据(Meta/Context)合并,并提供优雅的取消与重定向逻辑,同时完美保留了原生的类型安全特性。

✨ 核心特性

  • 🛡️ 基于类的中间件:通过继承 Middleware 类,以管道模式组织鉴权、日志、埋点等逻辑。
  • 🧬 自动 Meta 合并:在 beforeLoad 阶段自动合并父子路由的 context,无需手动遍历。
  • 🚦 智能取消与重定向:提供 cancel() 机制,自动记录被拦截前的页面位置(lastLocation),实现登录后自动跳回。
  • 💪 TypeScript 优先:完全保留 @tanstack/react-router 的类型推导,无 any 污染。
  • 🔌 无缝集成:仅替换 Root Route 的创建方式,其余 API 与原生保持一致。

📦 安装

npm install oh-tanstack-router
# 或者
pnpm add oh-tanstack-router
# 或者
yarn add oh-tanstack-router

🚀 快速开始

1. 定义全局 Context 类型

首先定义应用中通用的路由上下文(Meta 数据)接口。

// types.ts
export interface AppRouterContext {
  title?: string;
  auth?: boolean;   // 是否需要登录
  roles?: string[]; // 角色权限
}

2. 编写中间件

继承 Middleware 类来实现你的业务逻辑。

// middlewares/AuthMiddleware.ts
import { Middleware, type MiddlewareContext, cancel } from 'oh-tanstack-router';
import { AppRouterContext } from '../types';

export class AuthMiddleware extends Middleware<AppRouterContext> {
  // 1. register: 决定当前中间件是否需要运行
  register(ctx: MiddlewareContext<AppRouterContext>) {
    // 只有当路由配置了 auth: true 时才执行 handle
    return !!ctx.meta.auth;
  }

  // 2. handle: 执行具体的业务逻辑
  async handle(ctx: MiddlewareContext<AppRouterContext>) {
    const isLogged = checkUserLoginStatus(); // 你的检查逻辑

    if (!isLogged) {
      // 抛出 cancel(),库会自动处理重定向并记录当前页面
      throw cancel();
    }
  }
}

3. 创建 Root Route

使用 createRootRouteWithMiddleware 替代原生的 createRootRouteWithContext

// routes/__root.tsx
import { createRootRouteWithMiddleware, Outlet } from 'oh-tanstack-router';
import { AuthMiddleware } from '../middlewares/AuthMiddleware';
import { AppRouterContext } from '../types';

export const rootRoute = createRootRouteWithMiddleware<AppRouterContext>({
  // 注入中间件数组,按顺序执行
  middlewares: [new AuthMiddleware()],
  
  // 当中间件抛出 cancel() 时的默认跳转路径(通常是登录页)
  defaultCancelPath: '/login',
  
  // 初始 context
  context: { auth: false },
  
  component: () => <Outlet />,
});

4. 定义子路由

像平时一样定义子路由。 ⚠️ 重要:为了让中间件生效,context 必须定义为对象字面量(静态对象)。

// routes/index.tsx
import { createRoute } from 'oh-tanstack-router';
import { rootRoute } from './__root';

// 🔒 需要登录的页面
export const adminRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/admin',
  // ✅ 静态对象:中间件可以读取到 auth: true
  context: { auth: true, title: '控制台' }, 
  component: () => <div>Admin Page</div>,
});

// 🔓 公开页面
export const loginRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: '/login',
  // ✅ 明确标记不需要鉴权
  context: { auth: false }, 
  component: () => <div>Login Page</div>,
});

5. 创建 Router 实例

这步与原生 TanStack Router 完全一致。

// router.ts
import { createRouter } from 'oh-tanstack-router';
import { rootRoute } from './routes/__root';
import { adminRoute, loginRoute } from './routes/index';

const routeTree = rootRoute.addChildren([loginRoute, adminRoute]);

export const router = createRouter({
  routeTree,
  context: { auth: false },
});

// 注册类型安全
declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

📖 API 参考

createRootRouteWithMiddleware(options)

创建带有中间件能力的 Root Route。

| 参数 | 类型 | 说明 | 默认值 | |Data | Type | Description | Default | | --- | --- | --- | --- | | middlewares | Middleware[] | 中间件实例数组,按顺序执行 | [] | | defaultCancelPath | string | 捕获 CancelError 后的重定向目标路径 | '/' | | context | T | 初始 Context 对象 | - | | ...others | - | 其他 TanStack Router 原生配置 (loader, component等) | - |

class Middleware<Context>

抽象基类,用于定义中间件。

  • register(ctx: MiddlewareContext): boolean
    • 用于过滤。返回 true 时,handle 方法会被调用;返回 false 则跳过。
  • abstract handle(ctx: MiddlewareContext): Promise<void> | void
    • 执行核心逻辑。如果验证失败,应抛出错误或重定向。

MiddlewareContext 对象

传递给中间件的上下文对象,包含以下核心属性:

  • meta: Context - 核心属性。已自动合并了当前匹配链上所有路由的 context
  • to: ParsedLocation - 即将前往的目标位置。
  • from: ParsedLocation | undefined - 来源位置。
  • ...others: 包含 router, params, search 等原生属性。

cancel()

一个辅助函数,用于中断导航。

  • 当你在中间件中 throw cancel() 时:
    1. 库会检查是否存在 lastLocation(你从哪里被拦截的)。
    2. 如果有,它会自动携带这个位置信息重定向到 defaultCancelPath
    3. 如果当前已经在 defaultCancelPath,则停止重定向以防止死循环。

⚠️ 最佳实践与注意事项

1. Context 必须是静态对象

oh-tanstack-routerbeforeLoad 阶段使用 matchRoutes 策略预先获取子路由信息。为了确保中间件能正确读取子路由的 Meta 数据,请务必使用对象字面量定义 context

// ✅ 正确:中间件能读到 auth: true
createRoute({
  path: '/admin',
  context: { auth: true }
})

// ❌ 错误:中间件读不到,因为这是运行时函数
createRoute({
  path: '/admin',
  context: () => ({ auth: true })
})

2. 中间件执行时机

中间件运行在 Root Route 的 beforeLoad 钩子中。这意味着它会先于所有子路由的 loader 执行。这非常适合做全局鉴权,因为可以在数据加载前就拦截请求。

📄 License

MIT