Розробка веб-бекенду на Node.js (Koa)
Koa—мінімалістичний фреймворк від творців Express, переосмислений під async/await. Де Express вимагає next() й callback'и, Koa працює через async/await й middleware-стек, що виконується за принципом "цибулі": запит проходить middleware зверху вниз, потім відповідь—знизу вгору.
Вибирають Koa, коли: потрібна повна свобода вибору бібліотек без думок фреймворку, але з нормальною обробкою async-коду на відміну від Express.
Паттерн Middleware
import Koa from 'koa'
import Router from '@koa/router'
const app = new Koa()
// Middleware логування—обертає все нижче
app.use(async (ctx, next) => {
const start = Date.now()
await next() // виконує все інше
const ms = Date.now() - start
console.log(`${ctx.method} ${ctx.url} - ${ctx.status} - ${ms}ms`)
})
// Обробка помилок
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = err.statusCode || err.status || 500
ctx.body = {
error: process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message
}
}
})
Принципова різниця від Express: у Koa після await next() ви повертаєтесь до middleware з доступом до фінального стану відповіді. У Express це неможливо без хаків.
Роутинг
@koa/router—офіційний маршрутизатор:
import Router from '@koa/router'
import bodyParser from '@koa/bodyparser'
const router = new Router({ prefix: '/api/v1' })
router.get('/products', authenticate, async (ctx) => {
const { page = 1, limit = 20 } = ctx.query
const offset = (page - 1) * limit
const [items, total] = await Promise.all([
db.query('SELECT * FROM products LIMIT $1 OFFSET $2', [limit, offset]),
db.query('SELECT COUNT(*) FROM products')
])
ctx.body = {
data: items.rows,
pagination: {
page: Number(page),
limit: Number(limit),
total: Number(total.rows[0].count)
}
}
})
router.post('/products', authenticate, requireRole('admin'), async (ctx) => {
const data = ctx.request.body
const product = await ProductService.create(data)
ctx.status = 201
ctx.body = product
})
app.use(router.routes())
Middleware валідації
const validateBody = (schema) => {
return async (ctx, next) => {
try {
ctx.request.body = await schema.validate(ctx.request.body)
await next()
} catch (err) {
ctx.status = 422
ctx.body = { error: 'Validation failed' }
}
}
}
router.post('/products', validateBody(createProductSchema), async (ctx) => {
// body вже валідований
})
Доступ до бази даних
import { Pool } from 'pg'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
// Middleware для приєднання db до контексту
app.use(async (ctx, next) => {
ctx.db = pool
await next()
})
Обробка помилок
class AppError extends Error {
constructor(message, statusCode) {
super(message)
this.statusCode = statusCode
}
}
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
if (err instanceof AppError) {
ctx.status = err.statusCode
ctx.body = { error: err.message }
} else {
ctx.status = 500
ctx.body = { error: 'Internal server error' }
}
}
})
Запуск сервера
// index.js
const app = new Koa()
app.use(errorHandler)
app.use(bodyParser())
app.use(router.routes())
app.listen(process.env.PORT || 3000, () => {
console.log('Server running on port', process.env.PORT || 3000)
})
Таймлайн
Базовий сетап з роутингом і middleware—1 день. Інтеграція БД, аутентифікація—2–3 дні. Повний API з валідацією, тестуванням—1 тиждень.
Koa підходить малим командам, greenfield проектам або коли ви переважаєте компонування бібліотек над умовностями фреймворку.







