dragonrend
TypeScript icon, indicating that this package has built-in type declarations

0.20.5 • Public • Published

Build Status version GitHub license

Dragonrend

Dragonrend is productive and fast Node.js framework for building web applications.

All these advantages are achieved due to the fact that there is nothing superfluous. You have only what it is needed for creating Backend apps - no more no less. Then you get easy to read code and performance close to bare Node.

Installation

$ npm install dragonrend

Usage

The framework supports two options for writing code: classic and new way. А new way allows you to create programs due to the extracted functions. This method helps to split the code into blocks that are easier to read. And in the following examples both design options will be shown.

const { dragonrend } = require('dragonrend')

const app = dragonrend()

app.get('/', ctx => ctx.response.json({ message: 'Hi There' }))

app.start(8080).then(() => console.log('Server has been started'))
const { dragonrend } = require('dragonrend')

const { GET, START } = dragonrend()

GET('/', ctx => ctx.response.json({ message: 'Hi There' }))

START(8080, () => console.log('Server has been started'))

API

Context

All middleware functions and handlers get the context object. Context contains the request and response objects by default.

dragonrend(options)

This is builder function, which returns Dragonrend instance.

const { dragonrend } = require('dragonrend')

const app = dragonrend({
  server: false, // your server instance, default: false
  https: false, // object with `key` and `cert` like `https.createServer`, default: false
  http2: false, // true or false, default: false
  noDelay: true, // disable Nagle algorithm, default: false
  errorHandler(e, ctx) { // this is default error handler
    console.log(e)
    ctx.response.status(500).text('Internal Server Error')
  },
  routing: { // default: {}
    prefix: '/api', // default: ''
    notFoundHandler(ctx) { // this is default not found handler
      ctx.response.status(404).text('Not Found')
    }
  },
  autoIncluding: { // default: false
    rootDir: 'dir', // default: process.cwd() (Node.js process directory)
    autoIncludeRoutes: true, // this is default value
    routesDir: 'routes', // this is default value
    autoIncludeMiddleware: true, // this is default value
    middlewareDir: 'middleware', // this is default value
    autoIncludeContentTypeParsers: true, // this is default value
    contentTypeParsersDir: 'parsers' // this is default value
  }
})

Dragonrend Instance

Dragonrend inherits Router.

context(object: Object)

Stores the values you can get from ctx.

// add value
app.context({
  someValue: 'mock'
})

app.get('/path', ctx => {
  const { someValue } = ctx // <- and use it
})
const { CONTEXT, GET } = app

CONTEXT({
  someValue: 'mock'
})

GET('/path', ctx => {
  const { someValue } = ctx // <- and use it
})

addContentTypeParser(contentType: String, fn: Function)

Method add parser of requests body by content type.

app.addContentTypeParser('text/plain', body => {
  return body.toUpperCase()
})
const { PARSER } = app

PARSER('text/plain', body => body.toUpperCase())

Feature: Parsers can be added to the application automatically. Read more in the section "Auto Including".

middleware(...fns: Function)

Adds handler which will called before Router's handler.

// async/await or return promise
app.middleware(async ctx => {
  // do something
})

app.middleware(
  ctx => {},
  ctx => {}
)
const { MIDDLEWARE } = app

MIDDLEWARE(async ctx => {
  // do something
})

MIDDLEWARE(
  ctx => {},
  ctx => {}
)

To break/stop middleware chain you should return false.

const { MIDDLEWARE } = app

MIDDLEWARE(ctx => {
  return false
})

MIDDLEWARE(async ctx => {
  return false
})

MIDDLEWARE(ctx => {
  return Promise.resolve(false)
})

Feature: Middleware-functions can be added to the application automatically. Read more in the section "Auto Including".

setErrorHandler(fn: Function)

fn should have (error, ctx) signature. error is an error occurred, ctx is context.

Sets error handler. By default Dragonrend returns status 500 and body {"error":"Internal Server Error"}.

app.setErrorHandler((error, ctx) => {
  ctx.response.status(500).json({ error: error.message })
})
const { CATCH_ERROR } = app

CATCH_ERROR((error, ctx) => {
  ctx.response.status(500).json({ error: error.message })
})

start(portOrOptions: Number|Object)

Method gets port number or options object like Net server.listen(). Method returns Promise, also it is possible to use callback.

app.start(8080).then(() => console.log('Started'))
// or
app.start({
  host: 'localhost',
  port: 80,
  exclusive: true
}).then(() => console.log('Started'))
const { START } = app

START(8080, () => console.log('Started'))

stop()

Method stops server and returns Promise, also it is possible to use callback.

dragonrend.stop().then(() => console.log('Stopped'))
const { STOP } = app

STOP(() => console.log('Stopped'))

Routing

Routing is performed using Impetuous.

routing(options: Object)

Gets the object with a prefix and not found handler, which appends to all routes of that instance of Router. Returns prepared Router instance.

const router = routing({
  prefix: '/api',
  notFoundHandler() {
    ctx.response.status(404).text('Not Found')
  }
})

NotFound handler

Also NotFound-handler can be set via method or function.

const router = routing()

router.setNotFoundHandler(ctx => {
  ctx.response.status(404).text('Not Found')
})
const { NOT_FOUND } = routing()

NOT_FOUND(ctx => ctx.response.status(404).text('Not Found'))

Classic Express-like routing

Router instance has get, put, patch, post, delete, head, options methods with the same arguments (path: String, ...fn: Function).

const { routing } = require('dragonrend')

const router = routing()

router.get('/path/:param', async ctx => {})

router.post('/path', ({ request, response }) => {})

module.exports = router

GET PUT PATCH POST DELETE HEAD OPTIONS (path: String, ...fn: Function)

These functions add request handlers.

For example, a file with routes may look like this:

const { routing } = require('dragonrend')

const { GET, POST } = module.exports = routing()

GET('/path/:param', async (ctx) => {})

POST('/path', ({ request, response }) => {})

merge(...routers: Router)

Combines one or more instances of Router.

const router1 = routing({ prefix: '/base' })
const router2 = routing()
const router3 = routing({ prefix: '/api' })

router1.merge(router2, router3)
const router1 = routing({ prefix: '/base' })
const router2 = routing()
const router3 = routing({ prefix: '/api' })

const { MERGE } = router1

MERGE(router2, router3)

Instance of Router should be added to Dragonrend

Dragonrend inherits Router, therefore it has method merge.

const app = dragonrend()
const router = routing()
// add some handlers to router
app.merge(router)
// start server...
const { MERGE } = dragonrend()
const router = routing()

MERGE(router)

Feature: Routers can be added to the application automatically. Read more in the section "Auto Including".

Request

Request objects is added to context by default.

Fields of Request instance:

Field Description
headers object, which contains all headers of request
url url from request
method request's method
body parsed request's body
raw native Node.js's http Request
app.middleware(async ctx => {
  const { headers, url, method, raw, query } = ctx.request
  const query = await ctx.request.query() // lazy parsed query
  const body = await ctx.request.body() // lazy parsed body
}

Response

Response objects is added to context by default.

Method Description
header(key: String, value: String) Adds header key-value pair to Response
status(statusCode: Number) Sets custom status code to Response, default value is 200
json(data: Object) Sends response with application/json body
text(data: String) Sends response with text/plain body
html(data: String) Sends response with text/html body
send(data: String|Buffer, contentType: String) Sends response with custom body
raw Native Node.js' http Response
app.middleware({ response } => {
  response
    .header('x-total-count', '0')
    .status(201)
    //
    .json({ message: 'Hi There' })
    // or
    .text('Hi There')
    // or
    .html('<p>Hi There</p>')
    // or
    .send(imageBuffer, 'image/jpeg')
})

Return Response

It is possible to return response in middleware function by using returnable wrapper-function.

const { returnable } = require('dragonrend')

app.middleware(returnable(ctx => {
  return {
    status: 201 // default: 200,
    headers: { 'content-type': 'text/plain' } // default: {},
    body: 'body' // default: ''
  }
}))

Response helper-functions

These functions set the content type and in the case of JSON stringify it.

Functions have three call options.

Option Description
json(body) one parameter is the request body
json(status, body) two parameters are status and body in this order
json(status, headers, body) three parameters are status, headers and body in this order
const { dragonrend, returnable, json, html, text } = require('dragonrend')

const { GET } = dragonrend()

GET('/json', returnable(ctx => json({ message: 'Hi There' })))

GET('/html', returnable(ctx => html(201, '<p>Hi There</p>')))

GET('/text', returnable(ctx => text(201, { 'header': 'value' }, 'Hi There')))

Auto Including

Dragonrend supports auto including of Middleware-functions, Routers and Content Type Parsers. You should follow some rules for this functionality to work.

This feature is disabled by default. You can enable it:

const app = dragonrend({
  autoIncluding: true
})
// or with options
const app = dragonrend({
  autoIncluding: {
    // options
  }
})
  • Files must be in specific directories at the root of project.
    • Middleware-functions in middleware directory.
    • Routers Instances in routes directory.
    • Content Type Parsers in parsers directory.
  • The root directory is the directory that contains the file that is launched very first, i.e. directory of the Node.js process. If you start project with npm script (like npm run start) and all JS files are in src directory and main file is src/index.js, then root is ../src and Auto Including will not work. In this case, it can be customized.
// There are default values of options
const app = dragonrend({
  autoIncluding: {
    routesDir: 'routes',
    autoIncludeMiddleware: true,
    middlewareDir: 'middleware',
    autoIncludeContentTypeParsers: true,
    contentTypeParsersDir: 'parsers',
    rootDir: process.cwd()
  }
})

If you use src directory for example as root of application, then you should set that args:

const app = dragonrend({
  autoIncluding: {
    rootDir: __dirname
  }
})

Parsers

Modules should export an object like that:

module.exports = {
  contentType: 'text/plain',
  parse(body) {
    // do something
    return body
  }
}

Middleware

Modules should export a function.

module.exports = ctx => {
  // do something
}

Router

Router module should export Router instance.

const router = routing()

module.exports = router
const { GET } = module.exports = routing()

Example

You can find an example of using the framework by link. This is a simple Blog implementation with Articles API and JWT-authentication.

Also there is a Profile microservice of a simple social network Artaeum.

Author

Egor Repnikov - GitHub

License

This project is licensed under the MIT License - see the LICENSE file for details

Package Sidebar

Install

npm i dragonrend

Weekly Downloads

9

Version

0.20.5

License

MIT

Unpacked Size

44.5 kB

Total Files

25

Last publish

Collaborators

  • egorrepnikov