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

matecitodb

v4.0.2

Published

SDK oficial de matecito.dev — Auth, queries, realtime, storage, batch, functions, AI, analytics y más para Next.js, React y Node.js

Readme

matecitodb 🧉

NPM Version License: MIT

El SDK oficial de matecito.dev — Backend-as-a-Service con TypeScript nativo, consultas fluidas, tiempo real, autenticación completa y mucho más.

npm install matecitodb

Inicio rápido

import { createClient } from 'matecitodb'

const db = createClient({
  url:    'https://tu-proyecto.matecito.dev',
  apiKey: 'mk_anon_...',
})

// Consultar registros
const { data: posts } = await db.from('posts')
  .select('id, title, created_at')
  .eq('status', 'publicado')
  .latest()
  .limit(10)

// Insertar
const { data: post } = await db.from('posts').insert({
  title:   'Mi primer post',
  content: 'Hola desde MatecitoDB!',
})

// Escuchar cambios en tiempo real
const unsub = db.from('posts').subscribe((event) => {
  console.log(event.action, event.record)
})

Tabla de contenidos


Instalación y configuración

npm install matecitodb
import { createClient } from 'matecitodb'

const db = createClient({
  url:        'https://tu-proyecto.matecito.dev',
  apiKey:     'mk_anon_...',     // clave pública (cliente)
  // serviceKey: 'mk_service_...', // clave de servicio (servidor/admin)
  autoRefresh: true,              // refresca el token JWT automáticamente
  timeout:     30_000,
  retry:       { attempts: 3, baseDelay: 500 },
  debug:       false,             // loguea requests (headers sensibles son redactados)
})

Variables de entorno (Next.js / Vite)

NEXT_PUBLIC_MATECITODB_API_KEY=mk_anon_...
MATECITODB_API_KEY=mk_service_...
MATECITODB_URL=https://tu-proyecto.matecito.dev

Records & Query Builder

Accedé a cualquier colección con db.from('nombre'). El builder es thenable — podés usar await directamente.

Consultar

// Traer todos
const { data: posts } = await db.from('posts').find()

// Con filtros
const { data } = await db.from('posts')
  .select('id, title, author')
  .eq('status', 'publicado')
  .gte('views', 100)
  .ilike('title', '%matecito%')
  .latest()
  .limit(20)
  .page(2)

// Traer uno por ID
const { data: post } = await db.from('posts').getOne('abc-123')

// Traer el primero que coincide
const { data: post } = await db.from('posts')
  .eq('slug', 'mi-post')
  .getFirst()

// findOne: acepta ID string o filtros
const post = await db.from('posts').findOne('abc-123')
const post = await db.from('posts').findOne({ slug: 'mi-post' })

// Contar
const count = await db.from('posts').eq('status', 'publicado').count()

Filtros disponibles

| Método | Operador SQL | |--------|-------------| | .eq(col, val) | = | | .neq(col, val) | != | | .gt(col, val) | > | | .gte(col, val) | >= | | .lt(col, val) | < | | .lte(col, val) | <= | | .like(col, pattern) | LIKE | | .ilike(col, pattern) | ILIKE | | .inValues(col, [v1, v2]) | IN (...) | | .notInValues(col, [v1, v2]) | NOT IN (...) | | .or('price.gte.100,name.ilike.%cafe%') | OR (...) | | .search('texto') | ILIKE en todos los campos |

Fechas como filtro

// Acepta objetos Date — se envía como ISO 8601 completo
await db.from('events')
  .gte('date', new Date('2025-01-01'))
  .lt('date', new Date('2026-01-01'))
  .find()

Ordenar y paginar

db.from('posts').latest()                      // ORDER BY created_at DESC
db.from('posts').oldest()                      // ORDER BY created_at ASC
db.from('posts').order('title', { ascending: true })
db.from('posts').limit(50).page(3)

Insertar

const { data, error } = await db.from('posts').insert({
  title:   'Nuevo post',
  content: 'Contenido...',
})

Actualizar

// Por ID
await db.from('posts').eq('id', 'abc-123').update({ title: 'Título nuevo' })

// Bulk update — actualiza todos los que coinciden con los filtros
await db.from('posts').eq('status', 'borrador').update({ archived: true })

// Merge — actualiza solo los campos enviados, preserva el resto
await db.from('posts').eq('id', 'abc-123').merge({ views: 42 })

// Con fecha de expiración
await db.from('sessions').eq('id', 'xyz').update(
  { active: true },
  { expiresAt: new Date(Date.now() + 3600_000) }
)

Upsert

const { data, upserted } = await db.from('profiles').upsert(
  { user_id: 'abc', bio: 'Dev apasionado' },
  'user_id'  // campo de conflicto
)

console.log(upserted) // true = insertado, false = actualizado

Eliminar

// Soft-delete (recuperable)
await db.from('posts').delete('abc-123')

// Restaurar
await db.from('posts').restore('abc-123')

// Borrado permanente
await db.from('posts').hardDelete('abc-123')

// Bulk soft-delete
await db.from('posts').eq('status', 'spam').delete()

Registros eliminados y expirados

// Incluir registros soft-deleted en los resultados
const { data } = await db.from('posts').includeDeleted().find()

// Incluir registros expirados
const { data } = await db.from('sessions').includeExpired().find()

Insertar múltiples (batch)

const records = await db.from('products').insertMany([
  { name: 'Yerba', price: 500 },
  { name: 'Mate',  price: 1200 },
])

Paginar como stream

for await (const batch of db.from('posts').paginate({ batchSize: 100 })) {
  console.log('Lote de', batch.length, 'posts')
}

Exportar

// Devuelve un Blob con todos los registros (hasta 10k)
const blob = await db.from('posts').export({ format: 'csv' })
const blob = await db.from('posts').export({ format: 'json' })

// Descargar en el browser
const url = URL.createObjectURL(blob)

Autenticación

Registro e inicio de sesión

// Registrar
const { data, error } = await db.auth.signUp({
  email:    '[email protected]',
  password: 'contraseña123',
  username: 'miusuario',         // campo extra opcional
})

// Iniciar sesión
const { data, error } = await db.auth.signIn({
  email:    '[email protected]',
  password: 'contraseña123',
})

// Cerrar sesión
await db.auth.signOut()

// Estado actual
const user  = db.auth.user
const token = db.auth.token
const loggedIn = db.auth.isLoggedIn

Escuchar cambios de sesión

const unsubscribe = db.auth.onAuthChange((user) => {
  if (user) {
    console.log('Sesión activa:', user.email)
  } else {
    console.log('Sesión cerrada')
  }
})

// Dejar de escuchar
unsubscribe()

Perfil y contraseña

// Actualizar perfil
await db.auth.updateProfile({ name: 'Juan', avatar_seed: 'abc' })

// Solicitar reset de contraseña
await db.auth.requestPasswordReset('[email protected]', {
  resetUrlBase: 'https://miapp.com/reset',
})

// Confirmar nuevo password con el token del email
await db.auth.resetPassword(token, 'nueva-contraseña')

Verificación de email

// Verificar email con el token del link
await db.auth.verifyEmail(token)

// Reenviar email de verificación
await db.auth.resendVerification({ email: '[email protected]' })
await db.auth.resendVerification({ userId: 'user-uuid' })

OAuth

// 1. Generar URL de redirección (incluye state para CSRF)
const state = crypto.randomUUID()
const url = db.auth.getOAuthUrl('google', 'https://miapp.com/callback', { state })

// 2. Redirigir al usuario a `url`

// 3. En el callback, completar el flujo con los params de la URL
const { data, error } = await db.auth.handleOAuthCallback({
  access_token:  searchParams.get('access_token'),
  refresh_token: searchParams.get('refresh_token'),
})

Inyectar sesión manualmente (SSO / deep links)

db.auth.setSession({
  accessToken:  'jwt...',
  refreshToken: 'refresh...',
})

Administración de usuarios (requiere serviceKey)

// Listar usuarios
const { data } = await db.auth.admin.listUsers({ limit: 20, search: 'juan' })

// Eliminar usuario
await db.auth.admin.deleteUser('user-uuid')

Configurar proveedores OAuth (requiere serviceKey)

// Configurar Google
await db.auth.oauth.configure('google', {
  clientId:     'TU_CLIENT_ID',
  clientSecret: 'TU_CLIENT_SECRET',
  redirectUri:  'https://miapp.com/callback',
})

// Listar proveedores activos
const { data: providers } = await db.auth.oauth.listProviders()

// Eliminar proveedor
await db.auth.oauth.remove('github')

Realtime

// Suscribirse a todos los cambios de una colección
const unsub = db.from('messages').subscribe((event) => {
  console.log(event.action)     // 'created' | 'updated' | 'deleted'
  console.log(event.collection) // 'messages'
  console.log(event.record)     // el registro como objeto plano
  console.log(event.recordId)   // ID del registro
})

// Dejar de escuchar
unsub()

// Escuchar estado de conexión
const unsub = db.realtime.onStatusChange((status) => {
  // 'connected' | 'disconnected'
  console.log('Realtime:', status)
})

// Desconectar manualmente
db.realtime.disconnect()

El SDK mantiene la conexión automáticamente con reconexión exponencial y un ping/pong cada 30 segundos para detectar conexiones zombi.


Storage

// Subir un archivo (browser File)
const { data, error } = await db.storage.upload(file, {
  path:   'avatars/user-1.png',
  public: true,
})

// Subir desde URL (server-side)
await db.storage.uploadFromUrl('https://ejemplo.com/img.jpg', {
  path: 'imports/img.jpg',
})

// Listar archivos
const { data: files } = await db.storage.list('avatars/')

// Eliminar
await db.storage.delete('avatars/user-1.png')

// URL pública
const url = db.storage.getPublicUrl('avatars/user-1.png')

Batch

Ejecuta múltiples operaciones en una sola transacción del servidor.

const result = await db.batch()
  .insert('posts',    { title: 'Post A' })
  .insert('posts',    { title: 'Post B' })
  .update('settings', { key: 'theme', value: 'dark' }, { collection: 'settings' })
  .delete('drafts',   'draft-uuid')
  .execute()

for (const item of result.results) {
  if (item.ok) {
    console.log('OK:', item.record)
  } else {
    console.error('Error:', item.error)
  }
}

SQL Runner

Ejecuta SQL raw directamente contra tu proyecto. Requiere serviceKey.

const { data, error } = await db.sql.run(
  'SELECT id, title FROM posts WHERE views > $1 LIMIT $2',
  [100, 10]
)

console.log(data.rows)        // array de filas
console.log(data.rowCount)    // cantidad de filas
console.log(data.durationMs)  // tiempo de ejecución
console.log(data.truncated)   // true si se cortaron resultados

Permisos

Gestioná los permisos de cada colección. Requiere serviceKey.

// Ver todos los permisos
const { data } = await db.permissions.getAll()

// Ver permisos de una colección
const { data } = await db.permissions.get('posts')

// Actualizar permisos específicos
await db.permissions.set('posts', {
  list:   'public',   // sin autenticación
  get:    'public',
  create: 'auth',     // requiere JWT
  update: 'auth',
  delete: 'service',  // solo serviceKey
})

// Aplicar el mismo nivel a todas las operaciones
await db.permissions.setAll('private_data', 'service')

Niveles disponibles: public | auth | service | nobody


Colecciones y Schema

Requiere serviceKey.

// Listar colecciones
const { data: cols } = await db.collections.list()

// Crear colección con campos
await db.collections.create('products', {
  fields: [
    { name: 'name',  type: 'text',   required: true },
    { name: 'price', type: 'number', required: true },
    { name: 'image', type: 'file' },
    { name: 'category', type: 'relation', options: { collection: 'categories' } },
  ]
})

// Renombrar
await db.collections.rename('old_name', 'new_name')

// Eliminar (irreversible)
await db.collections.delete('temp_data')

// ─── Campos ────────────────────────────────────────────

// Listar campos
const { data: fields } = await db.collections.fields('products').list()

// Agregar campo
await db.collections.fields('products').create({
  name: 'discount',
  type: 'number',
  required: false,
})

// Actualizar campo
await db.collections.fields('products').update(fieldId, { required: true })

// Eliminar campo
await db.collections.fields('products').delete(fieldId)

Tipos de campo disponibles: text | number | boolean | email | date | file | json | relation | select


Stats y Logs

Requiere serviceKey.

// Estadísticas del proyecto
const { data: stats } = await db.stats.get()
console.log(stats.total_records, stats.storage_used_mb, stats.requests_today)

// Logs de requests recientes
const { data: logs } = await db.logs.list({ limit: 50 })

// Filtrar logs
const { data } = await db.logs.list({ status: 500 })       // solo errores
const { data } = await db.logs.list({ method: 'POST' })    // solo POSTs
const { data } = await db.logs.list({ path: '/records' })  // por path

SMTP y Email Templates

Requiere serviceKey.

// Configurar SMTP
await db.smtp.set({
  host: 'smtp.gmail.com',
  port: 587,
  user: '[email protected]',
  pass: 'app-password',
  from: 'Mi App <[email protected]>',
})

// Enviar email de prueba
await db.smtp.test('[email protected]')

// ─── Templates ─────────────────────────────────────────

// Seed de templates del sistema (welcome, reset-password, etc.)
await db.emailTemplates.seed()

// Crear template personalizado (soporta {{variable}})
const { data } = await db.emailTemplates.create({
  name:    'pedido_confirmado',
  subject: 'Tu pedido {{order_id}} fue confirmado',
  body:    '<h1>Hola {{user_name}}</h1><p>¡Tu pedido está en camino!</p>',
})

// Listar templates
const { data: list } = await db.emailTemplates.list()

// Actualizar template
await db.emailTemplates.update('template-uuid', { subject: 'Nuevo asunto' })

// Eliminar template
await db.emailTemplates.delete('template-uuid')

Webhooks

Requiere serviceKey.

// Crear webhook
const { data: hook } = await db.webhooks.create({
  url:    'https://miapp.com/hooks/matecito',
  events: ['record.created', 'record.deleted'],
  secret: 'mi-secreto-de-firma',
})

// Listar webhooks
const { data: hooks } = await db.webhooks.list()

// Actualizar (ej. deshabilitar)
await db.webhooks.update('hook-uuid', { enabled: false })

// Eliminar
await db.webhooks.delete('hook-uuid')

Eventos disponibles: record.created | record.updated | record.deleted | auth.signup | auth.login | storage.upload


CLI

# Inicializar proyecto (genera .env.local con tus credenciales)
npx matecitodb init

# Generar interfaces TypeScript desde tu schema actual
npx matecitodb generate types

# Scaffold de autenticación para Next.js
npx matecitodb generate auth

# Generar un React hook para una colección
npx matecitodb generate hook posts

Tipado automático

Después de correr generate types, podés usar el schema como genérico:

import type { Database } from './matecito.types'

const db = createClient<Database>({ url, apiKey })

// Ahora db.from('posts') está completamente tipado
const { data } = await db.from('posts').eq('status', 'publicado').find()
// data es Post[] con autocompletado completo

Manejo de errores

Todos los métodos devuelven { data, error }. Nunca tiran excepciones salvo find() / findOne() / count().

const { data, error } = await db.from('posts').insert({ title: 'Test' })

if (error) {
  console.error(error.message)  // mensaje legible
  console.error(error.status)   // código HTTP (0 = error de red)
  console.error(error.raw)      // respuesta raw del servidor
}

Reintentos y auto-refresh

El SDK maneja automáticamente:

  • Auto-retry: reintentos con backoff exponencial ante errores de red
  • Auto-refresh: si recibe un 401, refresca el JWT y reintenta la request original una vez

Configurable al crear el cliente:

const db = createClient({
  url,
  apiKey,
  autoRefresh: true,
  retry: { attempts: 3, baseDelay: 500 },
})

Tipado con Schema

// matecito.types.ts (generado por `npx matecitodb generate types`)
export interface Database {
  posts: {
    id:         string
    title:      string
    content:    string
    status:     'borrador' | 'publicado'
    views:      number
    created_at: string
  }
  users: {
    id:    string
    email: string
    name:  string
  }
}

// Uso con tipado completo
import { createClient } from 'matecitodb'
import type { Database } from './matecito.types'

const db = createClient<Database>({ url, apiKey })

const { data: posts } = await db.from('posts').find()
// posts es Database['posts'][] — completamente tipado

Documentación por módulo


Hecho con ❤️ en Argentina 🧉 · matecito.dev