jresponse-node

0.4.7 • Public • Published

JResponse for Node

Middleware for Node/Express. Use this middleware to get a standard output for the API Request.
You can also use JResponse as a simple object to standardize the Express router response.

When working with a large data set it obviously makes sense to offer pagination options to the endpoint.
To avoid writing your own pagination output into every endpoint, JResponse provides you JPagination. JPagination is inspired by the Fractal Pagination for PHP, and it offers two solution for paginate the response: JPaginator and JCursor

Install

npm i jresponse-node

Usage

Set the middleware (app.use() or router.use())

The module will append the formatted response to the res object of ExpressJS.

ES6
// routes.js ES6
 
import { Router } from 'express'
import MyController from './controllers/MyController'
import { setJResponse } from 'jresponse-node'
 
const router = new Router()
 
router.use(setJResponse())
 
router.route('/').get((...args) => MyController.list(...args))
ES5
// routes.js ES5
 
var express = require('express')
var router = express.Router()
var MyController = require('./controllers/MyController')
var jresponse = require('./../jresponse-node')
 
router.use(jresponse.setJResponse())
 
router.route('/')
  .get((...args) => MyController.list(...args))
  

Use 'JRes' instead the 'res' object in Express

ES6
// MyController.js
 
import { MyModel } from '../models'
 
async function list (req, res) {
    const items = await MyModel.findAll()
    return res.JRes.sendSuccess(items)
  }
ES5
// MyController.js
 
var MyModel = require('../models/MyModel')
const list = function (req, res) {
    MyModel.findAll()
    .then(items => {
        return res.JRes.sendSuccess(items)
    })
  }

Output

the JRes object will return always the same output: success, count, data, and errors

{
    "success": true,
    "count": 4,
    "data": [
        {
            "_id": "5c66b96f1b66bc00096ced46",
            "_otherId": "5c656fa71b66bc00096ced3d",
            "itemId": "849823662150",
            "itemRef": "15",
            "otherData": "data"
        },
        { 
            "_id": "5c66b96f1b66bc00096ced57",
            // ... 
        },
        // ...
    ],
    "errors": []
}
  • success: (_boolean) true or false. True if there is not errors.
  • count (integer). The size of data array.
  • data (array). The data expected from request. If there are errors, it will be empty.
  • errors (array). The errors occurred in the request. If there are some errors, success will be false.

Methods

  • res.JRes.sendSuccess(data). Short success output, it will call the sendResponse method with success as true
  • res.JRes.sendErrors(message [, code]). Short error output, it will call the sendResponse method with success as false
  • res.JRes.appendError(message [, code]). Append the current error message to the JRes object, and will set the currect error code.
  • res.JRes.sendResponse(success, data, errors). You can use this method in place of the previous ones
  • res.JRes.paginate(JPagination). Get the pagination object in the response (see the Pagination section)
Customize the output
  • res.JRes.merge(data). Merge the data object passed to the function (object or array) with the default JResponse object
  • res.JRes.key(key, value). Add the key and the value to the default JResponse object
res.JRes.merge([{ page: 1, }, { reference: '01REF999' }])
 
// or
 
res.JRes.set('page', 1)
res.JRes.set('reference', '01REF999')
 
Output
{
    "success": true,
    "count": 1,
    "data": [
        {
            "_id": "5c66b96f1b66bc00096ced46",
            "_otherId": "5c656fa71b66bc00096ced3d",
            "itemId": "849823662150",
            "itemRef": "15",
            "otherData": "data"
        }
    ],
    "errors": [],
    "page": 1,
    "reference": "01REF999"
}
 

Use "JRes" and pass it to "res" Express object

You can use the JRes in order to format the response, without use the 'res' Express route object. In this call, you can call JResponse statically

// MyController.js
 
import { MyModel } from '../models'
import { JResponse } from 'jresponse-node'
 
async function list (req, res) {
    try {
        const orders = await MyModel.findAll()
        const result = JResponse.success(orders)
        return res.status(200).send(result)
    } catch(e) {
        const result = JResponse.errors(e.message)
        return res.status(404).send(result)
    }
}

Static Methods

  • JResponse.success(data). Short success output, it will call the sendResponse method with success as true
  • JResponse.errors(errors). Short error output, it will call the sendResponse method with success as false
  • JResponse.send(success, data, errors). You can use this method in place of the previous ones

Pagination

Pagination is the process of dividing content into result-set and JPagination provides you with two solutions.

JPaginator

JPaginator is an auto form of pagination. TO perform the response it requires the Request object, and a total items count. This adds a pagination node to the response, which will contain next/previous links if applicable. Use the method res.JRes.paginate(<JPagination>) to append the pagination to JResponse. This intelligence comes at the cost of having to count the number of entries in a database on each call.

For some data sets this might not be an issue, but for some it certainly will. If pure speed is an issue, consider using JCursors instead.

// MyController.js
 
import { MyModel } from '../models'
import { JPaginator } from 'jresponse-node'
 
async function list (req, res) {
    const counter = await MyModel.findAndCountAll()
    const totalItems = counter.count // For example 10 items
    
    // Optionals parameters for the pagination
    const options = { 
      maxItems: 250, 
      metaKey: 'pagination', 
      limitKey: 'limit', 
      pageKey: 'page' 
    }
    
    // Instantiate the pagination
    let paging = new JPaginator(req, totalItems, options)
    
    const items = await MyModel.findAll({ limit: paging.limit, offset: paging.offset })
    
    return res.JRes.paginate(paging).sendSuccess(items)
}

For example, you can call the following address and see the output.

http://localhost:3000/test?limit=2&page=1
Output
{
    "success": true,
    "count": 2,
    "data": [
        {
            "test1": 1
        },
        {
            "test2": 2
        }
    ],
    "errors": [],
    "pagination": {
        "limit": 2,
        "page": 1,
        "pages": 5,
        "offset": 0,
        "start": "http://localhost:3000/test?limit=2&page=1",
        "prev": null,
        "next": "http://localhost:3000/test?limit=2&page=2",
        "last": "http://localhost:3000/test?limit=2&page=5"
    }
}
Options
  • maxItems. Set the max results per page. If the "limit" is greater than maxItems the limit will be equal to this value (default: 250)
  • metaKey. Set the "pagination" key name for the JResponse object (default: "pagination")
  • limitKey. Set the "limit" key name for the JPagination object. If you change this name you will change also the key in the querystring (default: "limit").
  • pageKey. Set the "page" key name for the JPagination object. If you change this name you will change also the key in the querystring (default: "page").

JCursor

When we have large sets of data and running a MyModel.findAndCountAll() isn't really an option, we need a proper way of fetching results. One of the approaches is to use cursors that will indicate to your backend where to start fetching results. It's really easy to use.

// MyController.js
 
import { MyModel } from '../models'
import { JCursor } from 'jresponse-node'
 
async function list (req, res) {
    let Cursor = new JCursor(req)
    
    Cursor.displayFilter('_id', 'nosql')
    
    let direction = Cursor.direction
 
    let items = await MyModel
        .find(Cursor.currentFilter, null, { sort: { '_id': Cursor.order } })
        .select({})
        .limit(Cursor.limit)
        .exec()
    
    if (items.length == 0)
    return res.JRes.sendErrors('No data')
    
    if (Cursor.direction == 'after') { items = items.reverse() }
    
    // Update maxId and sinceId values
    const beforeId = items[items.length - 1]._id
    const afterId = items[0]._id
    
    return res.JRes.paginate(Cursor, beforeId, afterId).sendSuccess(events, 200)
}
 

For example, you can call the following address and see the output.

http://localhost:3000/items?limit=2
{
    "success": true,
    "count": 2,
    "data": [
        {
            "_id": "5c793547ac87ecc484832811",
            "timestamp": "1551144090433",
            "name": "John"
        },
        {
            "_id": "5c793543ac87ecc484832810",
            "timestamp": "1551144090433",
            "name": "Mark"
        }
    ],
    "errors": [],
    "pagination": {
        "before": "5c793543ac87ecc484832810",
        "after": "5c793547ac87ecc484832811",
        "limit": 2,
        "direction": "before",
        "order": -1,
        "nextUrl": "http://localhost:3001/items?limit=2&before=5c793543ac87ecc484832810",
        "prevUrl": "http://localhost:3001/items?limit=2&after=5c793547ac87ecc484832811",
        "currentFilter": null
    }
}

If you move to the next url, the response will be the following

{
    "success": true,
    "count": 2,
    "data": [
        {
        "_id": "5c74949673c56000137ef4f0",
        "timestamp": "1551144090433",
        "name": "Robert"
        },
        {
        "_id": "5c6add371f089800163ca368",
        "timestamp": "1550507318112",
        "name": "Grete"
        }
    ],
    "errors": [],
    "pagination": {
        "before": "5c6add371f089800163ca368",
        "after": "5c74949673c56000137ef4f0",
        "limit": 2,
        "direction": "before",
        "order": -1,
        "nextUrl": "http://localhost:3001/items?limit=2&before=5c6add371f089800163ca368",
        "prevUrl": "http://localhost:3001/items?limit=2&after=5c74949673c56000137ef4f0",
        "currentFilter": {
            "_id": {
                "$lt": "5c793543ac87ecc484832810"
            }
        }
    }
}
Methods
  • displayFilter([id] [, dialect]). Display the filter node in pagination object response. You can use the returned value to set the query filter automatically. Set the id with the identifier key for your collection (default "id") Choose the proper dialect for your database between "nosql", "sql", "plain" (default "nosql").

Output for nosql

"filter": {
    "_id": {
        "$lt": "5c793543ac87ecc484832810"
    }
}

Output for sql

"filter": {
    "_id": "< 5c793543ac87ecc484832810"
}

Output for plain

"filter": {
    "_id": "less than 5c793543ac87ecc484832810"
}
Options

The same of JPaginator

Package Sidebar

Install

npm i jresponse-node

Weekly Downloads

1

Version

0.4.7

License

ISC

Unpacked Size

21.4 kB

Total Files

11

Last publish

Collaborators

  • alelancieri