Implementación del SDK de TypeScript
Mirko Franichevic · 27 de agosto de 2026
¿Qué vamos a hacer?
En esta guía vas a conectar un proyecto de TypeScript con Praxsuite y a poner datos reales en movimiento en unos veinte minutos. Vas a instalar el SDK (Software Development Kit), apuntarlo a tu workspace, iniciar la sesión de un usuario, leer y escribir una tabla, y llamar a un Endpoint del Gateway.
Al terminar esta guía vas a saber:
Qué es Praxsuite y con qué parte de él estás hablando
Qué clave va en el código de cliente y cuál no va nunca
Cómo crear el cliente, iniciar la sesión de un usuario y conservarla
Cómo consultar y modificar filas de una tabla, y cuándo el servidor se va a negar
Required level: Necesitas manejarte con TypeScript y con `async`/`await`. No hace falta experiencia previa con Praxsuite, con APIs ni con bases de datos. Sirve cualquier entorno de TypeScript: una aplicación de navegador, Node, Deno, Bun o React Native.
¿Qué es Praxsuite?
Praxsuite es una plataforma de workspaces. Creas tablas de datos (como hojas de cálculo, pero mucho más poderosas) y las conectas con tus propias aplicaciones mediante una API.
Analogía simple: piensa en Praxsuite como una base de datos en la nube a la que tu aplicación le habla, más un portero en la puerta que revisa qué tiene permitido ver cada visitante.
Tu aplicación llega a ella por el Gateway, y el Gateway ofrece dos puertas. La primera es el acceso directo a tablas: tu aplicación escribe una consulta y el Gateway la ejecuta. La segunda es un Endpoint, una URL atada a una Automation que construiste en el portal, donde el servidor decide qué pasa en lugar de confiar en tu payload.
Las dos importan, y el Paso 4 y el Paso 5 cubren una cada uno.
Requisitos previos
Requisito | Descripción |
Una cuenta de Praxsuite | Regístrate en praxsuite.com y asegúrate de tener un workspace activo |
Node.js 18 o superior | El SDK usa el |
Una tabla con datos | Sirve cualquiera. Esta guía lee y escribe una que ya tengas |
Tu workspace id | Un UUID que copias del portal. La sección de credenciales muestra dónde |
Un proyecto de TypeScript | Lo que sea: una aplicación de Vite, un script de Node, una ruta de Next.js |
What is a workspace? En Praxsuite, un workspace es tu espacio de trabajo, como una carpeta grande donde viven todas tus tablas, formularios, automatizaciones y usuarios. Todo lo de esta guía pasa dentro de uno, y su id es el único valor que tu aplicación necesita sí o sí.
Cómo obtener tus credenciales
Importan dos valores, y sólo uno es obligatorio. Los dos viven en el portal, así que inicia sesión primero.
https://portal.praxsuite.com
Tu Workspace ID
Abre tu workspace en el portal y mira la barra de direcciones. El UUID que va después de /workspace/ es tu workspace id:
https://portal.praxsuite.com/workspace/ffd80539-a1e2-4a9e-8b33-f716bf690281
^--------------------------------^También lo encuentras en Settings, en la sección General.
Tu clave: publishable contra secret
Praxsuite emite dos clases de clave, y confundirlas es el error más caro que puedes cometer con este SDK.
Clave | Prefijo | Dónde va |
Publishable |
| Código de cliente. Es un identificador, no una credencial, y está pensada para ser legible |
Secret |
| Sólo código de servidor. Lleva acceso completo al workspace |
Para crear una, ve al panel Gateway en el menú lateral, crea una clave nueva y cópiala. Se muestra una sola vez.

Y aquí viene la buena noticia para una aplicación de cliente: no necesitas manejar ninguna clave. El SDK busca la publishable key en la ruta pública /auth/config de tu workspace la primera vez que necesita una. Rotarla en el portal no obliga a volver a desplegar.
Golden rule: Nunca pongas una clave `sklive` en código que un usuario pueda ejecutar. Cualquiera que abra las herramientas de desarrollo obtiene con ella acceso total a tu workspace. El SDK la rechaza de plano en lugar de dejar que se publique.
Si lo intentas igual, obtienes un PraxSecurityError antes de que salga una sola petición:
Refusing to use a secret key (sk_live_...) from client code in PraxOptions.publishableKey.Paso 1 - Instalar el SDK
Repositorio: https://github.com/TesseractSoftwares/Praxsuite-SDK-TypeScript
El paquete no tiene dependencias, así que es un solo comando.
npm install @praxsuite/sdkEsa es toda la instalación. No hay archivo de configuración ni paso de generación de código.
Paso 2 - Crear el cliente
Crea el cliente una sola vez y expórtalo. Una única instancia sirve para toda tu aplicación, porque además guarda la sesión iniciada.
// src/praxsuite.ts
import { createClient } from '@praxsuite/sdk'
export const prax = createClient({
workspaceId: 'ffd80539-a1e2-4a9e-8b33-f716bf690281',
})Ese es el mínimo: un solo campo. El cliente expone cinco módulos, y esta guía usa tres.
Módulo | Qué hace |
| Cuentas: registro, inicio de sesión, sesiones, flujos de contraseña |
| Lecturas y escrituras de filas |
| Endpoints del Gateway, el camino con autoridad en el servidor |
| Traduce nombres de tabla a los ids que necesita la API de consultas |
| Configuración, la sesión y el método de petición crudo |
Para una aplicación de navegador vas a querer una opción más, así una recarga de página no expulsa a tu usuario:
export const prax = createClient({
workspaceId: 'ffd80539-a1e2-4a9e-8b33-f716bf690281',
persistSession: true,
})Important: `persistSession` guarda la sesión en `localStorage`, que puede leer cualquier JavaScript de tu origen. Es una decisión con costo, y la mitigación no es guardarla mejor: mantén la autoridad en el servidor, da a los roles scopes de sólo lectura donde puedas, y haz pasar cualquier cosa valiosa por un endpoint. Así una sesión robada vale muy poco.
¿Por qué el SDK 1.0.1 necesita un workaround de fetch?
Si estás en la versión 1.0.1, agrega una opción más o todas las peticiones del navegador van a fallar:
export const prax = createClient({
workspaceId: 'ffd80539-a1e2-4a9e-8b33-f716bf690281',
fetch: (...args) => globalThis.fetch(...args),
})Esa versión llama a fetch como método de su objeto de transporte interno, y los navegadores lo rechazan cuando su receptor no es el window. Node no revisa el receptor, así que el bug sólo aparece en un navegador. Está corregido en 1.0.2, donde puedes borrar la línea.
Paso 3 - Iniciar la sesión de un usuario
Un end user es un cliente de tu aplicación, no un compañero de equipo de Praxsuite. Crearlo e iniciar su sesión son llamadas únicas.
const r = await prax.auth.register({
email: 'player@example.com',
password: 'atLeast8Chars',
username: 'mirko',
})
console.log(r.isSignedIn) // true
console.log(r.user?.displayName) // "mirko"Verifica isSignedIn antes de dejar pasar al usuario. Si tu workspace exige confirmar el email, la cuenta se crea pero no se emite sesión, y requiresEmailConfirmation te dice que eso fue lo que ocurrió.
Iniciar y cerrar la sesión de un usuario existente es simétrico:
await prax.auth.login('player@example.com', 'atLeast8Chars')
console.log(prax.auth.isSignedIn) // true
console.log(prax.auth.currentUserId) // el claim "sub" del JWT
await prax.auth.logout()logout limpia el estado local aunque falle la llamada de red, así que un usuario nunca queda pareciendo conectado con una sesión que el SDK ya dio por perdida.
Para reaccionar al inicio y al cierre de sesión desde cualquier parte de tu aplicación, suscríbete. Las dos funciones devuelven una función para darse de baja:
const stop = prax.auth.onSignedIn((user) => console.log('hola', user.displayName))
// más adelante
stop()What is a JWT? Un JSON Web Token (JWT) es el pase firmado que emite el Gateway cuando un usuario inicia sesión. Tu aplicación nunca lo inspecciona; el SDK lo adjunta a cada petición y lo renueva antes de que expire. Lo que importa es que su claim `sub` identifica al usuario, y el servidor confía en ese valor porque lo firmó él mismo.
Paso 4 - Leer y escribir datos
prax.data arma consultas de forma fluida. No se envía nada hasta que esperas un método terminal, así que un objeto de consulta es barato de construir.
import { f } from '@praxsuite/sdk'
const filas = await prax.data
.from('Demos Leaderboard')
.select('Alias', 'Points', 'Motor')
.where(f.gt('Points', 100))
.orderByDescending('Points')
.limit(10)
.all()Los métodos terminales son all() para las filas, page() para las filas con sus metadatos, first() para una fila o null, any() para un booleano, y count() para la cantidad de coincidencias.
Escribir es igual de directo. Ten en cuenta que update y delete exigen filtros:
await prax.data.insert('Demos Leaderboard', {
Record: 'Buscaminas demo',
Alias: 'mirko',
Points: 1200,
})
await prax.data.updateById('Demos Leaderboard', rowId, { Points: 1500 })No envíes columnas nativas como ID, CREATEDDATE o POSITION. El backend las completa solo y rechaza una petición que las incluya.
Golden rule: `update` y `delete` lanzan de forma síncrona cuando no les das ningún filtro, en vez de devolver una promesa rechazada. Quien las dispare sin esperarlas obtendría si no ninguna escritura y ningún error, que para una barrera contra un borrado accidental de toda la tabla es el peor desenlace posible.
¿Por qué una consulta puede dejar de funcionar al iniciar sesión?
Esta sorprende a todo el mundo, así que conviene encontrarla a propósito. Ejecuta la misma lectura dos veces, una anónima y otra con sesión iniciada:
await prax.data.from('Demos Leaderboard').limit(2).all() // funciona
await prax.auth.login(email, password)
await prax.data.from('Demos Leaderboard').limit(2).all() // 400 No access to table 't'No se rompió nada. Cambió la credencial. Mientras no hay nadie con sesión iniciada, el SDK envía la publishable key del workspace, y la lectura funciona porque esa clave tiene un scope sobre la tabla. En cuanto un usuario inicia sesión, el SDK envía el token de ese usuario, y ahora decide el rol del usuario, no la clave. Si el rol no tiene scope sobre la tabla, la lectura se rechaza.
La solución es un ajuste en el portal, no un cambio de código: dale al rol un scope sobre esa tabla, y ponle un row filter como __SELF__ para que cada usuario vea sólo sus propias filas.
Esto también explica por qué los table scopes en la publishable key merecen desconfianza. Esa clave es pública, así que cada scope que le des se lo das a cualquiera que tenga tu workspace id.
Paso 5 - Llamar a un Endpoint del Gateway
Un endpoint es una URL de tu workspace atada a una Automation. Tu aplicación envía un payload; la Automation decide qué pasa realmente.
const resultado = await prax.endpoints.call<{ ok: boolean; total: number }>(
'5e4cecb3-00c3-458c-8803-2a69742bfc8e',
{ limite: 10 },
)call() devuelve lo que respondió la Automation, tipado como se lo pidas. El token de sesión del usuario se adjunta automáticamente, así que la Automation puede identificar a quien llama a partir de un claim verificado y no de un id en el payload.
Para eventos que no te importan, usa fire(). Nunca lanza, y devuelve false cuando la llamada no llegó:
await prax.endpoints.fire('<endpointId>', { evento: 'nivel_completado' })¿Por qué un endpoint y no una escritura directa?
Usa esta prueba: si un cliente modificado enviando un payload arbitrario pudiera obtener algo que no le corresponde, esa operación va en un endpoint, y la tabla que hay detrás no debe ser escribible por el rol del usuario.
Otorgar moneda, enviar un puntaje, gastar un saldo, tocar datos de otro usuario: todos endpoints. El estado cosmético propio de un usuario, como una preferencia o la última página vista, es una escritura directa perfectamente razonable.
Ejemplo completo
Todo lo anterior, en un archivo que puedes ejecutar con node ejemplo.mjs:
import { createClient, f } from '@praxsuite/sdk'
const prax = createClient({
workspaceId: 'ffd80539-a1e2-4a9e-8b33-f716bf690281',
// Sólo hace falta en el SDK 1.0.1. Bórralo en 1.0.2 y superiores.
fetch: (...args) => globalThis.fetch(...args),
})
// 1. Leer sin nadie con sesión iniciada, usando la publishable key del workspace.
const top = await prax.data
.from('Demos Leaderboard')
.select('Alias', 'Points')
.where(f.gt('Points', 0))
.orderByDescending('Points')
.limit(5)
.all()
console.log('Top 5:', top)
// 2. Crear una cuenta. En una aplicación real esto es tu formulario de registro.
const email = `demo.${Date.now()}@example.com`
const cuenta = await prax.auth.register({
email,
password: 'atLeast8Chars',
username: 'demo',
})
console.log('Sesión iniciada:', cuenta.isSignedIn, 'como', prax.auth.currentUserId)
// 3. Llamar a un endpoint. La Automation decide el resultado, no este código.
const marcador = await prax.endpoints.call('5e4cecb3-00c3-458c-8803-2a69742bfc8e', { limite: 3 })
console.log('El endpoint respondió:', marcador)
await prax.auth.logout()Ejecútalo y deberías ver cinco filas, un id de usuario y la respuesta del endpoint. Si la primera lectura falla, salta a la tabla de errores de abajo.
Errores comunes y cómo evitarlos
Error | Causa | Solución |
| El SDK 1.0.1 llama a | Actualiza a 1.0.2, o pasa |
| Se pasó una clave | Usa la clave |
| La credencial que llama no tiene scope sobre esa tabla. Aparece muy seguido justo después de un login, cuando el rol del usuario reemplazó a la publishable key | Dale al rol un scope sobre la tabla en el portal, con un row filter |
| Se llamó a | Copia el UUID de la barra de direcciones del portal |
| Workspace id equivocado, host equivocado, o sin red | Un workspace vive en exactamente un tier. El host equivocado devuelve 404, no un mensaje útil |
|
| Verifica que la constante realmente tenga un valor |
Las filas se insertan pero vuelven vacías | Los nombres de columna no coinciden con la tabla | Los nombres distinguen mayúsculas y espacios. |
Consejos para producción
Dale a la publishable key la menor cantidad posible de table scopes, idealmente ninguno. Es pública, así que cada scope que tenga se lo estás dando a cualquiera que conozca tu workspace id. Que los usuarios con sesión obtengan su acceso de un rol.
Configura el row filter y el valor por defecto de la columna juntos. Un filtro
__SELF__en el table scope cubre select, update y delete, pero no el insert, porque un insert no tiene cláusulaWHERE. Pon también{{claim:sub}}como valor por defecto de la columna Enduser, o las filas se guardan sin dueño y el propio filtro las esconde.Pasa un `AbortSignal` en las llamadas atadas a un componente o a una petición, para que cancelar cancele de verdad.
Prefiere `insertMany` antes que un bucle. Un solo viaje de red en lugar de muchos, y una sola llamada contra tu plan.
Pon siempre un `limit`. El Gateway recorta en silencio las peticiones demasiado grandes, así que lee
page.limiten vez de suponer que respetó el tuyo.Prueba en un navegador real, no sólo en Node. El
fetchde Node ignora su receptor, así que toda una clase de fallas propias del navegador es invisible desde un script de consola.
Próximos pasos
Ahora que los datos se mueven, algunas direcciones para seguir:
Construye una pantalla de registro e inicio de sesión con
getWorkspaceConfig(), que devuelve el nombre, el logo y los colores de tu workspace para que la pantalla combine con tu marca.Lleva una regla que importe a una Automation y llámala con
endpoints.call(), para que decida el servidor y no el cliente.Agrega el flujo de recuperación de contraseña:
forgotPassword,verifyResetCodeyresetPassword.Lee la guía TypeScript SDK Use Case, que construye un juego completo sobre estas bases, backend incluido.
Ya tienes una aplicación de TypeScript hablando con un workspace real, con una identidad de usuario real detrás de cada petición. Mucha suerte.