Skip to content

cblanquera/browser-http

Repository files navigation

browser-http

This is just a thought experiment, but what if NodeJS's http had a baby with push states...

Install

  1. Add script
<script src="https://raw.githubusercontent.com/cblanquera/browser-http/main/dist/browser-http.js"></script>
  1. Add this somewhere in your script
const server = http.createServer(function(req, res) { ... });
server.listen();

Notes

  • req is an extended version of the native window.Request.
  • res is an extended version of the native window.Response.
  • This means you can fetch a request as in fetch(req) to get info from the server.
  • This also kind of makes it possible to have a client side expressjs framework.
  • It's possible to make this work as a React Router alternative like the following.

Example React Plugin

So here we create a very small plugin to use in the main http.Server handler.

//define a basic react router plugin
//(we'll try to avoid JSX for now...)
const reactPlugin = function(root) {
  function isReactElement(element) {
    return element && element.$$typeof === Symbol.for('react.element')
  }
  
  //this will be populated in a bit...
  const plugin = {}
  const NotFound = function() {
    return React.createElement('div', {}, '404 - Not Found')
  }

  //main react component
  const Response = function() {
    //let's use a state hook
    const [body, setBody] = React.useState(NotFound())

    //a listener needs to go here
    //because state hooks cannot be 
    //used outside of a react component
    plugin.handler = (req, res) => {
      if (isReactElement(res.body)) {
        setBody(res.body)
        res.body = null
      } else {
        setBody(NotFound())
      }
    }

    //return element here
    return React.createElement('div', {}, body)
  }

  //go ahead and render the react to the root
  if (typeof root === 'string') {
    root = document.getElementById(root)
  }
  ReactDOM.render(React.createElement(Response), root)

  //return the plugin
  return function(req, res) {
    if (typeof plugin.handler === 'function') {
      plugin.handler(req, res)
    }
  }
}

Next we want to create some pages.

const page1 = function(req, res) {
  const App = function() {
    const [counter, setCounter] = React.useState(600)
    React.useEffect(() => {
      const interval = setInterval(() => setCounter(counter => counter - 1), 1000)
      return function() {
        clearInterval(interval)
      }
    })
    
    return React.createElement('h1', {}, 
      'Countdown ', 
      counter
    )
  }

  res.body = React.createElement(App)
}

const page2 = function(req, res) {
  const App = function() {
    const [counter, setCounter] = React.useState(0)

    React.useEffect(() => {
      const interval = setInterval(() => setCounter(counter => counter + 1), 1000)
      return function() {
        clearInterval(interval)
      }
    })
    
    return React.createElement('h1', {}, 
      'Countup ', 
      React.createElement(CounterText, { counter })
    )
  }

  const CounterText = function({ counter }) {
    return React.createElement('span', {}, counter)
  }

  res.body = React.createElement(App)
}

Last thing is to add it to the main http.Server handler.

const plugin = reactPlugin('root')
http.createServer(function(req, res) {
  const url = new URL(req.url)
  //make a router
  switch(url.pathname) {
    case '/foo/bar':
      page1(req, res);
      break;
    case '/bar/foo':
      page2(req, res);
      break;
  }
  
  plugin(req, res)
}).listen()

More Examples

More examples can be found in the example folder in this repo.

About

If node http had a baby with push states

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published