Getting request.body undefined in POST requests

Hello everyone,

I’m trying to set up some simple API for my application.
I followed this tutorial with some modifications (I am not using Iron Router).
As of now, the GET request works flawlessly, but the POST is not passing the correct body in the request. Actually, it’s not really passing anything.

I’m using Meteor’s HTTP package to test the calls.

  const data = {
    params: {
      url: "http://www.example.com/"
    }
  };

  HTTP.call("POST", "http://localhost:3000/api/v1/link", data, function(
    error,
    response
  ) {
    if (error) {
      console.log(error);
    } else {
      console.log("Response:", response.message);
    }
  });

I tried changing “params” to “data” as someone suggested, but with no luck.

In my main.js server file I am using WebApp as a middleware,

import { Meteor } from "meteor/meteor";
import { WebApp } from "meteor/webapp";

import { API } from "../imports/api/links";

Meteor.startup(() => {
  WebApp.connectHandlers.use((req, res, next) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    API.handleRequest(res, req);
  });
});

Here basically doing something like

console.log(req.body);

gives an undefined value.

There’s no need to talk about the API file because req.body is already undefined before being passed to the API file.

Could you help me?

Thank you!

2 Likes

You’ll need a middleware to parse the body for you. I recommend the very well used body-parser package

npm install --save body-parser

Then in your main.js:

import bodyParser from 'body-parser';
// For requests with content-type JSON:
WebApp.connectHandlers.use('/path', bodyParser.json());
// For requests with content-type application/x-www-form-urlencoded
WebApp.connectHandlers.use('/path', bodyParser.urlencoded());
// Then your handler:
WebApp.connectHandlers.use((req, res, next) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    API.handleRequest(res, req);
  });

4 Likes

Thank you, it works!

I am using the same url for both GET and POST requests, so I just pasted the two lines like this:

WebApp.connectHandlers.use("/api/v1/link", bodyParser.json());
  WebApp.connectHandlers.use(
    "/api/v1/link",
    bodyParser.urlencoded({ extended: true })
  );

  WebApp.connectHandlers.use((req, res, next) => {
    const code = req.url.slice(1, 12);

    if (condition) {
      // do something
    } else if (code.toString() === "api/v1/link") {
      res.setHeader("Access-Control-Allow-Origin", "*");
      API.handleRequest(res, req);
    } else {
      next();
    }
  });

Is this approach correct?

1 Like

Yeah I’m pretty sure it’s fine to do both, as they act on different content types

Guys, thank you both!

Can you explain how does this code work:

WebApp.connectHandlers.use('/path', bodyParser.json()); ?

From what I’ve read about webapp - the second argument to WebApp.connectHandlers.use is built-in function. So how does bodyParser.json() get to work here??

Because if you run bodyParser.json(), the return value of that function call is a function.

2018-11-21 08:58:25 - Freds-MacBook-Pro in ~/development/scratch/body-parser
○ → node
> const bodyParser = require('body-parser')
undefined
> bodyParser.json()
[Function: jsonParser]
>

This allows you to use different parser options for different routes, because all the options will be saved in the returned middleware function that is used for that route.

3 Likes

If you don’t want to use library, you can view this way:


WebApp.connectHandlers.use('/path', (req, res) => {
  req.on('data', Meteor.bindEnvironment((data) => {
    const body = JSON.parse(data);
    // if you use data for return result
    req.end(data);
  }));

  req.on('end', Meteor.bindEnvironment(() => {
    res.setHeader('Content-Type', 'application/json');
    res.writeHead(200);
    res.end(JSON.stringify({ status: 'ok' }));
  }));
});
4 Likes

req.on(‘end’, Meteor.bindEnvironment(() => {

AMAZING – thank you.

I see this post is a little bit old but I can’t find any other useful posts on this topic. I created a React/Meteor web application. I use hookrouter for routing from a React client and then I need to access the same Meteor methods using REST APIs. I read your suggestions and I put into server.js the following code:

import {WebApp} from 'meteor/webapp';
import {Meteor} from 'meteor/meteor';
import bodyParser from 'body-parser';

Meteor.startup( () => {
  WebApp.connectHandlers.use('/api/getchatid/:chatId', bodyParser.json());
  WebApp.connectHandlers.use('/api/getchatid/:chatId', (req, res, next) => {
    console.log('request', req)
    res.writeHead(200);
    res.end();
  });
})

I have 2 problems here:

  1. I have an error message that says bodyParser,json() is not a function
  2. even if without the first WebApp call the API path is not intercepted by Meteor

I’m thinking that maybe it’s better to use the express package directly to manage REST APIs in Meteor.

Hey, sorry for slow reply, I’ve been on holiday :beach_umbrella: !

This should work, this sometimes happens after using npm install and not restarting meteor. If it still happens, try removing node_modules and installing again with meteor npm install

These should be called outside a Meteor.startup so that it happens in the “setup” phase of the app. Meteor.startup runs after the web-server has already started.

Also, connectHandlers is not smart enough to parse route parameters (like :chatId). You should use a more advanced package for that like json-routes, picker`, or yes, even express:

import express from 'express';
const app = express();
// Set up your route definitions like you would for any other express app
WebApp.connectHandlers.use(app);

Which will give you back all the power you’re probably used to having :smiley:

1 Like

Thanks for your reply. Really helpful