// Laboratório 4 — Um cliente configurado, duas implementações // Execute: npx tsx praticar/04-cliente.ts (com a API da aula 10 no ar) // // src/cliente-axios.ts já está pronto. Aqui você escreve o equivalente com // fetch, sem olhar src/cliente-fetch.ts, e o mesmo roteiro roda nos dois. import { ADMINISTRADOR, API_URL, NOVO_LIVRO, type Livro, type Pagina } from '../src/api.js'; import { criarClienteAxios } from '../src/cliente-axios.js'; import { ErroDaApi, ehCorpoDeErro, sessao, type ClienteDaApi } from '../src/erro-da-api.js'; function criarMeuClienteFetch(): ClienteDaApi { async function requisitar(metodo: string, caminho: string, corpo?: unknown): Promise { // TODO 1: cabeçalhos — Authorization quando houver `sessao.token`; // Content-Type quando houver corpo. // TODO 2: a chamada, com tempo limite de 5 s. Sem resposta → ErroDaApi(null, ...). // TODO 3: fora da faixa 2xx → ErroDaApi(status, detalhes do envelope). // Use ehCorpoDeErro: o corpo pode não estar no formato esperado. // TODO 4: 204 → undefined; demais → corpo interpretado. void metodo; void caminho; void corpo; void ehCorpoDeErro; void API_URL; throw new Error('TODO: requisitar'); } return { get: (c) => requisitar('GET', c), post: (c, b) => requisitar('POST', c, b), patch: (c, b) => requisitar('PATCH', c, b), delete: (c) => requisitar('DELETE', c), }; } // TODO 5: escreva o guarda `ehLivro(valor: unknown): valor is Livro` e use-o // em `buscarConferido`, para que um corpo fora do contrato falhe aqui, // e não longe daqui. async function buscarConferido(api: ClienteDaApi, id: number): Promise { return api.get(`/livros/${id}`); } async function roteiro(nome: string, api: ClienteDaApi): Promise { const linhas: string[] = []; const registrar = async (rotulo: string, f: () => Promise) => { try { linhas.push(`${rotulo}: ${await f()}`); } catch (erro) { linhas.push(`${rotulo}: ${erro instanceof ErroDaApi ? `ErroDaApi ${erro.status}` : String(erro)}`); } }; sessao.token = undefined; const acervo = await api.get>('/livros?tamanho=100'); const livro = { ...NOVO_LIVRO, autorId: acervo.dados[0].autor.id }; await registrar('conferido', async () => (await buscarConferido(api, acervo.dados[0].id)).titulo); await registrar('404', async () => (await api.get('/livros/999999')).titulo); await registrar('sem token', async () => (await api.post('/livros', livro)).titulo); sessao.token = (await api.post<{ accessToken: string }>('/auth/login', ADMINISTRADOR)).accessToken; let id = 0; await registrar('cadastro', async () => { id = (await api.post('/livros', livro)).id; return 'ok'; }); await registrar('remoção', async () => { await api.delete(`/livros/${id}`); return 'ok'; }); console.log(`${nome.padEnd(6)} ${linhas.join(' | ')}`); } await roteiro('axios', criarClienteAxios()); await roteiro('fetch', criarMeuClienteFetch()); // TODO 6: as duas linhas precisam ser iguais. Se não forem, a diferença está // no seu cliente — não no roteiro.