SDK Frontend
El @dynamia-tools/sdk es la librería cliente oficial de JavaScript y TypeScript para Dynamia Platform. Proporciona una interfaz limpia y completamente tipada para interactuar con las APIs REST de tu backend, facilitando la construcción de aplicaciones web modernas con React, Vue, Angular, o cualquier otro framework de JavaScript.
¿Por Qué Usar el SDK?
Sección titulada «¿Por Qué Usar el SDK?»En lugar de crear manualmente peticiones fetch y gestionar endpoints de API, el SDK te ofrece:
- 🎯 Soporte Completo de TypeScript – Autocompletado y seguridad de tipos para todas las llamadas a la API
- 🚀 Cero Dependencias en Tiempo de Ejecución – Ligero y rápido
- 📦 Cobertura Completa de la API – Acceso a metadatos, operaciones CRUD, acciones, reportes, archivos y más
- 🔐 Autenticación Flexible – Soporte para tokens Bearer, autenticación básica y cookies de sesión
- 🛡️ Manejo de Errores Integrado – Mensajes de error claros con tipos apropiados
Comenzando
Sección titulada «Comenzando»Instalación
Sección titulada «Instalación»Instala el paquete usando tu gestor de paquetes favorito:
# pnpm (recomendado)pnpm add @dynamia-tools/sdk
# npmnpm install @dynamia-tools/sdk
# yarnyarn add @dynamia-tools/sdkUso Básico
Sección titulada «Uso Básico»Crea una instancia del cliente y comienza a hacer llamadas a la API:
import { DynamiaClient } from '@dynamia-tools/sdk';
// Inicializar el clienteconst client = new DynamiaClient({ baseUrl: 'https://tu-app.ejemplo.com', token: 'tu-token-jwt-aqui',});
// Obtener metadatos de la aplicaciónconst app = await client.metadata.getApp();console.log(`Conectado a ${app.name} v${app.version}`);
// Trabajar con entidadesconst books = await client.crud('store/books').findAll();console.log(`Se encontraron ${books.total} libros`);Autenticación
Sección titulada «Autenticación»El SDK soporta múltiples métodos de autenticación para ajustarse a las necesidades de tu aplicación:
Token Bearer (Recomendado para SPAs)
Sección titulada «Token Bearer (Recomendado para SPAs)»Perfecto para aplicaciones de página única usando tokens JWT:
const client = new DynamiaClient({ baseUrl: 'https://api.ejemplo.com', token: 'eyJhbGciOiJIUzI1NiJ9...',});Autenticación Básica
Sección titulada «Autenticación Básica»Para comunicación servidor a servidor:
const client = new DynamiaClient({ baseUrl: 'https://api.ejemplo.com', username: 'admin', password: 'secret',});Basado en Sesión (con Cookies)
Sección titulada «Basado en Sesión (con Cookies)»Para aplicaciones web tradicionales usando cookies de sesión:
const client = new DynamiaClient({ baseUrl: 'https://api.ejemplo.com', withCredentials: true, // Incluir cookies en las peticiones});Características Principales
Sección titulada «Características Principales»📋 Trabajando con Entidades (CRUD)
Sección titulada «📋 Trabajando con Entidades (CRUD)»La API CRUD facilita la gestión de tus entidades de dominio:
// Obtener una referencia a una colección de entidadesconst books = client.crud('catalog/books');
// Listar todos los libros con paginaciónconst page = await books.findAll({ page: 1, size: 20 });
// Buscar y filtrarconst results = await books.findAll({ q: 'typescript', author: 'Martin Fowler'});
// Obtener un solo libro por IDconst book = await books.findById(42);
// Crear un nuevo libroconst newBook = await books.create({ title: 'Clean Code', author: 'Robert C. Martin', isbn: '978-0132350884',});
// Actualizar un libro existenteconst updated = await books.update(42, { price: 49.99,});
// Eliminar un libroawait books.delete(42);🎯 Ejecutando Acciones
Sección titulada «🎯 Ejecutando Acciones»Dispara lógica de negocio y acciones del lado del servidor:
// Ejecutar una acción globalconst result = await client.actions.executeGlobal('sendWelcomeEmail', { params: { userId: 123 },});
// Ejecutar una acción específica de una entidadconst orderResult = await client.actions.executeEntity( 'com.example.domain.Order', 'approveOrder', { data: { orderId: 99 }, params: { notify: true } });
console.log(orderResult.message); // "Orden aprobada exitosamente"🗂️ Metadatos de la Aplicación
Sección titulada «🗂️ Metadatos de la Aplicación»Descubre la estructura de tu aplicación en tiempo de ejecución:
// Obtener información de la aplicaciónconst app = await client.metadata.getApp();
// Obtener el árbol de navegación completoconst nav = await client.metadata.getNavigation();
// Construir un menú dinámiconav.modules.forEach(module => { console.log(`📦 ${module.name}`); module.groups.forEach(group => { group.pages.forEach(page => { console.log(` → ${page.name} (${page.virtualPath})`); }); });});
// Obtener metadatos para una entidad específicaconst bookMeta = await client.metadata.getEntity('com.example.domain.Book');
// Obtener descriptores de vista (formulario, tabla, etc.)const formDescriptor = await client.metadata.getEntityView( 'com.example.domain.Book', 'form');📊 Reportes
Sección titulada «📊 Reportes»Accede y genera reportes desde tu backend:
// Listar todos los reportes disponiblesconst reports = await client.reports.list();
// Obtener datos de un reporte con filtrosconst salesData = await client.reports.get('sales', 'monthly-summary', { year: '2026', month: '03',});
// O usar POST para filtros complejosconst data = await client.reports.post('sales', 'monthly-summary', { filters: [ { name: 'year', value: '2026' }, { name: 'status', value: 'CLOSED' }, ],});📁 Gestión de Archivos
Sección titulada «📁 Gestión de Archivos»Descarga y gestiona archivos desde la extensión entity-files:
// Descargar un archivo como Blobconst blob = await client.files.download('factura.pdf', 'uuid-123');
// O obtener una URL directa para imágenesconst imageUrl = client.files.getUrl('foto-perfil.jpg', 'uuid-456');
// Usar en tu HTML<img src={imageUrl} alt="Perfil" />🏢 Multi-tenencia (SaaS)
Sección titulada «🏢 Multi-tenencia (SaaS)»Gestiona cuentas de inquilinos en aplicaciones multi-tenant:
// Obtener información de la cuentaconst account = await client.saas.getAccount('account-uuid');
console.log(`Cuenta: ${account.name}`);console.log(`Estado: ${account.status}`);console.log(`Subdominio: ${account.subdomain}`);Manejo de Errores
Sección titulada «Manejo de Errores»El SDK lanza errores tipados que son fáciles de capturar y manejar:
import { DynamiaApiError } from '@dynamia-tools/sdk';
try { const book = await client.crud('books').findById(9999);} catch (error) { if (error instanceof DynamiaApiError) { console.error(`Error de API [${error.status}]: ${error.message}`);
// Manejar códigos de estado específicos if (error.status === 404) { console.log('Libro no encontrado'); } else if (error.status === 401) { console.log('No autorizado - por favor inicia sesión'); } } else { console.error('Error inesperado:', error); }}Soporte de TypeScript
Sección titulada «Soporte de TypeScript»El SDK está escrito en TypeScript y proporciona definiciones de tipos completas para todas las APIs:
import type { ApplicationMetadata, NavigationTree, EntityMetadata, ViewDescriptor, ActionExecutionResponse, CrudListResult,} from '@dynamia-tools/sdk';
// Operaciones CRUD genéricas con tipos personalizadosinterface Book { id: number; title: string; author: string; isbn: string; price: number;}
const books = client.crud<Book>('catalog/books');const result: CrudListResult<Book> = await books.findAll();
result.content.forEach(book => { // ✅ Autocompletado completo y verificación de tipos console.log(`${book.title} por ${book.author} - $${book.price}`);});Ejemplo del Mundo Real
Sección titulada «Ejemplo del Mundo Real»Aquí hay un ejemplo completo de construcción de una función de gestión de libros:
import { DynamiaClient } from '@dynamia-tools/sdk';
// Inicializar clienteconst client = new DynamiaClient({ baseUrl: import.meta.env.VITE_API_URL, token: localStorage.getItem('auth_token'),});
// Definir el tipo de tu entidadinterface Book { id?: number; title: string; author: string; isbn: string; price: number; publishedDate: string;}
// Crear una clase gestora de librosclass BookManager { private crud = client.crud<Book>('store/catalog/books');
async searchBooks(query: string) { return await this.crud.findAll({ q: query, page: 1, size: 10 }); }
async getBook(id: number) { return await this.crud.findById(id); }
async createBook(book: Omit<Book, 'id'>) { return await this.crud.create(book); }
async updateBook(id: number, updates: Partial<Book>) { return await this.crud.update(id, updates); }
async deleteBook(id: number) { await this.crud.delete(id); }
async exportBooks() { const result = await client.actions.executeGlobal('exportBooksToExcel', { params: { format: 'xlsx' } }); return result.data; }}
// Usar en tu aplicaciónconst bookManager = new BookManager();
// Buscarconst results = await bookManager.searchBooks('clean code');console.log(`Se encontraron ${results.total} libros`);
// Crearconst newBook = await bookManager.createBook({ title: 'The Pragmatic Programmer', author: 'Andrew Hunt', isbn: '978-0135957059', price: 44.95, publishedDate: '2019-09-13',});
// Actualizarawait bookManager.updateBook(newBook.id!, { price: 39.99 });
// Exportarawait bookManager.exportBooks();Configuración Avanzada
Sección titulada «Configuración Avanzada»Implementación Fetch Personalizada
Sección titulada «Implementación Fetch Personalizada»Puedes proporcionar tu propia implementación de fetch (útil para Node.js o pruebas):
import fetch from 'node-fetch';
const client = new DynamiaClient({ baseUrl: 'https://api.ejemplo.com', token: 'token', fetch: fetch as any,});Interceptores de Peticiones
Sección titulada «Interceptores de Peticiones»Aunque el SDK no incluye interceptores integrados, puedes envolver los métodos del cliente:
class TrackedDynamiaClient extends DynamiaClient { async request(endpoint: string, init?: RequestInit) { console.log(`[API] ${init?.method || 'GET'} ${endpoint}`); const start = Date.now();
try { const response = await super.request(endpoint, init); console.log(`[API] Completado en ${Date.now() - start}ms`); return response; } catch (error) { console.error(`[API] Falló después de ${Date.now() - start}ms`, error); throw error; } }}Mejores Prácticas
Sección titulada «Mejores Prácticas»1. Reutilizar Instancias del Cliente
Sección titulada «1. Reutilizar Instancias del Cliente»Crea una instancia del cliente y reutilízala en toda tu aplicación:
export const apiClient = new DynamiaClient({ baseUrl: import.meta.env.VITE_API_URL, token: () => localStorage.getItem('auth_token'), // Evaluación diferida});2. Manejar Renovación de Tokens
Sección titulada «2. Manejar Renovación de Tokens»Implementa lógica de renovación de tokens en tu capa de autenticación:
async function refreshToken() { const newToken = await fetch('/auth/refresh').then(r => r.json()); localStorage.setItem('auth_token', newToken.token);
// Crear nuevo cliente con el token actualizado return new DynamiaClient({ baseUrl: API_URL, token: newToken.token, });}3. Tipar Tus Entidades
Sección titulada «3. Tipar Tus Entidades»Siempre define interfaces de TypeScript para tus entidades de dominio:
export interface Book { id: number; title: string; author: string; // ... otros campos}
// Usar en toda tu aplicaciónconst books = client.crud<Book>('books');4. Centralizar el Manejo de Errores
Sección titulada «4. Centralizar el Manejo de Errores»Crea un wrapper para un manejo consistente de errores:
async function safeApiCall<T>( operation: () => Promise<T>, fallback?: T): Promise<T | undefined> { try { return await operation(); } catch (error) { if (error instanceof DynamiaApiError) { notifyUser(`Error: ${error.message}`); } return fallback; }}
// Usoconst books = await safeApiCall( () => client.crud('books').findAll(), { content: [], total: 0, page: 1, pageSize: 20 });Ejemplos de Integración con Frameworks
Sección titulada «Ejemplos de Integración con Frameworks»import { useState, useEffect } from 'react';import { apiClient } from './api-client';
function BookList() { const [books, setBooks] = useState([]); const [loading, setLoading] = useState(true);
useEffect(() => { apiClient.crud('books').findAll() .then(result => setBooks(result.content)) .finally(() => setLoading(false)); }, []);
if (loading) return <div>Cargando...</div>;
return ( <ul> {books.map(book => ( <li key={book.id}>{book.title}</li> ))} </ul> );}<script setup lang="ts">import { ref, onMounted } from 'vue';import { apiClient } from './api-client';
const books = ref([]);const loading = ref(true);
onMounted(async () => { const result = await apiClient.crud('books').findAll(); books.value = result.content; loading.value = false;});</script>
<template> <div v-if="loading">Cargando...</div> <ul v-else> <li v-for="book in books" :key="book.id"> {{ book.title }} </li> </ul></template><script lang="ts"> import { onMount } from 'svelte'; import { apiClient } from './api-client';
let books = []; let loading = true;
onMount(async () => { const result = await apiClient.crud('books').findAll(); books = result.content; loading = false; });</script>
{#if loading} <div>Cargando...</div>{:else} <ul> {#each books as book (book.id)} <li>{book.title}</li> {/each} </ul>{/if}Próximos Pasos
Sección titulada «Próximos Pasos»- Consulta la referencia completa de la API en el repositorio
- Explora las aplicaciones de demostración para más ejemplos
- Únete a nuestro Discord de la comunidad para soporte y discusiones
Contribuir
Sección titulada «Contribuir»¡Damos la bienvenida a contribuciones! Por favor consulta nuestra Guía de Contribución para más detalles.
Licencia
Sección titulada «Licencia»Apache License 2.0 - ver LICENSE para más detalles.