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

@appgaleno/database

v0.3.0

Published

Official GalenoBase database REST client for external webapps.

Downloads

223

Readme

@appgaleno/database

Official GalenoBase database client for external apps.

App user administration (server only)

Use a project-bound gb_live_ Secret API Key with the explicit scope auth:users:admin. Neither *, db:* nor db:admin grants this permission. This namespace is available only on the server client; it is separate from the browser PKCE authentication helper.

const client = createGalenoBaseServerClient({ baseUrl, projectId, apiKey });
await client.authAdmin.suspendUser('app-user-id', { reason: 'Administrative review' });
await client.authAdmin.reactivateUser('app-user-id');

These methods call POST /api/v1/auth/users/:userId/suspend and /reactivate. The suspension reason is trimmed and must contain 1–500 characters; avoid personal data. Reactivation sends an empty JSON object. Both return { data: { id, projectId, status, suspendedAt, suspensionReason } }, with ISO dates or null. Suspension revokes prior sessions; reactivation requires a fresh login. Errors retain GalenoBaseApiError status, code and requestId. This documents the local additive contract and does not establish remote availability.

This package uses the public REST v1 contract. It does not use direct PostgreSQL access, does not ask for DATABASE_URL, and does not create apps.

Easiest setup: Secure Connect

If you are not technical, start in the GalenoBase Console:

  1. Open Project Settings > Connect your App.
  2. Generate the secure command.
  3. Paste that command in the external app terminal.

The Secure Connect flow writes the server-side environment locally in your app and creates AI-safe context files. Share only galenobase.integration.json and galenobase.integration.md with Codex, Cursor, Claude CLI, Antigravity, Lovable or another AI tool.

Do not paste the Secret API Key in chat, Git, frontend code, NEXT_PUBLIC_*, VITE_*, PUBLIC_* or REACT_APP_* variables. The SDK helps your backend call GalenoBase, but the Secret API Key must still stay server-side.

Install

Use this command only from the approved GalenoBase distribution channel:

npm install @appgaleno/database

Server-side only in this release

Direct browser/public keys are planned for a later release. In this P0, keep all Secret API Keys server-side only. React, Vue, Flutter Web and no-code frontend surfaces should call your own backend route, and that backend route should call GalenoBase with GALENOBASE_API_KEY.

Do not configure VITE_GALENOBASE_READ_KEY, VITE_GALENOBASE_PUBLIC_KEY, or any live Secret API Key in browser bundles.

Generated schema types

Generate typed helpers in the external app repository:

galenobase pull-schema --collection tasks --out galenobase.schema.json
galenobase gen --schema galenobase.schema.json --out src/lib/galenobase.generated.ts

Then use the generated document type with the package:

import { createGalenoBaseServerClientFromEnv } from '@appgaleno/database/server';
import type { TasksDocument } from './lib/galenobase.generated';

const db = createGalenoBaseServerClientFromEnv();
const page = await db.collection<TasksDocument>('tasks').list();

Generated files use REST v1 schemas and placeholders only; they never contain API keys, cookies, DATABASE_URL or direct PostgreSQL credentials.

Server client

Use server-only env vars for reads and writes.

import { createGalenoBaseServerClientFromEnv } from '@appgaleno/database/server';

const db = createGalenoBaseServerClientFromEnv();

await db.collection('tasks').create({ title: 'Ship package', done: false });
await db.collection('tasks').upsert('task-1', { title: 'Ship package', done: false });

Document field projection (P2-7)

Browser and server collections accept an optional, readonly fields array on list, get and getWithVersion:

const tasks = db.collection<{ title: string; done: boolean }>('tasks');
const fields = ['title'] as const;
const page = await tasks.list({ fields, limit: 10 });
const item = await tasks.get('task-1', { fields });
const versioned = await tasks.getWithVersion('task-1', { fields });

Projected documents contain only requested, present business fields plus id, _createdAt and _updatedAt, which are always present. Unknown metadata timestamps are null. Business properties are typed as partial; calls without fields retain their existing document type and query serialization. Native revision still comes from the response header in the same GET request.

Use 1–50 safe top-level names, each up to 128 ASCII letters, digits, underscores or hyphens. Leading hyphens, nested paths, wildcards, exclusions and reserved internal names are rejected. Unknown fields are omitted. The SDK sorts and deduplicates the selection without mutating the supplied array. Pagination must keep the same selection; reordering it is allowed.

Projection does not grant read access or establish field-level permissions. It runs after existing authorization and sanitization. Responses from create, update, upsert and _atomic remain complete, even after a projected read. exportData does not accept fields. This option requires a P2-7-capable server; older servers reject it. See the full projection contract.

Optimistic document concurrency (P2-5)

The additive local contract exposes the native database revision through the GalenoBase-Document-Version response header. Business JSON, including _version and ifVersion fields, keeps its meaning. Read the item and its revision together:

const tasks = db.collection('tasks');
const { item, version } = await tasks.getWithVersion('task-1'); // One GET request.
await tasks.update('task-1', { ...item, done: true }, { ifVersion: version });

get(id) keeps its existing return shape. update(id, data, options?), upsert(id, data, options?), delete(id, options?) and atomic(id, input, options?) accept { ifVersion: positiveSafeInteger } and send it as a query parameter. Create does not accept a condition. Omit the options to retain last writer wins.

A stale revision raises GalenoBaseApiError with status 409 and code VERSION_CONFLICT without a mutation. Read again and resolve the conflict before retrying. Conditional upsert of a missing item returns 409 without creating it; missing update/delete/atomic targets remain 404 without the P2-6 atomic opt-in below. There is no automatic merge, ETag/If-Match/412 contract or per-row revision in list/read-model results. Call getWithVersion before editing a selected item.

Matching atomic idempotency replays retain the original result and stored version without advancing the counter. Historical replays persisted before migration 0050 may lack responseData.documentVersion: their original result is preserved and GalenoBase-Document-Version is omitted. The service does not substitute the current revision or repeat the mutation to fill that metadata. Changing ifVersion changes the normalized request. Dry-run does not advance revisions. Batch/UOW/import/logical restore writes get native database revisions automatically, without new public per-operation ifVersion conditions. Revisions may have gaps; retained counters prevent reuse after delete/recreate or logical restore. Physical restore/PITR requires clients to resynchronize and must not treat revision as an external clock.

Atomic initialization (P2-6)

The server SDK adds an optional createIfMissing object to the existing atomic(id, input, options?) call:

await db.collection('daily_counts').atomic('fictional-day', {
    createIfMissing: { total: 0, status: 'open' },
    increment: { total: 1 },
    idempotencyKey: 'fictional-request-001',
});

The collection must already exist. Initial values fill only absent top-level fields; they never reset existing values. A missing document is initialized and mutated in the same transaction. At least one set or increment is still required. Initial counters must be safe integers; an existing invalid counter still fails with 422. Collection rules, schema, project isolation and scopes remain enforced. A false where condition leaves no initialization behind.

Without the option, missing documents/counters keep their existing 404/422 behavior. With the option and a positive ifVersion, a missing document returns 409 VERSION_CONFLICT, without creation. createIfMissing participates in the idempotency payload only when provided: use the same initial values on retries. The SDK does not retry or merge automatically. This additive option requires a server that supports P2-6; older servers reject the unknown field.

See atomic initialization and document concurrency for migration 0050, rollback compatibility and validation boundaries. This is a local contract, not proof of package publication or remote availability.

Admin SDK (rare and privileged)

@appgaleno/database/admin is a Node-only entrypoint for migrations, user management, rules, indexes and deliberate privileged automation. It requires the service-account JSON emitted by the GalenoBase Console and an explicit purpose:

import { readFile } from 'node:fs/promises';
import { createGalenoBaseAdminClient } from '@appgaleno/database/admin';

const credential = JSON.parse(await readFile(process.env.GALENOBASE_ADMIN_CREDENTIAL_FILE!, 'utf8'));
const db = createGalenoBaseAdminClient({
    baseUrl: process.env.GALENOBASE_URL!,
    credential,
    purpose: 'migration',
});

Never import this entrypoint in frontend/mobile bundles. Never use an Admin SDK credential as the default credential for Codex, Claude Code, Antigravity or another AI agent; give agents a temporary, minimally scoped Server SDK key instead. Keep the private-key JSON in a server-side secret manager and never in Git, chat or generated integration context.

Required env vars:

GALENOBASE_URL=https://api.example.com
GALENOBASE_PROJECT_ID=project_placeholder
GALENOBASE_API_KEY=<server-side-secret-api-key>

Use the minimum API key scope:

  • db:read for server-side reads.
  • db:write for server create/update.
  • db:delete for server document delete.
  • db:schema for server schema reads/writes.
  • db:indexes for server index management.
  • db:views for server saved views.
  • db:admin only for collection delete.

Hosted Login Auth client

End-user authentication is different from database access. Use the Auth client in the browser with public project/app identifiers only. Never pass GALENOBASE_API_KEY, live Secret API Keys, service account secrets or Admin SDK credentials to this client.

import { createGalenoBaseAuthClient } from '@appgaleno/database/auth';

const auth = createGalenoBaseAuthClient({
    projectId: 'project_placeholder',
    appId: 'registered_app_id',
    redirectUri: 'https://yourapp.com/auth/callback',
    authUrl: 'https://galenobase.appgaleno.com',
    apiUrl: 'https://api.galenobase.appgaleno.com',
});

document.querySelector('#login')?.addEventListener('click', () => {
    void auth.login();
});

const tokens = await auth.handleCallback();
if (tokens) {
    // Keep tokens in memory or a secure server-side session strategy.
    // Do not log tokens and do not store them in localStorage.
}

With no new option, login() keeps the backwards-compatible Hosted form. An app can also request either experience explicitly:

// The themed Hosted form with every ready method.
await auth.login({ interaction: 'hosted' });

// One app button, then the selected provider (no second provider picker).
await auth.login({ interaction: 'direct', provider: 'google' });
await auth.login({ interaction: 'direct', provider: 'microsoft' });

interaction: 'direct' requires an allowlisted provider. The SDK rejects an unknown or missing provider locally; the server still verifies that direct login is enabled and that the provider's redirect mode is ready for this project. A provider passed with interaction: 'hosted' never auto-redirects. If direct login is unavailable, offer a deliberate Hosted-login action instead of retrying redirects automatically.

auth.logoutAllSessions(accessToken) encerra a sessão em todos os dispositivos do usuário do app: o servidor revoga todos os access tokens emitidos e desconecta todos os sockets ativos desse usuário. O Auth client é stateless; apague também a cópia local dos tokens depois da chamada.

await auth.logoutAllSessions(tokens.access_token);
tokens = null;

Before using Hosted Login, register an app in the GalenoBase console and add the exact redirect URI to the allowed origins/redirect list. The Auth client uses PKCE and validates state before token exchange. Transient verifier/state keys are scoped by project and App ID in sessionStorage; callbacks started by the previous SDK can still consume its App-ID-only keys once during the compatibility window.

See the full Hosted/direct login contract and the activation and rollback runbook.

Server-side database parity

The server client mirrors the REST v1 database operations used by external apps:

const tasks = db.collection('tasks');

await tasks.setSchema({
    mode: 'strict',
    fields: {
        title: { type: 'text', required: true },
        status: { type: 'select', enum: ['todo', 'done'] },
        score: { type: 'number' },
    },
});

await tasks.createIndex({ fields: ['status'], mode: 'single' });
await tasks.createView({
    name: 'Open tasks',
    definition: {
        where: [{ field: 'id', op: 'eq', value: 'task-1' }],
        visibleFields: ['id', 'title', 'status'],
    },
});

await tasks.importData({ items: [{ title: 'A' }, { title: 'B' }] });
await tasks.bulk({
    dryRun: false,
    idempotencyKey: 'bulk-seed-1',
    operations: [{ action: 'create', data: { title: 'C' } }],
});
await db.executeCrossCollectionTransaction({
    idempotencyKey: 'confirm-order-123',
    dryRun: false,
    operations: [
        { collection: 'orders', action: 'update', id: 'order-123', data: { status: 'confirmed' } },
        { collection: 'inventory', action: 'update', id: 'sku-456', data: { available: 9 } },
        { collection: 'reservations', action: 'delete', id: 'reservation-789' },
    ],
});
await tasks.search({ query: 'Ship', limit: 10, scanLimit: 100 });
await tasks.aggregate({ operations: [{ op: 'count', alias: 'total' }] });
await tasks.join({
    include: [{ field: 'ownerId', as: 'owner', limit: 5 }],
    limit: 10,
});

Use galenobase request for advanced REST v1 calls from a trusted server or CI environment:

galenobase request --method GET --path /api/v1/db/tasks/_indexes
galenobase request --method POST --path /api/v1/db/tasks/_indexes --body '{"fields":["status"],"mode":"single"}'
galenobase request --method DELETE --path /api/v1/db/tasks/_indexes/index_id --confirm DELETE

The MCP server stays read-only in this release. It can inspect manifest, documents, schemas, indexes, views, search and aggregate results, but it cannot create, update or delete data.

Knowledge retrieval

The server client exposes generic project-scoped Knowledge/RAG retrieval. Use a server-side API key with db:knowledge:read, db:* or *:

const result = await db.knowledge.retrieve({
    namespace: 'product-manuals',
    queryText: 'How do I reset a device?',
    embeddingModel: 'local-keyword-hash-v1',
    topK: 5,
});

Production callers can supply a 1536-dimension queryEmbedding instead of queryText. The SDK never returns the query embedding, and the response marks that raw questions were not stored. Knowledge ingestion remains a server-side REST operational surface and is not exposed as a browser SDK operation.

Schema field types

setSchema accepts the public field types:

await db.collection('tasks').setSchema({
    mode: 'strict',
    fields: {
        title: { type: 'text', required: true, minLength: 1 },
        dueDate: { type: 'date' },
        status: { type: 'select', enum: ['todo', 'done'] },
        tags: { type: 'multi_select', enum: ['vip', 'internal'] },
        attachment: { type: 'file' },
        metadata: { type: 'json' },
        owner: { type: 'relation' },
    },
});

Realtime payload type

The public socket event is db:change. The package exports the TypeScript payload type without bundling a socket client:

import type { GalenoBaseRealtimeChangePayload } from '@appgaleno/database';

function handleChange(event: GalenoBaseRealtimeChangePayload<{ id: string; title?: string }>) {
    if (event.collection !== 'tasks') return;
    console.log(event.type, event.id, event.data, event.at);
}