A Node.js fast, declarative framework for writing web servers, no taradiddles.
-
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.
- 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:
- 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
- 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
npm i -S bliz
or
yarn add bliz
// if youre using es6 modules
import Bliz from 'bliz'
or
// if youre not using es2016 modules
const Bliz = require('bliz').default
const app = Bliz()
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.
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 ?
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
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.
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.
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
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
The MIT License (MIT) 2018 - Yuri Khomyakov. Please have a look at the LICENSE.txt for more details.