<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jan Küster 🔥</title>
    <description>The latest articles on Forem by Jan Küster 🔥 (@jankapunkt).</description>
    <link>https://forem.com/jankapunkt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F157294%2F1c3c63fd-d0d4-4db4-97ca-2a3d825ece7c.jpeg</url>
      <title>Forem: Jan Küster 🔥</title>
      <link>https://forem.com/jankapunkt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jankapunkt"/>
    <language>en</language>
    <item>
      <title>I'm frozen and I cannot move</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Fri, 13 Mar 2026 15:44:02 +0000</pubDate>
      <link>https://forem.com/jankapunkt/im-frozen-and-i-cannot-move-547g</link>
      <guid>https://forem.com/jankapunkt/im-frozen-and-i-cannot-move-547g</guid>
      <description>&lt;p&gt;This text is 100% handmade, like the code I am writing...&lt;/p&gt;

&lt;p&gt;Recent developments in the AI realm made me think a lot about my next moves in order to not fall behind. The pace is so fast that I am very picky about what to do, because I cannot waste much of my time learning new things that I might not finally use in my daily workflow.&lt;/p&gt;

&lt;p&gt;I have to admit, that I ever was a late adopter. I am consciously waiting for hypes being over (or becoming a real thing) and watching technology to proof itself, before fully committing to it and throwing my money at it. I am not the type who buys the next new shiny thing, plays with it for a week and then throws it away if it does not serve the purpose I expected it to.&lt;/p&gt;

&lt;p&gt;However, the situation is different now. While I am watching AI coding tools and agents to proof themselves to become a serious help in software engineering, everybody else is doing it already, despite all the downsides. I am still hesitating to ship code that I am not fully aware of, because the output of coding tools and agents totally exceeds my capabilities to review. Output speed triples but my QA seems to slow down by 10x.&lt;/p&gt;

&lt;p&gt;At that point I am now standing in some kind of void, frozen from this situation and I cannot move in any direction. On the one hand, manual programming feels wrong in 2026 (damn, it used to be fun!). It feels like making fire with stones while everybody else uses a blow torch. At the same time I am hesitating to go all in on agentic coding as I am watching people burn their entire house, because they left the blow torch doing its work...&lt;/p&gt;

&lt;p&gt;Is this just my perception or is this a real thing? Am I the only one who is not seeing a clear direction where to move next?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Wasted Open Source efforts 😮</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Wed, 10 Sep 2025 08:29:52 +0000</pubDate>
      <link>https://forem.com/jankapunkt/wasted-open-source-efforts-8ia</link>
      <guid>https://forem.com/jankapunkt/wasted-open-source-efforts-8ia</guid>
      <description>&lt;p&gt;Long time no see my friends! 👋&lt;/p&gt;

&lt;p&gt;Today I got notified by GitHub stale bot that &lt;a href="https://github.com/pytorch/pytorch/pull/155102" rel="noopener noreferrer"&gt;a PR of mine in the famous PyTorch repo&lt;/a&gt; got closed by stale bot. 🤖 &lt;br&gt;
Yes, by a bot; not by an author or repo owner but a bot. &lt;br&gt;
You may argue that this is normal for huge projects like this one but by looking deeper into the PR this reveals to me how much wasted effort is out there in open source!&lt;/p&gt;
&lt;h2&gt;
  
  
  How it all began
&lt;/h2&gt;

&lt;p&gt;In June 2025 I wanted to try &lt;a href="https://github.com/SWivid/F5-TTS" rel="noopener noreferrer"&gt;a tool for one shot speech cloning&lt;/a&gt;.&lt;br&gt;
The project was based on PyTorch and while installation via Docker &lt;strong&gt;is intended&lt;/strong&gt; to be reproducible, in reality it is not.&lt;/p&gt;

&lt;p&gt;At first, I cloned the repo and ran docker build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; f5tts:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This already took (as you might know from similar projects) ages to download and build. 😴&lt;/p&gt;

&lt;p&gt;After the build was finally complete, I tried to run the project via&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--gpus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;all &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="s1"&gt;'type=volume,source=f5-tts,target=/root/.cache/huggingface/hub/'&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 7860:7860 ghcr.io/swivid/f5-tts:main f5-tts_infer-gradio &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There, I got stuck with the following entirely confusing error message:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;daemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;could&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;gpu&lt;/span&gt;&lt;span class="p"&gt;]].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Thank you for nothing. Anyway, I searched the web for a good amount of time, until I finally found the solution. On Linux I need to manually install the Nvidia container toolkit if you want to use PyTorch with GPUs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html" rel="noopener noreferrer"&gt;https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, this was neither described in the repo nor in the PyTorch repo.&lt;br&gt;
Nowhere. Who would ever know this? Nobody, right!? This seemed perfect for a little pull request.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Motivation for the PRs
&lt;/h2&gt;

&lt;p&gt;I wasted time and got emotionally affected by this and I thought that others should have a better developer experience with this. Plus the effort for the PR is minimal.&lt;/p&gt;

&lt;p&gt;This is living open source to me! I solved something and save time for all of us by making it available to the rest of the world. In turn you do the same for me and we benefit altogether.&lt;br&gt;
What a nice little dreamworld I am living in.&lt;/p&gt;
&lt;h2&gt;
  
  
  Waking up from the dreamworld: the most unfriendly GitHub conversation I have had in years 🤬
&lt;/h2&gt;

&lt;p&gt;Well, I thought it might be beneficial to everyone to add a line to the installation instructions to save everyone's time. The reponse was totally unexpected though. Read for yourself:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/SWivid/F5-TTS/issues/1073" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Docker installation is missing requirements for cuda support
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1073&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F1135285%3Fv%3D4" alt="jankapunkt avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;jankapunkt&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/SWivid/F5-TTS/issues/1073" rel="noopener noreferrer"&gt;&lt;time&gt;Jun 03, 2025&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Checks&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;[x] This template is only for bug reports, usage problems go with 'Help Wanted'.&lt;/li&gt;
&lt;li&gt;[x] I have thoroughly reviewed the project documentation but couldn't find information to solve my problem.&lt;/li&gt;
&lt;li&gt;[x] I have searched for existing issues, including closed ones, and couldn't find a solution.&lt;/li&gt;
&lt;li&gt;[x] I am using English to submit this issue to facilitate community communication.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Environment Details&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;The documentation should note, that the container will not run, if the &lt;a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html" rel="nofollow noopener noreferrer"&gt;nvidia docker toolkit&lt;/a&gt; is not installed and configured.&lt;/p&gt;
&lt;p&gt;If you don't install the toolkit, you will get&lt;/p&gt;
&lt;div class="highlight highlight-source-shell js-code-highlight"&gt;
&lt;pre&gt;docker: Error response from daemon: could not &lt;span class="pl-k"&gt;select&lt;/span&gt; &lt;span class="pl-smi"&gt;device&lt;/span&gt; driver &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; with capabilities: [[gpu]].&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;I can open a PR and add it to the docs if you like.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Steps to Reproduce&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;install docker (follow official docker installation guide)&lt;/li&gt;
&lt;li&gt;follow installation instruction for Docker setup, defined in Readme&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;✔️ Expected Behavior&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Installation should be reproducible. When following the installation for Docker step by step, it should start without issues.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;❌ Actual Behavior&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell js-code-highlight"&gt;
&lt;pre&gt;docker container run --rm -it --gpus=all --mount &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;type=volume,source=f5-tts,target=/root/.cache/huggingface/hub/&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; -p 7860:7860 ghcr.io/swivid/f5-tts:main f5-tts_infer-gradio --host 0.0.0.0
docker: Error response from daemon: could not &lt;span class="pl-k"&gt;select&lt;/span&gt; &lt;span class="pl-smi"&gt;device&lt;/span&gt; driver &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; with capabilities: [[gpu]].&lt;/pre&gt;

&lt;/div&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/SWivid/F5-TTS/issues/1073" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;While this obviously their choice as owner, I was rather shocked by the total lack of empathy and aggressiveness against me as if I was a burden or something.&lt;/p&gt;

&lt;p&gt;What would a newcomer think in such a situation? Honestly,I hope no newcomer would have to face such bullsh*t but I fear this way of communication is not an exception.&lt;/p&gt;

&lt;p&gt;Anyway, the obvious outcome of this was to step up and make the PR directly to the where the issue was caused in the first place. &lt;/p&gt;

&lt;h2&gt;
  
  
  Going big: PR in the official PyTorch repo
&lt;/h2&gt;

&lt;p&gt;I am no shy person when it comes to action so I opened the same PR on the PyTorch repo, willing to face any upcoming trouble and discussion  that might come up.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/pytorch/pytorch/pull/155102" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        docs: link to Nvidia Container Toolkit in README
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#155102&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F1135285%3Fv%3D4" alt="jankapunkt avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;jankapunkt&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/pytorch/pytorch/pull/155102" rel="noopener noreferrer"&gt;&lt;time&gt;Jun 04, 2025&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;The Nvidia Container Toolkit is needed on Linux Distributions to properly run Docker images with --gpus flag.&lt;/p&gt;
&lt;p&gt;The dependency is OS-level and cannot be pinned from within the container.&lt;/p&gt;
&lt;p&gt;Otherwise containers will not run and provide and ambiguous error message&lt;/p&gt;
&lt;div class="highlight highlight-source-shell js-code-highlight"&gt;
&lt;pre&gt;docker: Error response from daemon: could not &lt;span class="pl-k"&gt;select&lt;/span&gt; &lt;span class="pl-smi"&gt;device&lt;/span&gt; driver &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; with capabilities: [[gpu]].&lt;/pre&gt;

&lt;/div&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pytorch/pytorch/pull/155102" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Accepting the PR was already blocked by CLA assistant, making me sign some bureaucratic documents online with a digital signature, complying to some legalese terms nobody understands (not even the PyTorch devs and maintainers) besides a few lawyers. 🤢&lt;br&gt;
No worries, I didn't read as I was supposed to because I just wanted to open the PR.&lt;/p&gt;

&lt;p&gt;Then it was waiting for the CI. I changed just a README so please no fails. Fortunately everything passed.&lt;/p&gt;

&lt;p&gt;One day later a maintainer added the &lt;code&gt;triaged&lt;/code&gt; label and requested reviews from other maintainers. Awesome, it looked like this passed initial triage and would not be rejected as "not important" as in the other repo beforehand.&lt;/p&gt;

&lt;p&gt;But then: silence. Nothing. No reaction. No comment or review. After a month stale bot notified the PR getting marked stale and maintainers can remove the stale label.&lt;br&gt;
I commented "not stale" but this had no effect: Two months after I opened the PR it got closed by stale bot. &lt;strong&gt;What a 💩&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My personal thoughts
&lt;/h2&gt;

&lt;p&gt;Fortunately the PR was just an additional line in a README but it was also a test - a test how contributions are honored and if people care on the small things that developers care about because they are the ones to fiddle with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It seems nobody cared about the efforts of ohers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From this point I am really in doubt of whether I will open a PR to one of the huge repos again. It is much easier to create a gist and spread the word plus it saves me some annoying conversations.&lt;/p&gt;

&lt;p&gt;Anyway, if you ever get into the issue on Linux, Docker and PyTorch telling you &lt;code&gt;docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].&lt;/code&gt; then you now know to install the Nvidia Docker Toolkit.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>productivity</category>
      <category>python</category>
      <category>github</category>
    </item>
    <item>
      <title>Reactive Data Structures in MeteorJS - Reactive Stack</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Thu, 28 Nov 2024 15:13:00 +0000</pubDate>
      <link>https://forem.com/meteor/reactive-data-structures-in-meteorjs-reactive-stack-1hbi</link>
      <guid>https://forem.com/meteor/reactive-data-structures-in-meteorjs-reactive-stack-1hbi</guid>
      <description>&lt;p&gt;&lt;a href="https://meteor.com" rel="noopener noreferrer"&gt;MeteorJS&lt;/a&gt; brings client-side reactivity out of the box. No matter which frontend framework you choose, you will always have an integrated reactivity that synchronizes your data and the UI. This is one of the core strengths of MeteorJS.&lt;/p&gt;

&lt;p&gt;But that's not the only advantage. It allows you to write your data structures of any complexity and while you access them using their normal API, reactivity is handled under the hood automatically.&lt;/p&gt;

&lt;p&gt;The tutorial is also available as a video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/RJEnPwjf2P8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Goal of this tutorial
&lt;/h2&gt;

&lt;p&gt;In this tutorial I will show you how to write your own reactive data structure using a &lt;a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)" rel="noopener noreferrer"&gt;simple stack implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the end you will have a fully functional stack, that automatically updates UI and autorun functions when elements are pushed or popped.&lt;/p&gt;




&lt;h2&gt;
  
  
  Short reminder: What is a stack?
&lt;/h2&gt;

&lt;p&gt;A stack manages data using the list-in-first-out (in short, LIFO) principle. The &lt;strong&gt;last&lt;/strong&gt; item that is pushed onto the stack will be the &lt;strong&gt;first&lt;/strong&gt; item that is accessed when popped from the stack.&lt;/p&gt;

&lt;p&gt;Underlying items are inaccessible until they become the top item by popping the stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4m5f2dw46x9x4ivlm8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4m5f2dw46x9x4ivlm8j.png" alt="Wikipedia Image of Stack" width="800" height="559"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;CC0 &lt;a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)#/media/File:Lifo_stack.svg" rel="noopener noreferrer"&gt;wikimedia commons&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's start coding
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Create a new project
&lt;/h3&gt;

&lt;p&gt;First of all, make sure &lt;a href="https://docs.meteor.com/about/install.html" rel="noopener noreferrer"&gt;MeteorJS is installed on your machine&lt;/a&gt;. In case you have no existing MeteorJS project available, create one by opening your terminal and enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;meteor&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provide a name and the frontend framework you intend to use. for this tutorial, I will use Blaze. However, the reactive stack will work with any of the frontends, available in MeteorJS.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Remove boilerplate
&lt;/h3&gt;

&lt;p&gt;In the next step, remove the boilerplate from the &lt;code&gt;client/main.html&lt;/code&gt;.&lt;br&gt;
It should look like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Meteor App&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Reactive Data Structures&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then remove the boilerplate from &lt;code&gt;client/main.js&lt;/code&gt; or &lt;code&gt;client/main.jsx&lt;/code&gt; if you chose React or Solid. It should look like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/templating&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./main.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./main.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Initial Stack implementation
&lt;/h3&gt;

&lt;p&gt;Now, create a new file under &lt;code&gt;client/ReactiveStack.js&lt;/code&gt; and implement a basic stack. In this first approach, the focus is on the data structure itself and not the reactivity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReactiveStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;push &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;pop &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;peek &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;all &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;size &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Integrate the stack into the UI
&lt;/h3&gt;

&lt;p&gt;You can now add a simple template that includes the &lt;code&gt;ReactiveStack&lt;/code&gt;. Update your &lt;code&gt;client/main.html&lt;/code&gt; file to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;reactive-datastructures&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Reactive Data Structures&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="k"&gt;{{&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;stack&lt;/span&gt; &lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"stack"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Stack&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- user input --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"push"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"push-btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Push&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pop-btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pop&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- reactively peek the top item --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;top item: &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;topItem&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;List:&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;each&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;each&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🤓 A quick explanation about this template&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The template renders the stack as a list of items, using an &lt;code&gt;{{#each}}&lt;/code&gt; loop. Read the &lt;a href="https://www.blazejs.org/api/spacebars#Each" rel="noopener noreferrer"&gt;Blaze docs&lt;/a&gt; if you don't know how these work.&lt;/p&gt;

&lt;p&gt;The top item is delivered by the &lt;code&gt;{{topItem}}&lt;/code&gt; &lt;a href="https://www.blazejs.org/api/templates#Template-helpers" rel="noopener noreferrer"&gt;helper&lt;/a&gt;. The user input is a simple text input and two buttons for push and pop. The topmost item is constantly peeked from the stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Adding Template logic
&lt;/h3&gt;

&lt;p&gt;The template from the previous step won't work at all, since you have defined only the rendering logic. It doesn't know about a stack, items and topItem. Let's add them by updating &lt;code&gt;client/main.js&lt;/code&gt; to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/templating&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ReactiveStack&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./ReactiveStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./main.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create a new reactive stack&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
  &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReactiveStack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// simple demonstration of autorun&lt;/span&gt;
  &lt;span class="c1"&gt;// within templates, based on reactive changes&lt;/span&gt;
  &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autorun&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="c1"&gt;// reactive helpers to use within the template&lt;/span&gt;
&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;topItem &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;items &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toReversed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// event listeners for the buttons&lt;/span&gt;
&lt;span class="nx"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;events&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click .push-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.item-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// clear input&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click .pop-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try it out by navigating to &lt;code&gt;http://localhost:3000&lt;/code&gt;. You will see that there is nothing reactive yet. This will change with the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Make it reactive
&lt;/h3&gt;

&lt;p&gt;Let's add some magic. 🧙‍♂️ This is where Tracker comes into play.&lt;/p&gt;

&lt;p&gt;Tracker has a simple way to track computations using &lt;a href="https://docs.meteor.com/api/Tracker.html#Tracker-Dependency" rel="noopener noreferrer"&gt;its Dependency class&lt;/a&gt;. While Tracker offers complex functionality to support many use cases, this step focuses solely on two methods this time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.meteor.com/api/Tracker.html#Tracker-Dependency-changed" rel="noopener noreferrer"&gt;changed&lt;/a&gt; - to inform about data has changed
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.meteor.com/api/Tracker.html#Tracker-Dependency-depend" rel="noopener noreferrer"&gt;depend&lt;/a&gt; - to inform about data is observed and Tracker is "active" (the method is called within an active computation, e.g. within an autorun). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two functions are basically responsible for invoking a new computation call each time they are called. In order to make our ReactiveStack truly reactive, we simply add this dependency to it and call &lt;code&gt;changed&lt;/code&gt; on any change to &lt;code&gt;items&lt;/code&gt; and &lt;code&gt;depend&lt;/code&gt; on any read.&lt;/p&gt;

&lt;p&gt;Afterwards, the stack looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Tracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/tracker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReactiveStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dependency&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;push &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;pop &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;peek &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;depend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;all &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;depend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;size &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;depend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, navigate back to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; and try again. At this point, the template will automagically update. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is because in Blaze, every Template.helper is actually a wrapped autorun computation that invokes a re-render of the attached DOM elements.&lt;br&gt;
Updating the stack invokes the surrounded autorun which itself invokes a rerender of the attached Blaze view with the updated data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Congratulations, you have implemented a ReactiveStack. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here?
&lt;/h2&gt;

&lt;p&gt;Keep looking (aka follow me) for upcoming articles on more Reactive Data Structures in MeteorJS. Alim, StoryTeller and I also covered the whole topic of reactivity (with focus on signals) in &lt;a href="https://youtube.com/live/XtCM6FAz5ro" rel="noopener noreferrer"&gt;episode 62 of our weekly live stream "This week in MeteorJS"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're interested in getting in touch with the community, then join the &lt;a href="https://forums.meteor.com/" rel="noopener noreferrer"&gt;MeteorJS forums&lt;/a&gt; and become a member today.&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles about &lt;strong&gt;MeteorJS&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; here on dev.to. I also recently co-hosted the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly MeteorJS Community Podcast&lt;/a&gt;, which covers the latest in Meteor and the community.&lt;/p&gt;

&lt;p&gt;You can also find me (and contact me) on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you read and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt; or buy me a book from &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>meteor</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Transform any MeteorJS App to a PWA in 2024</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Mon, 28 Oct 2024 14:54:15 +0000</pubDate>
      <link>https://forem.com/meteor/transform-any-meteorjs-app-to-a-pwa-in-2024-15ll</link>
      <guid>https://forem.com/meteor/transform-any-meteorjs-app-to-a-pwa-in-2024-15ll</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; It's easier than ever to transform your MeteorJS app into a PWA. Only few things have changed since my &lt;a href="https://dev.to/jankapunkt/transform-any-meteor-app-into-a-pwa-4k44"&gt;original article from 2020&lt;/a&gt;. These are mostly improvements on ServiceWorker, Manifest file and Head tags. Transforming to PWA will be a great benefit for your app's performance, UX and SEO.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What are PWAs?&lt;/li&gt;
&lt;li&gt;Why should I make my MeteorJS app a PWA?&lt;/li&gt;
&lt;li&gt;
Enough talk, show me how to do it

&lt;ol&gt;
&lt;li&gt;Add a service worker&lt;/li&gt;
&lt;li&gt;Provide a manifest&lt;/li&gt;
&lt;li&gt;Add Head entries&lt;/li&gt;
&lt;li&gt;Force ssl&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Where to go from here?&lt;/li&gt;

&lt;li&gt;About me&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  What are PWAs?
&lt;/h2&gt;

&lt;p&gt;According to &lt;a href="https://web.dev/learn/pwa/progressive-web-apps?continue=https%3A%2F%2Fweb.dev%2Flearn%2Fpwa%2F%23article-https%3A%2F%2Fweb.dev%2Flearn%2Fpwa%2Fprogressive-web-apps" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt; PWAs are&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...] web apps built and enhanced with modern APIs to deliver enhanced capabilities, reliability, and installability while reaching anyone, anywhere, on any device, all with a single codebase. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This makes them very different from native apps, which are build specifically for their target platforms (desktop or mobile devices). PWAs instead leverage the universal availability of the web platform. Does this sound familiar as a MeteorJS developer? Right, MeteorJS does so, too. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why should I make my MeteorJS app a PWA?
&lt;/h2&gt;

&lt;p&gt;With MeteorJS you build your apps by default and primarily for the web platform (although you can also build for &lt;a href="https://github.com/Meteor-Community-Packages/meteor-desktop" rel="noopener noreferrer"&gt;desktop using Electron&lt;/a&gt; and for mobile using &lt;a href="https://guide.meteor.com/cordova.html" rel="noopener noreferrer"&gt;Cordova&lt;/a&gt; or &lt;a href="https://github.com/meteorrn/meteor-react-native" rel="noopener noreferrer"&gt;React Native&lt;/a&gt;). They rely on a single codebase, as PWAs do, and use JavaScript across the full stack.&lt;/p&gt;

&lt;p&gt;Transforming your MeteorJS app into a PWA is a low-hanging fruit that comes with great benefits for your users in terms of performance (caching), SEO (manifest, meta tags), user experience (responsive design) or universal availability (offline capabilities).&lt;/p&gt;

&lt;h2&gt;
  
  
  Enough talk, show me how to do it
&lt;/h2&gt;

&lt;p&gt;As a preliminary requirement you either need to have an existing MeteorJS app, optimally using a responsive design library, such as Tailwind or Bootstrap.&lt;/p&gt;

&lt;p&gt;If you don't have one, you can create it with ease. Simply &lt;a href="https://docs.meteor.com/about/install.html" rel="noopener noreferrer"&gt;installing MeteorJS&lt;/a&gt;, then enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;meteor create &lt;span class="nt"&gt;--tailwind&lt;/span&gt; pwa-example
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;pwa-example
&lt;span class="nv"&gt;$ &lt;/span&gt;meteor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Add a service worker
&lt;/h3&gt;

&lt;p&gt;The ServiceWorker is the &lt;strong&gt;core component&lt;/strong&gt; that drives your PWA.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It takes the form of a JavaScript file that can control the web page/site that it is associated with, intercepting and modifying navigation and resource requests, and caching resources in a very granular fashion to give you complete control over how your app behaves in certain situations (the most obvious one being when the network is not available).&lt;br&gt;
&lt;small&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener noreferrer"&gt;MDN Service Worker API&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To integrate a ServiceWorker you need to create a new folder and fetch the foundation file from &lt;a href="https://github.com/jamauro/pwa-kit/" rel="noopener noreferrer"&gt;jamauro/pwa-kit&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; public
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s2"&gt;"https://raw.githubusercontent.com/jamauro/pwa-kit/refs/heads/main/sw.js"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ./public/sw.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can extend or modify it to your needs or leave it as a good default. Your next goal is to init the ServiceWorker as soon as possible. For this, create a startup file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; imports/startup/client
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;imports/startup/client/serviceworker.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following content to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Service Worker registration failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you need to import the startup file in &lt;code&gt;client/main.js&lt;/code&gt; or &lt;code&gt;client/main.jsx&lt;/code&gt; (depending on which frontend you have chosen)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../imports/startup/client/serviceworker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! You're already halfway-through on your way to your PWA.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But in MeteorJS, most traffic between clients and the server happens through DDP, a WebSocket communication protocol. ServiceWorkers work on HTTP-level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's correct. However, the MeteorJS client app itself is still a bundle of HTML, JavaScript, CSS and various assets and it is beneficial to control most of these resources in the most performant way possible. Additionally, Meteor does not restrict you from using HTTP requests, for example via &lt;code&gt;fetch&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Provide a manifest
&lt;/h3&gt;

&lt;p&gt;In the next step you will provide a manifest file that describes your app and configures appearance and behavior, when installed as PWA.&lt;/p&gt;

&lt;p&gt;In order to do that, create a new JSON file in your public folder via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"{}"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; public/app.webmanifest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need to provide at least the following minimal information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your app name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icons/icon-192.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192x192"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icons/icon-512.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512x512"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#0096FF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#0096FF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;a href="https://web.dev/learn/pwa/web-app-manifest?continue=https%3A%2F%2Fweb.dev%2Flearn%2Fpwa%2F%23article-https%3A%2F%2Fweb.dev%2Flearn%2Fpwa%2Fweb-app-manifest" rel="noopener noreferrer"&gt;more at web.dev about the web-app manifest&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;As you can see, there are &lt;strong&gt;several icons&lt;/strong&gt; included. Make sure to adjust the paths to match your icons and that you have at least an icon sized in 192x192px and 512x512px available under these paths!&lt;/p&gt;

&lt;p&gt;If you don't then you can use the following code to grab two placeholder icons from my older article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; public/icons
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; public/icons/icon-192.png https://media.dev.to/dynamic/image/width&lt;span class="o"&gt;=&lt;/span&gt;800%2Cheight&lt;span class="o"&gt;=&lt;/span&gt;%2Cfit&lt;span class="o"&gt;=&lt;/span&gt;scale-down%2Cgravity&lt;span class="o"&gt;=&lt;/span&gt;auto%2Cformat&lt;span class="o"&gt;=&lt;/span&gt;auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fid91jftqd0ualcuirtyf.png
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; public/icons/icon-512.png https://media.dev.to/dynamic/image/width&lt;span class="o"&gt;=&lt;/span&gt;800%2Cheight&lt;span class="o"&gt;=&lt;/span&gt;%2Cfit&lt;span class="o"&gt;=&lt;/span&gt;scale-down%2Cgravity&lt;span class="o"&gt;=&lt;/span&gt;auto%2Cformat&lt;span class="o"&gt;=&lt;/span&gt;auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fevszcjg6ix64lkcc5wb0.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To show you the awesomeness of these images, let me link them here again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthocznmxzyrjma22mngi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthocznmxzyrjma22mngi.png" alt="icon-192.png" width="192" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvinl2xod28pnab5nmzmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvinl2xod28pnab5nmzmg.png" alt="icon-512.png" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add Head entries
&lt;/h3&gt;

&lt;p&gt;In order to make your manifest file and icons discoverable you need to link them in your HTML &lt;code&gt;head&lt;/code&gt; using the &lt;code&gt;meta&lt;/code&gt; and &lt;code&gt;link&lt;/code&gt; tags in your &lt;code&gt;client/main.html&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/title&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;equiv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-ua-compatible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ie=edge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;
          &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viewport&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;width=device-width, height=device-height, viewport-fit=cover, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mobile-web-app-capable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apple-mobile-web-app-capable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;manifest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/app.webmanifest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;192x192&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/icon-192.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="nx"&gt;based&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msapplication-TileColor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0096FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme-col4. Force sslor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0096FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;these&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt; &lt;span class="nx"&gt;further&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;32x32&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/favicon-32x32.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;16x16&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/favicon-16x16.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mask-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/safari-pinned-tab.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0096FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple-touch-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icons/apple-touch-icon.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;noscript&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sorry, your browser does not support JavaScript!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/noscript&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/head&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this tutorial provides you the minimal set of icons, required for a working PWA, you need to action to meet platform-specific icon formats in order to be compliant.&lt;/p&gt;

&lt;p&gt;Also note, there are various &lt;a href="https://www.smashingmagazine.com/2021/09/css-head-tag/" rel="noopener noreferrer"&gt;optimizations in head tags load order&lt;/a&gt;, something you might want to further investigate, too.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Force ssl
&lt;/h3&gt;

&lt;p&gt;The ServiceWorker requires HTTPS, which you can easily enforce via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;meteor add force-ssl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;a href="https://www.meteor.com/cloud" rel="noopener noreferrer"&gt;Galaxy (Meteor Cloud)&lt;/a&gt; as your hosting platform, then this is sufficient on it's own and you need no further configuration when you deploy as subdomain of &lt;code&gt;meteorapp.com&lt;/code&gt;. If you use a &lt;a href="https://galaxy-support.meteor.com/en/article/domains-16cijgc/" rel="noopener noreferrer"&gt;custom domain&lt;/a&gt; then you can &lt;a href="https://galaxy-support.meteor.com/en/article/encryption-pt8wbl/" rel="noopener noreferrer"&gt;configure SSL in a single step&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you deploy to another hosting provider or envirely custom environment, then you need to make sure you have SSL certificates configured correctly on your own. Meteor-UP might be a good choice in this case, as &lt;a href="https://meteor-up.com/docs.html#ssl-support" rel="noopener noreferrer"&gt;it provides SSL config&lt;/a&gt; via let's encrypt out of the box.  &lt;/p&gt;

&lt;p&gt;There are also other resources linked in this &lt;a href="https://guide.meteor.com/security#ssl" rel="noopener noreferrer"&gt;Meteor guide section on SSL&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here?
&lt;/h2&gt;

&lt;p&gt;You should checkout &lt;a href="https://github.com/jamauro/offline" rel="noopener noreferrer"&gt;jam:offline&lt;/a&gt; if you want to gradually add offline-capabilities to your new upgraded PWA.&lt;/p&gt;

&lt;p&gt;If the provided ServiceWorker is not sufficient for your needs, then you might checkout &lt;a href="https://developer.chrome.com/docs/workbox/modules/workbox-recipes/" rel="noopener noreferrer"&gt;Google workbox and its recipes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you think about push notifications, then you might want to consider &lt;a href="https://packosphere.com/activitree/push" rel="noopener noreferrer"&gt;activitree:push&lt;/a&gt; or solutions like &lt;a href="https://github.com/novuhq/novu/" rel="noopener noreferrer"&gt;Novu&lt;/a&gt;. If you want to be free of any vendor infrastructure then you might want to implement &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Tutorials/js13kGames/Re-engageable_Notifications_Push" rel="noopener noreferrer"&gt;Web Push Notifications for PWAs&lt;/a&gt;, which are also supported by ServiceWorkers.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://web.dev/learn/pwa/" rel="noopener noreferrer"&gt;web.dev guide on PWA&lt;/a&gt; covers nearly every aspect of PWAs and how they work.&lt;/p&gt;

&lt;p&gt;Finally, we also covered PWAs in one of our livestreams episodes "This Week in MeteorJS":&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZTtXRJTUCIU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles about &lt;strong&gt;MeteorJS&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; here on dev.to. I also recently co-hosted the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly MeteorJS Community Podcast&lt;/a&gt;, which covers the latest in Meteor and the community.&lt;/p&gt;

&lt;p&gt;You can also find me (and contact me) on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you read and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt; or buy me a book from &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>javascript</category>
      <category>meteor</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MeteorJS 3.0 major impact estimated for July 2024 ☄️ - here is all you need to know 🧐</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Mon, 24 Jun 2024 18:14:25 +0000</pubDate>
      <link>https://forem.com/meteor/meteorjs-30-major-impact-estimated-for-july-2024-here-is-all-you-need-to-know-13oh</link>
      <guid>https://forem.com/meteor/meteorjs-30-major-impact-estimated-for-july-2024-here-is-all-you-need-to-know-13oh</guid>
      <description>&lt;p&gt;The next major &lt;a href="https://meteor.com" rel="noopener noreferrer"&gt;MeteorJS&lt;/a&gt; release is coming in July 2024! After more than two years of development, this is the final result. The &lt;a href="https://github.com/meteor/meteor/discussions/11505" rel="noopener noreferrer"&gt;first discussions started in June 2021&lt;/a&gt; and there has been multiple alphas, betas, rcs and a huge amount of package updates. These were constantly battle-tested by the Meteor Core team and the Community, shaping the features and performance of the platform one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Back in 2021 - the Fibers situation&lt;/li&gt;
&lt;li&gt;Biggest changes&lt;/li&gt;
&lt;li&gt;Will it have consequences?&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Resources to help you migrate your Meteor app&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Back in 2021 - the Fibers situation
&lt;/h2&gt;

&lt;p&gt;MeteorJS emerged from a JavaScript framework towards a true full-stack development platform, batteries included. One of its &lt;em&gt;greatest advantages&lt;/em&gt; was to provide features like &lt;strong&gt;optimistic UI, zero-config, reactivity out of the box and support for many popular front-ends&lt;/strong&gt;, which are still among &lt;a href="https://dev.to/meteor/5-core-concepts-you-should-know-about-meteorjs-in-2024-5fpb"&gt;its core features nowadays&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since pre-1.0 versions, MeteorJS relied on &lt;code&gt;fibers&lt;/code&gt; (coroutines) as its back-end for async programming, long before &lt;code&gt;async/await&lt;/code&gt; became a thing. That was revolutionary for developers: writing async code naturally, in sync-style, without callbacks or other nesting structures, resulting in more comprehensible code and a better developer experience.&lt;/p&gt;

&lt;p&gt;In April 2021, &lt;code&gt;fibers&lt;/code&gt; became &lt;a href="https://github.com/meteor/meteor/discussions/11505" rel="noopener noreferrer"&gt;incompatible with Node 16&lt;/a&gt; and therefore, Meteor was pinned to Node 14. It was clear that that replacing &lt;code&gt;fibers&lt;/code&gt; with &lt;code&gt;async/await&lt;/code&gt; for the entire ecosystem was the only future for MeteorJS. That way, it would always be up-to-date with the latest Node LTS and support the majority of NPM packages out there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Biggest changes
&lt;/h2&gt;

&lt;p&gt;The core-repository development for this major release gathered &lt;a href="https://github.com/meteor/meteor/pull/13163" rel="noopener noreferrer"&gt;over 2300 commits, affecting over 800 files&lt;/a&gt;, split in &lt;a href="https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Aopen+base%3Arelease-3.0" rel="noopener noreferrer"&gt;over 200 pull requests&lt;/a&gt;, involving contributors from the Meteor Core team and the Community. The efforts for all affected packages are uncountable as they are distributed across the whole community.&lt;/p&gt;

&lt;p&gt;The following table lists the most impactful changes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;change&lt;/th&gt;
&lt;th&gt;what happend&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dropping Fibers&lt;/td&gt;
&lt;td&gt;replace any &lt;code&gt;fibers&lt;/code&gt;-reliance with &lt;code&gt;async/await&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top-level await&lt;/td&gt;
&lt;td&gt;use &lt;code&gt;async/await&lt;/code&gt; without the need for an IIFE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;async database&lt;/td&gt;
&lt;td&gt;make all MongoDB interactions &lt;code&gt;async/await&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;async accounts and oauth&lt;/td&gt;
&lt;td&gt;ensure accounts, 2FA, passwordless and OAuth packages are async and thus, still zero-config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ARM support&lt;/td&gt;
&lt;td&gt;finally support MeteorJS on ARM architectures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;replace connect with express&lt;/td&gt;
&lt;td&gt;drop connect to use express as webserver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docs upgrade&lt;/td&gt;
&lt;td&gt;the entire documentation system was rewritten, replacing hexo with docusaurus, integrating Ask AI into the new docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;upgrade Node&lt;/td&gt;
&lt;td&gt;stick to the latest Node LTS (20 as of writing this article)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update DDP&lt;/td&gt;
&lt;td&gt;make the whole DDP/Websocket infrastructure and data layer compatible with the &lt;code&gt;async/await&lt;/code&gt; while avoid breaking isomorphism where possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update build system&lt;/td&gt;
&lt;td&gt;make the build system (isobuild) work without &lt;code&gt;fibers&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Galaxy 2&lt;/td&gt;
&lt;td&gt;the official &lt;strong&gt;zero-config hosting platform for MeteorJS apps&lt;/strong&gt; has been upgraded with a facelift and many quality of life features&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Furthermore, there were &lt;a href="https://github.com/meteor/meteor/blob/release-3.0/docs/history.md" rel="noopener noreferrer"&gt;many fixes, patches, and updates to NPM dependencies and skeleton projects&lt;/a&gt;, all aimed at ensuring that MeteorJS remains zero-config by default for many of its core features.&lt;/p&gt;




&lt;h2&gt;
  
  
  Will it have consequences?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If you are new to MeteorJS:
&lt;/h3&gt;

&lt;p&gt;No worries! Just follow the &lt;a href="https://v3-docs.meteor.com/" rel="noopener noreferrer"&gt;new docs&lt;/a&gt; to create a new project with Meteor 3.0 and develop amazing apps quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; meteor
&lt;span class="nv"&gt;$ &lt;/span&gt;meteor create &lt;span class="nt"&gt;--release&lt;/span&gt; 3.0-rc.4 &lt;span class="c"&gt;# soon --release is not required anymore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  If you have an existing Meteor apps or packages:
&lt;/h3&gt;

&lt;p&gt;Every (&lt;strong&gt;I repeat: every&lt;/strong&gt;) Meteor app out there &lt;strong&gt;was affected!&lt;/strong&gt; They had to be rewritten in order to being able to upgrade towards 3.0 or otherwise being stuck on Node 14.&lt;/p&gt;

&lt;p&gt;This especially involves any database interaction, the backbone of most MeteorJS apps. It also applied to any package with reliance on &lt;code&gt;fibers&lt;/code&gt;. Consider the following example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/meteor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/mongo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Before 3.0, using fibers&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;updateTask &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdBy&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;permission denied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not owner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After 3.0, using async/await&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;updateTask &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdBy&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;permission denied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not owner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It looks not that much but beware that it breaks &lt;em&gt;isomorphism&lt;/em&gt;, another core  concept of MeteorJS, where code can be &lt;strong&gt;shared across server and client&lt;/strong&gt;. This, combined with the size and structure of medium or large-sized apps can result in a migration effort of multiple months, involving multiple developers.&lt;/p&gt;

&lt;p&gt;Another big task was to resolve the version conflicts in the packages, since all of the MeteorJS core packages were updated to a new major version. Thankfully, the Meteor Community Ambassadors (&lt;a href="https://dev.to/storytellercz"&gt;StoryTellerCZ&lt;/a&gt;, Alim Gafar, and I) collaborated to create a detailed guide covering this topic: &lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/live/hGoxWQNIrfs?feature=share" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The next major MeteorJS release is coming in a few weeks, and of course, there will be changes if you have Meteor apps and packages. To help you prepare for all of them, I have made a list below to help you. &lt;/p&gt;




&lt;h2&gt;
  
  
  Resources to help you migrate your Meteor app
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Read the &lt;a href="https://guide.meteor.com/3.0-migration" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Read on how to &lt;a href="https://guide.meteor.com/prepare-meteor-3.0" rel="noopener noreferrer"&gt;prepare your 2.x app for async&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Read the &lt;a href="https://v3-docs.meteor.com/" rel="noopener noreferrer"&gt;new meteor 3.0 docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Read on &lt;a href="https://guide.meteor.com/using-node-v14.21.4" rel="noopener noreferrer"&gt;extended Node 14 support with security patches&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe to the &lt;a href="https://www.youtube.com/channel/UCqi1HYAD1Mm2vZtAn3-H-Cw" rel="noopener noreferrer"&gt;Meteor Community Dispatches podcast&lt;/a&gt; to stay up to date&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cal.com/storyteller" rel="noopener noreferrer"&gt;Consult StorytellerCZ&lt;/a&gt;, Meteor ambassador and one of the (if not the) most active contributors from the community for help&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://forums.meteor.com/" rel="noopener noreferrer"&gt;Meteor forums&lt;/a&gt; and ask for help and guidance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you enjoy this read and see you on Meteor Community!&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles about &lt;strong&gt;MeteorJS&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; here on dev.to. I also recently co-hosted the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly MeteorJS Community Podcast&lt;/a&gt;, which covers the latest in Meteor and the community.&lt;/p&gt;

&lt;p&gt;You can also find me (and contact me) on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you read and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt; or buy me a book from &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>meteor</category>
    </item>
    <item>
      <title>5 core concepts you should know about MeteorJS in 2024</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Mon, 29 Apr 2024 15:30:18 +0000</pubDate>
      <link>https://forem.com/meteor/5-core-concepts-you-should-know-about-meteorjs-in-2024-5fpb</link>
      <guid>https://forem.com/meteor/5-core-concepts-you-should-know-about-meteorjs-in-2024-5fpb</guid>
      <description>&lt;p&gt;MeteorJS is a JavaScript development platform for web- and mobile apps. It's most suited for, but not limited to, building single page applications (SPA), mobile apps, and complex web apps with feature-rich clients.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/meteor" rel="noopener noreferrer"&gt;
        meteor
      &lt;/a&gt; / &lt;a href="https://github.com/meteor/meteor" rel="noopener noreferrer"&gt;
        meteor
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Meteor, the JavaScript App Platform
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
  &lt;a href="https://www.meteor.com" rel="nofollow noopener noreferrer"&gt;
    
      
      
      &lt;img alt="Meteor logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F0467afb6-4f36-4cad-9d78-237150d5d881" width="300"&gt;
    
  &lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;&lt;a href="https://app.travis-ci.com/github/meteor/meteor" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6ea56ea6de86a7e2f0626f05a31878f27acb192c8761ba7df270cb89a4155f6e/68747470733a2f2f6170692e7472617669732d63692e636f6d2f6d6574656f722f6d6574656f722e7376673f6272616e63683d646576656c" alt="Travis CI Status"&gt;&lt;/a&gt;
&lt;a href="https://app.circleci.com/pipelines/github/meteor/meteor?branch=devel" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fa859952fb4c47c4ff34c5f4b8f2aa0032c2d5d25c2c7bd5d0a62c23682ed888/68747470733a2f2f636972636c6563692e636f6d2f67682f6d6574656f722f6d6574656f722e7376673f7374796c653d737667" alt="CircleCI Status"&gt;&lt;/a&gt;
&lt;a href="https://meteor.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/502ac36966770cada78656be9b1a55ae141749a1e4046997f5eac64d6bbced84/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6574656f722d332e302e332d677265656e3f6c6f676f3d6d6574656f72266c6f676f436f6c6f723d7768697465" alt="built with Meteor"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/be44677808e18237e016dde144d9d2b18f752c697ea29e008f3484c90f6a8a39/68747470733a2f2f696d672e736869656c64732e696f2f6e6f64652f762f6d6574656f72"&gt;&lt;img src="https://camo.githubusercontent.com/be44677808e18237e016dde144d9d2b18f752c697ea29e008f3484c90f6a8a39/68747470733a2f2f696d672e736869656c64732e696f2f6e6f64652f762f6d6574656f72" alt="node-current"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9d4dbee11e2e00362434216a05186d4600c050a21f457e73928ee05864073bf9/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f31323437393733333731303430323339363736"&gt;&lt;img src="https://camo.githubusercontent.com/9d4dbee11e2e00362434216a05186d4600c050a21f457e73928ee05864073bf9/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f31323437393733333731303430323339363736" alt="Discord"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f8bcfe4f68d88a2f6aa6a4b7ba3dec0dce3ec2624a9beb2e9157ef0e2050ec36/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f6d6574656f726a733f7374796c653d736f6369616c"&gt;&lt;img src="https://camo.githubusercontent.com/f8bcfe4f68d88a2f6aa6a4b7ba3dec0dce3ec2624a9beb2e9157ef0e2050ec36/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f6d6574656f726a733f7374796c653d736f6369616c" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Meteor is an &lt;strong&gt;ultra-simple&lt;/strong&gt; environment for building &lt;strong&gt;modern&lt;/strong&gt; web applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.meteor.com" rel="nofollow noopener noreferrer"&gt;Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.meteor.com/about/install.html" rel="nofollow noopener noreferrer"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.meteor.com/#/full/" rel="nofollow noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📚 &lt;strong&gt;Create your applications using modern JavaScript&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Benefit from the latest technology updates to rapidly prototype and develop your applications.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;✨ &lt;strong&gt;Integrate technologies you already use&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Use popular frameworks and tools right out-of-the-box. Focus on building features instead of configuring disparate components yourself.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;💻 &lt;strong&gt;Build apps for any device&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Use the same code whether you’re developing for web, iOS, Android, or desktop for a seamless update experience for your users.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔥 Getting Started&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;How about trying a tutorial to get started with your favorite technology?&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://docs.meteor.com/tutorials/react/" rel="nofollow noopener noreferrer"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/9242244a22ff5237b5410291986a5d911e829e494f47913c7d9bd0404627d789/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f612f61372f52656163742d69636f6e2e737667"&gt; React&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://blaze-tutorial.meteor.com/" rel="nofollow noopener noreferrer"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/46ac7a7f26b6cbf6a82f9c5c8eaaf112cb81c9872da818f57682c0823814ec94/68747470733a2f2f70726f67736f66742e6e65742f696d616765732f626c617a652d6373732d69636f6e2d336538306163623339393630343761666430396631313530663533666364373865393863316531622e706e67"&gt; Blaze&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.meteor.com/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.html" rel="nofollow noopener noreferrer"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/f9e0c6f8571a04d30382d14f47fc110badd1a3f136b530902ef28995ae4bd50a/68747470733a2f2f7675656a732e6f72672f696d616765732f6c6f676f2e706e67"&gt; Vue&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 Quick Start&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;On your platform, use this line:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; npx meteor&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;🚀 To create a project:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; meteor create&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;☄️ Run it:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; my-app
meteor&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🧱 Developer Resources&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Building an application with Meteor?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy on &lt;a href="https://www.meteor.com/cloud" rel="nofollow noopener noreferrer"&gt;Galaxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discuss on &lt;a href="https://forums.meteor.com/" rel="nofollow noopener noreferrer"&gt;Forums&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the Meteor Discord by clicking this &lt;a href="https://discord.gg/hZkTCaVjmT" rel="nofollow noopener noreferrer"&gt;invite&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/meteor/meteor" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It's is actively developed since 2012 and thus, among the most stable platforms in the JavaScript ecosystem. Chances are high, you have heard of it or knew it from past experiences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furtoxe37imauv7x64mxj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furtoxe37imauv7x64mxj.png" alt="MeteorJS commit frequency 2012-2024" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article I want to present you the 5 core concepts of MeteorJS and their state in 2024. Compare for yourself, if this is still the MeteorJS you know from back then.&lt;/p&gt;




&lt;h2&gt;
  
  
  True Full Stack
&lt;/h2&gt;

&lt;p&gt;MeteorJS uses JavaScript across the whole stack, from DB to client. This philosophy is one of the most important factors of why getting started with Meteor is such an ease: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;you only need to deal with one programming language.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the database-level it automatically integrates with &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt;, the biggest NoSQL database out there. Every Meteor release has a specific MongoDB binary bundled. There is no need for additional setup and you can start using it right away, due to a very carefully crafted integration of the MongoDB JavaScript driver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2F6910f0503efdd315c8f9b858234310c06e04d9c0%2Ficons%2Fmongodb%2Fmongodb-original-wordmark.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2F6910f0503efdd315c8f9b858234310c06e04d9c0%2Ficons%2Fmongodb%2Fmongodb-original-wordmark.svg" alt="mongo db logo" width="128" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In contrast to the tight coupling on the database-level, MeteorJS is very flexible in supporting many of the popular frontends. By default it supports React, Vue (2 + 3), Svelte and Solid. It also provides core-support for TypeScript, GraphQL (via Apollo), Tailwind, Chakra-UI, plus whatever you want to integrate via NPM packages.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Great&lt;/th&gt;
&lt;th&gt;tech&lt;/th&gt;
&lt;th&gt;MeteorJS&lt;/th&gt;
&lt;th&gt;supports&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2Fmaster%2Ficons%2Freact%2Freact-original.svg" alt="react" width="128" height="128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2Fmaster%2Ficons%2Fvuejs%2Fvuejs-original.svg" alt="vuejs" width="128" height="128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2Fmaster%2Ficons%2Fsvelte%2Fsvelte-original.svg" alt="svelte" width="128" height="128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fdevicons%2Fdevicon%2Fraw%2Fmaster%2Ficons%2Fsolidjs%2Fsolidjs-original.svg" alt="solid" width="128" height="128"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2Fmaster%2Ficons%2Ftailwindcss%2Ftailwindcss-original.svg" alt="tailwind" width="128" height="128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2Fmaster%2Ficons%2Ftypescript%2Ftypescript-original.svg" alt="typescript" width="128" height="128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevicons%2Fdevicon%2Fmaster%2Ficons%2Fgraphql%2Fgraphql-plain.svg" alt="graphql" width="128" height="128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F54212428%3Fs%3D150%26v%3D4" alt="chrakraui" width="150" height="150"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Finally,  it has it's own years-long battle-tested frontend &lt;a href="https://blazejs.org" rel="noopener noreferrer"&gt;Blaze&lt;/a&gt; and adding other frontends is also possible via &lt;a href="https://docs.meteor.com/api/packagejs.html#build-plugin-api" rel="noopener noreferrer"&gt;build plugins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Creating a new project with these technologies is as easy as it gets by using the builtin &lt;code&gt;meteor create&lt;/code&gt; dialog:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/shorts/FRzLfhl1Sbs?feature=shared" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  WebSocket and DDP
&lt;/h2&gt;

&lt;p&gt;By default clients connect to the server via WebSocket and communicate using a well-defined protocol (&lt;a href="https://github.com/meteor/meteor/tree/devel/packages/ddp" rel="noopener noreferrer"&gt;DDP; Distributied Data Protocol&lt;/a&gt;). No need to manage things on your own.&lt;/p&gt;

&lt;p&gt;With this you can for example declare and call RPC-style endpoints (&lt;a href="https://guide.meteor.com/methods" rel="noopener noreferrer"&gt;Meteor Methods&lt;/a&gt;) or subscribe to published data (&lt;a href="https://guide.meteor.com/data-loading" rel="noopener noreferrer"&gt;Meteor Publications&lt;/a&gt;) as easy as it gets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server/main.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;addTask &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myTasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client/main.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myTasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// yes, top-level await &lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addTask&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;do laundry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addTask&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clean kitchen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// ... subscription ready&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;fetchAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// reactive!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There is also an extensive  &lt;a href="https://guide.meteor.com/methods.html#advanced" rel="noopener noreferrer"&gt;guide, that also covers the DDP lifecycle of a Method call&lt;/a&gt; in detail.&lt;/p&gt;


&lt;h2&gt;
  
  
  Optimistic UI
&lt;/h2&gt;

&lt;p&gt;For every method there is a client-side "stub", which is basically a simulation of what is likely to happen on the server. If we defined this Method in client and server code, as all Methods should be, a Method simulation is executed in the client that called it. The client enters a special mode where it tracks all changes made to client-side collections, so that they can be rolled back later. When this step is complete, the user of your app sees their UI update instantly with the new content of the client-side database, but the server hasn’t received any data yet.&lt;/p&gt;

&lt;p&gt;In the meantime the server gets called and is likely to produce the same result, for which no further updates to the UI are needed. If the server produces a different result, then the UI will quickly update to comply with the "truth", returned from the server.&lt;/p&gt;

&lt;p&gt;This works in particular, since there is also an &lt;a href="https://guide.meteor.com/methods.html#consistent-id-generation" rel="noopener noreferrer"&gt;optimistic id-generation&lt;/a&gt; for the documents in the client-side MongoDB collections.&lt;/p&gt;


&lt;h2&gt;
  
  
  Zero Config
&lt;/h2&gt;

&lt;p&gt;MeteorJS comes with full batteries included. This means, there is no config needed for creating a development-build or production-build or to provide auto-reload and hmr. This is all taken care of by &lt;a href="https://guide.meteor.com/build-tool.html" rel="noopener noreferrer"&gt;Meteor's build system&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;meteor create myproject
&lt;span class="c"&gt;# ... create your project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;myproject &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; meteor
&lt;span class="c"&gt;# runs at localhost:3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There is no config or code required for connecting clients to server, establishing messaging or rules for their interaction. This is all handled by the aforementioned DDP protocol.&lt;/p&gt;

&lt;p&gt;There is no config or code needed for &lt;a href="https://guide.meteor.com/accounts" rel="noopener noreferrer"&gt;password based authentication&lt;/a&gt; and minimal config needed for &lt;a href="https://guide.meteor.com/accounts.html#supported-login-services" rel="noopener noreferrer"&gt;login with external OAuth providers&lt;/a&gt;, such as Twitter/X, Facebook, GitHub, Google, Meetup or Meteor developer accounts. Same applies to &lt;a href="https://docs.meteor.com/packages/accounts-passwordless.html" rel="noopener noreferrer"&gt;passwordless authenticaion&lt;/a&gt; and &lt;a href="https://docs.meteor.com/packages/accounts-2fa" rel="noopener noreferrer"&gt;2FA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Deployment to Galaxy (managed hosting for Meteor apps) can be done in a &lt;a href="https://galaxy-guide.meteor.com/deploy-command-line" rel="noopener noreferrer"&gt;single terminal command&lt;/a&gt;. Even easier it is by using &lt;a href="https://galaxy-guide.meteor.com/deploy-to-galaxy" rel="noopener noreferrer"&gt;push-to-deploy via their GitHub integration&lt;/a&gt;. Alternatively you can deploy to your own managed infrastructure using the free and open-source deployment tool &lt;a href="https://meteor-up.com/" rel="noopener noreferrer"&gt;"Meteor-Up"&lt;/a&gt; with ease.&lt;/p&gt;

&lt;p&gt;Despite all these out-of-the-box experiences, many things are still configurable. For example, you can write your own OAuth2 integration for third parties, such as &lt;a href="https://github.com/quavedev/apple-oauth" rel="noopener noreferrer"&gt;Apple&lt;/a&gt; or use an external bundler like Vite:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/fredmaiaarantes" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F276035%2F93f07ab9-0bc3-424f-957c-1d6ba2019582.jpg" alt="fredmaiaarantes"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/fredmaiaarantes/meteorjs-with-vite-solid-and-tailwind-css-5d2o" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Meteor.js with Vite, Solid, and Tailwind CSS&lt;/h2&gt;
      &lt;h3&gt;Frederico Maia ・ May 10 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#meteor&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#solidjs&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vite&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tailwindcss&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;






&lt;h2&gt;
  
  
  ESNext
&lt;/h2&gt;

&lt;p&gt;MeteorJS has always strived to be in sync with the latest ESNext, following the latest Babel versions or implement things on their own. To name a few examples: import/export were available very early on as core part of MeteorJS; &lt;a href="https://github.com/meteor/meteor/blob/devel/docs/history.md#v12-2015-09-21" rel="noopener noreferrer"&gt;Promises support started in 2015&lt;/a&gt;; &lt;a href="https://github.com/meteor/meteor/blob/devel/docs/history.md#v13-2016-03-27" rel="noopener noreferrer"&gt;async/await became available in 2016&lt;/a&gt;;nullish coalescing came day it entered the specifications; full typescript integration in core by &lt;a href="https://github.com/meteor/meteor/blob/devel/docs/history.md#v182-2019-11-14" rel="noopener noreferrer"&gt;2019&lt;/a&gt; after years of being supported by &lt;a href="https://packosphere.com/barbatus/typescript" rel="noopener noreferrer"&gt;community packages (since 2016)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;MeteorJS will continue to be on ESNext, giving devs the ability to be up-to-date, simply by updating their apps using &lt;code&gt;meteor update&lt;/code&gt; since these features are baked into the &lt;code&gt;ecmascript&lt;/code&gt; core package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Packages
&lt;/h2&gt;

&lt;p&gt;While the list contains five core concepts, there is one meta-level concept that is crucial for the MeteorJS ecosystem. It's all the packages, developed and maintained by the community!&lt;/p&gt;

&lt;p&gt;Many packages are zero-config drop-ins to provide an extended experience to the MeteorJS core functionality.&lt;/p&gt;

&lt;p&gt;Some of them play such an important role and are used by so many projects, that they got moved to Meteor Community Packages, an org to maintain and improve the most popular packages and to ensure a vital ecosystem:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Meteor-Community-Packages" rel="noopener noreferrer"&gt;
        Meteor-Community-Packages
      &lt;/a&gt; / &lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2" rel="noopener noreferrer"&gt;
        meteor-collection2
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Meteor package that extends Mongo.Collection to provide support for specifying a schema and then validating against that schema when inserting and updating.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Collection2 (aldeed:collection2 Meteor package)&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.repostatus.org/#active" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7e20e1589fb5e2aa55bb1de94227dc006b7639c2d2e1020eb08025ffae9f61df/68747470733a2f2f7777772e7265706f7374617475732e6f72672f6261646765732f6c61746573742f6163746976652e737667" alt="Project Status: Active – The project has reached a stable, usable state and is being actively developed."&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f4c50ad4be5f0e685fba2e5ddadd4ec788bc73e22425a09d6b1322d5f76e3541/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d636f6c6c656374696f6e32"&gt;&lt;img src="https://camo.githubusercontent.com/f4c50ad4be5f0e685fba2e5ddadd4ec788bc73e22425a09d6b1322d5f76e3541/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d636f6c6c656374696f6e32" alt="GitHub License"&gt;&lt;/a&gt;
&lt;a href="https://standardjs.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5338a68a0f130dc684279ff3e42e45c9c74006018a1bdeaac76905979b3ccd49/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64655f7374796c652d7374616e646172642d627269676874677265656e2e737667" alt="JavaScript Style Guide"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f9973490c4c4da27f3a91e67876e27ff53ac98262914a8ab7332a90475757593/68747470733a2f2f696d672e736869656c64732e696f2f6f7373662d73636f7265636172642f6769746875622e636f6d2f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d636f6c6c656374696f6e32"&gt;&lt;img src="https://camo.githubusercontent.com/f9973490c4c4da27f3a91e67876e27ff53ac98262914a8ab7332a90475757593/68747470733a2f2f696d672e736869656c64732e696f2f6f7373662d73636f7265636172642f6769746875622e636f6d2f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d636f6c6c656374696f6e32" alt="OSSF-Scorecard Score"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0f7cf590002056488c70ae9c6b421f65ee713947ab52d86bd42e7c4c027e6a8a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d636f6c6c656374696f6e323f6c6162656c3d6c617465737426736f72743d73656d766572"&gt;&lt;img src="https://camo.githubusercontent.com/0f7cf590002056488c70ae9c6b421f65ee713947ab52d86bd42e7c4c027e6a8a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d636f6c6c656374696f6e323f6c6162656c3d6c617465737426736f72743d73656d766572" alt="GitHub tag (latest SemVer)"&gt;&lt;/a&gt;
&lt;a href="http://semver.org/spec/v2.0.0.html" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8b16ff020fffd1d6cce1c7d8131edc9ab1a2470944623a77f9d14d6d457a5ce7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73656d7665722d322e302e302d73756363657373" alt=""&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/Meteor-Community-Packages/meteor-collection2/workflows/Test%20suite/badge.svg"&gt;&lt;img src="https://github.com/Meteor-Community-Packages/meteor-collection2/workflows/Test%20suite/badge.svg" alt="Test suite"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A Meteor package that allows you to attach a schema to a Mongo.Collection. Automatically validates against that schema when inserting and updating from client or server code.&lt;/p&gt;
&lt;p&gt;This package requires the &lt;a href="https://github.com/aldeed/simple-schema-js" rel="noopener noreferrer"&gt;simpl-schema&lt;/a&gt; NPM package, which defines the schema syntax and provides the validation logic.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;TOC&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#installation" rel="noopener noreferrer"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#import-using-static-imports" rel="noopener noreferrer"&gt;Import using static imports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#import-using-dynamic-imports" rel="noopener noreferrer"&gt;Import using dynamic imports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#why-use-collection2" rel="noopener noreferrer"&gt;Why Use Collection2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#attaching-a-schema-to-a-collection" rel="noopener noreferrer"&gt;Attaching a Schema to a Collection&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#attaching-multiple-schemas-to-the-same-collection" rel="noopener noreferrer"&gt;Attaching Multiple Schemas to the Same Collection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#attachschema-options" rel="noopener noreferrer"&gt;attachSchema options&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#transform" rel="noopener noreferrer"&gt;transform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#replace" rel="noopener noreferrer"&gt;replace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#attach-a-schema-to-meteorusers" rel="noopener noreferrer"&gt;Attach a Schema to Meteor.users&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#schema-format" rel="noopener noreferrer"&gt;Schema Format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#schema-clean-options" rel="noopener noreferrer"&gt;Schema Clean Options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#passing-options" rel="noopener noreferrer"&gt;Passing Options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#validation-contexts" rel="noopener noreferrer"&gt;Validation Contexts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#validating-without-inserting-or-updating" rel="noopener noreferrer"&gt;Validating Without Inserting or Updating&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#inserting-or-updating-without-validating" rel="noopener noreferrer"&gt;Inserting or Updating Without Validating&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#inserting-or-updating-without-cleaning" rel="noopener noreferrer"&gt;Inserting or Updating Without Cleaning&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#skip-removing-properties-that-are-not-in-the-schema" rel="noopener noreferrer"&gt;Skip removing properties that are not in the schema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#skip-conversion-of-values-to-match-what-schema-expects" rel="noopener noreferrer"&gt;Skip conversion of values to match what schema expects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#skip-removing-empty-strings" rel="noopener noreferrer"&gt;Skip removing empty strings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#skip-generating-automatic-values" rel="noopener noreferrer"&gt;Skip generating automatic values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#pick-or-omit-from-the-attached-schema" rel="noopener noreferrer"&gt;Pick or omit from the attached schema&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#inserting-or-updating-bypassing-collection2-entirely" rel="noopener noreferrer"&gt;Inserting or Updating Bypassing Collection2 Entirely&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#additional-simpleschema-options" rel="noopener noreferrer"&gt;Additional SimpleSchema Options&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-collection2#index-and-unique" rel="noopener noreferrer"&gt;index&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Meteor-Community-Packages/meteor-collection2" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Meteor-Community-Packages" rel="noopener noreferrer"&gt;
        Meteor-Community-Packages
      &lt;/a&gt; / &lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles" rel="noopener noreferrer"&gt;
        meteor-roles
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Authorization package for Meteor, compatible with built-in accounts packages
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;meteor-roles v3&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.repostatus.org/#active" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7e20e1589fb5e2aa55bb1de94227dc006b7639c2d2e1020eb08025ffae9f61df/68747470733a2f2f7777772e7265706f7374617475732e6f72672f6261646765732f6c61746573742f6163746976652e737667" alt="Project Status: Active – The project has reached a stable, usable state and is being actively developed."&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a182f0e0edc93c2a81d0de6d4cd2a0372f32a5f484cd830b852ae9d9013079ec/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d726f6c6573"&gt;&lt;img src="https://camo.githubusercontent.com/a182f0e0edc93c2a81d0de6d4cd2a0372f32a5f484cd830b852ae9d9013079ec/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d726f6c6573" alt="GitHub"&gt;&lt;/a&gt;
&lt;a href="https://standardjs.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5338a68a0f130dc684279ff3e42e45c9c74006018a1bdeaac76905979b3ccd49/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64655f7374796c652d7374616e646172642d627269676874677265656e2e737667" alt="JavaScript Style Guide"&gt;&lt;/a&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles/actions/workflows/github-code-scanning/codeql" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Meteor-Community-Packages/meteor-roles/actions/workflows/github-code-scanning/codeql/badge.svg" alt="CodeQL"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e53e7ea3b82644482cbbb4141ab7f35db74757411cd29a0ecb96305c0b0ae212/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d726f6c65733f6c6162656c3d6c617465737426736f72743d73656d766572"&gt;&lt;img src="https://camo.githubusercontent.com/e53e7ea3b82644482cbbb4141ab7f35db74757411cd29a0ecb96305c0b0ae212/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f4d6574656f722d436f6d6d756e6974792d5061636b616765732f6d6574656f722d726f6c65733f6c6162656c3d6c617465737426736f72743d73656d766572" alt="GitHub tag (latest SemVer)"&gt;&lt;/a&gt;
&lt;a href="http://semver.org/spec/v2.0.0.html" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8b16ff020fffd1d6cce1c7d8131edc9ab1a2470944623a77f9d14d6d457a5ce7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73656d7665722d322e302e302d73756363657373" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Authorization package for Meteor - compatible with built-in accounts package.&lt;/p&gt;
&lt;p&gt;There are also older versions of this package:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles/tree/v1" rel="noopener noreferrer"&gt;v1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles/tree/v2" rel="noopener noreferrer"&gt;v2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles/tree/v3" rel="noopener noreferrer"&gt;v3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#meteor-roles-v3" rel="noopener noreferrer"&gt;meteor-roles v3&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#table-of-contents" rel="noopener noreferrer"&gt;Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#contributors" rel="noopener noreferrer"&gt;Contributors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#authorization" rel="noopener noreferrer"&gt;Authorization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#permissions-vs-roles--or-whats-in-a-name" rel="noopener noreferrer"&gt;Permissions vs roles  (or What's in a name...)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#what-are-scopes" rel="noopener noreferrer"&gt;What are "scopes"?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#changes-to-default-meteor-behavior" rel="noopener noreferrer"&gt;Changes to default Meteor behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#installing" rel="noopener noreferrer"&gt;Installing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#migration-to-30" rel="noopener noreferrer"&gt;Migration to 3.0&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#changes-between-2x-and-30" rel="noopener noreferrer"&gt;Changes between 2.x and 3.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#usage-examples" rel="noopener noreferrer"&gt;Usage Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#api-docs" rel="noopener noreferrer"&gt;API Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#example-apps" rel="noopener noreferrer"&gt;Example Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Meteor-Community-Packages/meteor-roles#contributions-development-and-tests" rel="noopener noreferrer"&gt;Contributions, development and tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributors&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Thanks to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sponsors/StorytellerCZ" rel="noopener noreferrer"&gt;@storytellerCZ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sponsors/jankapunkt" rel="noopener noreferrer"&gt;@jankapunkt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alanning" rel="noopener noreferrer"&gt;@alanning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mitar" rel="noopener noreferrer"&gt;@mitar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/challett" rel="noopener noreferrer"&gt;@challett&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ianserlin" rel="noopener noreferrer"&gt;@ianserlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/leebenson" rel="noopener noreferrer"&gt;@leebenson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pward123" rel="noopener noreferrer"&gt;@pward123&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dandv" rel="noopener noreferrer"&gt;@dandv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aldeed" rel="noopener noreferrer"&gt;@aldeed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kevb" rel="noopener noreferrer"&gt;@kevb&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zimme" rel="noopener noreferrer"&gt;@zimme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danieljonce" rel="noopener noreferrer"&gt;@danieljonce&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pascoual" rel="noopener noreferrer"&gt;@pascoual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nickmoylan" rel="noopener noreferrer"&gt;@nickmoylan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mcrider" rel="noopener noreferrer"&gt;@mcrider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/simonsimcity" rel="noopener noreferrer"&gt;@simonsimcity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Authorization&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;This package lets you attach roles to a user, which you can then check against later when deciding whether to grant
access to Meteor methods or publish data. The core concept is very simple, essentially you are creating an assignment
of roles to a user and then checking for the existence of those roles later. This package provides helper methods
to…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Meteor-Community-Packages/meteor-roles" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;These are just two example among over 60 repositories of &lt;a href="https://github.com/Meteor-Community-Packages" rel="noopener noreferrer"&gt;the Meteor-Community-Packages org&lt;/a&gt;. Finally, some packages even make it to the core, such as the aforementioned typescript package. This shows the constant importance of community packages as vital part of an active and alive development platform.&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles about &lt;strong&gt;Meteor.js&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; here on dev.to. I also recently co-hosted the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly Meteor.js Community Podcast&lt;/a&gt;, which covers the latest in Meteor.js and the community.&lt;/p&gt;

&lt;p&gt;You can also find me (and contact me) on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you read and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt;, &lt;a href="https://paypal.me/kuesterjan" rel="noopener noreferrer"&gt;send me a tip via PayPal&lt;/a&gt; or sponsor a book from &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>meteor</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I fixed the "Save draft" Button on dev.to - No Accidental Publishing Anymore 😇</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Mon, 25 Mar 2024 12:02:54 +0000</pubDate>
      <link>https://forem.com/jankapunkt/i-fixed-the-save-draft-button-on-devto-no-accidental-publishing-anymore-1kk1</link>
      <guid>https://forem.com/jankapunkt/i-fixed-the-save-draft-button-on-devto-no-accidental-publishing-anymore-1kk1</guid>
      <description>&lt;p&gt;Today I accidentally published an article AGAIN, because "publish" and "safe draft" are so close together.&lt;/p&gt;

&lt;p&gt;I even opened a &lt;a href="https://github.com/forem/forem/discussions/20456" rel="noopener noreferrer"&gt;discussion, which got no responses so far&lt;/a&gt; (which I think existed somewhere else or I am the only one with this issue...).&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://unsplash.com/@francisco_legarreta?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Francisco De Legarreta C.&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-man-with-glasses-is-looking-at-a-laptop-hHg9MC-G8_Y?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;br&gt;
  &lt;/small&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How to fix it?
&lt;/h2&gt;

&lt;p&gt;I use Greasemonkey and Tampermonkey, depending on the browser.&lt;br&gt;
The fix script is rather simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;observe the DOM using &lt;code&gt;MutationObserver&lt;/code&gt; in &lt;/li&gt;
&lt;li&gt;use &lt;code&gt;body&lt;/code&gt; as observe target, in case the user navigates to the edit route from dashboard or preview route&lt;/li&gt;
&lt;li&gt;"wait" until the &lt;code&gt;editor-actions&lt;/code&gt; are added as &lt;code&gt;childList&lt;/code&gt; to the &lt;code&gt;article-form&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;if they're added, get the second button ("save draft" button) and update it's margin-left style&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The script looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ==UserScript==&lt;/span&gt;
&lt;span class="c1"&gt;// @name     dev.to save button space&lt;/span&gt;
&lt;span class="c1"&gt;// @version  1&lt;/span&gt;
&lt;span class="c1"&gt;// @grant    none&lt;/span&gt;
&lt;span class="c1"&gt;// ==/UserScript==&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BUTTON_STYLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;200px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutationList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;mutationList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;childList&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addedNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;editor-actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marginLeft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BUTTON_STYLE&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Create an observer instance linked to the callback function&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Start observing the target node for configured mutations&lt;/span&gt;
&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;childList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;subtree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also available as gist: &lt;a href="https://gist.github.com/jankapunkt/a442d10a2d3cda905843ca35645007d8" rel="noopener noreferrer"&gt;https://gist.github.com/jankapunkt/a442d10a2d3cda905843ca35645007d8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to comment or optimize in any way :-)&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles about &lt;strong&gt;Meteor.js&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; here on dev.to. I also recently co-hosted the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly Meteor.js Community Podcast&lt;/a&gt;, which covers the latest in Meteor.js and the community.&lt;/p&gt;

&lt;p&gt;You can also find me (and contact me) on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you read and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt;, &lt;a href="https://paypal.me/kuesterjan" rel="noopener noreferrer"&gt;send me a tip via PayPal&lt;/a&gt; or sponsor a book from &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>meta</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>SpeechSynthesis in &lt;= 256 characters (Web Speech API)</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Sun, 24 Mar 2024 13:09:03 +0000</pubDate>
      <link>https://forem.com/jankapunkt/speechsynthesis-in-256-characters-web-speech-api-34b2</link>
      <guid>https://forem.com/jankapunkt/speechsynthesis-in-256-characters-web-speech-api-34b2</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV Challenge v24.03.20, One Byte Explainer: Browser API or Feature.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explainer
&lt;/h2&gt;

&lt;p&gt;Make texts &amp;amp; UIs more accessible by using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis" rel="noopener noreferrer"&gt;&lt;code&gt;SpeechSynthesis&lt;/code&gt;&lt;/a&gt;: Create a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/SpeechSynthesisUtterance" rel="noopener noreferrer"&gt;&lt;code&gt;new SpeechSynthesisUtterance&lt;/code&gt;&lt;/a&gt; + given &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/text" rel="noopener noreferrer"&gt;text&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/pitch" rel="noopener noreferrer"&gt;pitch&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/rate" rel="noopener noreferrer"&gt;rate&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/volume" rel="noopener noreferrer"&gt;volume&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/lang" rel="noopener noreferrer"&gt;lang&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance/voice" rel="noopener noreferrer"&gt;voice&lt;/a&gt; (vary by OS and browser). Tap into &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance#events" rel="noopener noreferrer"&gt;various events&lt;/a&gt;. Finally, pass the utterance to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/speak" rel="noopener noreferrer"&gt;&lt;code&gt;speechSynthesis.speak&lt;/code&gt;&lt;/a&gt; → 🔊&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Context
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;No external tools were used for the above text in any circumstance. 🤞&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;SpeechSynyhesis&lt;/code&gt; is the "output" part of the Web Speech API, where the input counterpart is &lt;code&gt;SpeechRecognition&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Despite its potential in terms of accessibility (people with limited reading skills or generally low literacy), it hasn't been developed in years! Custom voices cannot be loaded, although remote voice loading is generally implemented. &lt;/p&gt;

&lt;p&gt;Alternatives are either closed + commercial or require a server to generate the voice and stream it as audio to the client. It's time for the webdev community to shed some light on this and not let speech synthesis become a walled garden solution.&lt;/p&gt;

&lt;p&gt;I also wrote an article about maximising cross-browser compatibility for speech synthesis here on dev.to:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jankapunkt" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F157294%2F1c3c63fd-d0d4-4db4-97ca-2a3d825ece7c.jpeg" alt="jankapunkt"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/jankapunkt/cross-browser-speech-synthesis-the-hard-way-and-the-easy-way-353" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Cross browser speech synthesis - the hard way and the easy way&lt;/h2&gt;
      &lt;h3&gt;Jan Küster 🔥 ・ Dec 7 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#browser&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#standards&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;&lt;small&gt;Cover image by &lt;a href="https://unsplash.com/@smajor15?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Scott Major&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/black-and-brown-round-speaker-ZP2iqHtUtyc?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build Your Own Uptime Monitor with MeteorJS + Fetch + Plotly.js ☄️🔭</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Fri, 08 Mar 2024 13:44:52 +0000</pubDate>
      <link>https://forem.com/meteor/build-your-own-uptime-monitor-with-meteorjs-fetch-plotlyjs-4fe0</link>
      <guid>https://forem.com/meteor/build-your-own-uptime-monitor-with-meteorjs-fetch-plotlyjs-4fe0</guid>
      <description>&lt;p&gt;In this tutorial I'll going to show you how to create your own uptime monitor in six easy steps. The app will use of the following stack, database to frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; to store our data as documents, close to JS objects&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.meteor.com/" rel="noopener noreferrer"&gt;MeteorJS&lt;/a&gt; to build and run our app and handle the data layer out-of-the-box&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="noopener noreferrer"&gt;fetch&lt;/a&gt; at the heart of the monitoring requests to the external server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; as our frontend library&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://plotly.com/javascript/" rel="noopener noreferrer"&gt;Plotly&lt;/a&gt; as our chart renderer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Concept and Architecture&lt;/li&gt;
&lt;li&gt;Create a New React Project with MeteorJS&lt;/li&gt;
&lt;li&gt;
Implementation

&lt;ul&gt;
&lt;li&gt;Step 1 - Configuration and Configurability&lt;/li&gt;
&lt;li&gt;Step 2 - Create Collections&lt;/li&gt;
&lt;li&gt;Step 3 - Create a Handler for the Collections&lt;/li&gt;
&lt;li&gt;Step 4 - Create an Uptime Request&lt;/li&gt;
&lt;li&gt;Step 5 - Server Startup&lt;/li&gt;
&lt;li&gt;Step 6 - The Monitoring Frontend&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Where to go from here&lt;/li&gt;

&lt;li&gt;About me&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Concept and Architecture
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;There is an HTTP endpoint on your external server that is constantly requested by your Meteor app's server using &lt;code&gt;fetch&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each response and it's status, including timeouts and latency measures, is stored as a "document" in the Database, which is MongoDB by default. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Meteor "watches" the collection of documents and "publishes" updates of the collection to clients, that "subscribe" to it. This allows live-monitoring to be set up for custom periods of time. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The React client application subscribes to the live changes and uses &lt;a href="https://plotly.com/javascript/react/" rel="noopener noreferrer"&gt;Plotly&lt;/a&gt; to display them as a line graph showing uptime and response time for a given period.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lm5591fz42ywnfylmfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lm5591fz42ywnfylmfj.png" alt="Uptime Monitor App Architecture" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that you should only use this for websites that you own or have the permission to monitor. Make sure that you always act within the law.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create a New React Project with MeteorJS
&lt;/h2&gt;

&lt;p&gt;If you do not have MeteorJS installed on your system, you may want to perform this quick install of the latest stable version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; meteor
&lt;span class="c"&gt;# on Linux||MacOS, you can also use the shell-installer script via&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl https://install.meteor.com/ | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, it's time to create a new project, once the &lt;code&gt;meteor&lt;/code&gt; tool is installed and available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;meteor create &lt;span class="nb"&gt;uptime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new React-based Meteor project in the folder &lt;code&gt;uptime&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you don't want to use React, then you can also see what other frontends are available by running &lt;code&gt;meteor create --help&lt;/code&gt;. Note, however, that this tutorial focuses on Meteor+React&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go to the &lt;code&gt;uptime&lt;/code&gt; folder and install some dependencies, then run the project for the first time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;uptime&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;meteor&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;plotly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;plotly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;abort&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;echo&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;meteor&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your project is now running on &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Most of the following steps are on the &lt;code&gt;server&lt;/code&gt;. However, shared (isomorphic) code between client and server is placed in a separate folder called &lt;code&gt;/imports&lt;/code&gt;. A new React project should have this folder by default, though.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - Configuration and Configurability
&lt;/h3&gt;

&lt;p&gt;For our uptime measurements we use a simple &lt;a href="https://developer.mozilla.org/en-US/docs/web/http/methods/head" rel="noopener noreferrer"&gt;HTTP HEAD request&lt;/a&gt;. We use HEAD, because there is no response body sent over the wire and being parsed. &lt;/p&gt;

&lt;p&gt;However, the request can be configured using &lt;a href="https://docs.meteor.com/api/core#Meteor-settings" rel="noopener noreferrer"&gt;Meteor's &lt;code&gt;settings.json&lt;/code&gt; file&lt;/a&gt;, similar to &lt;code&gt;.env&lt;/code&gt;, allowing for easy changes in the future without the need to modify any code.&lt;/p&gt;

&lt;p&gt;Let's create a new monitor in &lt;code&gt;settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"monitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"myDomain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyDomainXYZ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HEAD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mycooldomain.xyz/reachability"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"interval"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration involves sending a HEAD request to the specified URL every 5 seconds and measuring the response time. The expected response is a 204 status code, but if there is no response within 10 seconds, the server will be considered 'down' and the request will be aborted.&lt;/p&gt;

&lt;p&gt;It is important to note that using other request methods besides HEAD may take longer to process and could potentially consume more server resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Create Collections
&lt;/h3&gt;

&lt;p&gt;To send data to the client and persist it between server restarts, two MongoDB collections are required: one to preserve the overall monitor status and another to store the summary of each request as a document.&lt;/p&gt;

&lt;p&gt;Create both collections in &lt;code&gt;/imports/collections/MonitorCollection.js&lt;/code&gt; and &lt;code&gt;/imports/collections/StatusCollection.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/mongo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MonitorCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/mongo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They will be used later to automatically sync the data from the server to the client using "publish/subscribe".&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Create a Handler for the Collections
&lt;/h3&gt;

&lt;p&gt;Instead of directly accessing the collections we actually want to control inflow and outflow of data. For this we create a &lt;code&gt;Monitor&lt;/code&gt; object in &lt;code&gt;server/Monitor.js&lt;/code&gt;, that will manage all this for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/meteor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MonitorCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../imports/collections/MonitorCollection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../imports/collections/StatusCollection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we want to be able to initially create a status for a configured domain, so that it's status/info is available, even if we haven't fired a request yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... continue in server/Monitor.js&lt;/span&gt;

&lt;span class="nx"&gt;Monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upsertAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will also update the status, in case we changed our configuration in &lt;code&gt;settings.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Okay, now let's think about what we actually want to measure. We need a &lt;code&gt;name&lt;/code&gt; as key, a timestamp of the request, a duration, a &lt;code&gt;hasTImedOut&lt;/code&gt; flag and an error, in case we want to read error details.&lt;/p&gt;

&lt;p&gt;Furthermore, we want to update status only once it has changed and not every time a new document arrives. A simple first implementation could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... continue in server/Monitor.js&lt;/span&gt;

&lt;span class="nx"&gt;Monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasTimedOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;statusChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;// we just went down :-/&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;hasTimedOut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;statusChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// we're back in business :-)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasTimedOut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;statusChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Monitor]: status changed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="c1"&gt;// here is a good spot to send an Email to&lt;/span&gt;
    &lt;span class="c1"&gt;// inform about the status change!&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;MonitorCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we want to define the amount of data, send to the client. We do this by defining Meteor Publications. On the one hand, we want to publish the status of the monitor as a single document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the other hand we want a good bunch of request summaries, that will help us to build a nice graph of the uptime and response duration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... continue in server/Monitor.js&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;MonitorCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$natural&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;hint: { $natural: -1 }&lt;/code&gt; which tells the collection to so a natural backwards search (start with the newest entry), omitting the need for &lt;code&gt;sort&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Create an Uptime Request
&lt;/h3&gt;

&lt;p&gt;Now, that we have set up a collection management, we still need a handler that fires and evaluates our requests by our given configuration.&lt;/p&gt;

&lt;p&gt;For this we create a &lt;code&gt;class&lt;/code&gt; this time. Create a new file &lt;code&gt;server/UptimeRequest.js&lt;/code&gt; and start with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AbortController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-abort-controller&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PerformanceObserver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:perf_hooks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UptimeRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// config&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expectedResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actualResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

    &lt;span class="c1"&gt;// results / measurements&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasTimedOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fire &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// here we implement the actual request&lt;/span&gt;
    &lt;span class="c1"&gt;// and measure the outcome&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;toDocument &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// here we will return results that matter&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there is no &lt;code&gt;interval&lt;/code&gt; as this class will be instantiated each time for a new request, thus one instance represents one request.&lt;/p&gt;

&lt;p&gt;Let's implement the &lt;code&gt;fire&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//... in the UptimeRequest class&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fire &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// prepare performance measures&lt;/span&gt;
    &lt;span class="c1"&gt;// see https://nodejs.org/api/perf_hooks.html#performanceentryduration&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PerformanceObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntries&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;entryTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;measure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - start`&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ended&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - ended`&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasTimedOut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ended&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ended&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actualResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;

    &lt;span class="c1"&gt;// post checkup&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expectedResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Response code mismatch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// normalize error&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without going into too much details, this method creates a fetch request with a so called &lt;code&gt;AbortSignal&lt;/code&gt; that aborts the request, once our given timeout has been exceeded. On top it's surrounded by Node's native performance measurement to measure the overall request-response duration.&lt;/p&gt;

&lt;p&gt;You can read up everything that happens in this method using the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/api/perf_hooks.html#performanceentryduration" rel="noopener noreferrer"&gt;https://nodejs.org/api/perf_hooks.html#performanceentryduration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/AbortController&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/26199752/3098783" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/26199752/3098783&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally we want to provide a summary as document to the outside consumers by implementing a simple &lt;code&gt;toDocument&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//... in the UptimeRequest class&lt;/span&gt;

  &lt;span class="nf"&gt;toDocument &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expectedResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actualResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;fired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;hasTimedOut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasTimedOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 - Server Startup
&lt;/h3&gt;

&lt;p&gt;We can finally run our server-side code that will request the external server and saves the results and status in the database. For this, remove all code in &lt;code&gt;server/main.js&lt;/code&gt; and replace it with this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/meteor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UptimeRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./UptimeRequest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Monitor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Monitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;myDomain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;monitors&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;monitor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UptimeRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDocument&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDomain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On startup we first register or update our domain (see the &lt;code&gt;Monitor.init&lt;/code&gt; method in step 1.3) and then run a request for each interval, adding the result to the collections and updating the status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - The Monitoring Frontend
&lt;/h3&gt;

&lt;p&gt;The client part is much shorter and will actually fit in one step. This is, because here it combines the magic of Meteor's reactivity together with the magic of Plotly's auto-generation of charts, just to save you hours and hours of coding! 🧙‍♂️&lt;/p&gt;

&lt;p&gt;For this you only need to change the file &lt;code&gt;imports/ui/App.jsx&lt;/code&gt; to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MonitorCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../collections/MonitorCollection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../collections/StatusCollection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useTracker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useFind&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/react-meteor-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Plot&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-plotly.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyDomainXYZ&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// subscribe to status, call&lt;/span&gt;
  &lt;span class="c1"&gt;// statusLoading() to know if it's loading or not&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statusLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// subscribe to monitor docs,&lt;/span&gt;
  &lt;span class="c1"&gt;// use monitorLoading() to know if it's loading or not&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;monitorLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// the actual status doc, which will be undefined,&lt;/span&gt;
  &lt;span class="c1"&gt;// as long as the subscription is still loading&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFind&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;StatusCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTracker&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MonitorCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// make sure the client sorts the docs,&lt;/span&gt;
      &lt;span class="c1"&gt;// in case they arrive in a different order&lt;/span&gt;
      &lt;span class="c1"&gt;// than the server queried them&lt;/span&gt;
      &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="c1"&gt;// doc index as Date&lt;/span&gt;
      &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="c1"&gt;// up or down&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scatter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scatter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;updatime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fired&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nx"&gt;updatime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasTimedOut&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;down&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasTimedOut&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;updatime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Uptime Monitor for "&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;"&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StatusInfo&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;statusLoading&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;monitorLoading&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Plot&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Uptime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responsive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Plot&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Duration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;responsive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// pure component, could be used with memo&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StatusInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Status loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Status: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;down&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; | &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;URL: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; | &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Method: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; | &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Interval: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;s&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result are two line-graphs that update every 5 seconds, as defined in our config:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlijiwop6yf9lv3qmvzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlijiwop6yf9lv3qmvzh.png" alt="Final plot result" width="800" height="922"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The magic behind this very short code is actually the hooks, provided by &lt;a href="https://packosphere.com/meteor/react-meteor-data" rel="noopener noreferrer"&gt;the &lt;code&gt;react-meteor-data&lt;/code&gt; package&lt;/a&gt;, which is already included in every new Meteor-React project. Here are some excerpts:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;useTracker&lt;/code&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;You can use the useTracker hook to get the value of a Tracker reactive function in your React "function components." The reactive function will get re-run whenever its reactive inputs change, and the component will re-render with the new value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To provide some more context: This hook uses Meteor's &lt;a href="https://docs.meteor.com/api/tracker.html" rel="noopener noreferrer"&gt;Tracker&lt;/a&gt; to listen on changes in a Data-set, such as our two collections. Under the hood there is an observer that runs, every time a document is added, removed or updated on this collection, invoking a Tracker computation, which in turn invokes a new React computation, similar to what you already know from other React hooks.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;useSubscribe&lt;/code&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;At its core, it is a very simple wrapper around useTracker (with no deps) to create the subscription in a safe way, and allows you to avoid some of the ceremony around defining a factory and defining deps. Just pass the name of your subscription, and your arguments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;useFind&lt;/code&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;The useFind hook can substantially speed up the rendering (and rerendering) of lists coming from mongo queries (subscriptions). It does this by controlling document object references. By providing a highly tailored cursor management within the hook, using the Cursor.observe API, useFind carefully updates only the object references changed during a DDP update.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;The app in this tutorial is far from complete. However, I compiled a few ideas for your next steps with this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;update to Meteor 3.0 and deploy to an ARM device like a Raspberry PI (we may cover this in the future if demand is high)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.meteor.com/cloud?utm_source=jkuester&amp;amp;utm_medium=online&amp;amp;utm_campaign=Q2-2022-Ambassadors" rel="noopener noreferrer"&gt;deploy your app to Meteor Cloud (managed hsoting)&lt;/a&gt; or your own infrastructure using &lt;a href="https://meteor-up.com/" rel="noopener noreferrer"&gt;Meteor-Up&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;extend the app to monitor multiple clients&lt;/li&gt;
&lt;li&gt;use a job / cron-job library, instead of &lt;code&gt;setInterval&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;create a dashboard for your monitors&lt;/li&gt;
&lt;li&gt;add &lt;a href="https://docs.meteor.com/api/accounts.html" rel="noopener noreferrer"&gt;Meteor Accounts&lt;/a&gt; to manage users, authentication etc.&lt;/li&gt;
&lt;li&gt;improve the Plot layouts and make them visually more appealing&lt;/li&gt;
&lt;li&gt;use caching and capped collections or redis-oplog for scaling&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles about &lt;strong&gt;Meteor.js&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt; here on dev.to. I also recently co-hosted the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly Meteor.js Community Podcast&lt;/a&gt;, which covers the latest in Meteor.js and the community.&lt;/p&gt;

&lt;p&gt;You can also find me (and contact me) on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you read and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt;, &lt;a href="https://paypal.me/kuesterjan" rel="noopener noreferrer"&gt;send me a tip via PayPal&lt;/a&gt; or sponsor a book from &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>meteor</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Dead or not dead 💀 - Exploring the term and why Meteor.js is super-alive! ☄️</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Tue, 16 Jan 2024 12:47:15 +0000</pubDate>
      <link>https://forem.com/meteor/dead-or-not-dead-exploring-the-term-and-why-meteorjs-is-super-alive-4j2c</link>
      <guid>https://forem.com/meteor/dead-or-not-dead-exploring-the-term-and-why-meteorjs-is-super-alive-4j2c</guid>
      <description>&lt;p&gt;I recently picked up a comment on Reddit that directly feeds into the never-ending "Meteor is dead" &lt;del&gt;FUD&lt;/del&gt; talk. Usually, I immediately join these kinds of conversation and beam a huge list of facts straight into the heads of them peoplezzZz to prove a point. 🛸&lt;/p&gt;

&lt;p&gt;Well, this time I thought different. Let's skip this highly annoying type of interaction and cover the "dead debate" beyond Meteor.js and on a much deeper level. 🤯&lt;/p&gt;

&lt;p&gt;Don't worry, as the end of the article will still contain all the facts on Meteor.js, so next time I can beam the article into them brainzzZz. 🛸&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;‼️Trigger warning‼️ The following article includes themes and topics about death that go beyond the realm of software engineering and web development.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Dead or not dead? 💀
&lt;/h2&gt;

&lt;p&gt;We all know, in JavaScript there is only hype-driven-development (hdd) as the one and only methodology to rule them all. If you don't ride the hype-train then you're dead. Thankfully, this is just a superstition and far from being correct. However, &lt;a href="https://2022.stateofjs.com/en-US/opinions/" rel="noopener noreferrer"&gt;many users feel the JS ecosystem is moving very fast&lt;/a&gt; and new tools emerge on a weekly basis.&lt;br&gt;
If your software can't keep up with and ride the hype-train constantly then it becomes dead, right?  &lt;/p&gt;

&lt;p&gt;Well, it's not that easy. The term "xyz is dead" can actually refer to many more things than that and it also depends on the context and the intention of the person who used it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At this point we can't clearly say what "dead" means to a software, as there is no generally agreed definition for it. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, I don't want to go any further without a clear definition of what "dead" actually should refer to, when speaking about software, tools and frameworks.&lt;/p&gt;
&lt;h3&gt;
  
  
  When we should NOT consider a software as "dead" 😇
&lt;/h3&gt;

&lt;p&gt;The great part of this is, that there is already an article that unwrapped this question in an amazing way. I highly suggest you to read on &lt;a href="https://hub.packtpub.com/what-do-we-really-mean-when-we-say-that-software-is-dead-or-dying/" rel="noopener noreferrer"&gt;"What do we really mean when we say that software is ‘dead’ or ‘dying’?" by Richard Gall&lt;/a&gt;, before continuing to read. It perfectly summarizes misconceptions about software being considered "dead".&lt;/p&gt;

&lt;p&gt;Furthermore, the concept of "dead" seems to be used too often in order to coin less popular or aged software as being &lt;em&gt;not usable&lt;/em&gt; or that it  &lt;em&gt;should be avoided&lt;/em&gt; without reasoning why.  If objectively evaluated, some of the software/libraries coined as "dead" are actually far from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Famous examples in web development&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  jQuery
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;considered &lt;a href="https://2022.stateofjs.com/en-US/libraries/front-end-frameworks/" rel="noopener noreferrer"&gt;unpopular&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/jquery?activeTab=versions" rel="noopener noreferrer"&gt;active releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/jquery" rel="noopener noreferrer"&gt;&amp;gt;2m installs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;constant development since 2006(!)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftzvk4saq5p1x701b4is.png" alt="jquery commit graph" width="800" height="301"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  ember.js
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://2022.stateofjs.com/en-US/libraries/front-end-frameworks/" rel="noopener noreferrer"&gt;interest declined&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;however, well matured and &lt;a href="https://github.com/emberjs/ember.js/commits/main/" rel="noopener noreferrer"&gt;far from being dead&lt;/a&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farm7ag0cx2kxqx0xfi0r.png" alt="ember commit graph" width="800" height="306"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  less
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://2023.stateofcss.com/en-US/other-tools/" rel="noopener noreferrer"&gt;lost the battle to scss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/less" rel="noopener noreferrer"&gt;still over 4m weekly installs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/less/less.js/commits/master/" rel="noopener noreferrer"&gt;active development&lt;/a&gt;, although with pauses but nothing indicating death
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2gvse7uj4qmiqumqxj3.png" alt="less commit graph" width="800" height="300"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Interesting fact:&lt;/strong&gt; the famous state of js survey dropped the term "avoid" and replaced it by "harder to recommend currently", &lt;a href="https://2022.stateofjs.com/en-US/libraries/" rel="noopener noreferrer"&gt;when categorizing libraries into quadrants&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  When we should consider a software as "dead" 😵
&lt;/h3&gt;

&lt;p&gt;I believe, that we can validly categorize a software as "dead" or "alive", if there would be clear list of criteria for it.&lt;/p&gt;

&lt;p&gt;For this, let me bring in some context from the real life: We have various concepts of someone being dead: &lt;a href="https://en.wikipedia.org/wiki/Clinical_death" rel="noopener noreferrer"&gt;clinically dead&lt;/a&gt;, brain death, legal death, biologically dead. For clinical death, there are &lt;strong&gt;specific criteria&lt;/strong&gt; for physicians to diagnose the death of a person (without doubt). They may vary by country or even &lt;a href="https://link.springer.com/chapter/10.1007/978-3-031-15947-3_20" rel="noopener noreferrer"&gt;by state&lt;/a&gt; but in general they are very clear and unambiguous factors.&lt;/p&gt;

&lt;p&gt;Of course, there are fundamental differences between humans and software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;humans can live on their own, software always needs at least one external entity to advance any step in the lifecycle; not considering the whole AI debate here 🧐&lt;/li&gt;
&lt;li&gt;humans have a limited time for reanimation (the time from cardiac arrest to severe organ damage); software can be reanimated at any time&lt;/li&gt;
&lt;li&gt;humans who are lost cannot be restored, rebuild or reverse engineered; software can; not considering the whole trans-humanism debate here 🧐&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the above factors we can now build a very clear&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  Clinical Software Death
&lt;/h2&gt;

&lt;p&gt;A software can be considered as "dead" when the following factors are fulfilled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the software is confirmed to be "abandoned", "archived" or any other form of "active development stopped" by it's owners&lt;/li&gt;
&lt;li&gt;the software is in one of the following states:

&lt;ul&gt;
&lt;li&gt;it's non-functional (has critical bugs; memory leaks; critical performance issues)&lt;/li&gt;
&lt;li&gt;it's incomplete (missing critical features)&lt;/li&gt;
&lt;li&gt;it's vulnerable (security; dependencies)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;chances are low for any considerable effort to change these two factors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The status of being "dead" is not fixed and can change, if one or more of the above factors change for the positive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In an early version of the article I listed two projects here on the verge to death. However, they were still alive and as I know from the debate around "Meteor.js is dead" this would only create FUD. In contrast I advise you kindly to use the above list and do your check-marks on some projects you personally consider(ed) dead. &lt;/p&gt;
&lt;h3&gt;
  
  
  My 2cents on this 🪙
&lt;/h3&gt;

&lt;p&gt;I found the whole topic becoming increasingly morbid the more I dealt with all these death themes and criteria. Due to this, I even dropped some sections (e.g. about "Zombieware" as it became ridiculous 🧟‍♂️🧟‍♂️🧟‍♂️).&lt;br&gt;
I also think this whole death theme is a fundamental consequences of the negative framing of "is XYZ dead?". The question already sets a negative foundation for the whole upcoming debate, where proponents become highly defensive (often seen and sometimes practiced myself), while opponents may use invalid arguments ("nobody uses xyz today") or even become offensive. 🤬&lt;/p&gt;

&lt;p&gt;I would even go that far to say, that if such a question is deliberately used to undermine the credibility of whatever software, then it could be described as &lt;strong&gt;dead-framing&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  A change in perspective 🔭
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Instead of this, I propose&lt;/strong&gt; that everyone rethinks this terminology. We have seen, that a clinical software death is very rare (at least of you read the linked article above). A better alternative would be to ask&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How alive is software/tool/framework xyz?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the one hand, this question would not frame the involved software in a negative way and criticism on the software will not sound immediately offensive.&lt;/p&gt;

&lt;p&gt;At the same time it allows for a more distinct evaluation, because it's an open question that cannot be answered with a simple "yes, because..." or "no, because...". Let's all consider this for the next time. ❤ &lt;/p&gt;

&lt;p&gt;Finally, at this point I'd love to transit over to the Meteor.js part of the article.&lt;/p&gt;


&lt;h2&gt;
  
  
  How alive is Meteor.js? ☄️
&lt;/h2&gt;

&lt;p&gt;TL;DR: it's very alive and has never been dead (trust me, bro) but you better be reading the facts! 🛸 This part might also be interesting to you, in case you want to evaluate Meteor.js as your next fullstack development platform.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.meteor.com/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdmtgy0px4zdqn.cloudfront.net%2Fimages%2FOGs%2Foss%2Fhome.png" height="472" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.meteor.com/" rel="noopener noreferrer" class="c-link"&gt;
          Meteor Software - Build with Meteor.js, deploy on GalaxyThe full-stack JavaScript framework for real-time apps - Meteor.js
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Meteor.js is an open source platform for building Web, Mobile, and Desktop applications.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdmtgy0px4zdqn.cloudfront.net%2Ffavicon.ico%3Fv%3D2" width="48" height="48"&gt;
        meteor.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Let's get back to the initial topic that inspired me to write this article. For this I will check the current state of the Meteor.js ecosystem against the above criteria for clinical software death.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ownership and Company backing 👔
&lt;/h3&gt;

&lt;p&gt;For a project to not being abandoned there needs to be an owner that is present and reachable. For Meteor this is currently &lt;a href="https://www.meteor.com/company/about" rel="noopener noreferrer"&gt;Meteor Software&lt;/a&gt;. They took over the ownership, when &lt;a href="https://techcrunch.com/2019/10/02/tiny-acquires-meteor/?guccounter=1" rel="noopener noreferrer"&gt;Tiny Capital acquired Meteor&lt;/a&gt; from Meteor Development Group (MDG) in 2019.&lt;/p&gt;

&lt;p&gt;The company has currently 12 employees, working on the Meteor.js core, packages, documentation and the community around it. The team distributes across the globe with a great portion being located in Brazil. Furthermore, Meteor Software operates on providing cloud services around the Meteor.js platform, so they have an ongoing interest in it being continuously "alive and well".&lt;/p&gt;

&lt;h3&gt;
  
  
  Meteor.js core development ⌨️
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/meteor" rel="noopener noreferrer"&gt;
        meteor
      &lt;/a&gt; / &lt;a href="https://github.com/meteor/meteor" rel="noopener noreferrer"&gt;
        meteor
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Meteor, the JavaScript App Platform
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
  &lt;a href="https://www.meteor.com" rel="nofollow noopener noreferrer"&gt;
    
      
      
      &lt;img alt="Meteor logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F0467afb6-4f36-4cad-9d78-237150d5d881" width="300"&gt;
    
  &lt;/a&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;&lt;a href="https://app.travis-ci.com/github/meteor/meteor" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6ea56ea6de86a7e2f0626f05a31878f27acb192c8761ba7df270cb89a4155f6e/68747470733a2f2f6170692e7472617669732d63692e636f6d2f6d6574656f722f6d6574656f722e7376673f6272616e63683d646576656c" alt="Travis CI Status"&gt;&lt;/a&gt;
&lt;a href="https://app.circleci.com/pipelines/github/meteor/meteor?branch=devel" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fa859952fb4c47c4ff34c5f4b8f2aa0032c2d5d25c2c7bd5d0a62c23682ed888/68747470733a2f2f636972636c6563692e636f6d2f67682f6d6574656f722f6d6574656f722e7376673f7374796c653d737667" alt="CircleCI Status"&gt;&lt;/a&gt;
&lt;a href="https://meteor.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/502ac36966770cada78656be9b1a55ae141749a1e4046997f5eac64d6bbced84/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6574656f722d332e302e332d677265656e3f6c6f676f3d6d6574656f72266c6f676f436f6c6f723d7768697465" alt="built with Meteor"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/be44677808e18237e016dde144d9d2b18f752c697ea29e008f3484c90f6a8a39/68747470733a2f2f696d672e736869656c64732e696f2f6e6f64652f762f6d6574656f72"&gt;&lt;img src="https://camo.githubusercontent.com/be44677808e18237e016dde144d9d2b18f752c697ea29e008f3484c90f6a8a39/68747470733a2f2f696d672e736869656c64732e696f2f6e6f64652f762f6d6574656f72" alt="node-current"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9d4dbee11e2e00362434216a05186d4600c050a21f457e73928ee05864073bf9/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f31323437393733333731303430323339363736"&gt;&lt;img src="https://camo.githubusercontent.com/9d4dbee11e2e00362434216a05186d4600c050a21f457e73928ee05864073bf9/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f31323437393733333731303430323339363736" alt="Discord"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f8bcfe4f68d88a2f6aa6a4b7ba3dec0dce3ec2624a9beb2e9157ef0e2050ec36/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f6d6574656f726a733f7374796c653d736f6369616c"&gt;&lt;img src="https://camo.githubusercontent.com/f8bcfe4f68d88a2f6aa6a4b7ba3dec0dce3ec2624a9beb2e9157ef0e2050ec36/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f6d6574656f726a733f7374796c653d736f6369616c" alt="Twitter Follow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Meteor is an &lt;strong&gt;ultra-simple&lt;/strong&gt; environment for building &lt;strong&gt;modern&lt;/strong&gt; web applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.meteor.com" rel="nofollow noopener noreferrer"&gt;Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.meteor.com/about/install.html" rel="nofollow noopener noreferrer"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.meteor.com/#/full/" rel="nofollow noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📚 &lt;strong&gt;Create your applications using modern JavaScript&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Benefit from the latest technology updates to rapidly prototype and develop your applications.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;✨ &lt;strong&gt;Integrate technologies you already use&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Use popular frameworks and tools right out-of-the-box. Focus on building features instead of configuring disparate components yourself.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;💻 &lt;strong&gt;Build apps for any device&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Use the same code whether you’re developing for web, iOS, Android, or desktop for a seamless update experience for your users.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔥 Getting Started&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;How about trying a tutorial to get started with your favorite technology?&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://docs.meteor.com/tutorials/react/" rel="nofollow noopener noreferrer"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/9242244a22ff5237b5410291986a5d911e829e494f47913c7d9bd0404627d789/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f612f61372f52656163742d69636f6e2e737667"&gt; React&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://blaze-tutorial.meteor.com/" rel="nofollow noopener noreferrer"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/46ac7a7f26b6cbf6a82f9c5c8eaaf112cb81c9872da818f57682c0823814ec94/68747470733a2f2f70726f67736f66742e6e65742f696d616765732f626c617a652d6373732d69636f6e2d336538306163623339393630343761666430396631313530663533666364373865393863316531622e706e67"&gt; Blaze&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://docs.meteor.com/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.html" rel="nofollow noopener noreferrer"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/f9e0c6f8571a04d30382d14f47fc110badd1a3f136b530902ef28995ae4bd50a/68747470733a2f2f7675656a732e6f72672f696d616765732f6c6f676f2e706e67"&gt; Vue&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 Quick Start&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;On your platform, use this line:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; npx meteor&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;🚀 To create a project:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; meteor create&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;☄️ Run it:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; my-app
meteor&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🧱 Developer Resources&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Building an application with Meteor?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy on &lt;a href="https://www.meteor.com/cloud" rel="nofollow noopener noreferrer"&gt;Galaxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discuss on &lt;a href="https://forums.meteor.com/" rel="nofollow noopener noreferrer"&gt;Forums&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the Meteor Discord by clicking this &lt;a href="https://discord.gg/hZkTCaVjmT" rel="nofollow noopener noreferrer"&gt;invite&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/meteor/meteor" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The Meteor.js core platform is developed by the Meteor Software team on GitHub. There is a &lt;a href="https://github.com/meteor/meteor/commits/devel/" rel="noopener noreferrer"&gt;constant stream of commits&lt;/a&gt; since it's initial work in Nov. 2011. Also, contributions never really stopped, as you can also see in the following &lt;a href="https://github.com/meteor/meteor/graphs/contributors" rel="noopener noreferrer"&gt;contribution graph&lt;br&gt;
from the GitHub repository&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkfklypvpnd36mrwgd8y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkfklypvpnd36mrwgd8y.png" alt="contributions graph of https://github.com/meteor/meteor" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrations with popular tools and libraries 🔨
&lt;/h3&gt;

&lt;p&gt;Meteor officially supports various popular frontends and technologies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React (default)&lt;/li&gt;
&lt;li&gt;Blaze (Meteor's original frontend)&lt;/li&gt;
&lt;li&gt;Vue 3 + vite&lt;/li&gt;
&lt;li&gt;Vue 2&lt;/li&gt;
&lt;li&gt;Svelte&lt;/li&gt;
&lt;li&gt;Solid&lt;/li&gt;
&lt;li&gt;Chakra-UI&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind&lt;/li&gt;
&lt;li&gt;Apollo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Official support means, that there are ongoing commits in the repo to ensure these integrations into the stack and their documentation are up-to-date.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;meteor create&lt;/code&gt; with options from the above list and try out a new project and see for yourself! You can also find the source for these commands in the respective &lt;a href="https://github.com/meteor/meteor/tree/devel/tools/static-assets#isobuild-static-assets" rel="noopener noreferrer"&gt;skeleton projects&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Meteor.js package ecosystem and package development 📦
&lt;/h4&gt;

&lt;p&gt;Meteor.js is built on top of Node.js and is fully compatible to packages you can find in the NPM registry. Some packages of Meteor core &lt;a href="https://www.npmjs.com/package/meteor-node-stubs" rel="noopener noreferrer"&gt;are also NPM packages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, during early days of Meteor.js an own package ecosystem emerged in order to provide zero-config integration with Meteor, MongoDb, Tracker, compilers and many more from the platform.&lt;/p&gt;

&lt;p&gt;There are 14,646 packages available for Meteor.js as of today. Packages get constantly published by users and there is also &lt;a href="https://dev.tourl"&gt;Meteor-Community-Packages (MCP)&lt;/a&gt;, a GitHub org under which very popular / widespread packages are kept maintained or improved with new features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Community Activity 🤝
&lt;/h3&gt;

&lt;p&gt;Meteor has an active community of developers, individual as well as institutional. &lt;/p&gt;

&lt;p&gt;Communication heavily resides around the &lt;a href="https://forums.meteor.com/" rel="noopener noreferrer"&gt;Meteor forums&lt;/a&gt; which contains a stream of topics up until back to 2014/2015 (31.3k topics with 217K posts overall as of today). Some members from that time are still active there as well.&lt;/p&gt;

&lt;p&gt;Additionally there are active &lt;a href="https://meteor-community.slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; and &lt;a href="https://discord.gg/9GDmECsYdH" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; spaces as well. Many of the packages beyond Meteor-Community-Packages are actually developed and published by members of the Meteor.js community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion ✅
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;the software is confirmed to be "abandoned", "archived" or any other form of "active development stopped" by it's owners&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not at any time, as you can see in the repository and its commit history.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;the software is in one of the following states:

&lt;ul&gt;
&lt;li&gt;it's non-functional (has critical bugs; memory leaks; critical performance issues)&lt;/li&gt;
&lt;li&gt;it's incomplete (missing critical features)&lt;/li&gt;
&lt;li&gt;it's vulnerable (security; dependencies)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;A project of this size and in constant development will always contain one or more of the above as it hardly becomes "feature complete" as opposed to packages or small libraries. For Meteor.js you can see 222 open issues as of today, which reside around these topics. Most of them are related to "it's incomplete" as Meteor currently does a transition to a new Major version.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;chances are low for any considerable effort to change these two factors&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;If development would stop or be dropped by Meteor Software chances are very high that the community will jump in and continue development.&lt;br&gt;
Additionally, there are various companies running a business on top Meteor apps, which will make it more likely ongoing development is ensured. &lt;/p&gt;




&lt;h2&gt;
  
  
  Disclaimer 🧐
&lt;/h2&gt;

&lt;p&gt;I have to state a disclaimer here for those among y'all, who are already throwing the "full circle" argument at me. 🍅&lt;br&gt;
Yes, I created the definition myself and applied it to the Meteor.js framework and it's all working out positive. However, I tried to be as objective as possible when creating criteria for a definition. I think I came up with one, you could also apply to any other project. And yes, the majority of evaluated software would not be considered dead, according to this criteria. That's the whole point.&lt;/p&gt;

&lt;p&gt;Anyway, I'm also open for improvements of the criteria as my final goal is to either rationalize the term "dead" when speaking about software or even getting rid of it at all! In the end it's all about tools getting the job well done for their purpose. Personal opinions and preferences should be secondary to this.&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles here on dev.to about &lt;strong&gt;Meteor.js&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt;. Recently I also co-host the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly Meteor.js community podcast&lt;/a&gt;, which contains the newest from Meteor.js and the community.&lt;/p&gt;

&lt;p&gt;You can also find (and contact) me on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you are reading and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt;, &lt;a href="https://paypal.me/kuesterjan" rel="noopener noreferrer"&gt;send me a tip via PayPal&lt;/a&gt; or sponsor me a book form &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>meteor</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>community</category>
    </item>
    <item>
      <title>Gradually upgrading a Meteor.js project to 3.0</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Fri, 15 Dec 2023 12:28:47 +0000</pubDate>
      <link>https://forem.com/meteor/gradually-upgrading-a-meteorjs-project-to-30-5aj0</link>
      <guid>https://forem.com/meteor/gradually-upgrading-a-meteorjs-project-to-30-5aj0</guid>
      <description>&lt;p&gt;TL;DR - In this first part of the Migration series we will start upgrading an existing Meteor.js project to 3.0 (alpha). We will be using a determined strategy that should apply to &lt;strong&gt;any existing Meteor.js project&lt;/strong&gt;. Focus is fixing dependency conflicts during the update process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow-up articles will show how to fix dependencies (packages and build-tools) by example 💪&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
What is Meteor.js 3.0 and why should I care? ☄️

&lt;ul&gt;
&lt;li&gt;Meteor 3.0 is still in Alpha, why doing this now? 🧐&lt;/li&gt;
&lt;li&gt;Biggest challenges while migrating to 3.0 🏗️&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
A Generic Migration strategy 🧩

&lt;ul&gt;
&lt;li&gt;Part 1 - Preparations 📝&lt;/li&gt;
&lt;li&gt;Part 2 - updating to 3.0 and dependency triage 🔬&lt;/li&gt;
&lt;li&gt;Part 3 - Fixing a dependency 🔥&lt;/li&gt;
&lt;li&gt;Part 4 - migrate your project code ⌨️&lt;/li&gt;
&lt;li&gt;Step 5 - deployment 🚀&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Summary and Outlook 🔭&lt;/li&gt;
&lt;li&gt;About me 👋&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Meteor.js 3.0 and why should I care? ☄️
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you already know about Meteor's next major version then you can safely skip this section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meteor.js 3.0 is an upcoming major release. The platform  is moving away from &lt;a href="https://www.npmjs.com/package/fibers"&gt;Fibers&lt;/a&gt; (coroutines) in favor of Node's native async-await implementation. The reason behind this step is, that Fibers are not compatible anymore with Node (add links here), beginning with version 16. Due to this circumstance, Meteor is stuck in at Node 14 and this brings lots of implications (security, maintenance, being up to date etc.) for any developer and company.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/meteor"&gt;
        meteor
      &lt;/a&gt; / &lt;a href="https://github.com/meteor/meteor"&gt;
        meteor
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Meteor, the JavaScript App Platform
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
  &lt;a href="https://www.meteor.com" rel="nofollow"&gt;
    &lt;img width="225" src="https://res.cloudinary.com/practicaldev/image/fetch/s--k93DnTH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/841294/26841702-0902bbee-4af3-11e7-9805-0618da66a246.png"&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;
&lt;p&gt;&lt;a href="https://app.travis-ci.com/github/meteor/meteor" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/40c2db7ea374af02b73a6786b7da9bc18b9d54390d9f6b573d2659c278266478/68747470733a2f2f6170692e7472617669732d63692e636f6d2f6d6574656f722f6d6574656f722e7376673f6272616e63683d646576656c" alt="Travis CI Status"&gt;&lt;/a&gt;
&lt;a href="https://app.circleci.com/pipelines/github/meteor/meteor?branch=devel" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/8c88f8c2014d99e14e15250f87f17a924b34ebb82986ad052591751bd8e0d71e/68747470733a2f2f636972636c6563692e636f6d2f67682f6d6574656f722f6d6574656f722e7376673f7374796c653d737667" alt="CircleCI Status"&gt;&lt;/a&gt;
&lt;a href="https://meteor.com" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c24989ea65ee8bfe6e979453147a940b6fb5628a5fb5778bc7d749adf5eabe4f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6574656f722d322e31362d677265656e3f6c6f676f3d6d6574656f72266c6f676f436f6c6f723d7768697465" alt="built with Meteor"&gt;&lt;/a&gt;
&lt;a href="https://guide.meteor.com/3.0-migration" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/049c3e16626d4ce87f6aa7d2d19b246ef63fa037ff4c3d981a0e9d613c504d6e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6574656f722d332e305f52432d79656c6c6f773f6c6f676f3d6d6574656f72266c6f676f436f6c6f723d7768697465" alt="built with Meteor"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Meteor is an &lt;strong&gt;ultra-simple&lt;/strong&gt; environment for building &lt;strong&gt;modern&lt;/strong&gt; web applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.meteor.com" rel="nofollow"&gt;Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meteor.com/developers/install" rel="nofollow"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.meteor.com/#/full/" rel="nofollow"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📚 &lt;strong&gt;Create your applications using modern JavaScript&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Benefit from the latest technology updates to rapidly prototype and develop your applications.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;✨ &lt;strong&gt;Integrate technologies you already use&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Use popular frameworks and tools right out-of-the-box. Focus on building features instead of configuring disparate components yourself.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;💻 &lt;strong&gt;Build apps for any device&lt;/strong&gt;
&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Use the same code whether you’re developing for web, iOS, Android, or desktop for a seamless update experience for your users.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔥 Getting Started&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;How about trying a tutorial to get started with your favorite technology?&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://react-tutorial.meteor.com/" rel="nofollow"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/2caafe79e106ad099d02faea3fc3b5a5bc403fff8fc67727e13e1097c741fba6/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f612f61372f52656163742d69636f6e2e737667"&gt; React&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://blaze-tutorial.meteor.com/" rel="nofollow"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/9752c4c9279a42b5547848fb3f55c39f4a11e825c0669ca7c22eb90dd5c2ab22/68747470733a2f2f70726f67736f66742e6e65742f696d616765732f626c617a652d6373732d69636f6e2d336538306163623339393630343761666430396631313530663533666364373865393863316531622e706e67"&gt; Blaze&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://vue-tutorial.meteor.com/" rel="nofollow"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/0b17e5a01574a2c1251b51c910c422f6ca6cb968a52686a770b668a634792c09/68747470733a2f2f7675656a732e6f72672f696d616765732f6c6f676f2e706e67"&gt; Vue&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://svelte-tutorial.meteor.com/" rel="nofollow"&gt;&lt;img width="25" src="https://camo.githubusercontent.com/711a1e0a52330c863a86a9e43a3e2f2f04a410fbca729b089a8498d02324c061/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f7468756d622f312f31622f5376656c74655f4c6f676f2e7376672f3132303070782d5376656c74655f4c6f676f2e7376672e706e67"&gt; Svelte&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Next, read the &lt;a href="https://docs.meteor.com/" rel="nofollow"&gt;documentation&lt;/a&gt; and get some &lt;a href="https://github.com/meteor/examples"&gt;examples&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 Quick Start&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;On your platform, use this line:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; npm install -g meteor&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;🚀 To create a project:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; meteor create my-app&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;☄️ Run it:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; my-app
meteor&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🧱 Developer Resources&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Building an application with Meteor?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/meteor/meteor"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;However, this transition to async-await comes with a massive impact on the very core of Meteor's execution model: In the past you were able to write sync-style code for async operations, for example a MongoDB-Collection fetch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MyCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This MongoDB operation has always been async in nature, but Meteor used Fibers to keep track of thing in a way, that the above code resolved without the need of Promises or async/await. In 3.0 operations like these will be replaced by they native Promise implementations and therefore require to be awaited:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;MyCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;This implies a necessary upgrade for &lt;strong&gt;every&lt;/strong&gt; Meteor.js project out there.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  Meteor 3.0 is still in Alpha, why doing this now? 🧐
&lt;/h3&gt;

&lt;p&gt;Depending on the size of your project and your team being able to work on such an upgrade you will face challenges on a varying scale and this article should help you to get a first glimpse on the upcoming effort.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This will also help you to make a much better estimation with regards to the complexity and effort of the upcoming migration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The API of the Alpha versions is 99% stable and most development resides around details or bugs. You can therefore safely start upgrading and refactoring, while you later will only have to switch from Alpha to the stable release.&lt;/p&gt;

&lt;p&gt;I have already compiled some steps for preparing your existing projects that won't require an update to 3.0 yet:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jankapunkt" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F157294%2F1c3c63fd-d0d4-4db4-97ca-2a3d825ece7c.jpeg" alt="jankapunkt"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Prepare your Meteor.js project for the big 3.0 release!&lt;/h2&gt;
      &lt;h3&gt;Jan Küster ・ Nov 1 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#meteor&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;






&lt;h3&gt;
  
  
  Biggest challenges while migrating to 3.0 🏗️
&lt;/h3&gt;

&lt;p&gt;From my current efforts I could compile a short but fundamental list of challenges and their manageability:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Manageability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Direct dependency conflicts&lt;/td&gt;
&lt;td&gt;Easily manageable for your own packages; requires manual fixing for third-party packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Indirect dependencies version conflicts&lt;/td&gt;
&lt;td&gt;Will definitely require manual cloning and fixing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build tools&lt;/td&gt;
&lt;td&gt;Will depend on the complexity of the plugin; will require manual fixing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breaking Isomorphism&lt;/td&gt;
&lt;td&gt;Manageable with big efforts; may require major architectural changes, depending on how much your projects rely on isomorphic code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rewrite code to use async/await&lt;/td&gt;
&lt;td&gt;Involves effort but manageable; read the &lt;a href="https://guide.meteor.com/prepare-meteor-3.0"&gt;official docs&lt;/a&gt; and &lt;a href="https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf"&gt;my article&lt;/a&gt; on this topic for more info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Managed for you, when using Galaxy; effort when doing custom deployment; for example with MUP; involves finding correct images, may require to build custom images&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As you can see the actual change in your project's code is just a small portion of the overall challenges. This is why this article series tries to deal with all the other parts of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Generic Migration strategy 🧩
&lt;/h2&gt;

&lt;p&gt;My goal for this article was to provide a generic strategy that any of us could apply on their existing projects. Of course, the effort and complexity will vary but the overall procedure should work for anyone. A good approach is to start with a smaller project to get used to the process. Once you succeeded there you will already know most of the intricacies in order to be ready for your larger projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This strategy involves using git and assumes to commit often in order to being able to revert to the state of the last step/substep. This will apply for the project, as well as for cloned dependencies. The following example shows how to hard-revert to a certain commit hash:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# show the full list of commits on this branch&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git log
 commit 126669f9b29f9172bea4ebaf0599670bd4493f03 &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; migrate-3.0, origin/master, origin/HEAD&lt;span class="o"&gt;)&lt;/span&gt;
Merge: a8ac876 7dc118a
Author: Jan Küster &amp;lt;info@jankuester.com&amp;gt;
Date:   Mon Nov 28 16:37:38 2023 +0200

    migration&lt;span class="o"&gt;(&lt;/span&gt;1.2&lt;span class="o"&gt;)&lt;/span&gt;: prepared project structure
&lt;span class="c"&gt;# reset to the latest commit, omit anything newer than this commit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; 126669f9b29f9172bea4ebaf0599670bd4493f03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The following is a high-level overview of the strategy&lt;/p&gt;


&lt;h3&gt;
  
  
  Part 1 - Preparations 📝
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1.1 Work on an exclusive branch
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;commit any remaining changes&lt;/li&gt;
&lt;li&gt;checkout your most recent development branch&lt;/li&gt;
&lt;li&gt;checkout a new branch, exclusively to migration towards 3.0: &lt;code&gt;git checkout -b migrate-3.0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  1.2 Prepare project structure for fixing dependency conflicts
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;if project is in top-level

&lt;ul&gt;
&lt;li&gt;create a new folder &lt;code&gt;src&lt;/code&gt; and move project files in this folder&lt;/li&gt;
&lt;li&gt;keep all Git-related files in top-level&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;create a &lt;code&gt;lib&lt;/code&gt; folder in top-level (next to &lt;code&gt;src&lt;/code&gt;) where all package-repos will be cloned, that may contain multiple packages or nested structures (see &lt;a href="https://github.com/Meteor-Community-Packages/meteor-autoform-themes"&gt;this example&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;create &lt;code&gt;packages&lt;/code&gt; folder for package-repos that have a direct structure (see this &lt;a href="https://github.com/Meteor-Community-Packages/meteor-link-accounts"&gt;example&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;commit your state&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  1.3 Upgrade to latest 2.x
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;run &lt;code&gt;meteor update --release=2.13.3&lt;/code&gt; (or newer; see the &lt;a href="https://github.com/meteor/meteor/blob/devel/docs/history.md"&gt;changelog&lt;/a&gt;) &lt;/li&gt;
&lt;li&gt;if this causes issues you have to fix them first (out of scope; see the migration notes on the specific versions in the docs)&lt;/li&gt;
&lt;li&gt;commit your state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Optional:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;place `jkuester:migration-helper (after all core-packages, but before all non-core packages)&lt;/li&gt;
&lt;li&gt;flag all non-core packages as &lt;code&gt;# fixme:async&lt;/code&gt; that use sync mongo/cursor calls or create sync style methods/publications&lt;/li&gt;
&lt;li&gt;more at my previous article &lt;a href="https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf"&gt;Prepare your Meteor.js project for the big 3.0 release!&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  1.4 Prepare next update to 3.0
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Clear any caches of package version

&lt;ul&gt;
&lt;li&gt;clear all entries .meteor/versions file&lt;/li&gt;
&lt;li&gt;remove .meteor/local folder&lt;/li&gt;
&lt;li&gt;comment all packages, expect &lt;a href="https://github.com/meteor/meteor/tree/devel/packages"&gt;core packages&lt;/a&gt; in .meteor/packages&lt;/li&gt;
&lt;li&gt;remove @ suffix from all entries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;commit your state
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Part 2 - updating to 3.0 and dependency triage 🔬
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Important note&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;During this phase you will often have to run/rerun the app in order to see which dependencies cause a conflict. However, since many of your packages are commented you should not expect the app to successfully start. You will likely then encounter other errors. But that's not the point of this step, it's only about resolving dependency conflicts.&lt;/p&gt;
&lt;h4&gt;
  
  
  2.1 Update only with core packages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;run update &lt;code&gt;meteor update --release=3.0-alpha.19&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;all core packages should have upgraded successfully and there should be no error message, involving version conflicts (other errors are to be expected, though)&lt;/li&gt;
&lt;li&gt;if there is a version conflict, then

&lt;ul&gt;
&lt;li&gt;make sure the packages left uncommented in this step were only &lt;a href="https://github.com/meteor/meteor/tree/devel/packages"&gt;core packages&lt;/a&gt;!&lt;/li&gt;
&lt;li&gt;comment all core-packages that are involved with the conflict and flag them as &lt;code&gt;fixme:core&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;if you are &lt;strong&gt;100% sure&lt;/strong&gt; this is a core-package problem then open an issue in the &lt;a href="https://github.com/meteor/meteor"&gt;Meteor GitHub repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;best would be to also add the issue link to the comment flag: &lt;code&gt;fixme:core (link to issue)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;if you are &amp;lt; 100% sure then reach out to the community on the Forums, Slack or Discord
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;else commit your state&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  2.2 Uncomment non-core packages stepwise (loop)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;uncomment a single &lt;strong&gt;non-flagged&lt;/strong&gt; non-core package &lt;/li&gt;
&lt;li&gt;try to rerun the app&lt;/li&gt;
&lt;li&gt;if this package cause a version conflict

&lt;ul&gt;
&lt;li&gt;comment and flag as &lt;code&gt;fixme:deps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Proceed with step 3 for this package&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;else commit your state&lt;/li&gt;
&lt;li&gt;if there are commented packages left in &lt;code&gt;.meteor/packages&lt;/code&gt; then proceed with Step 2.2&lt;/li&gt;
&lt;li&gt;else proceed to step 4&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Part 3 - Fixing a dependency 🔥
&lt;/h3&gt;

&lt;p&gt;This step has the scope of a single dependency (package or build tool).&lt;/p&gt;
&lt;h4&gt;
  
  
  3.1. Determine the package's location
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;if it's one of your local packages then keep it in it's place&lt;/li&gt;
&lt;li&gt;else look up the package name on &lt;a href="https://packosphere.com/"&gt;Packosphere&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;look for the GitHub link on the package's page and navigate to the repository

&lt;ul&gt;
&lt;li&gt;if this package contains no GitHub repository link then you need to contact the owner to open this package or provide an upgrade, you may also seek help in the community&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;fork the repository&lt;/li&gt;
&lt;li&gt;check the folder structure and determine, whether to place it in &lt;code&gt;packages&lt;/code&gt; (proceed with 3.2) or in &lt;code&gt;lib&lt;/code&gt; (proceed with 3.3)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3.2. Clone the package into &lt;code&gt;packages&lt;/code&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;select the green button in the repo (labelled "code") and select the repo link (favorably ssh) &lt;/li&gt;
&lt;li&gt;cd into the &lt;code&gt;packages&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;git clone &amp;lt;link&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cd into the new cloned package&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;git checkout -b migrate-3.0&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;if you get &lt;code&gt;fatal: A branch named 'migrate-3.0' already exists.&lt;/code&gt; then you are still in the &lt;code&gt;packages&lt;/code&gt; folder!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3.3. Clone the package into &lt;code&gt;lib&lt;/code&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;select the green button in the repo (labelled "code") and select the repo link (favorably ssh)&lt;/li&gt;
&lt;li&gt;cd into the &lt;code&gt;lib&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;git clone &amp;lt;link&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cd into the new cloned package&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;git checkout -b migrate-3.0&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;if you get &lt;code&gt;fatal: A branch named 'migrate-3.0' already exists.&lt;/code&gt; then you are still in the top-level folder!&lt;/li&gt;
&lt;li&gt;now you need to link the content of this package (or a subdirectory of the package) with your &lt;code&gt;packages&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;cd back into &lt;code&gt;src/packages&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;link using &lt;code&gt;ln -sf ../../lib/&amp;lt;repo-name&amp;gt;/path/to/package&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3.3 Fixing the package versions
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;open &lt;code&gt;package.js&lt;/code&gt; in your editor/IDE&lt;/li&gt;
&lt;li&gt;fix &lt;code&gt;api.versionsFrom&lt;/code&gt;, it should look something like &lt;code&gt;api.versionsFrom(['2.8', '3.0-alpha.19'])&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;search for further dependencies that caused confwhat-is-meteorjs-30-and-why-should-i-care-licts&lt;/li&gt;
&lt;li&gt;fix their required dependency version, for example
&lt;code&gt;api.use('minifier-css@1.6.0 || 2.0.0-alpha300.19')&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;continue with all dependencies in this file until no conflicts occur&lt;/li&gt;
&lt;li&gt;commit this state in the package&lt;/li&gt;
&lt;li&gt;commit this state in your project&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3.4 Fixing the package's code to use async-await
&lt;/h4&gt;

&lt;p&gt;It might be a good opportunity to review the package's code for usage of being involved in any MongoDB Collection or Cursor activity or any imports of &lt;code&gt;'fibers/future'&lt;/code&gt; or &lt;code&gt;Promise.await&lt;/code&gt; inside the package's code. If any of them is the case then you will have to migrate this code as well, in order to have the package function properly.&lt;/p&gt;

&lt;p&gt;It might be exhausting to realize that the package you used for years and which ran fine now of sudden needs thewhat-is-meteorjs-30-and-why-should-i-care-se big changes. You have basically three options now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A) find a replacement (causing your project code to be rewritten to the new package's api)&lt;/li&gt;
&lt;li&gt;B) contact the package maintainer (can take from a day to infinity to be fixed)&lt;/li&gt;
&lt;li&gt;C) do it on your own (involves effort but you are in control of the situation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step assumes you go with option C and the follow-up articles will deal with this situation in detail.&lt;/p&gt;
&lt;h4&gt;
  
  
  3.5 post-fix cleanup
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;you may have to update the package's tests as well&lt;/li&gt;
&lt;li&gt;document and commit your changes&lt;/li&gt;
&lt;li&gt;push to your fork &lt;code&gt;git push origin migrate-3.0&lt;/code&gt; while being in the package repository folder&lt;/li&gt;
&lt;li&gt;If you want to make this fix available for others then I encourage you to open a pull request&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Part 4 - migrate your project code ⌨️
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;At this step you should have resolved all dependency conflicts and committed the current state. &lt;br&gt;
If not, make sure this is the case, in order to being able of reverting to the state of this commit at any time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the time where your own project code is to be migrated. In an optimal world you may have done this already when being on Meteor 2.x. If you didn't, worry not! 🧙‍♂️&lt;/p&gt;

&lt;p&gt;Here are a few important resources, that might help you to advance in your own code migration:&lt;/p&gt;
&lt;h4&gt;
  
  
  Guides on upgrading to async/await
&lt;/h4&gt;

&lt;p&gt;The following link should help you with the majority of issues when migrating your projects.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://guide.meteor.com/prepare-meteor-3.0" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--BOFWQAJ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://guide.meteor.com/images/meteor-logo.png" height="84" class="m-0" width="280"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://guide.meteor.com/prepare-meteor-3.0" rel="noopener noreferrer" class="c-link"&gt;
          How to migrate to Meteor Async in Meteor 2.x | Meteor Guide
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          How to migrate your application to async methods and be ready to 3.0.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--MNCwQwo_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://guide.meteor.com/images/favicon-meteor.png" width="64" height="65"&gt;
        guide.meteor.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://guide.meteor.com/3.0-migration" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--BOFWQAJ7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://guide.meteor.com/images/meteor-logo.png" height="84" class="m-0" width="280"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://guide.meteor.com/3.0-migration" rel="noopener noreferrer" class="c-link"&gt;
          Migrating to Meteor 3.0 | Meteor Guide
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          How to migrate your application to Meteor 3.0.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--MNCwQwo_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://guide.meteor.com/images/favicon-meteor.png" width="64" height="65"&gt;
        guide.meteor.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="/jankapunkt" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F157294%2F1c3c63fd-d0d4-4db4-97ca-2a3d825ece7c.jpeg" alt="jankapunkt"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Prepare your Meteor.js project for the big 3.0 release!&lt;/h2&gt;
      &lt;h3&gt;Jan Küster ・ Nov 1 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#meteor&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/meteor/meteor/blob/d5c3b2eeafd0ad78ee7e2553f3f269c5c2a2e2a9/docs/generators/changelog/versions/3.0.md"&gt;List of all Methods that needs to be updated (Meteor core and core packages)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.meteor.com/"&gt;Meteor API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guide.meteor.com/"&gt;Meteor Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Help and support for your upgrade process
&lt;/h4&gt;

&lt;p&gt;Engage with the Meteor community if you're getting stuck or if you want to share your migration success or if you simply want to get in touch with fellow Meteor developers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://forums.meteor.com/"&gt;Meteor forums&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://meteor-community.slack.com/"&gt;Meteor Community Slack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/9GDmECsYdH"&gt;Meteor Community Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Background topics
&lt;/h4&gt;

&lt;p&gt;If you are interested in the more technical reasons behind this whole migration topic when you might want to check out these links.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/meteor/meteor/discussions/11505"&gt;Original discussion about how Meteor should execute native async/await&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Part 5 - deployment 🚀
&lt;/h3&gt;

&lt;p&gt;You are finished with your migration and your project runs fine, including all tests. That's awesome! 💪&lt;br&gt;
However, now it's time to get this up and running on the target infrastructure&lt;/p&gt;

&lt;p&gt;Deploying a Meteor app can be done in multiple ways but you have to be aware that your deployment environment is 3.0 ready!&lt;/p&gt;
&lt;h4&gt;
  
  
  Hosting on Galaxy
&lt;/h4&gt;

&lt;p&gt;First of all, there is &lt;a href="https://www.meteor.com/cloud?utm_source=jkuester&amp;amp;utm_medium=online&amp;amp;utm_campaign=Q2-2022-Ambassadors"&gt;Meteor Cloud, the official hosting platform (formerly known as Galaxy)&lt;/a&gt; by Meteor Software, the company backing the Meteor.js ecosystem and core contributors to the Meteor.js open source repository. &lt;/p&gt;

&lt;p&gt;With the Meteor Cloud managed services &lt;strong&gt;you will already face a Meteor 3.0-enabled environment&lt;/strong&gt; and you can easily deploy your app in one command. You can try their services with the &lt;a href="https://www.meteor.com/cloud/pricing?utm_source=jkuester&amp;amp;utm_medium=online&amp;amp;utm_campaign=Q2-2022-Ambassadors"&gt;free plan&lt;/a&gt;, perfectly suitable for demos, MVP or proof of concepts.&lt;/p&gt;

&lt;p&gt;Deployment to Galaxy is a no-brainer, either via a single cli-command or automated via push-to-deploy. You can read it all up in the deployment guide:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://galaxy-guide.meteor.com/deploy-to-galaxy" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--zWoFnd_s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://galaxy-guide.meteor.com/images/push-to-deploy-override-meteor-settings.png" height="469" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://galaxy-guide.meteor.com/deploy-to-galaxy" rel="noopener noreferrer" class="c-link"&gt;
          Deploy to Galaxy | Galaxy Docs
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to deploy on Galaxy by setting up Push to Deploy. The easiest and fastest way of deploying your Meteor app.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--E-yzJsZU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://galaxy-guide.meteor.com/images/favicon-meteor.png" width="64" height="65"&gt;
        galaxy-guide.meteor.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h4&gt;
  
  
  Custom Hosting using Meteor-Up (MUP)
&lt;/h4&gt;

&lt;p&gt;For those who need alternatives, for example hosting on-premise, there is a great tool out there that simplifies the deployment process a lot: Meteor-Up&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://meteor-up.com/" rel="noopener noreferrer"&gt;
      meteor-up.com
    &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/zodern"&gt;
        zodern
      &lt;/a&gt; / &lt;a href="https://github.com/zodern/meteor-up"&gt;
        meteor-up
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Production Quality Meteor Deployment to Anywhere
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Meteor Up &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/57f359b4e7b8c86be8cd96124e9ca5f3115d1cc017b1128b0b4d9dd85cc41f23/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f7a6f6465726e2f6d6574656f722d75702f546573742f6d61737465723f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/57f359b4e7b8c86be8cd96124e9ca5f3115d1cc017b1128b0b4d9dd85cc41f23/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f7a6f6465726e2f6d6574656f722d75702f546573742f6d61737465723f7374796c653d666c61742d737175617265" alt="GitHub Workflow Status (branch)"&gt;&lt;/a&gt; &lt;a href="https://gitter.im/meteor-up/Lobby" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/7d16ef520f9520ca575c6a3a71b92788837a14ca74addf05413f68032abb4233/68747470733a2f2f696d672e736869656c64732e696f2f6769747465722f726f6f6d2f6d6574656f722d75702f4c6f6262792e7376673f7374796c653d666c61742d737175617265" alt="Gitter"&gt;&lt;/a&gt; &lt;a href="https://github.com/zodern/meteor-up#backers"&gt;&lt;img src="https://camo.githubusercontent.com/63f008f713996ea776b86883cf220c58cc30416a0b3175d114333152debb986f/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f6d6574656f722d75702f6261636b6572732f62616467652e737667" alt="Backers on Open Collective"&gt;&lt;/a&gt; &lt;a href="https://github.com/zodern/meteor-up#sponsors"&gt;&lt;img src="https://camo.githubusercontent.com/a0210308b48da810be9af9a28bbb9b0827d2c5d43b9fdb08d85ef174495664c2/68747470733a2f2f6f70656e636f6c6c6563746976652e636f6d2f6d6574656f722d75702f73706f6e736f72732f62616467652e737667" alt="Sponsors on Open Collective"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Production Quality Meteor Deployments&lt;/h4&gt;
&lt;/div&gt;
&lt;p&gt;Meteor Up is a command line tool that allows you to deploy any &lt;a href="http://meteor.com" rel="nofollow"&gt;Meteor&lt;/a&gt; app to your own server.&lt;/p&gt;
&lt;p&gt;You can install and use Meteor Up on Linux, Mac and Windows. It can deploy to servers running Ubuntu 14 or newer.&lt;/p&gt;
&lt;p&gt;This version of Meteor Up is powered by &lt;a href="http://www.docker.com/" rel="nofollow"&gt;Docker&lt;/a&gt;, making deployment easy to manage and reducing server specific errors.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="http://meteor-up.com/getting-started.html" rel="nofollow"&gt;getting started tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Features&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Single command server setup&lt;/li&gt;
&lt;li&gt;Single command deployment&lt;/li&gt;
&lt;li&gt;Deploy to multiple servers, with optional load balancing and sticky sessions&lt;/li&gt;
&lt;li&gt;Environment Variable management&lt;/li&gt;
&lt;li&gt;Support for &lt;a href="http://docs.meteor.com/#meteor_settings" rel="nofollow"&gt;&lt;code&gt;settings.json&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Password or Private Key (pem) based server authentication&lt;/li&gt;
&lt;li&gt;Access logs from the terminal (supports log tailing)&lt;/li&gt;
&lt;li&gt;Support for custom docker images&lt;/li&gt;
&lt;li&gt;Support for Let's Encrypt and custom SSL certificates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://github.com/zodern/meteor-upROADMAP.md"&gt;Roadmap&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Server Configuration&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Auto-restart if the app crashes&lt;/li&gt;
&lt;li&gt;Auto-start after server reboot&lt;/li&gt;
&lt;li&gt;Runs with docker for better security and isolation&lt;/li&gt;
&lt;li&gt;Reverts to the…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/zodern/meteor-up"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;With this option you are entirely free to choose where your Meteor apps will run. Of course, this also brings the duties to manage the entire infrastructure on your own.&lt;/p&gt;

&lt;p&gt;Furthermore, you will have to get deeper into Docker and Images in order to be able to successfully deploy your 3.0 app using MUP.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary and Outlook 🔭
&lt;/h2&gt;

&lt;p&gt;With this article you were given methods and resources to migrate your app towards Meteor 3.0 with a big focus on resolving dependencies.&lt;/p&gt;

&lt;p&gt;If you have any questions or trouble with any step of this process, please don't hesitate to share it in the comments or get in contact with the community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://forums.meteor.com/"&gt;Meteor forums&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://meteor-community.slack.com/"&gt;Meteor Community Slack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/9GDmECsYdH"&gt;Meteor Community Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next article I will demonstrate the migration process from 2.x to 3.0 with an example app that runs into some dependency conflicts. Together we will solve these conflicts, fix up the code and make the app run again but on the latest 3.0 alpha version.&lt;/p&gt;




&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles here on dev.to about &lt;strong&gt;Meteor.js&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt;. Recently I also co-host the &lt;a href="https://www.youtube.com/@meteorjscommunity"&gt;weekly Meteor.js community podcast&lt;/a&gt;, which contains the newest from Meteor.js and the community.&lt;/p&gt;

&lt;p&gt;You can also find (and contact) me on &lt;a href="https://github.com/jankapunkt/"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you are reading and want to support me, you can &lt;a href="https://github.com/jankapunkt"&gt;sponsor me on GitHub&lt;/a&gt;, &lt;a href="https://paypal.me/kuesterjan"&gt;send me a tip via PayPal&lt;/a&gt; or sponsor me a book form &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>meteor</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Prepare your Meteor.js project for the big 3.0 release!</title>
      <dc:creator>Jan Küster 🔥</dc:creator>
      <pubDate>Wed, 01 Nov 2023 11:04:21 +0000</pubDate>
      <link>https://forem.com/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf</link>
      <guid>https://forem.com/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf</guid>
      <description>&lt;p&gt;Meteor.js 3.0 is an upcoming major release with lots of breaking changes. The platform is moving away from Fibers to use Node's native async await implementation.&lt;/p&gt;

&lt;p&gt;In this article I'm going to show you a few techniques to detect code that requires changes in order to be ready for this upcoming Meteor.js 3.0 release. 🧙‍♂️&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;small&gt;Photo by &lt;a href="https://unsplash.com/@syinq?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Susan Q Yin&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/people-sitting-on-white-concrete-stairs-Ctaj_HCqW84?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note, that this will not be a complete or perfect analysis, because you may end up with some false-positives or missing pieces. Consider it as a helping hand to aid the start for the transition. 🤝&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Another primary goal&lt;/strong&gt; is to be non-intrusive to your current application. This means, there should be no breaking effect to your current application by implementing the techniques, presented in this article.&lt;/p&gt;

&lt;p&gt;This is important, because any breaking effect (server crashes etc.) creates frustration and decreases the chance of people finally starting to migrate now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background / why this is important? ☝
&lt;/h2&gt;

&lt;p&gt;If you know all about Meteor.js 3.0 then you can safely skip this section.&lt;/p&gt;

&lt;p&gt;Meteor.js is around for &lt;a href="https://meteor10.sachagreif.com/" rel="noopener noreferrer"&gt;over 10 years&lt;/a&gt; now, and it's core API-structure did not change significantly. I previously showed this in another &lt;a href="https://dev.to/jankapunkt/reviving-an-ancient-meteorjs-project-in-10-minutes-30hl"&gt;article on migrating an ancient Meteor.js project&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;However, while Meteor.js pioneered the fullstack technologies until about 2016 it eventually lost its amazing momentum in about 2017 (&lt;a href="https://dev.to/jankapunkt/why-choose-meteor-or-not-for-your-next-project-1gnh"&gt;read here&lt;/a&gt; on the why and how).&lt;/p&gt;

&lt;p&gt;The JavaScript ecosystem evolved and like in the cambrian explosion we saw a multitude of tools, frameworks and libraries emerging. &lt;br&gt;
A vast majority seemingly adopted the &lt;code&gt;async/await&lt;/code&gt; pattern, &lt;br&gt;
while asynchronous code execution in Meteor.js was implemented using  &lt;a href="https://en.wikipedia.org/wiki/Coroutine" rel="noopener noreferrer"&gt;coroutines&lt;/a&gt; via &lt;a href="https://www.npmjs.com/package/fibers" rel="noopener noreferrer"&gt;Fibers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, the release of Node 16 became basically incompatible with fibers and thus, Meteor.js got stuck on Node 14. This implied a &lt;a href="https://github.com/meteor/meteor/discussions/11505" rel="noopener noreferrer"&gt;major rewrite of a large potion of the platform's internals&lt;/a&gt; as it all depended on &lt;code&gt;fibers&lt;/code&gt;. Further, it implied that all Meteor.js projects and packages required a major rewrite as well. &lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;YOU&lt;/strong&gt; come into play.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do I know, if I need to migrate? 🤔
&lt;/h2&gt;

&lt;p&gt;This answer is simple: you will have to! 🤓&lt;/p&gt;

&lt;p&gt;This is, because all pre-3.0 projects run on a security-patched Node 14 (&lt;a href="https://guide.meteor.com/using-node-v14.21.4" rel="noopener noreferrer"&gt;read here&lt;/a&gt; how to apply the patched version) and you will need Meteor 3.0 to be back on the safe side of the Node game by being able to use the latest Node LTS for your deployments.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do I know what to migrate? 🤔
&lt;/h2&gt;

&lt;p&gt;This is a rather complex situation with no simple answer. There are basically three big parts of your codebase to review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mongo Collection and Cursor usage&lt;/li&gt;
&lt;li&gt;Meteor Methods + Publications&lt;/li&gt;
&lt;li&gt;Meteor Packages that depend on the "old" code style&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also an official list of core functionality that&lt;br&gt;
is to be migrated. It's part of the &lt;a href="https://guide.meteor.com/prepare-meteor-3.0" rel="noopener noreferrer"&gt;"How to migrate to Meteor Async in 2.x" guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each of these come with a different approach, different complexity and constraints. Implementation also varies, whether a static analysis is desired or if runtime analysis is okay, too. Let's take a look at each of them, one by one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Mongo Collection and Cursor usage 💽
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;classic mode&lt;/strong&gt; (using fibers) the Mongo collection calls to the MongoDB driver were executed within a Fiber and thus could be written in sync-style code, while being magically async under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the new &lt;strong&gt;async mode&lt;/strong&gt; (using async/await) we need to change this to an async counterpart:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This change targets the following &lt;code&gt;Mongo.Collection&lt;/code&gt; methods:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class mode&lt;/th&gt;
&lt;th&gt;Async Mode&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;insert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;insertAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Collection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;updateAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Collection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;removeAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Collection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findOne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;findOneAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Collection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;createIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;createIndexAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Collection&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;count&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;countAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Cursor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;forEach&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;forEachAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Cursor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;map&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mapAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Cursor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fetchAsync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;on &lt;code&gt;Mongo.Cursor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Additional info&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note, that &lt;code&gt;Cursor.count&lt;/code&gt; is deprecated and you should consider migrating to &lt;a href="https://docs.meteor.com/api/collections.html#Mongo-Collection-countDocuments" rel="noopener noreferrer"&gt;&lt;code&gt;Collection.countDocuments&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://docs.meteor.com/api/collections.html#Mongo-Collection-estimatedDocumentCount" rel="noopener noreferrer"&gt;&lt;code&gt;Collection.estimatedDocumentCount&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Static analysis 🔬
&lt;/h3&gt;

&lt;p&gt;If you want to analyze their sync usage statically you would have to either write an &lt;a href="https://docs.meteor.com/api/packagejs.html#build-plugin-api" rel="noopener noreferrer"&gt;isobuild plugin&lt;/a&gt; or a &lt;a href="https://babeljs.io/docs/plugins/" rel="noopener noreferrer"&gt;babel plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The hardest challenge here is to get rid of the ambivalence due to the very common naming of the methods. For example: &lt;code&gt;forEach&lt;/code&gt; and &lt;code&gt;map&lt;/code&gt; are typical Array methods and may create lots of false-positives. You will likely have to track, if the variable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Additional Info&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There seems to be &lt;a href="https://forums.meteor.com/t/meteor-3-0-async-functions-eslint-plugin/60738??utm_source=jkuester&amp;amp;utm_medium=online&amp;amp;utm_campaign=Q2-2022-Ambassadors" rel="noopener noreferrer"&gt;a discussion in the Meteor forums about an ESLINT plugin for static analysis&lt;/a&gt;. I suggest you to keep watching the topic, if this is an alternative to you.&lt;/p&gt;
&lt;h3&gt;
  
  
  Runtime analysis 🔬
&lt;/h3&gt;

&lt;p&gt;This approach is rather easy to implement and produces no false-positives. However, detection requires the code to run, which therefore requires a great test-coverage in order to really cover all possible calls. We can do that by &lt;strong&gt;monkey patching&lt;/strong&gt; the methods mentioned above.&lt;/p&gt;

&lt;p&gt;Big plus: we can also detect usage in dependent Meteor packages, if we make sure the pathing occurs before the package code runs on startup. This is fortunately very easy as you just need to follow these steps:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Create a local package 📦
&lt;/h4&gt;

&lt;p&gt;If your Meteor project does not contain a &lt;code&gt;packages&lt;/code&gt; folder then you need to create one:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;your-great-project
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; packages &lt;span class="c"&gt;# needs to be on the same level as server, client, imports etc.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then you create a new local package with a unique name:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;meteor create &lt;span class="nt"&gt;--package&lt;/span&gt; jkuester:migration-helper
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../ &lt;span class="c"&gt;# back to project root&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;meteor add jkuester:migration-helper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Let's change the following method in &lt;strong&gt;package.js&lt;/strong&gt; in order to &lt;strong&gt;immediately execute the code&lt;/strong&gt; on startup (not to be confused with &lt;code&gt;Meteor.startup&lt;/code&gt;, which runs &lt;strong&gt;after&lt;/strong&gt; all startup code):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onUse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;versionsFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ecmascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;migration-helper.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, open the Meteor packages list at &lt;code&gt;your-great-project/.meteor/packages&lt;/code&gt; and move the entry for the package &lt;strong&gt;at the very top&lt;/strong&gt; of the file in order to apply the patches before any other package code runs.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Create a new patch function 🔨
&lt;/h4&gt;

&lt;p&gt;Stay in the package and edit the main file &lt;code&gt;migration-helper.js&lt;/code&gt; which the following code (yes, you can copy/paste):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/meteor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/mongo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ValidatedMethod&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meteor/mdg:validated-method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// some common pattern&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newLine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whiteSpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;

&lt;span class="c1"&gt;// return the current location&lt;/span&gt;
&lt;span class="c1"&gt;// of function execution, considering&lt;/span&gt;
&lt;span class="c1"&gt;// multiple levels of wrappers&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newLine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whiteSpace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// monkey-patch a Collection/Cursor proto function&lt;/span&gt;
&lt;span class="c1"&gt;// to inject some analysis code without altering&lt;/span&gt;
&lt;span class="c1"&gt;// the original behavior&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;patch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;

  &lt;span class="nx"&gt;proto&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isWrappedAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`as &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Async`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isWrappedAsync&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Deprecated: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; needs to be migrated to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Async in collection "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"!`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// apply patching to Mongo.Collection functions&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;findOne&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createIndex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mProto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;
&lt;span class="nx"&gt;mNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mProto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;// applying patches Mongo.Cursor functions&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;forEach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cProto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;
&lt;span class="nx"&gt;cNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cProto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Start your Meteor project or run your tests and look for the deprecation messages. You can also replace the &lt;code&gt;console.warn&lt;/code&gt; with &lt;code&gt;throw new Error&lt;/code&gt; to make the tests fail but that will also likely prevent your app from running - this is up to you how to handle things here.&lt;/p&gt;
&lt;h2&gt;
  
  
  Meteor Methods ☄️
&lt;/h2&gt;

&lt;p&gt;Most of the time you will define Methods and Publications statically on startup. This allows us to create a neat little tool that provides a summary of all our methods and publications.&lt;/p&gt;

&lt;p&gt;Consider the following example, which inserts a new document into a Mongo.Collection (similar to what happens in the &lt;a href="https://www.meteor.com/developers/tutorials" rel="noopener noreferrer"&gt;Meteor tutorials&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server/main.js&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createTodo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TodosCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;TodosCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;docId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Building on top of the previous section, we continue to extend our migration helper package in order to detect the migration needs for this method. Let's start with Meteor Methods by patching the &lt;code&gt;Meteor.methods&lt;/code&gt; function. You can continue in &lt;code&gt;migration-helper.js&lt;/code&gt; by simply adding the following code at the end of the file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...continueing in migration-helper.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;asyncLine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*return Promise.asyncApply&lt;/span&gt;&lt;span class="se"&gt;\(\(\)&lt;/span&gt;&lt;span class="sr"&gt; =&amp;gt; {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;

&lt;span class="c1"&gt;// scans a function body for the above pattern&lt;/span&gt;
&lt;span class="c1"&gt;// to detect async functions&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;analyze&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byNewline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;asyncLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAsync&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Deprecated (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is not async, consider migrating now.`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;What is this actually good for? &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let me explain: In Meteor.js 2.x (using Fibers) any async declared function gets recompiled by the &lt;a href="https://guide.meteor.com/build-tool.html" rel="noopener noreferrer"&gt;Meteor build tool&lt;/a&gt;, transforming async functions into this &lt;code&gt;Promise.asyncApply&lt;/code&gt;. It basically connects the given function to a Fiber that keeps track of the execution stack.&lt;/p&gt;

&lt;p&gt;In turn - every Method or Publication that is not transformed into this &lt;code&gt;Promise.asyncApply&lt;/code&gt; will not contain this pattern on line 1. &lt;strong&gt;Thus, we can detect that this function is not async yet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's apply this to &lt;code&gt;Meteor.methods&lt;/code&gt;; you can simply continue at the end of the file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...continueing in migration-helper.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalMethods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;methods&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;originalMethods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Restarting the project yields to the following output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;W20231102-15:28:12.775&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;? &lt;span class="o"&gt;(&lt;/span&gt;STDERR&lt;span class="o"&gt;)&lt;/span&gt; Deprecated &lt;span class="o"&gt;(&lt;/span&gt;Method&lt;span class="o"&gt;)&lt;/span&gt;: createTodo is not async, consider migrating now.
W20231102-15:28:12.776&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;? &lt;span class="o"&gt;(&lt;/span&gt;STDERR&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; at module &lt;span class="o"&gt;(&lt;/span&gt;server/main.js:15:8&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And during tests or runtime we will see a message like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;W20231102&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;31.550&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Deprecated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insert&lt;/span&gt; &lt;span class="nx"&gt;needs&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;migrated&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;insertAsync&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nx"&gt;W20231102&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;31.551&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;insertAsync&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;mongo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1004&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;W20231102&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;31.560&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Deprecated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt; &lt;span class="nx"&gt;needs&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;migrated&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;findOneAsync&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="nx"&gt;W20231102&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;31.561&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;MethodInvocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTodo &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Both indicating the &lt;code&gt;createTodo&lt;/code&gt; method needs some migration efforts. Let's do that:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createTodo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;TodosCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;TodosCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;docId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The warnings are gone and you have safely prepared this method for the major 3.0 update.&lt;/p&gt;
&lt;h2&gt;
  
  
  Meteor Publications ☄️
&lt;/h2&gt;

&lt;p&gt;If you have already implemented the functions from the Meteor Methods section before then patching publications is as easy as it can get:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalPub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Publication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;originalPub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Note, that publications return cursors using &lt;code&gt;.find&lt;/code&gt; which does not need to be migrated to async. Therefore this may create some false-positives for functions that use no other Mongo.Collection or Mongo.Cursor functions that have to be async.&lt;/p&gt;
&lt;h2&gt;
  
  
  Validated Methods ☄️
&lt;/h2&gt;

&lt;p&gt;A special case is, when using &lt;code&gt;ValidatedMethod&lt;/code&gt; via &lt;code&gt;mdg:validated-method&lt;/code&gt;. If you don't know the package, I highly suggest to check it out as &lt;a href=""&gt;it's the recommended way (add guide link)&lt;/a&gt; to create method with builtin argument validation.&lt;/p&gt;

&lt;p&gt;We will not be able to detect non-async methods passed to the &lt;code&gt;ValidatedMethod&lt;/code&gt; constructor as with the &lt;code&gt;Meteor.methods&lt;/code&gt; pattern, described above. This is, because we basically can't override/monkey-patch an ES6 constructor function the same way we do with any other function.&lt;/p&gt;

&lt;p&gt;Fortunately we still have two alternative ways for detection.&lt;/p&gt;
&lt;h3&gt;
  
  
  Detect using a mixin 🔍
&lt;/h3&gt;

&lt;p&gt;This works by creating a mixin function that detects the &lt;code&gt;options.run&lt;/code&gt; function the same way we detect the &lt;code&gt;Meteor.methods&lt;/code&gt;. However, there is one disadvantage - you have to assign the mixin to every &lt;code&gt;ValidatedMethod&lt;/code&gt; construction options and at that point you'll likely check each method manually.&lt;/p&gt;

&lt;p&gt;Therefore, this approach makes only sense if you use a factory-function to create your Methods, which acts as a single point of construction. In that case, it's rather easy to inject the mixin into your validated methods:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mixins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mixins&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mixins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkAsyncMixin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidatedMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkAsyncMixin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;
  &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ValidatedMethod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Detect at runtime 🔬
&lt;/h3&gt;

&lt;p&gt;If you don't use a pattern, similar to the above-mentioned factory-function, then you still have one more option here. However, this will only detect the methods at runtime, and you will either have to have a decent test-coverage or other ways to make sure that every of your methods are called.&lt;/p&gt;

&lt;p&gt;It works by overriding the &lt;code&gt;_execute&lt;/code&gt; prototype method, which will run on every method invocation for methods, declared using &lt;code&gt;ValidatedMethod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalExec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ValidatedMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_execute&lt;/span&gt;

&lt;span class="nx"&gt;ValidatedMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_execute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;

  &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getLocation&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ValidatedMethod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;originalExec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Summary and outlook 🔭
&lt;/h2&gt;

&lt;p&gt;I showed in this article how to easily detect parts of your Meteor 2.x server environment that need change in order to be 3.0-ready. I purposefully leaved out the client side as this easily exceeds the scope of a short and simple article. &lt;/p&gt;

&lt;p&gt;Since Meteor.js supports many client side libraries and frameworks this may be subject to specific articles for each of them, and I encourage everyone to step up and start writing on their experience&lt;br&gt;
with them.&lt;/p&gt;

&lt;p&gt;You can use this code and refactor / modify it to your needs or simply use the package &lt;code&gt;jkuester:migration-helper&lt;/code&gt;.&lt;br&gt;
It's listed on Packosphere and available on GitHub, too:&lt;/p&gt;
&lt;h3&gt;
  
  
  Packosphere Link 📦
&lt;/h3&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://packosphere.com/jkuester/migration-helper" rel="noopener noreferrer"&gt;
      packosphere.com
    &lt;/a&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  GitHub repository ⌨️
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;
        jankapunkt
      &lt;/a&gt; / &lt;a href="https://github.com/jankapunkt/meteor-migration-helper" rel="noopener noreferrer"&gt;
        meteor-migration-helper
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Detect which parts of your Meteor.js server environment need to be migrated in your current 2.x code.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Meteor.js Migration Helper&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Detect which parts of your Meteor.js server environment need to be
migrated in your current 2.x code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://packosphere.com/jkuester/migration-helper" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ae1005d8d23eda2a0a96469f62757571cdbf7221fd3b2abbaa7f71adb99de43a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6574656f722d7061636b6167652d677265656e3f6c6f676f3d6d6574656f72266c6f676f436f6c6f723d7768697465" alt="built with Meteor"&gt;&lt;/a&gt;
&lt;a href="https://standardjs.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5338a68a0f130dc684279ff3e42e45c9c74006018a1bdeaac76905979b3ccd49/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64655f7374796c652d7374616e646172642d627269676874677265656e2e737667" alt="JavaScript Style Guide"&gt;&lt;/a&gt;
&lt;a href="https://www.repostatus.org/#active" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7e20e1589fb5e2aa55bb1de94227dc006b7639c2d2e1020eb08025ffae9f61df/68747470733a2f2f7777772e7265706f7374617475732e6f72672f6261646765732f6c61746573742f6163746976652e737667" alt="Project Status: Active – The project has reached a stable, usable state and is being actively developed."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;No need to upgrade to 3.0 now to find out, what's still using Fibers.&lt;/p&gt;
&lt;p&gt;There is also an article, which covers this packages functionality
&lt;a href="https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf" rel="nofollow"&gt;https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ meteor add jkuester:migration-helper&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now open in your Meteor.js project the file &lt;code&gt;.meteor/packages&lt;/code&gt;
and move the entry &lt;code&gt;jkuester:migration-helper&lt;/code&gt; to the top, in order
to also detect dependency packages that still use Fibers.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Run detection&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;This is a runtime detection. In order to cover all detectable
structures you need to either run your Meteor.js application
or the tests.&lt;/p&gt;
&lt;p&gt;The more your tests cover of your code (test-coverage),
the better you will be able to detect these.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Detect validated methods using mixins&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;This package also provides a mixin to be used
with &lt;code&gt;mdg:validated-method&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can import it via&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;checkAsyncMixin&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;'meteor/jkuester:migration-helper'&lt;/span&gt;
&lt;span class="pl-c"&gt;// ...&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jankapunkt/meteor-migration-helper" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  About me 👋
&lt;/h2&gt;

&lt;p&gt;I regularly publish articles here on dev.to about &lt;strong&gt;Meteor.js&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt;. Recently I also co-host the &lt;a href="https://www.youtube.com/@meteorjscommunity" rel="noopener noreferrer"&gt;weekly Meteor.js community podcast&lt;/a&gt;, which contains the newest from Meteor.js and the community.&lt;/p&gt;

&lt;p&gt;You can also find (and contact) me on &lt;a href="https://github.com/jankapunkt/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/kuester_jan" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jan-kuester/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like what you are reading and want to support me, you can &lt;a href="https://github.com/jankapunkt" rel="noopener noreferrer"&gt;sponsor me on GitHub&lt;/a&gt;, &lt;a href="https://paypal.me/kuesterjan" rel="noopener noreferrer"&gt;send me a tip via PayPal&lt;/a&gt; or sponsor me a book form &lt;a href="https://www.amazon.de/-/en/hz/wishlist/ls/12YMIY0QNH9TK?ref_=list_d_wl_lfu_nav_1" rel="noopener noreferrer"&gt;my Amazon wishlist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>meteor</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
