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.
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.
Details
HTTP/2-ready
Plant can push responses to the client using HTTP/2 resource push mechanics.
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()
.
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.
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:
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.
const plant = new Plant({
csp: Plant.CSP.STRICT,
})
For development should be used Plant.CSP.LOCAL
policy.
Router example
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
P.S.
I'm an author of this package, so you could AMA. Also, notify me about grammatical errors. I would very appreciate it.
Top comments (1)
Hy Paul
Thank you fro LPant.
I have no sucess when downloading zip project from CodeSanbox.
I got an error 'require string property" get null in router/index.js
Regards