Skip to content

itsminhz/convex-smart-tags

Repository files navigation

Convex Smart Tags Component

A reusable Convex component that provides intelligent tagging and categorization capabilities with hierarchical organization and analytics. Designed for easy integration into Convex projects and to work across multiple tables and entity types.

Why use this component

  • Core tag management (add, remove, query tags) across table boundaries
  • Tag hierarchy support (parent-child relationships, ancestor/descendant lookups)
  • Tag analytics and trending (daily counts, trending calculation)
  • Type-safe TypeScript client wrapper for convenient usage in Convex functions
  • Example app and test helpers included for fast onboarding

Features

  • Add and remove tags from any entity
  • Query tags for an entity and entities by tag
  • Create and query parent-child tag relationships
  • Track daily tag usage and compute trending tags
  • Cross-table tag usage statistics
  • Full TypeScript types for client and server integration

Installation

Install from npm:

npm install convex-smart-tags
# or
yarn add convex-smart-tags

Quick start

  1. Add the component to your Convex app configuration:
// convex/convex.config.ts
import { defineApp } from "convex/server";
import smartTags from "convex-smart-tags/convex.config";

const app = defineApp();
app.use(smartTags);

export default app;
  1. Use the client wrapper inside Convex functions:
// functions.ts (Convex server-side code)
import { SmartTags } from "convex-smart-tags";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";

const smartTags = new SmartTags(components.smartTags);

export const tagPost = mutation({
  handler: async (ctx) => {
    const postId = "post123";
    await smartTags.addTag(ctx, {
      tagName: "urgent",
      tableName: "posts",
      entityId: postId,
    });
  },
});

export const getPostTags = query({
  args: { postId: v.id("posts") },
  handler: async (ctx, args) => {
    return smartTags.getTagsForEntity(ctx, {
      tableName: "posts",
      entityId: args.postId,
    });
  },
});

See the example/ directory for a complete working example application.

API overview

The component exposes a client wrapper SmartTags with a type-safe surface. Primary methods include:

  • addTag(ctx, args: { tagName, tableName, entityId }): Promise
  • removeTag(ctx, args: { tagName, tableName, entityId }): Promise
  • getTagsForEntity(ctx, args: { tableName, entityId }): Promise<Array>
  • getEntitiesByTag(ctx, args: { tagName, tableName?, limit? }): Promise<Array<{ tableName, entityId }>>
  • createTagHierarchy(ctx, args: { parentTag, childTag }): Promise
  • getChildTags(ctx, args: { parentTag, limit? }): Promise<Array<{ tagName, level }>>
  • getTagAncestors(ctx, args: { tagName }): Promise<Array<{ tagName, level }>>
  • getTrendingTags(ctx, args: { timeRange, limit? }): Promise<Array<{ tagName, usageCount, trend }>>
  • getTagStats(ctx, args: { tagName }): Promise<{ totalUsage, entityCount, createdAt, recentTrend }>
  • getTagsAcrossTables(ctx, args?: { limit? }): Promise<Array<{ tableName, tagName, entityCount }>>

Each method is implemented in a type-safe manner; consult the generated types under src/_generated in your project for full signatures.

Schema

The component registers the following tables in your Convex schema:

  • tags

    • name: string
    • createdAt: number
    • usageCount: number
    • indexed by: by_name
  • entityTags

    • tagName: string
    • tableName: string
    • entityId: string
    • createdAt: number
    • indexes: by_tag, by_entity, by_tag_and_entity
  • tagHierarchy

    • parentTag: string
    • childTag: string
    • createdAt: number
    • indexes: by_parent, by_child, by_parent_and_child
  • tagAnalytics

    • tagName: string
    • usageDate: number
    • dailyCount: number

The full schema definition is in src/component/schema.ts.

Examples

Basic tag management:

// Add a tag to an entity
await smartTags.addTag(ctx, {
  tagName: "important",
  tableName: "posts",
  entityId: "post123",
});

// Get tags for an entity
const tags = await smartTags.getTagsForEntity(ctx, {
  tableName: "posts",
  entityId: "post123",
});

Tag hierarchy:

// Create a parent-child relationship
await smartTags.createTagHierarchy(ctx, {
  parentTag: "technology",
  childTag: "ai",
});

// Get child tags
const children = await smartTags.getChildTags(ctx, { parentTag: "technology" });

// Get ancestor chain
const ancestors = await smartTags.getTagAncestors(ctx, { tagName: "ai" });

Analytics and trending:

// Get trending tags for the last 7 days
const sevenDays = 7 * 24 * 60 * 60 * 1000;
const trending = await smartTags.getTrendingTags(ctx, {
  timeRange: sevenDays,
  limit: 10,
});

// Get comprehensive statistics for a single tag
const stats = await smartTags.getTagStats(ctx, { tagName: "ai" });

Example Convex component registration (from example/convex/convex.config.ts):

import { defineApp } from "convex/server";
import smartTags from "convex-smart-tags/convex.config";

const app = defineApp();
app.use(smartTags);

export default app;

Testing

The package includes test helper registration for the Convex test environment.

// src/test.ts — usage inside tests
import { convextTest } from "convex-test";
import smartTagsTest from "convex-smart-tags/src/test";

const t = convextTest();
smartTagsTest.register(t, "smartTags");

Run the project's test and typecheck commands as configured (see vitest.config.js and package.json scripts).

Development

To develop locally:

  1. Install dependencies
npm install
  1. Build and run example
npm run build
cd example
npm install
npx convex dev
npm run dev
  1. Typecheck and run tests
npm run typecheck
npm run test

License

Apache-2.0 — see the LICENSE file for details.

About

convex-smart-tags

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors