Библиотека Iron строит свою работу вокруг классической модели request–response, в которой каждый входящий запрос обрабатывается изолированно и приводит к формированию единственного ответа. В отличие от событийных или потоковых систем, здесь сохраняется строгая линейность: запрос → обработка → ответ.
Ключевая особенность Iron — минимализм и прозрачность цепочки обработки. Вся логика разбивается на небольшие, предсказуемые этапы, что упрощает отладку и масштабирование.
Запрос в Iron представляет собой абстракцию над HTTP-входящими данными. Он инкапсулирует:
GET, POST, PUT,
DELETE)Пример структуры:
{
method: 'POST',
url: '/api/users',
headers: { 'content-type': 'application/json' },
body: { name: 'Alice' }
}
Iron не навязывает строгую форму хранения данных — объект запроса может быть расширен пользовательскими полями в процессе обработки.
Ответ формируется отдельно и передаётся обратно клиенту после завершения обработки. Он включает:
Минимальный пример:
{
status: 200,
headers: { 'content-type': 'application/json' },
body: { success: true }
}
Iron поддерживает ленивое формирование ответа — данные могут добавляться по мере прохождения через обработчики.
Каждый запрос оборачивается в контекст — специальный объект, объединяющий request и response, а также дополнительные данные:
const context = {
request,
response,
state: {}
}
Поле state используется для передачи данных между
промежуточными слоями без загрязнения глобальной области.
Iron реализует модель обработки через middleware — функции, выполняющиеся последовательно. Каждая функция получает контекст и управление:
async function middleware(ctx, next) {
// логика до
await next()
// логика после
}
Особенности:
Пример цепочки:
app.use(async (ctx, next) => {
console.log('Request received')
await next()
})
app.use(async (ctx) => {
ctx.response.body = { message: 'Hello' }
})
Модель обработки запроса выглядит следующим образом:
Графически:
Request → Middleware 1 → Middleware 2 → ... → Response
Middleware могут:
await next())next)Пример прерывания:
app.use(async (ctx, next) => {
if (!ctx.request.headers.authorization) {
ctx.response.status = 401
ctx.response.body = { error: 'Unauthorized' }
return
}
await next()
})
Iron не скрывает ошибки, а делегирует их обработку middleware-уровню.
Базовый подход:
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.response.status = 500
ctx.response.body = { error: err.message }
}
})
Это позволяет централизовать обработку исключений.
Хотя request считается неизменяемым источником данных, Iron допускает его расширение:
ctx.request.user = decodedToken
Response, наоборот, проектируется как изменяемый объект — он постепенно формируется в ходе обработки.
Iron полностью основан на async/await, что
позволяет:
Пример:
app.use(async (ctx) => {
const users = await db.getUsers()
ctx.response.body = users
})
Каждый запрос обрабатывается независимо:
Iron не ограничивает разработчика:
Пример расширения:
app.use(async (ctx, next) => {
ctx.state.startTime = Date.now()
await next()
console.log(Date.now() - ctx.state.startTime)
})
Iron не навязывает способ парсинга тела — это ответственность middleware:
app.use(async (ctx, next) => {
if (ctx.request.headers['content-type'] === 'application/json') {
ctx.request.body = JSON.parse(ctx.request.rawBody)
}
await next()
})
Ответ можно формировать по частям:
app.use(async (ctx, next) => {
ctx.response.headers['x-powered-by'] = 'Iron'
await next()
})
app.use(async (ctx) => {
ctx.response.body = { data: 'example' }
})
Iron не ограничивает работу с HTTP:
ctx.response.status = 201
ctx.response.headers['cache-control'] = 'no-cache'
Каждый middleware выполняет одну задачу:
Это соответствует принципу single responsibility.
Хотя сама модель request–response не включает маршрутизацию, она легко интегрируется:
app.use(async (ctx, next) => {
if (ctx.request.url === '/users') {
ctx.response.body = getUsers()
return
}
await next()
})
Middleware вызываются только при наличии запроса. Нет фоновых процессов или скрытых вычислений.
Изолированная модель позволяет тестировать обработчики без запуска сервера:
await middleware(ctx, async () => {})
Каждый этап полностью контролируем:
Нет скрытых механизмов.
const app = new Iron()
app.use(async (ctx, next) => {
console.log(ctx.request.method, ctx.request.url)
await next()
})
app.use(async (ctx) => {
ctx.response.status = 200
ctx.response.body = { message: 'OK' }
})
app.listen(3000)
В отличие от callback-ориентированных фреймворков:
В отличие от потоковых систем:
Возможна организация циклов обработки внутри middleware:
app.use(async (ctx, next) => {
for (const item of ctx.request.body.items) {
// обработка
}
await next()
})
Middleware можно группировать:
const auth = async (ctx, next) => { /* ... */ }
const validate = async (ctx, next) => { /* ... */ }
app.use(auth)
app.use(validate)
Если response не был установлен, Iron может:
Поэтому важно явно задавать ctx.response.body.
Добавление таймингов:
app.use(async (ctx, next) => {
const start = Date.now()
await next()
ctx.response.headers['x-time'] = Date.now() - start
})
Модель request–response хорошо масштабируется:
Каждый этап явно показывает:
Это снижает сложность поддержки.
Client → Request → Context → Middleware Chain → Response → Client
Модель остаётся простой, но при этом достаточно мощной для построения сложных серверных приложений.