/Bliz

A fast, declarative framework for writing web servers, no taradiddles.

Primary LanguageJavaScriptMIT LicenseMIT

Bliz Bliz

code style: prettier License: MIT Generic badge Maintenance npm version NSP Status

Basic Overview

A Node.js fast, declarative framework for writing web servers, no taradiddles.

Philosophy:

  • Bliz is written with Concatenative Inheritance and RORO. I believe these are very powerful concepts, which lead to a more stable code base which in turn help build stable applications.

  • Bliz aims to provide a modular architecture approach out of the box for more maintanable, stable, readable code.

HTTP (implemented over Node's http module)

  • Per route middlewares (express like and support existing express middlewars)
  • Per route error handler
  • Middleware timeouts (if a middleware was not completed in X seconds, continue or throw error)
  • Global app.inject() method which helps you pass all your functions to any handler.
  • Optional validation on incoming requests using superstruct.js, a validation library.
  • Swagger file auto generation, OpenAPI 3.0 spec (experimental)
  • Pretty print all routes example of an http router:

Socket.io:

  • Global app.inject() method which helps you pass all your functions to any handler.
  • Per socket handler middlewares with optional timeouts
  • Pretty print all events

Graph QL (uses graphql-tools and graphql)

  • Global app.inject() method which helps you pass all your functions to any handler.
  • Declarative schema creation with a simple function call
  • Built in caching with DataLoader.js
  • Integrated Graphiql for testing
  • Declarative directive/enum/interface/union creation
  • Microservices ready out of the box, thanks to GraphQL remote schema stitching

Table of contents

Installation

npm  i  -S  bliz
or
yarn  add  bliz

Back to top

Usage

// if youre using es6 modules
import  Bliz  from  'bliz'
or
// if youre not using es2016 modules
const  Bliz  =  require('bliz').default
const  app  =  Bliz()

Http Server

a Bliz http server consists of 3 parts: path, router, global app let's see how to implement a simple http server.

// path.js
export  default  function  examplePath (app) {
	app
		// creates a new http path
		.createPath('/example/path')
		// creates a handler for the path
		.handler((req, res) => {
			res.json({message:  "This is so cool and simple!"})
		})
		// path middleware like in express
		.middleware((req, res, next)=>{
			console.log('hit /example/path!')
			next()
		})
		.errHandler((req, res, err)=>{
			console.log('my own custom error handler, so cool!!')
			// do some path specific error handling!
			res.json({error:  err.message})
		})
}
// router.js
import  examplePath  from  './path.js'
export  default  function  router(app) {
	app
		.createRouter('/api')
		.get(examplePath(app))
		.middleware((req, res, next)=>{
			console.log('hit /api router and attached a path to it with very few lines of code!')
			next()
		})
		.errHandler((req, res, err)=>{
			console.log('router err handler, for when you do no have a path error handler'  +
			'but want a router to have all routes under it with special error handling !')
			// do some path specific router error handling!
			res.json({error:  err.message})
		})
}
// index.js
import  Bliz  from  'bliz'
import  router  from  './router.js'
const  app  =  Bliz()
app
.registerRouters(router(app))
.middleware((req, res, next)=> {// global middleware})
.listen(4000)

Writing this way is testable, modular and readable. Now fire a GET request to /api/example/path and enjoy.

Back to top

Web Sockets

web sockets currently support and use socket.io behind the scenes. web sockets are handled differently in Bliz, they work the same as an http server, meaning an event is like a path, a router is a namespace of events with a delimiter, and the app combines all routers. lets see how this works

// event.js
export  default  function  exampleEvent(app) {
	app
		// creates a new socket listener
		.createSocketListener('event')
		// creates a handler for the event
		.handler((io, socket, msg, cb) => {
		// if emit fired you can cb to it immidietly
		// receive the msg and do something
		})
		// yes, yes, events have middlewares as well, because why not ???
		// abstraction usually helps readability
		.middleware((io, socket, msg, cb, next)=>{
			console.log('hit event')
			next()
		})
}
// events-router.js
import  exampleEventfrom  './event.js'
export  default  function  router(app) {
app
	.createSocketRouter('router')
	.event(exampleEvent(app))
	.middleware((io, socket, msg, cb, next)=>{
		console.log('hit router !')
		next()
	})
}
// index.js
import  Bliz  from  'bliz'
import  router  from  './events-router.js'
const  app  =  Bliz()
app
.sockets({useSockets:  true, delimiter:  ':'})
.registerSocketRouters(router(app))
.listen(4000)

Now if you fire an event to router:event it will pass through router middleware to route middleware to route handler, how cool is that ?

Back to top

Graphql

Bliz uses graphql-tools and all the apollo packages to give you the easiest way to write graphql servers

we'll start with a schema:

// schema.js
const  schema  =  `
type Post {
	name: String!
	id: Int!
	data: String
}

input newPost {
	name: String!
	id: Int!
	data: String
}
`
export  default  schema
// resolver.js
const  resolver  = {
	Query:{
		Post(obj, args, context, info){
			return {name:'some name', id:args.id}
		}
	},
	Post:{
		name(post, args, context, info){
		return  post.name
	},
	id(post, args, context, info){
		return  post.id
	},
	data(post, args, context, info){
		return  post.data
	}
	},
	Subscription:(pubsub)=> ({
		postAdded: {
			subscribe:()=>{
				return  pubsub.asyncIterator('POST_ADDED')
			}
		}
	}),
	Mutation:(pubsub)=>({
		Post(obj, args, context, info){
			pubsub.publish('POST_ADDED', {postAdded:  args.input});
			return {...args.input}
		}
	})
}
export  default  resolver

Notice Mutation and Subscription are functions injected with pubsub, Bliz creates a local pubsub instance to use with graphql-subscriptions, you can pass your own pubsub implementation, like redis pubsub in

app.graphql({pubsub:  new  yourOwnPubSub()})

Now, every time someone adds a post, who ever subscribes will get the updates, automatically! let's continue...

import  postSchema  from  './schema'
import  postResolver  from  './resolver'
// create a graphql schema with our schema and resolver, define query
// subscription and mutation
export  default  function  PostSchema (app) {
	return  app
	.createGraphQlSchema(postSchema)
	.resolver(postResolver)
	.query(`Post(id: Int!): Post`)
	.mutation(`Post(input: newPost): Post`)
	.subscription(`postAdded: Post`)
}
import  Bliz  from  'bliz'
import  PostSchema  from  './Post'
app
.graphql({useGraphql:  true, useGraphiql:  true, tracing:  true})
.registerGraphQlSchemas(PostSchema(app))
.listen(4000)

And voila, you do not need anything else to fire a full graphql server with subscriptions. Head to http://localhost:4000/graphiql - for testing playground Head to http://localhost:4000/graphql - for your graphql api Head to http://localhost:4000/subscriptions - for you real time goodness Back to top

Remote-schema-stitching

The graphql server we wrote just now can easily become part of a microservices style architecture without any changes, all we need to do is implement a gateway that wil stitch all responses and schemas for us.

import  Bliz  from  'bliz'
const  app  =  Bliz()
app
.graphql({useGraphql:  true, useGraphiql:  true})
.registerRemoteGraphQlSchemas({
	url:'http://localhost:4000/graphql',
	ws:  'ws://localhost:4000/subscriptions'
},{
	url:'http://localhost:5000/graphql',
	ws:  'ws://localhost:5000/subscriptions'
})
.listen(6000)

Head over to http://localhost:6000 and you will see all your schemas in one.

Back to top

Notes

Bliz is in version 0.4.0 and I implemented it as a proof of concept to a declarative web server supporting all kinds of technologies. I do not recommend using it in production untill version 1.0.0 is released. There are a lot of things to add, change and so many tests to write! I hope people will get involved and we would create something awsome together.

Back to top

Documentation

The examples shown here have only a fraction of the options included with Bliz, full documentation with every little detail coming soon! Help would be appreciated! Back to top

Contributing

If you decide to contribute, first of all thanks! steps:

  • clone project
  • npm install
  • create a seperate branch for your ideas or features
  • run prettier
  • submit a pull request

License

The MIT License (MIT) 2018 - Yuri Khomyakov. Please have a look at the LICENSE.txt for more details.