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

@graficos/nuxt-comments

v0.5.1

Published

Embeddable, unstyled comments system for Nuxt 4, backed by Cloudflare D1 and authenticated via @nuxtjs/better-auth.

Downloads

1,227

Readme

@graficos/nuxt-comments

E2E

An embeddable, unstyled comments system for Nuxt 4, backed by Cloudflare D1 and authenticated through the host application's Better Auth setup.

@graficos/nuxt-comments is a reusable Nuxt module — not a hosted widget. It ships the threaded comments primitive (data model, API, composables, unstyled components) and leaves identity to @nuxtjs/better-auth and presentation to you.

Features

  • 🧶 Threaded comments (adjacency list, arbitrary nesting via parentId)
  • 📖Cursor-based pagination for top-level comments and replies
  • 😊 Reactions with configurable types and database-enforced uniqueness
  • 🔑 Server-side ownership checks (edit/delete your own comments only)
  • 🕳️ Soft deletion that preserves threads ([deleted] placeholders)
  • 🚫 Admin user deletion (deleteCommentsUser) with documented semantics
  • 🧹 PII erasure primitive (DELETE /api/_comments/users/:userId) that leaves no user id, name, avatar, body or reaction behind — see privacy.md
  • 💪🏽 Shared validation, consistent HTTP error envelope (thanks to Valibot)
  • ⏱️ Rate-limiter extension point (Cloudflare-native integration documented)
  • 🎨 Unstyled, accessible components with typed slots and per-layer class props
  • 🌍 i18n-ready: override every string (including aria-labels) via comments.messages — no i18n dependency
  • 🔄 Adapter boundary for alternative persistence backends (potentially future feature)

Why

  • Nuxt-native. Server routes, composables and components are registered by the module. No external comment service, no embed script, no analytics.
  • Auth is borrowed, not rebuilt. The package consumes the Better Auth session. Any provider Better Auth supports works without provider-specific code.
  • Unstyled by design. Semantic HTML, accessibility attributes, props/slots/events. You own the CSS, the layout, the auth buttons, the emoji.
  • Backend-abstracted. The domain talks to a CommentsStore interface; D1 is the only shipped implementation. A future GitHub Discussions mirror can be added behind the same boundary.
  • One database per site. No tenant column, no cross-site data. Each Nuxt installation gets its own D1 binding.
  • Resource-scoped. Comments belong to an opaque resource identifier (usually a blog post path).

Architecture

Better Auth  ──►  session (who is the current user?)
                        │
Vue/Nuxt UI  ──►  Comments API (/api/_comments/...)  ──►  CommentsService
  (unstyled)                                                    │
                                                        CommentsStore (interface)
                                                                │
                                                        D1CommentsStore  ──►  D1
  • Server route handlers are thin: parse → session → service → store.
  • CommentsService owns validation, authorization, ownership and deletion policy.
  • D1CommentsStore owns SQL. The domain never imports D1 types.
  • See docs/adapters.md for the future GitHub Discussions architecture (not implemented).

Requirements

  • Nuxt >= 4.0.0
  • @nuxtjs/better-auth (installed automatically as a peer + module dependency in npm/pnpm; see docs/authentication.md)
  • A Cloudflare D1 database and a Wrangler binding for comments (see docs/cloudflare-d1.md)
  • A Better Auth database for durable identities — or accept the no-persistence fallback (OAuth-only, no email/password, no server-side session revocation). Cloudflare D1 can host both the auth tables and the comments.
  • wrangler available to run migrations

Install

pnpm add @graficos/nuxt-comments

@nuxtjs/better-auth is declared as both a peer dependency and a Nuxt moduleDependencies entry. npm/pnpm/yarn auto-install peer dependencies; if your package manager does not, install it explicitly:

pnpm add @nuxtjs/better-auth

Minimal setup

nuxt.config.ts:

export default defineNuxtConfig({
  modules: [
    // @nuxtjs/better-auth is auto-installed via moduleDependencies —
    // you do not need to list it here (listing it is harmless).
    "nuxt-comments",
  ],

  comments: {
    database: {
      binding: "DB", // Same as the Wrangler binding name for your D1 database for the comments
    },
    // Opt-in: persist Better Auth users/sessions in D1 (email/password, OAuth,
    // revocation). Omit to leave Better Auth on its in-memory default.
    auth: {
      database: {
        binding: "DB", // Binding for Better Auth's tables; may equal the comments binding
      },
    },
  },
});

wrangler.jsonc (or wrangler.toml) — the binding name must match comments.database.binding:

{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-comments",
      "database_id": "<your-database-id>",
    },
  ],
}

Better Auth configuration files (server/auth.config.ts and app/auth.config.ts) are created by the Better Auth module. Add providers there:

// server/auth.config.ts
import { defineServerAuth } from "@nuxtjs/better-auth/config";

export default defineServerAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  },
});

.env:

NUXT_BETTER_AUTH_SECRET=<at least 32 random chars>
NUXT_PUBLIC_SITE_URL=http://localhost:3000

Auth persistence. Better Auth stores users, sessions and accounts. Without a database, @nuxtjs/better-auth falls back to Better Auth's in-memory adapter (email/password unavailable, sessions do not survive the current isolate). Set comments.auth.database.binding to persist them in D1. See docs/authentication.md#turnkey-d1-persistence-opt-in.

Run the package migrations against your local D1. Either copy the shipped SQL into your own migrations/ folder, or point a Wrangler config's migrations_dir at the package:

# recommended: copy the package migrations into your app's migrations/ dir
cp -r node_modules/@graficos/nuxt-comments/migrations/* ./migrations/

# apply locally, then remotely when deploying
npx wrangler d1 migrations apply my-comments --local
npx wrangler d1 migrations apply my-comments --remote

Auth tables. migrations/auth/ holds Better Auth's tables and is only needed when you set comments.auth.database.binding. Wrangler's migrations_dir is not recursive, so apply it with a separate Wrangler config pointing at that folder. See docs/authentication.md.

Upgrading. migrations/0002_user_id_nullable.sql makes comments.user_id nullable so user erasure can leave no trace. Copy the new file (or re-point migrations_dir) and re-run wrangler d1 migrations apply; already-applied migrations are skipped.

Then use the component:

<template>
  <Comments resource="/blog/my-post" />
</template>

Component prefix. Nuxt best practice is to prefix module exports, so the default components.prefix is 'Nuxt' and the components are <NuxtComments>, <NuxtComment>, etc. Set components.prefix: '' to get the unprefixed <Comments> shown above. See docs/configuration.md.

Documentation

| Document | Contents | | ------------------------------------------ | --------------------------------------------------------------------------------------------- | | Configuration | Module options, runtime config, env vars, pagination, reactions, limits, resource identifiers | | Authentication | Better Auth integration, providers, email-optional identity, consumer-owned login UI | | Cloudflare & D1 | Database creation, Wrangler binding, local development, migrations, production | | Components | <NuxtComments> props/slots/events, composables, types, styling strategy | | Server API | Endpoints, validation, errors, pagination, deletion policy | | Privacy & erasure | Stored personal data, the PII erasure primitive, account-deletion recipe, controller duties | | Adapters | CommentsStore boundary, D1 adapter, future GitHub Discussions architecture | | Playground | Running the demo app locally, OAuth setup |

Development

pnpm install
pnpm run dev:prepare      # stub-build the module + prepare the playground
pnpm run playground:migrate
pnpm run dev              # http://localhost:3000

Quality gates:

pnpm run lint
pnpm run test             # workers-runtime + node/nuxt test suites (101 tests)
pnpm run test:types
pnpm run prepack          # production build of the published package

# Browser end-to-end tests (Playwright) against the playground
pnpm --dir playground exec playwright install chromium   # one-time
pnpm --dir playground e2e

See playground/README.md for OAuth setup, the admin deletion demo, and the end-to-end tests.

Package structure

src/
  module.ts                         # defineNuxtModule: meta, moduleDependencies, registration
  types.ts                          # module option types + runtime config augmentation
  runtime/
    app/components/                 # unstyled Vue components
    app/composables/                # useComments, useCommentsSession
    shared/                         # types, resources, Valibot schemas (server + client)
    server/
      api/_comments/                # Nitro route handlers (thin)
      services/                     # CommentsService, admin deletion, session helpers
      repositories/                 # CommentsStore interface + D1CommentsStore
      utils/                        # store resolution, errors, HTTP mapping, rate limiting
migrations/
  0001_init.sql                     # comments tables
  0002_user_id_nullable.sql         # nullable user_id for PII erasure
  auth/                             # Better Auth tables (opt-in D1 provider)
playground/                         # Nuxt 4 app exercising the module
  e2e/                              # Playwright end-to-end tests + fixtures
test/
  unit/                             # domain + service + resource tests
  api/                              # API handlers on real Miniflare D1
  nuxt/                             # component tests in a Nuxt environment
  module/                           # module registration/config tests

License

MIT