Restful web services (/wsapi)



Check out Profound API.  It offers all these features plus many more.



Creating restful web services is enabled through the creation of the file/table DbDefn module.

The web service routes will automatically be prefixed with /wsapi.

  • ProfoundJS: a get list would look like: http://server/wsapi/customers

  • Profound.js Spaces: a get list would look like: https://spaces.profoundjs.com/user/workspace/wsapi/customers



Here is an example of a file/table called "order_line" that is on a mySQL database, and use using all of the built-in rest services.

module.exports = { useSQL: true, dbObject: "order_line", uniqueKey: [ { field: "order_id", ascending: true }, { field: "line_id", ascending: true } ], routes: { add: "/orderdetails", get: "/orderdetails/:order/:line", getList: "/orderdetails/:order", update: "/orderdetails/:order/:line", delete: "/orderdetails/:order/:line" } }



You do not need to have anything in this module file unless you are using a database other than an IBM i and you will also use the built-in CRUD methods :: get, getList, add, update, and delete.

The only thing that was altered was the addition of the "routes" section.

These above routes are all of the standard built-in functions.

  • add uses the POST method

  • get and getList use the GET method

  • update uses the PUT method

  • deleted uses the DELETE method



If the add and update were not included, no web service would exist for them.

Any of these can be overridden by defining the route differently (see below).



Here is how a complete example of that same order_line file/table but with the built-in "add" function overridden with a custom find function that can be used as a web service or by any pjs module because the function is exported.

function customAdd(request, response) { pjs.defineTable("orderline", { keyed: true, add: true }); var qty = parseInt(request.body.qty) || 1; // Make sure the QTY is >= 1 request.body.qty = qty < 1 ? 1 : qty; var result = orderline.write(null, request.body); response.send(result); } function findOne(request, response) { pjs.defineTable("orderline", { keyed: true, read: true }); var order = request.order || request.query.order || request.param.order; var line = request.line || request.query.line || request.param.line; var data = orderline.getRecord(order, line); if (response) response.send(data); else return data; } module.exports = { useSQL: true, dbObject: "order_line", uniqueKey: [ { field: "order_id", ascending: true }, { field: "line_id", ascending: true } ],   findOne: findOne, routes: { add: { path:"/orderdetails", handler: customAdd }, // notice the handler property is set to this "customAdd" function. get: "/orderdetails/:order/:line", findOne: { method: "get", path:"/orderdetails/find"}, // notice that handler property is not set, but the name "findOne" is an exported function, so it will auto connect them. getList: "/orderdetails/:order", update: "/orderdetails/:order/:line", delete: {path: "/orderdetails/:order/:line", authorize:"apiAuthenticate.js"}, // notice the authorize property is set to a javascript file. This file is yours to create and maintain. It will be called before this action. } }



All functions that use any of the PJS APIs should be declared outside of the of the exports section.

Make sure your function names do not match the existing RLA functions, because the RLA functions will take precedence.

You can define the routes in a few different manners.

  • name: "url path"

    • The route method will default to POST (unless it is one of the built-in functions - see above)

    • The name is also the name of the function to be called


    Here is an example of creating your own web services.  The function named is changeQuantity, so the the name of the route should be the same.

function changeQuantity(request, response) { var result = null; pjs.defineTable("orderline", { keyed: true, read: true, update: true, delete: true }); var newQty = parseInt(request.body.qty) || 0; // If QTY is <= 0 then delete it, else update it if (newQty <= 0) result = orderline.delete([request.params.order, request.params.line]); else { var item = orderline.getRecord([request.params.order, request.params.line]); item.qty = newQty; result = orderline.update(null, item); } response.send(result); }   module.exports = { ... changeQuantity: changeQuantity, // This will allow this function to be available to other modules routes: { ... changeQuantity: "/orderdetails/:order/:line" } }







  • name: {method: "route method", path: "url path" }

 

  • name: {method: "route method", path: "url path", handler: "the function" }

    • Same as above with the option to override the function to be called

      • The function value should be the actual function object

      • Because this definition has both the function and path, the name is only needed to be unique to pass javascript rules.



Example of apiAuthentication.js