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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rakun-kit/next

v1.3.11

Published

Next.js adapter for Rakun APIs, manager UI, and media endpoints.

Downloads

1,892

Readme

@rakun-kit/next

Next.js App Router adapter for Rakun. It provides a catch-all route handler, optional tRPC mounting, local media handlers, and a manager page component.

API Route Handler

Create a catch-all App Router route and export the methods returned by rakunNext:

// app/api/rakun/[...slug]/route.ts
import { rakunNext } from "@rakun-kit/next";

export const { GET, POST, PUT } = rakunNext({
  bootstrap: {
    literals,
    contentTypes: [Page, Post],
    mongo: {
      MONGO_URI: process.env.MONGO_URI!,
    },
  },
});

By default, the handler:

  • calls ensureRakunBootstrap(bootstrap) when bootstrap is provided.
  • calls ensureRakunInitialized() before each request.
  • serves GET /api/rakun/health unless healthPath is false.
  • serves Rakun operation routes through rakunNextCrud().
  • serves media upload routes when media is configured.

Options:

type RakunNextOptions = {
  bootstrap?: RakunBootstrapOptions;
  healthPath?: string | false;
  integrations?: RakunNextIntegration[];
};

Use integrations for extra handlers. Each integration receives the Fetch Request, Next route context, and normalized path segments. The first integration that returns a Response wins.

tRPC

Mount a tRPC router in the same catch-all route with @rakun-kit/next/trpc:

// app/api/rakun/[...slug]/route.ts
import { rakunNext, rakunNextCrud } from "@rakun-kit/next";
import { rakunNextTrpc } from "@rakun-kit/next/trpc";
import { appRouter } from "@/server/trpc";

export const { GET, POST, PUT } = rakunNext({
  bootstrap,
  integrations: [
    rakunNextCrud(),
    rakunNextTrpc({
      path: "trpc",
      router: appRouter,
    }),
  ],
});

The tRPC integration creates a Rakun request context from Fetch API headers, cookies, and response headers.

Web Pages

Fetch Rakun page data from a Next Server Component with @rakun-kit/next/web:

// app/[[...slug]]/page.tsx
import {
  createRakunPageMetadata,
  getRakunPage,
  getRakunPathFromParams,
  RakunPageRenderer,
  type RakunNextPageParams,
  type RakunNextPageSearchParams,
} from "@rakun-kit/next/web";

type Props = {
  params: Promise<RakunNextPageParams>;
  searchParams: Promise<RakunNextPageSearchParams>;
};

export async function generateMetadata({ params, searchParams }: Props) {
  const page = await getRakunPage({
    path: getRakunPathFromParams({ params: await params }),
    search: await searchParams,
    apiBaseUrl: "/api/rakun",
  });

  return createRakunPageMetadata(page);
}

export default async function Page({ params, searchParams }: Props) {
  const page = await getRakunPage({
    path: getRakunPathFromParams({ params: await params }),
    search: await searchParams,
    apiBaseUrl: "/api/rakun",
  });

  return (
    <RakunPageRenderer
      page={page}
      loadModule={(name) => import(`@/modules/${name}`)}
    />
  );
}

RakunPageRenderer is a server component renderer. Modules are server modules by default, so they can fetch data and render without client JavaScript. If a module needs hooks or browser events, add "use client" at the top of that module file; Next.js will make only that module a client component. When a Rakun page response includes a redirect, the renderer calls the matching Next.js redirect helper before rendering modules.

Module files should export either default or component:

// modules/Hero.tsx
export default function Hero({ title }: { title: string }) {
  return <section>{title}</section>;
}

getRakunPage forwards request headers by default, including cookies, so redirects, preview state, locale, and auth-aware logic can use request context. It defaults to cache: "no-store". Pass fetchOptions for ISR/cache control:

await getRakunPage({
  path: "/",
  fetchOptions: {
    next: { revalidate: 86400, tags: ["rakun-page:/"] },
  },
});

Web API Client

Use @rakun-kit/next/web/client from client components that need to call custom Rakun operations. Keep the operation map in a separate server file, pass it to bootstrap, and import its type in the client:

// server/api-operations.ts
import { defineOperation } from "@rakun-kit/next";
import { z } from "zod";

export const apiOperations = {
  "demo.helloWorld": defineOperation<
    { text: string },
    { message: string },
    "query",
    "get",
    "public"
  >({
    access: "public",
    kind: "query",
    method: "get",
    input: z.object({
      text: z.string().default("world"),
    }),
    output: z.object({
      message: z.string(),
    }),
    resolve: ({ input }) => ({
      message: `Hello ${input.text}`,
    }),
  }),
};
// app/api/rakun/[...slug]/route.ts
import { rakunNext } from "@rakun-kit/next";
import { apiOperations } from "@/server/api-operations";

export const { GET, POST, PUT } = rakunNext({
  bootstrap: {
    // ...
    apiOperations,
  },
});
// modules/HelloWorld.tsx
"use client";

import {
  createRakunApiClient,
  type GetClient,
} from "@rakun-kit/next/web/client";
import type { apiOperations } from "@/server/api-operations";

type ApiClient = GetClient<typeof apiOperations>;

const apiClient: ApiClient = createRakunApiClient<typeof apiOperations>({
  baseUrl: "/api/rakun",
});

const result = await apiClient.query("demo.helloWorld", {
  text: "Rakun",
});

query only accepts operations declared with kind: "query". mutation only accepts operations declared with kind: "mutation". Input and output types are derived from each operation's Zod schemas.

Manager Page

Render the manager from a Next App Router page with @rakun-kit/next/manager:

// app/backend/[[...slug]]/page.tsx
import {
  RakunManagerPage,
  type RakunManagerPageProps,
} from "@rakun-kit/next/manager";

export default function Page(props: RakunManagerPageProps) {
  return (
    <RakunManagerPage
      {...props}
      apiBaseUrl="/api/rakun"
      basePath="/backend"
    />
  );
}

RakunManagerPage expects Next's Promise-based params and searchParams props.

Options:

  • apiBaseUrl: base URL used by the manager HTTP client. Defaults to /api.
  • managerClient: custom manager client. If omitted, a HTTP client is created.
  • basePath: manager mount path. Defaults to /backend.
  • paramKey: route param key used to read path segments. Defaults to slug.
  • loadingFallback, unauthenticatedFallback: optional React fallbacks.

Local Media

Use createLocalMediaServiceConfig from @rakun-kit/next/media in bootstrap media config:

// app/api/rakun/[...slug]/route.ts
import path from "node:path";
import { rakunNext } from "@rakun-kit/next";
import { createLocalMediaServiceConfig } from "@rakun-kit/next/media";

export const { GET, POST, PUT } = rakunNext({
  bootstrap: {
    // ...
    media: createLocalMediaServiceConfig({
      rootDir: path.join(process.cwd(), ".rakun/media"),
      baseUrl: "/api/rakun",
      publicBaseUrl: "/api/rakun",
      tokenSecret: process.env.RAKUN_MEDIA_TOKEN_SECRET!,
      defaultAccess: "private",
    }),
  },
});

When this config is detected, rakunNext serves:

  • PUT /api/rakun/media/local/upload/:token
  • GET /api/rakun/media/local/private/:token
  • GET /api/rakun/media/public/*

Exports

  • @rakun-kit/next: rakunNext, rakunNextCrud, local media helpers, and shared route utilities.
  • @rakun-kit/next/trpc: rakunNextTrpc.
  • @rakun-kit/next/media: LocalAdapter, local media config, and local HTTP handlers.
  • @rakun-kit/next/manager: RakunManagerPage and manager page types.
  • @rakun-kit/next/web: getRakunPage, RakunPageRenderer, and page path helpers.
  • @rakun-kit/next/web/client: Rakun React renderers and typed API client helpers for client components.

Build

bun run build --workspace @rakun-kit/next