Creating JS web server without Node.js
Tags: javascript, web, tutorial
Published: 2019-09-07 19:37:15

---
title: Creating JS web server without Node.js
published: true
description: How to create a complete web server with a code editor and a browser only.
tags: javascript, web, tutorial
---

Today there are exists a lot of web servers created for Node.js and it's hard to tell which one is better. Engineers are working to add something new into their products and using the last JS and v8 abilities to bring faster, handy and effective solutions. And I'm working on one too. It's [Plant](https://rumkin/plant). In this article I will tell about Plant's ability to work without Node.js.

Plant is a WebAPI charged web server, what means it's using the same interfaces as browsers do. Also it's transport agnostic and you can deliver requests using anything: TCP, WebSocket, WebRTC, etc. Tying all this up it could work in a browser and on a server in the same way. What this ability gives to us?

* We can develop a web server using only code editor and web browser, using DevTool's debugger instead of console.log for troubleshooting and then just move it to Node.js.
* We don't even need to install anything.
* It makes tests more reliable and simple to write, run and understand.
* Easy to learn: code is isolated into browser and free of environment influence.
* Truly crossplatform: it works in the same way everywhere.

> TL;DR. Here it is the [simple demo](https://rumkin.github.io/plant-browser-demo/simple-example.html) and [complex demo](https://rumkin.github.io/plant-browser-demo/).

[Demo repo](https://github.com/rumkin/plant-browser-demo) · [Plant repo](https://github.com/rumkin/plant)

##

Well let's start. All you need to start is to create simple HTML file with empty page.

```html
<!DOCTYPE html>
<html>
  <head></head>
  <body></body>
</html>
```


## Import

To use Plant in your web page just import it from unpkg.com:

```html
<script src="https://unpkg.com/@plant/plant@2.4.0/dist/plant.js"></script>
```

Put it into `<head>`. Then create new `<script>` into `<body>` element for our application.

## Create server

Server creation is similar to Express' or Koa's with small differences.

```js
// Instantiate server
const plant = new Plant()

// Add handler
plant.use('/index.html', ({req, res}) => {
  res.html('<!DOCTYPE html><html><body>Hello</body></html>')
})
```

That's it. We created a very simple request handler which serves '/index.html' page. Note that route is very strict. It would handle `/index.html` but not `/`.

## Making request

Browsers couldn't listen requests and we will simulate it on ourselves. This code could be used to handle requests delivered by WebSocket or any other transport, so it could be used by anything.

This is our final code which will de explained later:

```js
const handle = plant.getHandler()

const context = createContext('GET', 'https://localhost:8000/index.html')

handle(context)
.then(({res}) => {
  res.status // -> 200
  res.body // -> '<!DOCTYPE html>...
})
```


## Creating context

Plant context is a simple Object instance. It requires Socket, Request and Response instances to be passed as `socket`, `req` and `res` properties respectively.

```js
function createContext(method, url, {headers = {}, body} = {}) {
  const req = new Plant.Request({
    url: new URL(url),
    method,
    body,
    headers,
  })

  const res = new Plant.Response({
    url: new URL(url),
  })

  const socket = new Plant.Socket({
    peer: new Plant.Peer({
      uri: new Plant.URI({
        protocol: 'browser',
        host: navigator.appName,
      })
    })
  })

  return {req, res, socket}
}
```

Socket requires a peer instance which represents another party of a connection. Peer should always have an address provided as URI. URI is using here instead of standard URL due to URL's inability to work with custom schemes. All this needed due we suggest that peer could receive connections too, because Plant can use transport supporting this ability.



## POST requests

To simulate POST request we need to create [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) instance and pass it into Request constructor.

```js
const body = streamFrom('Hello')

new Request({
  method: 'GET',
  url: 'https://localhost:8000',
  body,
})

function streamFrom(value) {
  return new ReadableStream({
    start(controller) {
      // Encode UTF8 into Uint8Array and push it into the stream
      const encoder = new TextEncoder()
      controller.enqueue(encoder.encode(value))
      controller.close()
    },
  })
}
```

Function `createStream` creates ReadableStream instance and write whole passed value as a single chunk into it.

## Conclusion

Now you can put it all together and emulates request right in a browser on your own without using Node.js. Or you can get write and debug your API in a browser and then move it to Node.js with minimal changes. And the same code could be used in tests.

If you meet some difficulties or want to check yourself use [simple example](https://rumkin.github.io/plant-browser-demo/simple-example.html) or more complex demonstration.

## Repositories

{% github rumkin/plant %}
{% github rumkin/plant-browser-demo %}