[SOLVED] Materials for learning how to use the webapp feature in meteor

it appears to me that the underlying ways to access the data and manipulate it are directly from node.js calls on the req and res objects, but for example i have ZERO clue what the function next is or how to use it. or how to use the underlying node.js calls. i cannot find any complete examples anywhere of handling the post from the zoom app that sends the code and then reformulating that into a response that includes both the code and the secret key, and then receives the token back from zoom and allows you to do stuff with it.

I agree the docs aren’t very helpful in this case, but as webapp uses connect under the covers, you may find the connect readme more helpful for a general understanding of how it works. However, you will still most likely need a good understanding of HTTP methods in order to start from basics. Fortunately, there are a lot of tutorials on HTTP REST, which is a good place to start.

2 Likes

actually i have done some. but nothing seems to translate right now. i looked at connect, but i so far i have not managed to make the jump from whats happening there in connect to what is happening in webapp. ill look again and come back to you with some questions.

first is app.use in connect the same as webapp.connectHandlers.use?

from the reading im doing at the meteorpedia site it appears that next() is a function you call to enable the connecthandler.use to continue to another such connect handler. is this correct?

Yes. From the connect readme:

The core of Connect is “using” middleware. Middleware are added as a “stack” where incoming requests will execute each middleware one-by-one until a middleware does not call next() within it.

1 Like

Check also this thread.

If you search the forums for webapp, you’ll find a few examples.

1 Like

haha yea ive been doing that for like 8hours so far… a few sketchy examples but nothing id call through
im working on it

i dont get it why does this work?

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);
});

why do i call the connectHandler.use 3 times, the first with the body.parser, second the other thing and then the last time appears to be the actual instance of creating a server to handle a http request? never seen anything like that in 40 years of programming. but im coming from the land of embedded systems… and (OMG) assembler

Here’s an extract of how I use WebApp.connectHandlers.use:

import { WebApp } from 'meteor/webapp'
import { logErrorOnServer } from '../../....';
import getRawBody from 'raw-body';

/**
 * POST body contains the JSON message.
 */
const path = '/whatever-path-here';
WebApp.connectHandlers.use(path, async (request, response) => {
  try {
    const content = await parseFormData(request);
    // *** call function to process content here ***
  } catch (error) {
    const { headers } = request;
    const ip = headers['x-forwarded-for'];
    logErrorOnServer(error, { origin: path, ip });
  }
  response.writeHead(200)
  response.end();
});

async function parseFormData(req, { limit = '1mb '} = {}) {
  const encoding = 'UTF-8';
  req.rawBody = req.rawBody || getRawBody(req, { limit, encoding });
  const str = await req.rawBody;
  return JSON.parse(str);
}

Note that in my case I needed to JSON parse the payload, and, depending on the data you’re getting, you may not need to.

I hope that helps.

2 Likes

Like robfallows quoted from the connect readme, the middleware functions are added as a stack, so each one is called in order.
This means that you can share the json parser between lots of routes that need to parse json bodies, instead of making you parse the body yourself at the start of every handler you write.
Of course that doesn’t give you much benefit when you only have one handler, but it’s designed as a router to handle all the routes in your app.

Express uses the same structure, so you could look up some of the documentation around express for more details.

to be specific, bodyParser.json() returns a function that checks the content type HTTP header for application/json and sets req.body to the parsed object for future handlers to use and calls next() so that the middleware/handler stack continues to execute. If the content type is not json, it just calls next().
Same for bodyParser.urlencoded() with the relevent content type and parsing algorithm.

Also I really recommend that you post some code and what you’re trying to achieve, it will make it much easier for us to help you.

3 Likes

ok thanks guys, im getting there, this is all really helpful, need to sit down and really read this and digest. actually i have things sort of working but ill try to post some code later.

thank you again this is alot of help

yes thank you this is a big help

Hi Christopher,

just my 2 cents for a more general perspective from my side:

I see the “Webapp” - feature more as a bonus-feature in Meteor, an easy way to access the underlying “primitives” on which meteor is built.

But the Webapp - Feature isn’t something I use regularly in my development. I built my apps using client side routing (-> FlowRouter) and using pub/sub and meteor methods to communicate with the server.

The WebApp feature is useful to create specific things like download endpoints for certain kinds of files, or for example webhooks for other systems to latch on to.

But in general it’s only 1% of my apps at most, 99% it’s not part of my mental model when writing meteor stuff.

That’s probably also why it’s only mentioned in passing in the meteor docs. Those who need it will know / look up how it works somewhere else, it’s just there so you can access the “plumbing” basically.

im trying to communicate with various APIs from other apps like Zoom, google webex etc, this isnt anything to do to with my personal client server connection, im writing a backend for our server to talk to other apps in the outside world

That sounds more like you are initiating the request and handling the response, not that you are providing endpoints for the other services to contact you “out of the blue”.

For example, you’d be using the Zoom API to work with Zoom.

He is talking about providing endpoints for services to contact the app via webhooks.
From the first post:

For which, webapp is the correct tool in the toolbox

1 Like

thanks, im making progress. i have stuff coming in just need to figure out how to use the refresh token. then do the same with google and several other providers. this has been a very helpful thread, thank you to everyone.

1 Like

You’re right. My apologies.

If you’re trying to set up a few endpoints on your server I really recommend the meteor simple:rest packages. They provide lots of helpful stuff around creating REST endpoints and exposing data through REST instead of DDP like Meteor usually does. These are way easier than using the webapp package yourself.

If you just need to set up endpoints for receiving webhooks I recommend simple:json-routes which is part of the simple:rest suite of packages.

I use this for receiving incoming webhooks from different services within my application. Here’s one basic example I’ve pulled from my app which I use to register invoices from Stripe.


const setupWebhookRoute = () => {
   // i need to enable CORS for this route, so I added the OPTIONS request to allow it.
  // if you don't need CORS this isn't necessary
  JsonRoutes.add('OPTIONS', '/register-stripe-invoice', (req, res, next) => {
    JsonRoutes.sendResult(res, {
      headers: {
        'Access-Control-Allow-Origin': '*', // enable CORS
        'Access-Control-Allow-Methods': 'GET, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With'
      }
    });
  });

  JsonRoutes.add('POST', '/register-stripe-invoice', async function (req, res, next) {
    const webhookPayload = req.body;
    //... now do anything you need to with the webhook payload
 
    const dataResponse = {
      //...put whatever response data you want here (if you need to send a response)
    };
    return JsonRoutes.sendResult(res, {
      data: dataResponse,
      headers: {
        // enable CORS
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With'
      }
    });
  });
};

And then at server startup I call setupWebhookRoute(), then I have a new route at POST /register-stripe-invoice.

1 Like