WebApp package is a hidden Gem

Many people use Pub/sub it seems to be the default, every seminar I have been to for Meteor, every starter pack I have seen all uses Pub/sub.

In production, for static data such as a homepage, it’s really overkill as it’s slow and doesn’t work with SSR.

For that I have found WebApp to work much better, maybe I have missed something but I didn’t see anywhere yet where WebApp is a common practice. If anyone has anything to share on that I would love to hear

1 Like

By using just webapp, you might as well just use pure node or express, no need to use Meteor at all. Meteor makes building web/mobile/desktop applications easy, and most I should think use Meteor for the benefits provided there. If you are making just a “standard” server-side rendered website, you are probably better off using other tools in those instances, it is not where Meteor shines, although it does work for that purpose as well.

I haven’t really tried combining the two usually, we have a separate application for our website based in Django, but maybe it could be a thing. Working with webapp directly is a pain though, as it is too low-level to be productive in I find.


Depends on what kind of application you are building. For example, if you were building a backend SAAS product, you could use Meteor login, methods, etc. for building a nice admin UI, and the webApp + express for the user-facing REST APIs. And if you are building a client SDK or static frontend app it can then interact with the APIs without needing to know about Meteor at all.


I use everything in meteor for that reason. I just can’t compete with load times of 3 seconds etc for pub/sub also there’s no way to cache a sub so I use ground, memcache and webapp. Express sucks and Meteor is far better. But out the box it’s really slow and doesn’t need to be, that’s why WebApp should really be the default in my opinion. The majority of database queries do not need to be real-time reactive subscriptions.

Really, you’ll never hit this in prototyping it’s when you go to production that it becomes apparent. Which is maybe why the starters don’t have it at all. Pub/sub is great when you need it. Although for loading static db data to for instance just populate a page SSR, it’s just overkill. So is oplog, memcache and webapp are underrated.

Why would you not use methods in those cases? Are we talking about loading data or html?

1 Like

Yep I do use methods although it’s kinda slow to be honest, and of course it doesn’t work with the same performance as a simple json route from webapp, I think that’s part of the design really, webapp is surprisingly fast and bypasses my entire app to just the route from server/main.js. I launched a new embedded feature this week and it’s lightning fast via webapp. So I am migrating my other services from methods to use webapp instead.

The main issue for my development, is I need a static view of my data, and not a subscription. The client just needs to receive it once. And if that is sent with pub/sub it’s overkill and slow. I want to use meteor for my whole app, because meteor is good for work with npm and has a base package that’s mean you don’t start from scratch or have to spend weeks building your own stack. I can just get going and it kinda just works. The pub/sub is a great additional, although having an option to not use it, and just have a simple data retrieval protocol in place that can be client and server cached in the ram, is what production demands. After all PHP has done that since around a decade and a half. So no point taking steps forward if you also go backwards in other directions. If you get me.

I have, with great success and convenience. I use Meteor’s full pub/sub/method/optimistic-ui power for my web-app, but from the same app I use WebApp and onPageLoad (from the server-render package) to inject cached SSR landing pages for very fast first-paint times of my ‘static’ sales pages: Pre-rendered landing pages with Critical CSS

1 Like

You can make pub/sub work with SSR by using the fast-render package.

That package will also speed up your initial pub/sub load, even if you don’t use SSR.

I’m using fast-render but didn’t try using with server-render, will check that out. Thanks man, I just want a instant load for my pages and not stuck on loading icon

NP. Just make sure you follow the SSR section in the fast-render docs.

1 Like

Thanks man really helpful

One more tip; if you check the network for the static assets (fonts, images, sound, etc.) they are not cached in the client. This makes it easy to develop since you don’t need to worry about updating the assets. However, in the live environment, it is a network killer.

here is how I solved the issue (WARNING: when you need to update the assets, you need to change the name of them.)

const staticFolders = [

for (let i = 0; i < staticFolders.length; i++) {
    const staticFolder = staticFolders[i];

    WebApp.rawConnectHandlers.use(staticFolder, function (req, res, next) {
        res.setHeader('cache-control', 'max-age=1209600');

a better solution would be adding a hash to the files during the deployment and generating a map to access them in the client. But, the above example mostly does the job for static assets.


@akbay OH, I did not know?! I will definitely check my prod env. Questions:

  • I guess one could automatically generate eTag or lastModified headers instead?
  • I am serving behind NGINX, so perhaps this can be implemented at that layer instead?

@truedon I recenlty started using WebApp to serve a HTTP API (to connect from stuff outside Meteor). I reckon the documentation is lacking: see

@captainn Is there documentation about adding SSR to an existing app for all UI routes? I am using react-router/react-router-dom with the following startup code:

Meteor.startup(() => {
			<App />

Currently, there is no render-related code inside server/main.tsx.

That’s all good and you can run varnish reverse proxy also. For caching static assets afaik you should always serve them from a cookie-less sub domain to leverage browser caching effectively, as cookies obviously cannot be cached, and nowadays you can have a professional grade nameserver that supports geocaching, tier 1 asset routing, and webp, all of that is a flick of a button on cloudflare so you no longer need to do all the leg work. Previously I used to buy servers in East and west coast NA and one in EU etc, and setup varnish and do long hackathons messing around with imagemagick and all the headers with nginx and varnish etc and it was really good fun, but it isn’t 2008 anymore and all of this is available for $20 so I gotta be a boomer and just take the easy option, it’s also like 300ms to 1500ms faster routing, and I don’t need to call the NoC and ask for peering to be improved etc. So for this one I really recommend everyone use the pro service because it’s just better, and it’s not like you can save money by rolling your own on this, because this requires so much setup and you’ll never get the tier 1 routes they have because they are running all the big apps assets already, so it’s a good deal that’s incomparable. Give it a whirl I can assure that you really won’t be disappointed.

Both sounds possible. My suggestion was an easy way to solve the issue for a small project.

Yes, but I don’t remember where, and I don’t know if it’s all in one place. I have an old starter which may help though. You can see how to set up the client render here. Ignore the npdev:collections stuff. The main part for making data available data is to make sure you use FastRender.onPageLoad instead of Meteor.startup (This may have changed in the community version of FastRender to onDataReady). If you are using code splitting (dynamic imports) you’ll need to work out some chunk loading mechanism. npdev:react-loadable has a way to do that build in, but React.lazy does not.

1 Like

Thanks man this is gold, I am still learning it all and get it perfected

1 Like

this is really neat - could be a great package, along the lines of:

  • automatic asset folder detection
  • client pre-loads all the assets
  • ability to trigger refresh when necessary