====== 07 - Consumo de APIs REST ====== Desde el navegador o Node, la función ''fetch'' envía peticiones **HTTP** a una API REST y devuelve una promesa con la respuesta. Es el mecanismo con el que el front de TypeScript consume las APIs creadas en Programación. ===== 1. fetch y async/await ===== async function getBook(id: number): Promise { const response = await fetch(`/api/books/${id}`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return response.json(); } * ''await'' pausa hasta que llega la respuesta sin bloquear el hilo. * ''response.ok'' es ''true'' con códigos 2xx. * ''response.json()'' lee y parsea el cuerpo como JSON. Una función ''async'' devuelve una **Promise**; el error se captura con ''try/catch'': try { const book = await getBook(42); render(book); } catch (error) { showErrorMessage("No se pudo cargar el libro"); } ===== 2. GET y POST ===== // GET: petición por defecto const books = await fetch("/api/books?genre=novel").then(r => r.json()); // POST: envía un cuerpo JSON async function createBook(book: CreateBookRequest): Promise { const response = await fetch("/api/books", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(book), }); if (response.status !== 201) { throw new Error(`HTTP ${response.status}`); } } ^ Método ^ Uso ^ | ''GET'' | Consultar; sin cuerpo | | ''POST'' | Crear; cuerpo JSON | | ''PUT'' | Reemplazar un recurso | | ''DELETE'' | Eliminar | ===== 3. Cabeceras ===== headers: { "Content-Type": "application/json", // formato del cuerpo enviado "Accept": "application/json", // formato esperado en la respuesta "Authorization": `Bearer ${token}` // autenticación } ===== 4. Autenticación con tokens ===== La API de login de Programación entrega un **token de acceso**; el cliente lo guarda y lo envía en cada petición protegida: async function login(email: string, password: string): Promise { const response = await fetch("/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); if (!response.ok) { throw new Error("Invalid credentials"); } return response.json(); // { accessToken, refreshToken } } async function getProfile(token: string): Promise { const response = await fetch("/auth/me", { headers: { "Authorization": `Bearer ${token}` }, }); if (response.status === 401) { // token caducado o inválido: renovar o pedir login } return response.json(); } ^ Respuesta ^ Interpretación ^ | ''200''/''201'' | Operación correcta | | ''400'' | Petición mal formada | | ''401'' | Token ausente, caducado o inválido | | ''403'' | Autenticado pero sin permisos | | ''404'' | Recurso inexistente | ===== 5. Errores ===== El cuerpo de error es también JSON, con un formato acordado por la API: { "code": "USER_ALREADY_EXISTS", "message": "No se puede crear el usuario" } Guarda el token en memoria o en ''sessionStorage'', nunca lo imprimas en logs. Si la respuesta es ''401'', usa el refresh token para renovar el par y repite la petición.