GitHub - marblejs/marble: Functional reactive HTTP middleware framework built on...
source link: https://github.com/marblejs/marble
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
README.md
Functional reactive HTTP middleware framework built on top of Node.js platform, TypeScript and RxJS library.
Table of content
- Philosophy
- Installation
- Getting started
- Effects
- Routes composition
- Middlewares
- Custom error handling
- Examples
- Roadmap
Philosophy
If you don't have any experience with functional reactive programming, we strongly recommend to gain some basic overview first with ReactiveX intro or with The introduction to Reactive Programming you've been missing written by @andrestaltz.
If we think closely how typical HTTP API works we can quickly recognize that it deals with streams of asynchonous events also called as HTTP requests. Describing it very briefly - typically each request needs to be transformed into response that goes back to the client (which is our event initiator) using custom middlewares or designated endpoints. In reactive programming world, all those core concepts we can translate into very simple marble diagram:
In this world everyting is a stream. The core concept of Marble.js is based on the event flow of marble diagrams which are used to visually express time based behaviour of HTTP streams. Ok, but why the heck we need those observables
? Trends come and go, but asynchronously nature of JavaScript and Node.js platform constantly evolves. With reactive manner we can deliver complex features faster by providing the ability to compose complex tasks with ease and with less amount of code. If you have ever worked with libraries like Redux Observable, @ngrx/effects or other libraries that leverages functional reactive paradigm, you will feel like in home. Still there? So lets get started!
Installation
Marble.js requires node v8.0 or higher:
$ npm i @marblejs/core rxjs
or if you are a hipster:
$ yarn add @marblejs/core rxjs
Getting started
The bootstrapping consists of two very simple steps: HTTP handler definition and HTTP server configuration.
httpListener
is the starting point of every Marble.js application. It includes definitions of all middlewares and API effects.
const middlewares = [ logger$, bodyParser$, ]; const effects = [ endpoint1$, endpoint2$, ... ]; const app = httpListener({ middlewares, effects });
Because Marble.js is built on top of Node.js platform and doesn't create any abstractions for server bootstrapping - all you need to do is to call createServer
with initialized app and then start listening to given port and hostname.
const httpServer = http .createServer(app) .listen(PORT, HOSTNAME);
Effects
Effect is the main building block of the whole framework. Using its generic interface we can define
API endpoints (so called: Effects
), middlewares and error handlers (see next chapters).
The simplest implementation of API endpoint can look like this:
const endpoint$: Effect = request$ => request$ .pipe( mapTo({ body: `Hello, world!` }) );
The sample Effect above matches every HTTP request that passes through request$
stream and responds with Hello, world!
message. Simple as hell, right?
Every API Effect request has to be mapped to object which can contain attributes like body
, status
or headers
. If status code or headers are not passed, then API by default will respond with 200
status and application/json
Content -Type header.
A little bit more complex example can look like this:
const postUser$: Effect = request$ => request$ .pipe( matchPath('/user'), matchType('POST'), map(req => req.body), switchMap(Dao.postUser), map(response => ({ body: response })) );
The framework by default comes with two handy operators for matching urls (matchPath
) and matching
method types (matchType
). The example above will match every POST request that matches to /user
url.
Using previously parsed POST body (see $bodyParser
middleware) we can map it to sample DAO
which returns a response
object as an action confirmation.
The matchType
operator can also deal with parameterized URLs like /foo/:id/bar
Routes composition
Every API requires composable routing. With Marble.js routing composition couldn't be easier:
// user.controller.ts const getUsers$: Effect = request$ => request$ .pipe( matchPath('/'), matchType('GET'), // ... ); const postUser$: Effect = request$ => request$ .pipe( matchPath('/'), matchType('POST'), // ... ); export const user$ = combineRoutes( '/user', [ getUsers$, postUser$ ], ); // api.controller.ts import { user$ } from 'user.controller.ts'; const root$: Effect = request$ => request$ .pipe( matchPath('/'), matchType('GET'), // ... ); const foo$: Effect = request$ => request$ .pipe( matchPath('/foo'), matchType('GET'), // ... ); const api$ = combineRoutes( '/api/v1', [ root$, foo$, user$ ], );
Effects above will be mapped to following API endpoints:
GET /api/v1
GET /api/v1/foo
GET /api/v1/user
POST /api/v1/user
Middlewares
Because everything here is a stream, also plugged-in middlewares are based on simillar Effect interface. By default framework comes with composable middlewares like: logging, request body parsing. Below you can see how easily looks the dummy implementation of API requests logging middleware.
const logger$: Effect<HttpRequest> = (request$, response) => request$ .pipe( tap(req => console.log(`${req.method} ${req.url}`)), );
There are two important differences compared to API Effects:
- stream handler takes a response object as a second argument
- middlewares must return a stream of requests at the end of middleware pipeline
In the example above we are getting the stream of requests, tapping console.log
side effect and returning the same
stream as a response of our middleware pipeline. Then all you need to do is to attach the middleware to httpListener
config.
const middlewares = [ logger$, ]; const app = httpListener({ middlewares, effects });
Custom error handling
By default Marble.js comes with simple and lightweight error handling middleware. Because Middlewares and Effects are based on the same generic interface, your error handling middlewares works very similar to normal API Effects.
const error$: Effect<EffectResponse, ThrowedError> = (request$, response, error) => request$ .pipe( map(req => ({ status: // ... body: // ... }), );
As any other Effects, error middleware maps the stream of errored requests to objects of type EffectsResponse
(status
, body
, headers
). The difference is that it takes as a third argument an intercepted error object which can be used
for error handling-related logic.
To connect the custom middleware, all you need to do is to attach it to errorMiddleware
property in
httpListener
config object.
const app = httpListener({ middlewares, effects, // Custom error middleware: errorMiddleware: error$, });
Examples
To view the example project structure, clone the Marble.js repository and install the dependencies:
$ git clone git://github.com/marblejs/marble.git
$ cd marble/example
$ npm i
To run example just execute following command inside root repository folder:
$ npm run start
Roadmap
Marble.js is not yet a final and production ready product. Its APIs can improve over time when reaching stable version 1.0.0
. But in the meantime you can play easily and contribute to the growing community of functional reactive programming freaks.
- core mechanics
- custom middlewares
- custom error handlers
- composable routing
- ability to compose midddlewares inside
Effect
pipeline (v0.3.0) - intercepting query parameters (via middleware) (v0.3.0)
- intercepting url parameters (via
matchPath
operator) (v0.3.0) - more middlewares! (can think about moving
logger$
andbodyParser$
outside core library) - testing utilities
- improved, dedicated documentation (to move outside README)
Authors
License
marble.js is MIT licensed
Recommend
-
70
mobius - A functional reactive framework for managing state evolution and side-effects.
-
27
The new buzzword to impress your friends: Functional Reactive Programming (FRP)
-
51
README.MD Marble is a high performance in-memory hive sql engine based on Apache Calcite. It can help yo...
-
50
README.md JWT Middleware for Gin Framework
-
8
Functional reactive programming From Wikipedia, the free encyclopedia Jump to navigation
-
13
ReactiveCocoa与Functional Reactive Programming 2013-06-19 什么是Functional Reactive Programming Functional Reactive Programming(以下简称FRP)是一种响应变化的编程范式。先来看一小段代码
-
4
BackFunctional Reactive Programming in Angular with RxJSApril 27th, 2021 · 11 min read
-
7
.NET functional, reactive web development · GitHub Type: All Visibility:...
-
2
Example: Functional Reactive Programming decisively beats Imperative on simplicity, length June 17, 2015
-
6
Marble Zero – The Hackers Pet
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK