Plant: HTTP2 and P2P web server for Node.js and browsers.
Tags: javascript, node, web, p2p
Published: 2019-07-26 15:51:41

---
title: Plant: HTTP2 and P2P web server for Node.js and browsers.
published: true
description: This is release of the Plant web server package.
tags: javascript, node.js, web, p2p
---

Plant is a new WebAPI compatible HTTP2 web server. It's transport agnostic, highly modular, very secure by default and small: Plant's size is 8 KiB + optional node.js HTTP transport is 38 KiB (minified, gzipped) ; 74 KiB and 125 KiB respectively (unminified, ungzipped).

Plant was designed to use bleeding edge technologies, reduce complexity and make server portable. This portability gives you ability to write and test server-side APIs right in browser using only text editor. Plant has additional packages like http-adapter, router and bunch of http transports.

```javascript
    const Plant = require('@plant/plant')
    const {createServer} = require('@plant/http')
    
    const plant = new Plant()
    
    plant.use('/greet', ({res}) => {
        res.body = 'Hello, World!'
    })
    
    createServer(plant)
    .listen(8080)
```

# In-browser example

This is a very simple example of how it could work. It's just renders request into inlined iframe. It doesn't emulate browser. It's goal to show how to produce requests and emulate network connection with Plant.

[Codesandbox](https://codesandbox.io/s/plant-b8nft) · [Preview](https://b8nft.csb.app/)

# Details

**HTTP/2-ready**

Plant can push responses to the client using HTTP/2 resource push mechanics.

```javascript
    plant.use(({res}) => {
        res.push('/js/index.js')
        res.push('/css/style.css')
    
        res.html('<!DOCTYPE html><html><head>...')
    }
```

**WebAPI compatible**

Objects like Request, Response, Headers and streams have the same or familiar interfaces that already exists in WebAPI. Plant's Request and Response are mirrored from the Client, that's why Request object has Response's method `json()`.

```javascript
    plant.use(({req, res}) => {
      req.url.pathname // "/"
      req.headers.get('content-type')
      res.headers.set('content-length', 5)
      res.body = 'Hello'
    })
    
    // Retrieve JSON with one single command
    plant.use('/echo', async ({req, res}) => {
      const body = await req.json()
    
      res.json(json)
    })
```

Plant is using ReadableStreams instead of Node streams. That's why it can work seamlessly in browser. For example in ServiceWorker.

**Transport agnostic**

Plant isn't tightly coupled with the Node.js http module server, instead Plant is using it as external dependency. You can easely create your own transport. That's why you able to deliver requests via anything: WebSockets, MessageChannel, raw TCP, WebRTC or even email (why not). It makes things extremely simple, especially your tests.

```javascript
    const Plant = require('@plant/plant');
    const {createServer} = require('@plant/http2');
    
    const plant = new Plant();
    plant.use(({res, socket}) => {
      res.body = 'Hello, World!'
    })
    
    createServer(plant, {
      key: '...', 
      cert: '...',
    })
    .listen(443)
```

Create requests manually:

```javascript
    const plant = new Plant()
    
    plant.use(({res}) => {
        res.body = 'Hi'
    })
    
    const url = new URL('http://localhost:8080/')
    
    // Create HTTP context's params
    const req = new Plant.Request({
      url,
    });
    const res = new Plant.Response({
      url,
    });
    
    // Request peer. Peer represents other side of connection.
    const peer = new Plant.Peer({
      uri: new Plant.URI({
        protocol: 'ws:',
        hostname: window.location.hostname,
        port: window.location.port,
      }),
    });
    
    // Create connection socket
    const socket = new Plant.Socket({
        peer,
        // If socket allows write upstream, then onPush method could be defined to handle pushes.
        // onPush should return Promise which resolves when response sending completes.
        onPush(response) {},
    });
    
    const handleRequest = plant.getHandler()
    
    handleRequest({req, res, socket})
    .then(() => {
        // Request handled. All requests (even faulty) should get there.
    }, (error) => {
        // Something went wrong
    })
```

**Modular**

Plant is trying to separate responsibility between modules and not to bloat own size. Everything, especially transport related, is moved out of server package and could be replaced with you own code.

Additional packages:

|http|Node.js native http module transport|
|:-|:-|
|https|Node.js native https module transport|
|http2|Node.js native http2 module transport|
|https2|Node.js native http2 module with TLS transport|
|router|Router package|

**Secure by default**

Plant is using the most strict Content-Security-Policy out of the box. And this is the only web server which bring security first and doesn't sacrifice it. This policy doesn't allow webpage to do anything, even run a single piece of JS. Default value of Content-Security-Policy header is very denial and should be used in production to protect client and server from accidents. Developers should specify exact permissions which their site requires.

```javascript
    const plant = new Plant({
      csp: Plant.CSP.STRICT,
    })
```

For development should be used `Plant.CSP.LOCAL` policy.

# Router example

```javascript
const Plant = require('@plant/plant')
const Router = require('@plant/router')
const {createServer} = require('@plant/http')

// User API router
const router = new Router()

router.post('/', () => {})
router.get('/:id', () => {})
router.put('/:id', () => {})
router.delete('/:id', () => {})

plant.use('/api/users/*', router)

createServer(plant)
.listen(8080)
```

# References

[Github](https://github.com/rumkin/plant) · [NPM](https://www.npmjs.com/package/@plant/plant)

# P.S.

I'm an author of this package, so you could AMA. Also, notify me about grammatical errors. I would very appreciate it.