Другая сторона API
Программы на TypeScript бывают не только серверами — они и сами обращаются к API: получают данные, общаются с сервисами, интегрируют сторонние системы. Клиент — это fetch, встроенный в Node.js, и три привычки:
1. выполнить запрос (await fetch(url) для GET или fetch(url, { method, headers, body }) для полного контроля),
2. проверить res.status, прежде чем доверять телу ответа — fetch не выбрасывает исключение на 404 или 500,
3. прочитать тело один раз, с помощью await res.json() или await res.text() — никогда оба сразу.
Примеры ниже обращаются к небольшому локальному серверу, запущенному внутри той же программы, поэтому вывод стабилен — а код клиента ровно такой же, какой вы направили бы на настоящий URL.
GET и декодирование
Повседневная задача: выполнить GET к URL, проверить статус и превратить JSON-тело в данные с помощью await res.json(). Этот вызов возвращает any, поэтому as UserRecord[] сообщает проверщику типов, что именно вы ожидаете получить.
Получаем список в объекты
Тело ответа становится UserRecord[], а дальше это обычные данные TypeScript — их перебирают в цикле и печатают поле за полем.
Вывод: программа на TypeScript и захваченный вывод
import { createServer } from "node:http"
interface UserRecord {
id: number
name: string
}
async function main(): Promise<void> {
// Небольшой локальный сервер заменяет тот API, к которому обращается этот клиент.
const server = createServer((req, res) => {
res.writeHead(200, { "Content-Type": "application/json" })
res.end('[{"id": 1, "name": "Ana"}, {"id": 2, "name": "Ben"}]')
})
await new Promise<void>(resolve => server.listen(0, () => resolve()))
const base = `http://localhost:${(server.address() as { port: number }).port}`
const res = await fetch(`${base}/users`)
// Проверяем статус, прежде чем доверять телу ответа.
if (res.status !== 200) {
console.log("неожиданный статус:", res.status, res.statusText)
server.close()
return
}
const users = (await res.json()) as UserRecord[]
for (const user of users) {
console.log(`пользователь ${user.id}: ${user.name}`)
}
server.close()
}
main()
POST с JSON-телом
Отправка данных — зеркальное отражение: сериализуем объект в строку, передаём его как body с правильным типом содержимого, а затем читаем ответ сервера, который здесь несёт присвоенный сервером id.
Отправляем объект, читаем ответ
Объект уходит в формате JSON, а созданная запись возвращается с новым id 7 — весь путь записи API-клиента в одной маленькой программе. Node.js печатает разобранный объект как { id: 7, name: 'Cleo' }.
Вывод: программа на TypeScript и захваченный вывод
import { createServer, type IncomingMessage } from "node:http"
interface UserRecord {
id: number
name: string
}
async function readBody(req: IncomingMessage): Promise<string> {
const chunks: Buffer[] = []
for await (const chunk of req) chunks.push(chunk as Buffer)
return Buffer.concat(chunks).toString()
}
async function main(): Promise<void> {
// Небольшой локальный сервер заменяет API: именно он присваивает id.
const server = createServer(async (req, res) => {
const incoming = JSON.parse(await readBody(req)) as { name: string }
const created: UserRecord = { id: 7, name: incoming.name }
res.writeHead(201, { "Content-Type": "application/json" })
res.end(JSON.stringify(created))
})
await new Promise<void>(resolve => server.listen(0, () => resolve()))
const base = `http://localhost:${(server.address() as { port: number }).port}`
const res = await fetch(`${base}/users`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Cleo" }),
})
const created = (await res.json()) as UserRecord
console.log("статус: ", res.status, res.statusText)
console.log("создано:", created)
server.close()
}
main()