convex-smart-tags
v0.1.1
Published
A Convex component for smart tagging and categorization with hierarchy and analytics
Maintainers
Readme
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-tagsQuick start
- 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;- 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:
- Install dependencies
npm install- Build and run example
npm run build
cd example
npm install
npx convex dev
npm run dev- Typecheck and run tests
npm run typecheck
npm run testLicense
Apache-2.0 — see the LICENSE file for details.
