passing-notes
Workflows for transfering data between frontend and backend
Last updated 2 months ago by vinsonchuong .
MIT · Repository · Bugs · Original npm · Tarball · package.json
$ cnpm install passing-notes 
SYNC missed versions from official npm registry.

passing-notes

npm Build Status dependencies Status devDependencies Status

Workflows for transfering data between frontend and backend

Examples

Installation

Install passing-notes by running

yarn add passing-notes

API

pass-notes request-handler.js

A CLI script that takes the path to a file that exports a Node.js request handler and/or a Node.js WebSocket upgrade handler, transpiles that file, and uses it to start an HTTP server.

Example with HTTP Only

// api.js
import { respondToRequests } from 'passing-notes'

export default respondToRequests(
  next => async request => {
    console.log(request)
    // {
    //    method: 'GET',
    //    url: 'http://localhost:8080',
    //    headers: {},
    //    body: ''
    // }

    return {
      status: 200,
      headers: {},
      body: 'Hello World!'
    }
  }
)
$ yarn pass-notes api.js

Example with WebSocket Only

// api.js
import { acceptConnections } from 'passing-notes'

export const webSocket = acceptConnections(async socket => {
  for await (const message of socket) {
    await socket.send(`Echo: ${message}`)
  }
})
$ yarn pass-notes api.js

Example with Both HTTP and WebSocket

// api.js
import { respondToRequests } from 'passing-notes'
import { acceptConnections } from 'passing-notes'

export const http = respondToRequests(
  next => async request => {
    return {
      status: 200,
      headers: {},
      body: 'Hello World!'
    }
  }
)

export const webSocket = acceptConnections(async socket => {
  for await (const message of socket) {
    await socket.send(`Echo: ${message}`)
  }
})
$ yarn pass-notes api.js

ES.next code is automatically compiled using @babel/register. Changed files are hot-reloaded in development.

Environment variables specified in .env are loaded using dotenv.

This CLI tool is meant to be used in conjunction with other functions provided by passing-notes. But, if transpiling or hot-reloading is not needed, handlers can simply be run using node.

sendRequest(request)

A function usable both in Node.js and in the browser for making HTTP requests

import { sendRequest } from 'passing-notes'

async function run() {
  const response = await sendRequest({
    method: 'GET',
    url: 'http://example.com',
    headers: {
      accept: 'text/html'
    },
    body: ''
  })

  console.log(response)
  // {
  //   status: 200,
  //   headers: {
  //     'content-type': 'text/html; charset=UTF-8'
  //     ...
  //   },
  //   body: '<!doctype html>...'
  // }
}

run()

startServer(port, ?requestHandler, ?upgradeHandler)

Given a Node.js request handler and/or a Node.js WebSocket upgrade handler, starts a Node.js HTTP server, waits for it to start listening on the given port, and then returns the server.

import { startServer } from 'passing-notes'

async function run() {
  const server = await startServer(
    8080,
    (request, response) => {
      response.end('Hello World!')
    },
    (request, socket, head) => {
      socket.end()
    }
  ) 

  const response = await sendRequest({
    method: 'GET',
    url: 'http://localhost:8080',
    headers: {},
    body: ''
  })

  console.log(response.body)
}

run()

stopServer(server)

Stops the given Node.js HTTP server, returning after it is no longer listening.

import { startServer, stopServer } from 'passing-notes'

async function run() {
  const server = await startServer(8080, (request, response) => {
    response.end('Hello World!')
  })

  await stopServer(server)
}

run()

getPort()

Returns the PORT environment variable, if set. Returns port 8080 if it is open. Otherwise, returns a random open port.

import { getPort } from 'passing-notes'

async function run() {
  const openPort = await getPort()
}

run()

respondToRequests(...middleware)

Take an array of middleware and returns a Node.js request handler.

import { respondToRequests } from 'passing-notes'

export default respondToRequests(
  next => async request => {
    const response = next({
      ...request,
      body: JSON.parse(request.body)
    })

    return {
      ...response,
      headers: {
        ...response.headers,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(response.body)
    }
  },

  next => async request => {
    return {
      status: 200,
      headers: {},
      body: { message: 'Hello World!' }
    }
  }
)

Each incoming request is passed to the first given middleware. That middleware can then:

  • Perform side-effects
  • Return the results of the next middleware, passing in the same or a modified request
  • Return a Promise that resolves to a response
  • Throw an exception, which is translated into a 500 response.

In this way, requests go from top to bottom, and responses come back from bottom to top.

acceptConnections(socketAcceptor)

Interact with clients over WebSocket.

import { acceptConnections } from 'passing-notes'

export const webSocket = acceptConnections(async socket => {
  for await (const message of socket) {
    await socket.send(`Echo: ${message}`)
  }
})

A socket object is passed into the given handler whenever a client connects. It is an AsyncIterator that emits messages from the client (assumed to be strings). It also has a send method for sending messages to the client and a close method for closing the connection.

Middleware

puppet-strings provides a set of middleware to support modern web application development.

logRequestsAndResponses({ log })

Log all requests and responses, along with their timings.

import { respondToRequests, logRequestsAndResponses } from 'passing-notes'
import { printLog } from 'passing-notes/lib/log'

export default respondToRequests(
  logRequestsAndResponses({ log: printLog }),
  next => async request => {
    return {
      status: 200,
      headers: {},
      body: ''
    }
  }
)

log is a function that takes 2 arguments, a starting entry and an ending entry. For example, for each request-response cycle, when the request is received, a log entry is emitted with details about that request, and, when the response is returned, the same log entry is emitted alongside one giving details about the response.

Shown above, printLog from passing-notes/lib/log formats and prints log entries to stdout.

serveUi({ entry, log })

Compile and serve an ES.next web UI.

// api.js
import {
  respondToRequests,
  logRequestsAndResponses,
  serveUi
} from 'passing-notes'
import { printLog } from 'passing-notes/lib/log'

export default respondToRequests(
  serveUi({ entry: 'ui/index.html', log: printLog })
)
<!-- ui/index.html -->
<!doctype html>
<meta charset="utf-8">
<script async src="index.js"></script>
<div id="root"></div>
// ui/index.js
import React from 'react'
import { render } from 'react-dom'

render(<div>Hello World!</div>, window.root)

entry is the path (relative to package.json) to the HTML entry point of the web UI. It is assumed to be a file named index.html. It is compiled and served using parcel-bundler.

Hot Module Replacement (HMR) is provided by Parcel but defaults to triggering full-page reloads unless configured.

If a requested file cannot be found, the contents of / are returned.

log is as described above. Log entries are emitted to indicate compilation status and any compilation errors.

serveRpc({ actions, path, log, ...dependencies })

Define a server-side middleware that when used in conjunction with makeRpcClient, enables calling actions defined on the server over HTTP.

import { respondToRequests, serveRpc } from 'passing-notes'
import { printLog } from 'passing-notes/lib/log'

const actions = {
  getItems: () => () => ['Item 1', 'Item 2', 'Item 3'],
  add: () => (x, y) => x + y,
  readFromDatabase: ({ db }) => () => db.select('some_table')
}

export default respondToRequests(
  serveRpc({ actions, path: '/rpc', log: printLog, db: someDbAdapter }),
)

Each action can take any number of parameters and return any value so long as they are JSON serializable.

serveRpc will respond to POST requests to the given path:

POST /rpc

{
  "action": "add",
  "params": [1, 2]
}

The result returned by the action will be serialized to JSON and returned in response:

{
  "result": 3
}

If the action throws an error, the error message will be serialized and returned:

{
  "error": "ReferenceError: foo is not defined"
}

serveRpc can take additional dependencies, all of which are injected into each action. In this way, actions can be tested independently from HTTP and any outside dependencies.

makeRpcClient(serverUrl)

Define a client for use in conjunction with serveRpc to call actions defined on the server over HTTP.

import { makeRpcClient } from 'passing-notes'

const api = makeRpcClient('http://localhost:8080/rpc')

async function run() {
  const items = await api.getItems()
  const sum = await api.add(4, 3)
  const records = await api.readFromDatabase()
}

run()

makeRpcClient returns an object. When a method on that object is called, a POST request is sent to the given serverUrl with the name of the method and any parameters. For example:

async function run() {
  const api = makeRpcClient('http://localhost:8080/rpc')
  const sum = await api.add(4, 3)
}

is translated into:

POST http://localhost:8080/rpc
{
  "action": "add",
  "params": [4, 3]
}

The response:

POST http://localhost:8080/rpc
{
  "result": 7
}

is returned by the method.

If, instead the response indicates an error:

POST http://localhost:8080/rpc
{
  "error": "Something went wrong."
}

The method will throw an error with that message.

Current Tags

  • 5.5.3                                ...           latest (2 months ago)

40 Versions

  • 5.5.3                                ...           2 months ago
  • 5.5.2                                ...           2 months ago
  • 5.5.1                                ...           2 months ago
  • 5.5.0                                ...           2 months ago
  • 5.4.1                                ...           2 months ago
  • 5.4.0                                ...           3 months ago
  • 5.3.8                                ...           3 months ago
  • 5.3.7                                ...           4 months ago
  • 5.3.6                                ...           5 months ago
  • 5.3.5                                ...           6 months ago
  • 5.3.4                                ...           6 months ago
  • 5.3.3                                ...           6 months ago
  • 5.3.2                                ...           6 months ago
  • 5.3.1                                ...           6 months ago
  • 5.3.0                                ...           6 months ago
  • 5.2.2                                ...           6 months ago
  • 5.2.1                                ...           7 months ago
  • 5.2.0                                ...           7 months ago
  • 5.1.0                                ...           7 months ago
  • 5.0.0                                ...           7 months ago
  • 4.0.0                                ...           8 months ago
  • 3.3.1                                ...           8 months ago
  • 3.3.0                                ...           9 months ago
  • 3.2.0                                ...           9 months ago
  • 3.1.2                                ...           9 months ago
  • 3.1.1                                ...           9 months ago
  • 3.1.0                                ...           9 months ago
  • 3.0.1                                ...           9 months ago
  • 3.0.0                                ...           a year ago
  • 2.0.1                                ...           a year ago
  • 2.0.0                                ...           a year ago
  • 1.4.1                                ...           a year ago
  • 1.4.0                                ...           a year ago
  • 1.3.1                                ...           a year ago
  • 1.3.0                                ...           a year ago
  • 1.2.0                                ...           a year ago
  • 1.1.0                                ...           a year ago
  • 1.0.0                                ...           a year ago
  • 0.0.2                                ...           a year ago
  • 0.0.1                                ...           a year ago
Maintainers (1)
Downloads
Today 0
This Week 0
This Month 4
Last Day 0
Last Week 3
Last Month 25
Dependencies (25)
Dev Dependencies (15)
Dependents (1)

Copyright 2014 - 2016 © taobao.org |