RESTful API
Under each package, there are multiple component JS files.  RESTful API functions can be grouped under each JS file.  

Hotloading support - when the JS file is modified, it will be reloaded automatically during next time access.

Example

#
///

    ...

    packages/

        crud-api/

            ...

            contact.js



In this contact.js file,  there are three functions: load, add and delete exported.  The three functions are corresponding to the frontend actions with the same name.
const { sqlseries, capitalize } = require('@uteamjs/node')

exports.load = sqlseries((db, payload) => [
   db.query('select * from contact', rows => {
       rows.forEach(t => t.gender = capitalize(t.gender))
       payload.rows = rows
   })
])

exports.add = sqlseries((db, payload) => [
   db.updateInsert(null, payload.id, 'contact', payload)
])

exports.delete = sqlseries((db, payload) => [
   callback => eachSeries(payload, (item, cb) =>
       db.queryParam('delete from contact where id = ?', item)(cb)
   , callback)
])

sqlseries((db, payload, request, respond) => [...])(callback)

#
In building a backend API, most of the time you need to execute a series of database queries one by one.  sqlseries extends the async/waterfall library for asynchronous flow control and allows you to execute each SQL statement in series.

db

#
Database object provides the following functions to facilitate the data access:

query(sql_statement, postQueryFunction)(callback)

#
sql_statement: string
Any SQL statement support by corresponding database
postQueryFunction: function
(rows, error) => {
    ...
}

Post query function called after query executed with parameter:
rows  - Array of records returned from sql_statement.
error - Query error if any.

If there is any query error, the postQueryFunction will only be called when callback is null

callback: function
Function to be executed after asynchronous SQL statement.  If assigned null, then no callback will be executed.

queryParam(sql_statement, param, postQueryFunction)(callback)

#
Similar to query() function except with parameters in the sql_statement
param: array | any
Single item or array of parameter passed to the query() function
If you want to execute callback inside postQueryFunction, please refer to the following example:
exports.load = sqlseries((db, payload) => [
   cb => db.queryParam(`select id from nickname where name= ?`, payload.name,
       rows => {
           cb(null, rows[0].id) // Call back with parameters
       })(), // <-- null callback

   // id passed from previous statement
   (id, cb) => db.queryParam(`select * from contact where id= ?`, id, rows => {
       payload.rows = rows
   })(cb) // <-- passing callback
])

updateInsert(isUpdate, id, tablename, data, postQueryFunction)(callback)

#
isUpdate: boolean
true - update existing record
false, or null - also insert new record if id is null
id: string / object
string: ID used for record insert/update, assuming the column name is ‘id’.
object: Key value pair - {column_name: value}

If the value is null, insert will be executed.
tablename: string
Name of database table to be updated.
data: object
Data object with key value pairs where key is the database column name.
postQueryFunction: function
same as query() above
callback: function
same as query() above

payload

#
Object passed with the frontend api(<action>, payload).  If you want to return data to frontend, you should assign it to the properties of payload.

request, respond

#
Standard Express request and response object passed along.

[...]

#
Array of function with cb as parameters, please refer to async/waterfall for details.
[  
   cb => {
       ...
       cb(null, param)
   },

   (param, cb) => {
       ...
       cb()
   },
   
   db.query(...),
  
   cb => db.query(...)(cb),

   cb => db.query(..., rows => {
       ...
       cb()
   })()
]