Skip to content

Tags

Tags enable additional access patterns beyond the default entity list. While every entity can be listed and fetched by ID, tags let you query subsets of entities by group, sorted by a value — without scanning the entire table.

For example, without tags you can only do:

  • List all organisations
  • Get organisation by ID

With tags, you can also do:

  • List all organisations of type "club"
  • List all organisations in a specific country, sorted by creation date
  • List all orders with status "pending", sorted by amount

Tags are powered by a tag processor that runs automatically whenever an entity is created or updated, keeping tag indexes in sync.

Key characteristics

  • Tags are attached to a single type of entity
  • Each tag has a name and a processor that produces entries with optional group and sortValue
  • An entity can have multiple tags across different dimensions
  • Tags support filtering by group and range queries on sortValue
  • The tag processor runs automatically on create/update — no manual indexing needed

Defining tags

Tags are configured in your entity config. The processor function receives the entity and returns an array of tag entries.

Group only (filter by category)

Use group when you want to filter entities by a category:

ts
const config = createEntityConfig({
  name: 'organisation',
  displayName: 'Organisation',
  baseSchema,
  tags: [
    {
      name: 'type',
      processor: (entity) => {
        return [{ group: entity.data.type }];
      },
    },
  ],
});

This lets you query: "Give me all organisations of type club"

Sort value only (range queries)

Use sortValue when you want to sort or query a range without grouping:

ts
tags: [
  {
    name: 'created',
    processor: (entity) => {
      return [{ sortValue: entity.createdAt }];
    },
  },
],

This lets you query: "Give me all organisations created between Jan and March 2025"

Group + sort value (filter and sort)

Combine both for filtered, sorted queries:

ts
tags: [
  {
    name: 'region-activation',
    processor: (entity) => {
      return [
        {
          group: entity.data.region,
          sortValue: entity.data.activatedAt,
        },
      ];
    },
  },
],

This lets you query: "Give me all organisations in eu-west-1, sorted by activation date"

Multiple entries per tag

A single processor can return multiple entries. For example, an order that belongs to multiple categories:

ts
tags: [
  {
    name: 'status',
    processor: (entity) => {
      const entries = [{ group: entity.data.status }];
      // Also index by payment status
      if (entity.data.paymentStatus) {
        entries.push({ group: `payment-${entity.data.paymentStatus}` });
      }
      return entries;
    },
  },
],

Querying tags (API)

GET /core/tag/:entityType/:tagName?group=...&query=...&start=...&end=...&limit=...&lastKey=...
ParameterDescription
groupFilter by group value
queryMatch one exact sort value; cannot be combined with start or end
startSort value range start (inclusive)
endSort value upper bound; the current key encoding excludes an exact matching sort value
limitMax results per page
lastKeyOpaque cursor returned by the previous page

Results are ordered by tag sort key in descending order. The response contains entities, totalCount, and lastKey when another page is available.

Examples:

# All organisations of type "club"
GET /core/tag/organisation/type?group=club

# All organisations created in 2025; use the next boundary as the end value
GET /core/tag/organisation/created?start=2025-01-01&end=2026-01-01

# Organisations in eu-west-1, activated after 2025-01-01
GET /core/tag/organisation/region-activation?group=eu-west-1&start=2025-01-01

# Continue a paginated query
GET /core/tag/organisation/type?group=club&limit=20&lastKey=<cursor>

Querying tags (React)

Use the useTaggedEntities hook:

Filter by group

ts
// All organisations of type "club"
const { entities, isLoading } = useTaggedEntities(
  Entity.ORGANISATION,
  'type',
  { params: { group: 'club' } },
);

Range query on sort value

ts
// All organisations created in 2025
const { entities } = useTaggedEntities(
  Entity.ORGANISATION,
  'created',
  { params: { start: '2025-01-01', end: '2026-01-01' } },
);

Group + sort range

ts
// Organisations in eu-west-1, activated after 2025-01-01
const { entities } = useTaggedEntities(
  Entity.ORGANISATION,
  'region-activation',
  { params: { group: 'eu-west-1', start: '2025-01-01' } },
);

Pagination

ts
const { entities, lastKey, listMore, isLoading } = useTaggedEntities(
  Entity.ORGANISATION,
  'type',
  { params: { group: 'club' } },
);

// Load more when user scrolls to bottom
if (lastKey) {
  await listMore();
}

React hook limitation

The raw API supports group, exact sort-value, and range queries. useTaggedEntities requires at least one params filter for its initial fetch, and its current refetch and listMore helpers require params.group. Use the raw API through a custom request when paginating an ungrouped range or exact-value query.

Data layout

PatternKey structure
Tag recordPK = TAG#<entityType>#<tagName>[#group], SK = <sortValue?>#<entityType>#<entityId>
Reverse lookupBy entity for cleanup/updates

Released under the MIT License.