Módulo 4 — Uso de Servicios y API REST en TypeScript
1. Introducción a la asincronía
TypeScript hereda el modelo asíncrono y no bloqueante de JavaScript, pero añade tipado estático que permite detectar errores en el manejo de promesas y datos remotos antes de ejecutar la aplicación.
console.log('Iniciando petición...');
setTimeout((): void => {
console.log('Datos recibidos del servidor');
}, 2000);
console.log('Fin de la petición');
El flujo principal no se bloquea; la operación asíncrona se gestiona mediante el Event Loop. El Event Loop es el mecanismo que permite a Javascript (Typescript por herencia) ejecutar codigo en paralelo. Podría decirse que es un organizador de tareas o hilos.
Si se quisiera esperar a recibir los datos lo que habría que hacer es:
console.log('Iniciando petición...');
async function retraso(): Promise<string> {
return new Promise((resolve) => {
console.log('Cargando Datos');
setTimeout(() => {
resolve('Datos cargados');
}, 1500);
}
);
}
async function ejecutar() {
let respuesta = await retraso();
console.log(respuesta);
console.log('Fin de la petición');
}
ejecutar();
2. Qué es una API REST
Una API REST (Representational State Transfer) permite la comunicación entre aplicaciones a través de HTTP. En una aplicación de gestión de recursos, una API REST expone recursos como recursos, solicitudes o activaciones.
Las API REST intercambian datos en formato JSON, fácilmente mapeables a interfaces TypeScript. Por qué se usan interfaces en lugar de clases tiene que ver con la respuesta de la API. Las API, como se ha mencionado anteriormente, no devuleve objetos, aunque lo que devuelva sea una notación de los mismos. Por eso, de cara a la flexibilidad, es mejor registrarlos como interface y no como objetos.
3. Métodos HTTP más comunes
| Método | Acción | Ejemplo en la aplicación |
|---|---|---|
| GET | Obtener información | Consultar recursos |
| POST | Crear un recurso | Añadir personal |
| PUT | Actualizar completo | Editar recurso |
| PATCH | Actualizar parcial | Cambiar estado |
| DELETE | Eliminar | Dar de baja |
4. Qué es JSON
JSON es el formato estándar de intercambio de datos. En TypeScript, el JSON recibido se transforma en objetos tipados.
interface RecursoDTO {
id: string;
unidad: string;
tipoRecurso: 'HUMANO' | 'MATERIAL';
}
const recurso: RecursoDTO = {
id: '1hd72h3kw8fhs7dh34',
unidad: 'JMAPER',
tipoRecurso: 'HUMANO',
};
const json: string = JSON.stringify(recurso);
const obj: RecursoDTO = JSON.parse(json);
5. Peticiones HTTP con fetch
fetch devuelve una Promise<Response>. En TypeScript se tipa explícitamente la respuesta esperada.
async function obtenerRecursos(): Promise<RecursoDTO[]> {
const respuesta = await fetch('http://localhost:8080/panacea/rest/recursos');
if (!respuesta.ok) {
throw new Error(`Error HTTP: ${respuesta.status}`);
}
return respuesta.json();
}
obtenerRecursos()
.then((recursos: RecursoDTO[]) => {
console.log('Recursos disponibles:', recursos);
})
.catch((error: Error) => {
console.error('Error al obtener los datos:', error.message);
});
6. Uso de async y await
async/await permite escribir código asíncrono con estructura secuencial, manteniendo el tipado.
async function cargarRecursos(): Promise<void> {
try {
const respuesta = await fetch('http://localhost:8080/panacea/rest/recursos');
const datos: RecursoDTO[] = await respuesta.json();
console.table(datos);
} catch (error) {
console.error('Error al cargar recursos', error);
}
}
cargarRecursos();
7. Envío de datos (POST)
async function agregarRecurso(nuevo: RecursoDTO): Promise<RecursoDTO> {
const respuesta = await fetch('http://localhost:8080/panacea/rest/recursos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(nuevo),
});
if (!respuesta.ok) {
throw new Error('Error al crear el recurso');
}
return respuesta.json();
}
agregarRecurso({
id: '17d263hf84j6sh3h9sjd',
unidad: 'JMAPER',
tipoRecurso: 'HUMANO',
}).then((resultado: RecursoDTO) => {
console.log('Recurso agregado:', resultado);
});
8. Actualización y eliminación
PATCH (actualización parcial)
async function actualizarRecurso(id: string, datos: Partial<RecursoDTO>): Promise<void> {
await fetch(`http://localhost:8080/panacea/rest/recursos/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(datos),
});
}
DELETE (eliminación)
async function eliminarRecurso(id: string): Promise<void> {
await fetch(`http://localhost:8080/panacea/rest/recursos/${id}`, {
method: 'DELETE',
});
console.log('Recurso eliminado correctamente');
}
9. CORS (Cross-Origin Resource Sharing)
CORS es un mecanismo de seguridad del navegador. El servidor debe permitir explícitamente el origen del frontend.
Access-Control-Allow-Origin: http://localhost:4200
En Angular, este aspecto se gestiona principalmente en el backend o mediante proxies de desarrollo.
10. Ejemplo práctico: mostrar recursos en la interfaz
<h2>Recursos disponibles</h2>
<ul id="lista"></ul>
<button id="btnCargar">Cargar recursos</button>
const btn = document.getElementById('btnCargar') as HTMLButtonElement;
const lista = document.getElementById('lista') as HTMLUListElement;
btn.addEventListener('click', async (): Promise<void> => {
try {
const recursos = await obtenerRecursos();
lista.innerHTML = '';
recursos.forEach((r: RecursoDTO) => {
const li = document.createElement('li');
li.textContent = `${r.tipoRecurso}: ${r.unidad}`;
lista.appendChild(li);
});
} catch {
alert('Error al cargar recursos');
}
});
11. Buenas prácticas en TypeScript
- Centralizar las llamadas HTTP en servicios.
- Tipar siempre los datos entrantes y salientes.
- Usar
Partial<T>para actualizaciones. - Manejar errores HTTP explícitamente.