NodeJS용 구성 가능한 HTTP 클라이언트

그래서 NodeJS용 HTTP 클라이언트를 만들었습니다.

var compose = require('request-compose')


BTW if you want to skip ahead and immerse yourself in the FP goodness right away - look no further



어떻게 사용됩니까?

var {res, body} = await compose.client({
  url: 'https://api.github.com/users/simov',
  headers: {
    'user-agent': 'request-compose'
  }
})


Oh WOW! REALLY!?
Yet another HTTP Client for NodeJS!
A M A Z I N G ! ! !



.. API, API .. 모든 곳



최종 사용자로서 다른 사람의 모듈에서 무언가를 수정, 변경 또는 추가하려면 어떻게 해야 합니까? 내 옵션은 무엇입니까?
  • GitHub에서 문제를 열고 요청하십시오
  • 직접 구현하고 풀 요청 제출
  • 필요한 항목이 있는 대체 모듈 검색
  • 반복

  • 그 이유는 모듈 작성자가 수행할 수 있는 작업과 수행할 수 없는 작업에 대한 API를 제공하기 때문입니다. 당신은 본질적으로 갇혀 있습니다. 저자는 또한 관련되지 않은 무언가가 들어오는 것으로부터 프로젝트의 범위를 맹렬히 보호합니다.

    그러나 우리가 한 단계 아래로 내려가 우리 자신의 것을 우아하게 구성할 수 있게 해주는 더 강력한 프리미티브가 있다면 어떨까요? 다른 솔루션에 제시된 API 및 범위 병목 현상을 완전히 우회합니다.

    구성



    운 좋게도 Functional Composition이라는 프리미티브가 있습니다.

    In computer science, function composition (not to be confused with object composition) is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.



    실제로 request-compose이 노출하는 것은 exactly that입니다.

    var compose = (...fns) => (args) =>
      fns.reduce((p, f) => p.then(f), Promise.resolve(args))
    


    핵심request-compose은 클라이언트가 아니라 기능적 프로그래밍 패턴, 아이디어, 자신만의 것을 작성하는 데 도움이 되는 간단한 한 줄입니다.

    비동기식이든 아니든 모든 함수를 작성할 수 있습니다.

    var sum = compose(
      (x) => x + 1,
      (x) => new Promise((resolve) => setTimeout(() => resolve(x + 2), 1000)),
      (x) => x + 3,
      async (x) => (await x) + 4
    )
    await sum(5) // 15 (after one second)
    


    또는 주제에 대해 좀 더 자세히 알아보려면 자신의 HTTP 클라이언트를 구성하십시오.

    var compose = require('request-compose')
    var https = require('https')
    
    var request = compose(
      (options) => {
        options.headers = options.headers || {}
        options.headers['user-agent'] = 'request-compose'
        return options
      },
      (options) => new Promise((resolve, reject) => {
        https.request(options)
          .on('response', resolve)
          .on('error', reject)
          .end()
      }),
      (res) => new Promise((resolve, reject) => {
        var body = ''
        res
          .on('data', (chunk) => body += chunk)
          .on('end', () => resolve({res, body}))
          .on('error', reject)
      }),
      ({res, body}) => ({res, body: JSON.parse(body)}),
    )
    
    var {res, body} = await request({
      protocol: 'https:',
      hostname: 'api.github.com',
      path: '/users/simov',
    })
    


    Can you spot the API?
    There is none.
    It's all yours, your own Promise based HTTP Client.
    Congratulations!



    실용적인 사항



    멋지고 실용적이지만 그다지 실용적이지는 않습니다. 결국 우리는 일반적으로 모든 것을 한 곳에 코딩하지 않고 코드를 모듈로 추출하려고 합니다.

    그리고 모든 작업을 직접 수행해야 하는 경우 왜 귀찮게 request-compose을 사용하게 될까요?

    대답은 간단합니다.

    You can choose what you want to use, extend it however you want to, or don't use it all - compose your own thing from scratch.



    bunch of functions 그러나 유용한 HTTP 클라이언트 논리 조각을 캡슐화하는 교묘한 이름의 미들웨어가 있습니다.

    var compose = require('request-compose')
    var Request = compose.Request
    var Response = compose.Response
    
    var request = compose(
      Request.defaults({headers: {'user-agent': 'request-compose'}}),
      Request.url('https://api.github.com/users/simov'),
      Request.send(),
      Response.buffer(),
      Response.string(),
      Response.parse(),
    )
    
    var {res, body} = await request()
    


    이러한 미들웨어는 가능한 구현의 예일 뿐이라는 점에 유의해야 합니다. 내 자신의 구현. 그러나 API 벽 뒤에 숨겨져 있지 않기 때문에 잠겨 있지 않습니다.

    당신은 당신 자신의 것을 자유롭게 구성할 수 있습니다:

    var compose = require('request-compose')
    var Request = compose.Request
    var Response = compose.Response
    
    var request = (options) => compose(
      Request.defaults(),
      // my own stuff here - yay!
      ({options}) => {
        options.headers['user-agent'] = 'request-compose'
        options.headers['accept'] = 'application/vnd.github.v3+json'
        return {options}
      },
      // base URL? - no problem!
      Request.url(`https://api.github.com/${options.url}`),
      Request.send(),
      Response.buffer(),
      Response.string(),
      Response.parse(),
    )(options)
    
    var {res, body} = await request({url: 'users/simov'})
    


    Put that in a module on NPM and call it a day.



    전체 원



    우리가 원하는 대로 배열하고 확장할 수 있는 별도의 미들웨어를 갖는 것은 좋지만 코드가 훨씬 더 표현적이고 덜 장황할 수 있습니까?

    이것이 compose.client 인터페이스가 존재하는 유일한 목적입니다.

    var {res, body} = await compose.client({
      url: 'https://api.github.com/users/simov',
      headers: {
        'user-agent': 'request-compose'
      }
    })
    


    And as you may have guessed the options passed to compose.client are merely composing the HTTP client under the hood using the exact same built-in middlewares.



    크게 되다



    동전의 다른 면을 살펴보겠습니다. HTTP 내부에 집중하는 대신 다음과 같이 스스로에게 물어볼 수 있습니다.

    How can we use the Functional Composition to build something bigger?



    고차 HTTP 클라이언트를 구성하는 것은 어떻습니까?

    var compose = require('request-compose')
    
    var search = ((
      github = compose(
        ({query}) => compose.client({
          url: 'https://api.github.com/search/repositories',
          qs: {q: query},
          headers: {'user-agent': 'request-compose'},
        }),
        ({body}) => body.items.slice(0, 3)
          .map(({full_name, html_url}) => ({name: full_name, url: html_url})),
      ),
      gitlab = compose(
        ({query, token}) => compose.client({
          url: 'https://gitlab.com/api/v4/search',
          qs: {scope: 'projects', search: query},
          headers: {'authorization': `Bearer ${token}`},
        }),
        ({body}) => body.slice(0, 3)
          .map(({path_with_namespace, web_url}) =>
            ({name: path_with_namespace, url: web_url})),
      ),
      bitbucket = compose(
        ({query}) => compose.client({
          url: 'https://bitbucket.org/repo/all',
          qs: {name: query},
        }),
        ({body}) => body.match(/repo-link" href="[^"]+"/gi).slice(0, 3)
          .map((match) => match.replace(/repo-link" href="\/([^"]+)"/i, '$1'))
          .map((path) => ({name: path, url: `https://bitbucket.org/${path}`})),
      ),
      search = compose(
        ({query, cred}) => Promise.all([
          github({query}),
          gitlab({query, token: cred.gitlab}),
          bitbucket({query}),
        ]),
        (results) => results.reduce((all, results) => all.concat(results)),
      )) =>
        Object.assign(search, {github, gitlab, bitbucket})
    )()
    
    var results = await search({query: 'request', {gitlab: '[TOKEN]'}})
    


    이제 GitHub, GitLab 및 BitBucket에서 리포지토리를 동시에 검색하는 HTTP 클라이언트가 있습니다. 또한 프런트엔드 앱에서 사용할 수 있도록 배열에 깔끔하게 압축된 결과를 반환합니다.

    Wrap it up in a Lambda and deploy it on the Cloud.
    That's your Serverless!



    결론



    우리를 가두지 않는 모듈이 있다면 어떨까요? API가 없거나 완전히 선택적이고 확장 가능한 API가 있으면 어떻게 됩니까? 우리가 직접 작가가 될 수 있는 도구가 있고 우리에게 가장 적합한 것을 직접 만들 수 있다면 어떨까요?

    이면의 아이디어request-compose는 바로 이것에 더하여 NodeJS용 완전한 기능을 갖춘 기능적(이해?) HTTP 클라이언트입니다. 또는 오히려 이렇게 말해야 할까요: 여기에는 번들로 제공되는 독단적인 HTTP 클라이언트가 포함되어 있습니다. 그것은 당신이 접할 수 있는 대부분의 사용 사례를 다루며 장난감 프로젝트와도 거리가 멀고 나의 첫 번째 HTTP 클라이언트도 아닙니다.

    Not saying it's the best one, but just so you know :)



    행복한 코딩!

    좋은 웹페이지 즐겨찾기