UNPKG

node-tailor

Version:

Tailor assembles a web page from multiple fragments

101 lines (69 loc) 5.66 kB
<h1><img width="400" alt="Tailor" src="https://rawgithub.com/zalando/tailor/master/tailor.svg"></h1> [![Build Status](https://travis-ci.org/zalando/tailor.svg?branch=master)](https://travis-ci.org/zalando/tailor) [![Test Coverage](https://codeclimate.com/github/zalando/tailor/badges/coverage.svg)](https://codeclimate.com/github/zalando/tailor/coverage) Tailor is a layout service that uses streams to compose a web page from fragment services. # Why Layout Service? Microservices get a lot of traction these days. They allow multiple teams to work independently from each other, choose their own technology stacks and establish their own release cycles. Unfortunately, frontend development hasn’t fully capitalized yet on the benefits that microservices offer. The common practice for building websites remains “the monolith”: a single frontend codebase that consumes multiple APIs. What if we could have microservices on the frontend? This would allow frontend developers to work together with their backend counterparts on the same feature and independently deploy parts of the website — “fragments” such as Header, Product, and Footer. Bringing microservices to the frontend requires a layout service that composes a website out of fragments. Tailor was developed to solve this need. # Installation `npm i node-tailor --save` ```javascript const http = require('http'); const Tailor = require('node-tailor'); const tailor = new Tailor({/* Options */}); const server = http.createServer(tailor.requestHandler); server.listen(process.env.PORT || 8080); ``` # Options * `fetchContext(request)` a function that returns a promise of the context, that is an object that maps fragment id to fragment url, to be able to override urls of the fragments on the page, defaults to `Promise.resolve({})` * `fetchTemplate(request, parseTemplate)` a function that should fetch the template, call `parseTemplate` and return a promise of the result. Useful to implement your own way to retrieve and cache the templates, e.g. from s3. Default implementation `lib/fetch-template.js` streams the template from the file system * `fragmentTag` a name of the fragment tag, defaults to `fragment` * `handledTags` an array of custom tags, check `tests/handle-tag` for more info * `handleTag(request, tag)` receives a tag or closing tag and serializes it to a string or returns a stream * `requestFragment(url, fragmentAttributes, request)` a function that returns a promise of request to a fragment server, check the default implementation in `lib/request-fragment` * `amdLoaderUrl` - URL to AMD loader. We use [RequireJS from cdnjs](https://cdnjs.com/libraries/require.js) as deafult. # Template Tailor uses [sax](https://github.com/isaacs/sax-js) to parse the template, where it replaces each `fragmentTag` with a stream from the fragment server and `handledTags` with the result of `handleTag` function. ```html <html> <head> <fragment src="http://assets.domain.com"> </head> <body> <fragment src="http://header.domain.com"> <fragment src="http://content.domain.com" primary> <fragment src="http://footer.domain.com" async> </body> </html> ``` ## Fragment attributes * *id* — optional unique identifier (autogenerated) * *src* — URL of the fragment * *primary* — denotes a fragment that sets the response code of the page * *timeout* - optional timeout of fragment in milliseconds (default is 3000) * *async* — postpones the fragment until the end of body tag * *public* — doesn't send the headers to fragment server * *fallback-src* - URL of the fallback fragment in case of timeout/error on the current fragment ## Fragment server A fragment is an http(s) server that renders only the part of the page and sets `Link` header to provide urls to CSS and JavaScript resources. Check `example/fragment.js` for the draft implementation. A JavaScript of the fragment is an AMD module, that exports an `init` function, that will be called with DOM element of the fragment as an argument. # Events `Tailor` extends `EventEmitter`, so you can subscribe to events with `tailor.on('eventName', callback)`. Events may be used for logging and monitoring. Check `perf/benchmark.js` for an example of getting metrics from Tailor. ## Top level events * Client request received: `start(request)` * Response started (headers flushed and stream connected to output): `response(request, status, headers)` * Response ended (with the total size of response): `end(request, contentSize)` * Error: `error(request, error)` in case an error from template (parsing,fetching) and primary error(socket/timeout/50x) * Context Error: `context:error(request, error)` in case of an error fetching the context ## Fragment events * Request start: `fragment:start(request, fragment.attributes)` * Response Start when headers received: `fragment:response(request, fragment.attributes, status, headers)` * Response End (with response size): `fragment:end(request, fragment.attributes, contentSize)` * Error: `fragment:error(request, fragment.attributes, error)` in case of socket error, timeout, 50x * Fallback: `fragment:fallback(request, fragment.attributes, error)` in case of timeout/error from the fragment if the *fallback-src* is specified **Note:** `fragment:response`, `fragment:fallback` and `fragment:error` are mutually exclusive. `fragment:end` happens only in case of successful response. # Example To start an example execute `npm run example` and open [http://localhost:8080/index](http://localhost:8080/index). # Benchmark To start running benchmark execute `npm run benchmark` and wait for couple of seconds to see the results.