1

I am trying to send a post request from a node + express server to my Foxx service on Arangodb.

On the node side :

var route = arangopi + '/edge/' + col.name ;
var body = {data: data, from: fromId, to: toId} ;
console.log('|| body :', route, body) ;

>> || body : http//XXX/_db/my-DB/my-foxx-service/path/to/visitedBy { data: { isBackup: true, text: '', isHint: true, continuance: 3441.5 }, from: 'Drop/27237133', to: 'Bot/41116378' }

return requestify.post (route, body)

On the Foxx side, I receive the request but the logs tell me it has no body :

router.post('/path/to/:param', function (req, res) {
  console.log ('|| body :', req.body)
  var data = req.body ;
  var result = api.DoSomething (req.stateParams.param, data)
   res.send(result)
})
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description') 

>>  || body : [Object { "binarySlice" : function binarySlice() { [native code] }, "asciiSlice" : function asciiSlice() { [native code] }, "base64Slice" : function base64Slice() { [native code] }, "ucs2Slice" : function ucs2Slice() { [native code] }, "hexSlice" : f...

On the node side I also tried the 'request' module.

return request.post(route, {form:body}, function (error, response, body) {
  console.log('error:', error);
  console.log('statusCode:', response && response.statusCode);
  console.log('body:', body);
  return response ;
});

And I get the same logs from Foxx.

What do I do wrong ?

Here is a screenshot of my operation on the Foxx interface. Is it normal that I cannot specify a request body for testing ?

enter image description here

user6403833
  • 269
  • 5
  • 13
  • Have you tried this from the node.js side: `return requestify.post (route, JSON.stringify(body));` – David Thomas Feb 24 '17 at 01:49
  • @DavidThomas Thanks I tried this but it didn't work – user6403833 Feb 24 '17 at 07:00
  • How about on the Foxx side, you try JSON.stringify or JSON.parse on the req.body. You could also try the [request](https://github.com/request/request) module, I use it to call Foxx services from node.js and haven't had any issues, runs well. – David Thomas Feb 24 '17 at 15:12
  • @DavidThomas I tried that but it didn't work out. I copied the 'request' code in the question, do you see a mistake ? I am trying other libraries now. What is weird is that I get the request without a body with all libraries ! – user6403833 Feb 24 '17 at 15:20
  • @DavidThomas do you get the same web interface parameters when you check out your query ? Is there also a 'body' ? – user6403833 Feb 24 '17 at 15:38

2 Answers2

4

I think the reason is because you haven't specified in the end point in Foxx that there is a body expected as part of the .post.

It took me a while to work out a way of defining Foxx MicroServices, and I read through a number of ArangoDB example code before I settled on a pattern.

To help you get started, I've provided how I would quickly mock up the Foxx MicroService code in a way that is extensible, allowing you to separate your Routes from your Models.

Use these as examples to get your example working.

I've made assumptions that there are two document collections, 'Drop' and 'Bot' with an edge collection that joins them called 'VisitedBy'.

All these files are stored on your Foxx MicroService:

main.js

'use strict';
module.context.use('/v1/visitedBy', require('./routes/visitedBy'), 'visitedBy');

routes/visitedBy.js

'use strict';
const request = require('@arangodb/request');
const joi = require('joi');
const createRouter = require('@arangodb/foxx/router');
const VisitedBy = require('../models/visitedBy');

const visitedDataSchema = joi.object().required().description('Data that tracks a visited event');

const router = createRouter();
module.exports = router;


/*********************************************
 * saveVisitedBy
 * Path Params:
 * none
 * Query Params:
 * none
 * Body Params:
 * body         (required)    The data that is used to record when something is visited
 */
router.post('/', function (req, res) {
  const visitedData = req.body;
  const savedData = VisitedBy.saveVisitedByData(VisitedBy.fromClient(visitedData));
  if (savedData) {
    res.status(200).send(VisitedBy.forClient(savedData));
  }  else {
    res.status(500).send('Data not saved, internal error');
  }
}, 'saveVisitedBy')
  .body(visitedDataSchema, 'visited data')
  .response(VisitedBy.savedDataSchema, 'The response after the data is saved')
  .summary('Save visited data')
  .description('Save visited data');

models/visitedBy.js

'use strict';
const _ = require('lodash');
const joi = require('joi');
const db = require('@arangodb').db;
const visitedByEdgeCollection = 'VisitedBy';

/*
 Schema for a response after saving visitedBy data
 */
const savedDataScema = {
  id: joi.string(),
  data: joi.object(),
  _from: joi.string(),
  _to: joi.string()
};

module.exports = {
  savedDataSchema: savedDataScema,

  forClient(obj) {
    // Implement outgoing transformations here
    // Remove keys on the base object that do not need to go through to the client
    if (obj) {
      obj = _.omit(obj, ['_id', '_rev', '_oldRev', '_key']);
    }

    return obj;
  },

  fromClient(obj) {
    // Implement incoming transformations here
    return obj;
  },

  saveVisitedByData(visitedData) {
    const q = db._createStatement({
      "query": `
            INSERT {
              _from: @from,
              _to: @to,
              data: @data,
              date: DATE_NOW()
            } IN @@col
            RETURN MERGE ({ id: NEW._id }, NEW)
      `
    });
    q.bind('@col', visitedByEdgeCollection);
    q.bind('from', visitedData.from);
    q.bind('to', visitedData.to);
    q.bind('data', visitedData.data);

    const res = q.execute().toArray();

    return res[0];
  }
};

Your service should look like this in the Swagger interface: Swagger view of service

You can learn more about using joi to define data structures here.

It takes a bit getting used to joi, but once you get some good working examples you can define great data definitions for incoming and outgoing data.

I hope this helps, it was difficult for me getting a basic MicroService code model that made it clear how things operated, I'm sure a lot can be done for this example but it should be a good starting spot.

David Thomas
  • 2,264
  • 2
  • 18
  • 20
0

As David Thomas explained in his answer, I needed to specify a body format in my router code (Foxx side).

In short :

const bodySchema = joi.object().required().description('Data Format');

router.post('/path/to/:param', function (req, res) {
  var data = req.body ;
  var result = api.DoSomething (req.stateParams.param, data)
  res.send(result)
})
.body(bodySchema, 'Body data')
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')    
user6403833
  • 269
  • 5
  • 13