DEV Community

Cover image for Applying Koa's onion model to front-end requests
John Wu
John Wu

Posted on • Updated on

Applying Koa's onion model to front-end requests

As anyone who has used koa knows, the onion model is a clever middleware pattern that uses nested functions, which elegantly encapsulates response and request processing in a single middleware

Koa middleware example:

app.use(async (ctx, next) => {
  await next();
})
Enter fullscreen mode Exit fullscreen mode

When we process the request, we are splitting it for each request and the minimum focus should be on the request as a whole (Request & Response) rather than splitting the Request and Response of the request in two separate logics

It is better to visualize the causal relationship between request and response directly through the syntax level of the code, rather than through the function call order

For example, using Koa and Axios to implement the same request timing logic.

Koa

app.use(async (ctx, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(ms)
})
Enter fullscreen mode Exit fullscreen mode

Axios

let start = 0

axios.interceptors.request.use(function(config){
  start = Date.now()
})

axios.interceptors.response.use(function(response){
  const ms = Date.now() - start
  console.log(ms)
})
Enter fullscreen mode Exit fullscreen mode

In the axios code, we just implement the single logic of timing, but it is forcibly split into two Hooks, and it is not clear from the syntax level alone that they have any relationship, only that an external variable is created to couple them together

As you can see, the Onion Middleware looks cleaner and more logically cohesive than the regular Hooks.

The onion model has a natural advantage in handling Before / After logic, such as vue-router, redux also use this approach, since the onion model can be used for back-end requests, it can also be used for client-side requests

So, an HTTP client based on the onion model is created, and it looks like this.

Resreq

import Resreq from 'resreq'

const resreq = new Resreq({
  baseUrl: 'https://example.com'
})

// Intercepting responses and requests using middleware
resreq.use((next) => async (req) => {
  try {
    console.log(req) // Request can be changed here
    const res = await next(req)
    console.log(res) // Response can be changed here
    return res
  } catch (error) {
    console.log(error) // Catch errors here
    throw error
  }
})

const res = await resreq.get('/api', {
  params: { foo: 'bar' }
})

console.log(res)
Enter fullscreen mode Exit fullscreen mode

GitHub: https://github.com/molvqingtai/resreq

Top comments (1)

Collapse
 
maoxoam profile image
maoXoam

That's great idea!