Meteor REST APIs with JsonRoutes

Thanks @afruth & @serkandurusoy, I built a example repo for reference: GitHub - aadamsx/meteor-web-app-test: fine-rest example in Meteor

/web-api is the Meteor Web API using JsonRoutes.

/web-api-user is the Meteor client to the Web API, with a front end and a server side that makes the call to the Web API.

To use: If you click the ‘Click Me’ button it will make a server side Meteor Method call, which in turn calls to the Web API.

You’ll need to run both Web API and Web API User app at the same time with this command: npm start web-app-test. The ports are set up so the Meteor Method will be able to find the Web API.

I left the API exactly how @afruth coded his, and in this case the Meteor Method call will just hang.

Thanks for the help!

1 Like

You should also commit your .meteor directory for both apps so we don’t have to guess the packages used :slight_smile:

Still, do you get a correct answer using GET and the browser?

I changed my code to POST and still works, using Postman to check.

Can you test these two things: does it work in browser with GET? Does it work in Postman with POST (or even curl if you have it curl --request POST http://localhost:3000/test-route?error=true?

You are adding many variables in your test case (two meteor apps, http.call etc) and you need to simplify a bit to see where the issue is.

Coming back to your code above:

  • you don’t need to await HTTP.call server-side it’s a sync method unless you give it a callback.
  • maybe try to try{}catch(){} the HTTP.call and see if you get any error.

Done.

Sorry, I’m not following here. My Web API is meant to be called from a Meteor Server HTTP.call() method. So I build a very basic one to test it out. I built the example repo so we could all get on the same page is all. :slight_smile:

I see the status code in Postman too, but examining the return to the HTTP.call(), the entire response object, I do not see it, anything but a statusCode of 200, what did you see running the example app I posted?

I was trying to make it as simple as possible, but still use the tech I’m going to use in the end. I thought the HTTP.call() was very basic and clean.

If you don’t make it async, the method will just return from the HTTP call without waiting for a response, and therefore my front end code will not have the correct values it needs. I tried to do this sync to start – doesn’t work when your UI code depends on a response value from the Web API.

In my original code, not the repo example I posted, I DO have everything in a try/catch block. I removed it to make the example more simple and remove as many variables as possible, yet still keep some semblance of the real-world application I ultimately will make. And with a try/catch block, there is no difference on in the Meteor Server side Method – there is no change to how the call reacts to the Web API or the response from my testing.

Did you run the code example, what did you find, does it hang for you?

The HTTP.call returns something when the call is ok or throws and error when it gets an error. That’s why it returned. Otherwise it would just wait until it gets a response.

I’ll try again your apps now with the meteor directories. Tried before but stopped after too many errors related to missing packages :slight_smile:

1 Like

Right, but it doesn’t return anything when this sendResults contains a code property:

It just hangs.

If I remove the code property, you’re right, it will return ok or error. I can’t set the 401 for the HTTP.call().

Now in Postman, it does find the code when its set as your gif earlier showed. Although I don’t know how to see the raw response in Postman – in other words, I coudn’t find where in the response the code was being set in Postman.

Sorry about that, don’t know how .meteor got in my .gitignore file. :smiley:

I’ve tried it with your latest code and i get a server side error:

Error found [TypeError: Cannot read property 'findOne' of undefined]

This is because Meteor.users is undefined, which probably is because you don’t have accounts-base in the package (JsonRoutes has some code pertaining to Meteor.users to get tokens and stuff).

Adding the Accounts package breaks the mongo connectivity (can’t connect anymore to the Mongo server, which is weird).

Is this your actual code repo you’re using to test? Or is something you put together for us? In this case can you please try to install it locally and see if you have the same issues as me?

If you see the code in Postman and the response body, for both calls (with or without error) then it’s working correctly (the JsonRoutes part) and the issue is in the receiving code (HTTP.call part).

When you are saying it hangs, are you referring that the server-side console.log(“Error found:”) is not run? Or that your client (well server on client app) doesn’t work as expected?

The fact that you are having issues only with errors make me pretty sure you need to catch them when HTTP.call-ing.

1 Like

So sorry, I was using my local version, I switch over to use the test version and indeed got the error. I added the accounts package and can now show the “hang” when called with HTTP.call(). The repo is now updated.

Also, in the web-api middleware, to show the error case without a URL param, I changed the “if” statement a bit. Otherwise everything is the same.


Also, so we are in sync, if I call that same route with Postman, it does not hang:

So maybe the issue IS with the HTTP.call()?


The hang on error does indeed exist. Do I need to do another sendResult() inside the second middleware function perhaps?

Ok so here’s what’s wrong :slight_smile: Simple Meteor magic

You are throwing another error in your catch clause but you don’t do nothing with it. You need to get it on client and show or something.

If you console.log the error in your catch clause you’ll see the following error:

{ [Error: failed [401] {   "result": "ERROR" }]
I20170714-17:50:08.306(3)?   response: 
I20170714-17:50:08.306(3)?    { statusCode: 401,
I20170714-17:50:08.307(3)?      content: '{\n  "result": "ERROR"\n}',
I20170714-17:50:08.307(3)?      headers: 
I20170714-17:50:08.308(3)?       { 'content-type': 'application/json',
I20170714-17:50:08.308(3)?         vary: 'Accept-Encoding',
I20170714-17:50:08.309(3)?         date: 'Fri, 14 Jul 2017 14:50:08 GMT',
I20170714-17:50:08.309(3)?         connection: 'close',
I20170714-17:50:08.309(3)?         'transfer-encoding': 'chunked' },
I20170714-17:50:08.310(3)?      data: { result: 'ERROR' } } }

Which is what should happen. So it doesn’t hang, you’re not treating the error :slight_smile: Also i’ve removed async and await, HTTP.call doesn’t need it server side.

Just to describe the flow in case someone needs it in the future:

If you use HTTP.call server-side, a sync function, it returns the result on succesful HTTP code or throws an Error if it failed with an unsuccessful HTTP code (400+).
The Error should be caught with a try / catch block. If you are in a meteor method and throw the error further, the Error goes to the client as the first param of the callback function. Server-side you can do stuff with the error before throwing it again, like console.logging it.

Hope that solves your issue man.

1 Like

Wow! Thank you!

I’m just a little confused, so … your saying the response IS getting back to the client, in this case the Meteor Method with the HTTP.call(), but since I have a try/catch around it, it’s going to the catch, and i’m throwing again in the catch and not picking it up in the Browswer client? Is this response from the Web API handled as an Error in the HTTP.call()?

I also looked at the client side now and indeed you were getting the error client side and you were throwing it again. Just log the error on client instead of throwing it yet again and see what happens.

And by the way, in the catch block you get an HTTP error not a Meteor one. You shouldn’t just re-throw it, maybe try something like throw new Meteor.Error(err.response.statusCode, err.response.data.result) to have a proper Meteor Error client side

Why is the response handled like an error anyhow? It’s just a JSON object.

Because that’s how HTTP.call works :smiley:

From the docs:

On the server, this function can be run either synchronously or asynchronously. If the callback is omitted, it runs synchronously and the results are returned once the request completes successfully. If the request was not successful, an error is thrown.

What property within the response generates the error? Is it the statusCode? If we return a statusCode of 200, then no error on the HTTP.call(), but a 401 will generate an exception error?

I’m not sure i follow you there.

HTTP.call throws an error when it gets a response with a http code of 400+ (the equivalent of statusCode yes). The response can have or not data , but the http code represents an http error. HTTP.call returns that code as statusCode on the response object inside the error it throws.

1 Like

Yep, you follow, that’s exactly what I was looking for.

This should sum it up I think. :smiley:

1 Like

I updated the example applciation to incorporate what we discussed here.

Hey @aadams sorry for sitting silent all this time but it seems you’re in good hands with excellent comments from @afruth

Yet it seems like you’re still having the problem.

I’m currently swamped with work, but I will have some free time during the weekend and I have a proposition for you :slight_smile:

Since we’ve relaunched our good old meteor university as mentor academy and need to promote it, would you like to do a live debugging session with me where we try to solve your problem and record the screen for public view?

I’ve already been itching to do host some sessions like I used to do, and this meteor rest thing seems to come up a lot. So this would be a good win win win win win situation for a lot of people.

Mind you, this would mean showing parts of your actual code in video.

3 Likes

Thank you for the offer!

I wouldn’t mind at all, although, I only have a test project built with the Web API, I don’t plan to incorporate this tech is into my main project until I’ve flushed out all the bugs and use cases. :slight_smile: (but this project, should be enough for use to work with I would think)

I’d really love to get the token logging into the WebAPI working when the user logs in, so that would we something more to add.

And after that, the next step here is to incorporate the multiple database setup, and if you like we can go through that as well.

I’m free to live session this Saturday or Sunday and my times are very flexable. I live in Houston, that’s CST.

Please let me know what time, and what I need to do in order to prep for the live session. Maybe we can do a pre-flight setup even.

I’m really excited at this opportunity, thank you again! :smiley:

1 Like

Well I don’t mind working on the example repo as long as it demonstrates the problem you’re having.

Let’s have a private chat about scheduling on slack and then we can announce it back here if anyone’s interested.

1 Like

@aadams, i got an error :

W20170813-19:56:46.212(7)? (STDERR)   let user;
W20170813-19:56:46.214(7)? (STDERR)   ^^^
W20170813-19:56:46.216(7)? (STDERR) 
W20170813-19:56:46.218(7)? (STDERR) SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode