Meteor logo

Skill

migrate-to-meteor-3

migrate Meteor 2.x apps to 3.x

Published by Meteor Updated Aug 28
Covers Meteor Migration

Description

Use when migrating a Meteor 2.x application to Meteor 3.x. Triggers on callAsync, *Async Mongo, removed Fibers, implicit-global ReferenceError, lost Blaze reactivity, a publish function returning a Promise, a scheduler dropping a Promise, a read API receiving update modifiers, async allow/deny, an Iron Router controller not running, "Method stub took too long", Atmosphere resolution, Express 5 WebApp handlers, lost async context, rawCollection callbacks, meteor/* TypeScript types, useTracker, and useSubscribe. Use this skill when the user asks about upgrading Meteor, async caller propagation, iterators with await, zodern:types, or replacing and forking packages.

SKILL.md

Migrate a Meteor 2.x application to Meteor 3.x

Meteor 3 removed Fibers. Server-side Mongo APIs are async. The module system enforces strict mode. Client reactivity inside async code needs care. Atmosphere packages often need forking or replacement. Approach the migration in phases. Do not flip the framework version flag first.

  1. Update the project to the latest 2.x release.
  2. Run the app with WARN_WHEN_USING_OLD_API=true meteor run. The console logs every sync-API call that needs an async sibling, giving you a to-do list before the framework flip.
  3. Migrate server-side sync Mongo calls to *Async siblings while still on 2.x. Trace each changed function through every server-side caller: await where the caller consumes the value, forward Promises deliberately, and restructure sync-only boundaries. Stop only at an async-capable framework boundary. See references/async-rewrites.md and references/call-vs-callAsync.md. A community jscodeshift codemod automates the easy cases, but it misses non-standard collection imports (for example, meteor/<publisher>:collections). Review the diff by hand, then audit callback Promise ownership and collection argument shapes.
  4. Audit Atmosphere packages. Find replacements or fork outdated ones; pin api.versionsFrom(['2.x', '3.0']). See references/package-triage.md. Save .meteor/versions and npm lockfile checkpoints so package-major changes remain distinguishable from Meteor.
  5. Upgrade to Meteor 3.x.
  6. Sweep implicit globals; rewrite to const or export / import. See references/module-system.md.
  7. Audit Blaze helpers and Tracker.autorun blocks for lost reactivity after await. See references/client-reactivity.md.
  8. Replace iterators that contain await (forEach, map, filter) with for...of or Promise.all. See references/js-iterators.md.
  9. Audit publications using internal cursor APIs (_cursorDescription, manual sub.added) and framework handlers that read invocation this. Both synchronous and async publish handlers may return cursors; keep cursor transforms synchronous and use ordinary functions when Meteor must bind this. When a package patches Meteor.publish with an EnvironmentVariable, scope publish.call at the wrapper's top level, not inside the invoked handler. Verify invocation context before and after await. See references/publications.md and references/other-breaking-changes.md.
  10. For TypeScript projects, install zodern:types and update tsconfig.json. See references/typescript-migration.md.
  11. For React projects, decide whether to adopt the Suspense-aware react-meteor-data import. See references/react-migration.md, then use meteor-react for current hook, scaffold, and build guidance.

Symptom router

SymptomReference
TypeError: Collection.findOne is not a functionreferences/async-rewrites.md
Method returns undefined or returns a Promisereferences/async-rewrites.md
Downstream caller receives or reads from a Promisereferences/async-rewrites.md
Cron, hook, timer, or event callback drops a Promisereferences/async-rewrites.md
Read method receives $set, $push, or another modifierreferences/async-rewrites.md
allow / deny validator needs an async database readreferences/async-rewrites.md
Meteor.call callback never firesreferences/call-vs-callAsync.md
ReferenceError: X is not defined at startupreferences/module-system.md
Template renders, no data, Minimongo emptyreferences/module-system.md
Iron Router controller silently does not runreferences/module-system.md
{{> partial}} renders nothing in Blazereferences/module-system.md
Page renders but live data never updatesreferences/client-reactivity.md
Blaze helper returns a Promisereferences/client-reactivity.md
Cursor transform errors with "returned a Promise"references/publications.md
sub.added writes never reach the clientreferences/publications.md
Method or publication loses this.userIdreferences/publications.md
Atmosphere package fails to resolve or buildreferences/package-triage.md
forEach/map/filter with await skips itemsreferences/js-iterators.md
Middleware on WebApp.connectHandlers not firingreferences/webapp-express.md
Route uses an unnamed wildcard after Meteor 3.1references/webapp-express.md
rawCollection callback never firesreferences/other-breaking-changes.md
Patched publication loses Meteor.userId() or async contextreferences/other-breaking-changes.md
meteor reset did not wipe the local Mongoreferences/other-breaking-changes.md
Method stub (X) took too long console warningreferences/call-vs-callAsync.md
"Cannot enlarge memory array" during meteor updatereferences/other-breaking-changes.md
External callback lost this.userId or env varsreferences/other-breaking-changes.md
Monkey-patched Meteor.publish never runsreferences/other-breaking-changes.md
meteor/* imports resolve to any in TypeScriptreferences/typescript-migration.md
useTracker or useSubscribe not re-runningreferences/react-migration.md

Anti-patterns

  • Do not run meteor update --release=3 first. Async-convert and package-triage on 2.x first.
  • Do not global-replace findOne with findOneAsync. Many callers need rewriting, not just await.
  • Do not mechanically rewrite client Minimongo calls to async. Both APIs work on the client. Prefer sync calls in naturally synchronous Blaze and Tracker code; use async calls in shared or already-async flows. Wrap reactive reads after an await with Tracker.withComputation.
  • Do not rely on Iron Router controller naming-convention lookup. Pass controller: explicitly on every route.
  • Do not mix await and .then() in the same function. Pick one.
  • Do not assume implicit globals work. Every top-level identifier in 3.x must be const, let, or export-ed.
  • Do not invent async replacements. Meteor.userId() remains synchronous inside methods and publications; there is no Meteor.userIdAsync().
  • Do not use an arrow as a method or publication handler when it reads framework-bound this. An arrow ignores the invocation context Meteor supplies.
  • Do not rewrite api.addFiles or api.export only because the app moved to Meteor 3. They remain supported for Atmosphere packages.

See also

  • Async: async-rewrites.md, call-vs-callAsync.md, async-cheatsheet.md, js-iterators.md, removed-functions.md.
  • Runtime: module-system.md, client-reactivity.md, publications.md, webapp-express.md, other-breaking-changes.md.
  • Project: package-triage.md, typescript-migration.md, react-migration.md, eval-cases.md.
  • Current Meteor React integration after the upgrade: meteor-react.

Further reading (optional)

Real-world migration write-ups for context, not for fixing specific issues. The symptom router above is sufficient on its own. Open references/community-case-studies.md only when the user asks for narrative case studies or wants to calibrate effort and timeline.

© 2026 YourAI.tools. Every skill from an identity-verified publisher.

Independent catalog. Not affiliated with, endorsed by, or sponsored by Anthropic or any listed publisher. All trademarks belong to their respective owners.