// context projectapi

TypeScript SDK

@loguro/datasets is the typed npm package for dataset workflows.

Use it when you want schema definition, ingest payloads, filters, aggregates, and query rows to be checked by TypeScript instead of hand-writing raw JSON requests.

Install

npm i @loguro/datasets

Define a schema

import {
  DatasetIngestClient,
  DatasetQueryClient,
  defineDatasetSchema
} from '@loguro/datasets';

const Orders = defineDatasetSchema({
  country: 'string',
  plan: 'string',
  amount: 'number',
  paid: 'boolean',
  signup_at: 'timestamp'
});

The schema object is the source of truth. The SDK infers record fields, filter values, aggregate compatibility, and selected row shape from it.

Do not include timestamp or context in defineDatasetSchema. They are standard fields required on every record.

Clients

Datasets use two auth surfaces, so the SDK exposes two clients:

ClientTokenUsed for
DatasetQueryClientPAT (pat_...)create datasets, register/read schema, query
DatasetIngestClientproject API keypush records to ingest
const query = new DatasetQueryClient({
  token: process.env.LOGURO_PAT!
});

const ingest = new DatasetIngestClient({
  apiKey: process.env.LOGURO_PROJECT_API_KEY!
});

Create a dataset and register schema

const dataset = await query.createDataset({
  projectSlug: 'local',
  name: 'Orders',
  description: 'Paid orders from billing'
});

await query.registerSchema({
  projectSlug: 'local',
  datasetId: dataset.id,
  schema: Orders
});

registerSchema sends the schema contract expected by the dataset schema API.

Ingest records

await ingest.push(Orders, {
  datasetId: dataset.id,
  record: {
    timestamp: new Date().toISOString(),
    context: { source: 'billing-service', request_id: 'req_123' },
    country: 'RO',
    plan: 'pro',
    amount: 42.5,
    paid: true,
    signup_at: '2026-06-01T08:00:00Z'
  }
});

Batches use the same schema and record type:

await ingest.pushBatch(Orders, {
  datasetId: dataset.id,
  records: [
    {
      timestamp: '2026-06-10T11:00:00Z',
      context: { source: 'billing-service' },
      country: 'RO',
      plan: 'pro',
      amount: 20,
      paid: true,
      signup_at: '2026-06-01T08:00:00Z'
    }
  ]
});

The SDK validates records before sending:

  • required timestamp
  • required object context
  • declared fields are present
  • field values match the schema type
  • extra top-level fields are rejected

The ingest API still performs server-side validation. Client validation is there to catch mistakes earlier.

Query datasets

const result = await query.query(Orders, {
  projectSlug: 'local',
  datasetId: dataset.id,
  range: {
    from: '2026-06-01T00:00:00Z',
    to: '2026-06-10T23:59:59Z'
  },
  select: [
    Orders.field('country'),
    Orders.count().as('events'),
    Orders.sum('amount').as('revenue')
  ] as const,
  where: [
    Orders.where('plan', 'in', ['pro', 'team']),
    Orders.where('amount', '>', 20),
    Orders.where('paid', '=', true)
  ],
  groupBy: ['country'],
  orderBy: [{ field: 'revenue', direction: 'desc' }],
  limit: 50
});

for (const row of result.rows) {
  console.log(row.country, row.events, row.revenue);
}

The query DSL maps to the JSON query API:

SDKAPI JSON
Orders.field('country'){ "field": "country" }
Orders.count().as('events'){ "aggregate": "count", "as": "events" }
Orders.sum('amount').as('revenue'){ "aggregate": "sum", "field": "amount", "as": "revenue" }
Orders.where('amount', '>', 20){ "field": "amount", "op": ">", "value": 20 }

Type safety

The SDK rejects incompatible expressions at compile time:

Orders.sum('amount');       // ok
Orders.sum('country');      // TypeScript error

Orders.where('paid', '=', true);       // ok
Orders.where('paid', '=', 'true');     // TypeScript error
Orders.where('amount', 'contains', '4'); // TypeScript error

Selected row types are inferred from the select array when it is marked as const:

const select = [
  Orders.field('country'),
  Orders.sum('amount').as('revenue')
] as const;

const result = await query.query(Orders, {
  projectSlug: 'local',
  datasetId: dataset.id,
  select
});

// row.country is string
// row.revenue is number
for (const row of result.rows) {
  console.log(row.country, row.revenue);
}

Related API docs

// related

See also