Módulo 4 — Uso de Servicios y API REST en TypeScript
Aplicación práctica: Conexión del sistema de gestión sanitaria con una API REST propia (consulta y actualización de recursos y personal).
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 getDatos(): Promise<void> {
console.log('Iniciando petición...');
await retraso();
console.log('Datos recibidos del servidor');
console.log('Petición enviada');
}
getDatos();
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é
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.