--- url: /api/accounts.md --- # Accounts ## Accounts-base {#accounts-base} The Meteor Accounts system builds on top of the `userId` support in [`publish`](./meteor#Subscription-userId) and [`methods`](./meteor#methods-userId). The core packages add the concept of user documents stored in the database, and additional packages add [secure password authentication](#passwords), [integration with third party login services](#Meteor-loginWith%3CExternalService%3E), and a [pre-built userinterface](/packages/accounts-ui.html). The basic Accounts system is in the `accounts-base` package, but applications typically include this automatically by adding one of the login provider packages: `accounts-password`, `accounts-facebook`, `accounts-github`, `accounts-google`, `accounts-meetup`, `accounts-twitter`, or `accounts-weibo`. Read more about customizing user accounts in the [Accounts](http://guide.meteor.com/accounts.html) article in the Meteor Guide. ### Accounts with Session Storage {#accounts-session-storage} By default, Meteor uses Local Storage to store, among other things, login tokens in your browser session. But, for some applications, it makes sense to use Session Storage instead. Session Storage will not persist across client sessions. You can achieve this by adding this to your settings: ```json { // ... all other settings, "public": { // ... all your public settings "packages": { "accounts": { "clientStorage": "session" } } } } ``` Retrieves the user record for the current user from the [`Meteor.users`](#Meteor-users) collection. On the client, the available fields will be those that are published from the server (other fields won't be available on the client). By default the server publishes `username`, `emails`, and `profile` (writable by user). See [`Meteor.users`](#Meteor-users) for more on the fields used in user documents. On the server, this will fetch the record from the database. To improve the latency of a method that uses the user document multiple times, save the returned record to a variable instead of re-calling `Meteor.user()`. Fetching the full user document can cause unnecessary database usage on the server and over-reactivity on the client, particularly if you store lots of custom data on it. Therefore it is recommended to use the `options` parameter to only fetch the fields you need: ```js import { Meteor } from "meteor/meteor"; const userName = Meteor.user({ fields: { "profile.name": 1 } }).profile.name; ``` Same as [`Meteor.user`](#Meteor-user), but returns a promise and is available on the server. ```js import { Meteor } from "meteor/meteor"; const user = await Meteor.userAsync(); ``` This collection contains one document per registered user. Here's an example user document: ```js { _id: 'QwkSmTCZiw5KDx3L6', // Meteor.userId() username: 'cool_kid_13', // Unique name emails: [ // Each email address can only belong to one user. { address: 'cool@example.com', verified: true }, { address: 'another@different.com', verified: false } ], createdAt: new Date('Wed Aug 21 2013 15:16:52 GMT-0700 (PDT)'), profile: { // The profile is writable by the user by default. name: 'Joe Schmoe' }, services: { facebook: { id: '709050', // Facebook ID accessToken: 'AAACCgdX7G2...AbV9AZDZD' }, resume: { loginTokens: [ { token: '97e8c205-c7e4-47c9-9bea-8e2ccc0694cd', when: 1349761684048 } ] } } } ``` A user document can contain any data you want to store about a user. Meteor treats the following fields specially: - `username`: a unique String identifying the user. - `emails`: an Array of Objects with keys `address` and `verified`; an email address may belong to at most one user. `verified` is a Boolean which is true if the user has [verified the address](#Accounts-verifyEmail) with a token sent over email. - `createdAt`: the Date at which the user document was created. - `profile`: an Object which the user can create and update with any data. Do not store anything on `profile` that you wouldn't want the user to edit unless you have a deny rule on the `Meteor.users` collection. - `services`: an Object containing data used by particular login services. For example, its `reset` field contains tokens used by [forgot password](#Accounts-forgotPassword) links, and its `resume` field contains tokens used to keep you logged in between sessions. Like all [Mongo.Collection](./collections.md)s, you can access all documents on the server, but only those specifically published by the server are available on the client. You can also use all Collection methods, for instance `Meteor.users.remove` on the server to delete a user. By default, the current user's `username`, `emails` and `profile` are published to the client. You can publish additional fields for the current user with: ::: code-group ```js [server.js] Meteor.publish("userData", function () { if (this.userId) { return Meteor.users.find( { _id: this.userId }, { fields: { other: 1, things: 1 }, } ); } else { this.ready(); } }); ``` ```js [client.js] Meteor.subscribe("userData"); ``` ::: If the autopublish package is installed, information about all users on the system is published to all clients. This includes `username`, `profile`, and any fields in `services` that are meant to be public (eg `services.facebook.id`, `services.twitter.screenName`). Additionally, when using autopublish more information is published for the currently logged in user, including access tokens. This allows making API calls directly from the client for services that allow this. Users are by default allowed to specify their own `profile` field with [`Accounts.createUser`](#Accounts-createUser) and modify it with `Meteor.users.update`. To allow users to edit additional fields, use [`Meteor.users.allow`](./collections.md#Mongo-Collection-allow). To forbid users from making any modifications to their user document: ```js import { Meteor } from "meteor/meteor"; Meteor.users.deny({ update: () => true }); ``` For example, [the `accounts-ui` package](../packages/accounts-ui.md) uses this to display an animation while the login request is being processed. For example, when called in a user's browser, connections in that browser remain logged in, but any other browsers or DDP clients logged in as that user will be logged out. If there are multiple users with a username or email only differing in case, a case sensitive match is required. Although `createUser` won't let you create users with ambiguous usernames or emails, this could happen with existing databases or if you modify the users collection directly. This method can fail throwing one of the following errors: - "Unrecognized options for login request [400]" if `user` or `password` is undefined. - "Match failed [400]" if `user` isn't an Object or String, or `password` isn't a String. - "User not found [403]" if the email or username provided in `user` doesn't belong to a registered user. - "Incorrect password [403]" if the password provided is incorrect. - "User has no password set [403]" if `user` doesn't have a password. This function is provided by the `accounts-password` package. See the [Passwords](#passwords) section below. Logs the user in using a valid Meteor login token (also called a resume token). This is typically used to restore a user's session across browser reloads, between tabs, or across DDP connections (such as in multi-server setups). **Arguments:** - `token` (`String`): The login token to use for authentication. Usually obtained from `Accounts._storedLoginToken()` or from a previous login session. - `callback` (`Function`, optional): Called with no arguments on success, or with a single `Error` argument on failure. **Returns:** - `void` **Usage example:** ```js import { Meteor } from "meteor/meteor"; const token = Accounts._storedLoginToken(); Meteor.loginWithToken(token, (error) => { if (error) { console.error("Login with token failed", error); } else { console.log("Logged in with token!"); } }); ``` **Notes:** - If the token is invalid, expired, or revoked, the callback will be called with an error and the user will not be logged in. - This method is used internally by Meteor to automatically restore login state on page reload and across tabs. - Can be used with custom DDP connections to authenticate across multiple Meteor servers sharing the same database. Available functions are: - `Meteor.loginWithMeteorDeveloperAccount` - `Meteor.loginWithFacebook` - `options` may also include [Facebook's `auth_type` parameter](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#reaskperms) - `Meteor.loginWithGithub` - `Meteor.loginWithGoogle` - `options` may also include [Google's additional URI parameters](https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters) - `Meteor.loginWithMeetup` - `Meteor.loginWithTwitter` - `options` may also include [Twitter's `force_login` parameter](https://dev.twitter.com/oauth/reference/get/oauth/authenticate) - `Meteor.loginWithWeibo` These functions initiate the login process with an external service (eg: Facebook, Google, etc), using OAuth. When called they open a new pop-up window that loads the provider's login page. Once the user has logged in with the provider, the pop-up window is closed and the Meteor client logs in to the Meteor server with the information provided by the external service.

Requesting Permissions

In addition to identifying the user to your application, some services have APIs that allow you to take action on behalf of the user. To request specific permissions from the user, pass the `requestPermissions` option the login function. This will cause the user to be presented with an additional page in the pop-up dialog to permit access to their data. The user's `accessToken` — with permissions to access the service's API — is stored in the `services` field of the user document. The supported values for `requestPermissions` differ for each login service and are documented on their respective developer sites: - Facebook: - GitHub: - Google: - Meetup: - Twitter, Weibo, Meteor developer accounts: `requestPermissions` currently not supported External login services typically require registering and configuring your application before use. The easiest way to do this is with the [`accounts-ui` package](../packages/accounts-ui.md) which presents a step-by-step guide to configuring each service. However, the data can be also be entered manually in the `ServiceConfiguration.configurations` collection, which is exported by the `service-configuration` package. ## Configuring Services {#service-configuration} First, add the service configuration package: ```bash meteor add service-configuration ``` Then, inside the server of your app (this example is for the Weebo service), import `ServiceConfiguration`: ```js import { ServiceConfiguration } from "meteor/service-configuration"; ServiceConfiguration.configurations.upsertAsync( { service: "weibo" }, { $set: { loginStyle: "popup", clientId: "1292962797", // See table below for correct property name! secret: "75a730b58f5691de5522789070c319bc", }, } ); ``` Since Meteor 2.7 you no longer need to manually set the configuration and instead can use Meteor settings by setting your services under `Meteor.settings.packages.service-configuration.`. All the properties can be set under the service and will be added to the database as is, so make sure that they are correct. For the example above, the settings would look like: ```json { "packages": { "service-configuration": { "weibo": { "loginStyle": "popup", "clientId": "1292962797", "secret": "75a730b58f5691de5522789070c319bc" } } } } ``` The correct property name to use for the API identifier (i.e. `clientId` in the above example) depends on the login service being used, so be sure to use the correct one: | Property Name | Services | | ------------- | -------------------------------------------------------- | | `appId` | Facebook | | `clientId` | Github, Google, Meetup, Meteor Developer Accounts, Weibo | | `consumerKey` | Twitter | Additionally, each external service has its own login provider package and login function. For example, to support GitHub login, run the following in your terminal: ```bash meteor add accounts-github ``` and use the `Meteor.loginWithGithub` function: ```js import { Meteor } from "meteor/meteor"; Meteor.loginWithGithub( { requestPermissions: ["user", "public_repo"], }, (error) => { if (error) { Session.set("errorMessage", error.reason || "Unknown error"); } } ); ``` Login service configuration is sent from the server to the client over DDP when your app starts up; you may not call the login function until the configuration is loaded. The function `Accounts.loginServicesConfigured()` is a reactive data source that will return true once the login service is configured; you should not make login buttons visible or active until it is true. Ensure that your [`$ROOT_URL`](./meteor.md#Meteor-absoluteUrl) matches the authorized domain and callback URL that you configure with the external service (for instance, if you are running Meteor behind a proxy server, `$ROOT_URL` should be the externally-accessible URL, not the URL inside your proxy). ## Manual service configuration {#manual-service-configuration} You can use `Accounts.loginServiceConfiguration` to view and edit the settings collection: ```js import { Accounts } from "meteor/accounts-base"; Accounts.loginServiceConfiguration.find(); ``` ## Popup versus redirect flow {#popup-vs-redirect-flow} When configuring OAuth login with a provider (such as Facebook or Google), Meteor lets you choose a popup- or redirect-based flow. In a popup-based flow, when a user logs in, they will be prompted to login at the provider in a popup window. In a redirect-based flow, the user's whole browser window will be redirected to the login provider, and the window will redirect back to your app when the login is completed. You can also pick which type of login to do by passing an option to [`Meteor.loginWith`](#Meteor-loginWith%3CExternalService%3E) Usually, the popup-based flow is preferable because the user will not have to reload your whole app at the end of the login flow. However, the popup-based flow requires browser features such as `window.close` and `window.opener` that are not available in all mobile environments. In particular, we recommend using `Meteor.loginWith({ loginStyle: 'redirect' })` in the following environments: - Inside UIWebViews (when your app is loaded inside a mobile app) - In Safari on iOS8 (`window.close` is not supported due to a bug) Example: ```js import { Accounts } from "meteor/accounts-base"; Accounts.ui.config({ requestPermissions: { facebook: ["user_likes"], github: ["user", "repo"], }, requestOfflineToken: { google: true, }, passwordSignupFields: "USERNAME_AND_OPTIONAL_EMAIL", }); ``` Since Meteor 2.7 you can configure these in your Meteor settings under `Meteor.settings.public.packages.accounts-ui-unstyled`. ## Multi-server {#multi-server} The `accounts-base` package exports two constructors, called `AccountsClient` and `AccountsServer`, which are used to create the `Accounts` object that is available on the client and the server, respectively. This predefined `Accounts` object (along with similar convenience methods of `Meteor`, such as [`Meteor.logout`](#Meteor-logout)) is sufficient to implement most accounts-related logic in Meteor apps. Nevertheless, these two constructors can be instantiated more than once, to create multiple independent connections between different accounts servers and their clients, in more complicated authentication situations. The `AccountsClient` and `AccountsServer` classes share a common superclass, `AccountsCommon`. Methods defined on `AccountsCommon.prototype` will be available on both the client and the server, via the predefined `Accounts` object (most common) or any custom `accountsClientOrServer` object created using the `AccountsClient` or `AccountsServer` constructors (less common). Here are a few of those methods: From Meteor 2.5 you can set these in your Meteor settings under `Meteor.settings.packages.accounts-base`. Note that due to the nature of settings file you won't be able to set parameters that require functions. See description of [AccountsCommon#onLoginFailure](#AccountsCommon-onLoginFailure) for details. Either the `onLogin` or the `onLoginFailure` callbacks will be called for each login attempt. The `onLogin` callbacks are called after the user has been successfully logged in. The `onLoginFailure` callbacks are called after a login attempt is denied. These functions return an object with a single method, `stop`. Calling `stop()` unregisters the callback. On the server, the callbacks get a single argument, the same attempt info object as [`validateLoginAttempt`](#AccountsServer-validateLoginAttempt). On the client, the callback argument is an object containing a single `error` property set to the `Error`-object which was received from the failed login attempt. On the server, the `func` callback receives a single argument with the object below. On the client, no arguments are passed. ```js import { AccountsCommon } from "meteor/accounts-base"; const options = { //... }; const accountsCommon = new AccountsCommon(options); accountsCommon.onLogout(({ user, connection, collection }) => { console.log(user); // ˆˆˆˆˆˆ The Meteor user object of the user which just logged out console.log(connection); // ˆˆˆˆˆˆ The connection object the request came in on. See // `Meteor.onConnection` for details. console.log(collection); // ˆˆˆˆˆˆ The `collection` The name of the Mongo.Collection or the // Mongo.Collection object to hold the users. }); ``` At most one of `options.connection` and `options.ddpUrl` should be provided in any instantiation of `AccountsClient`. If neither is provided, `Meteor.connection` will be used as the `.connection` property of the `AccountsClient` instance. Note that `AccountsClient` is currently available only on the client, due to its use of browser APIs such as `window.localStorage`. In principle, though, it might make sense to establish a client connection from one server to another remote accounts server. Please [let us know](https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#feature-requests) if you find yourself needing this server-to-server functionality. These methods are defined on `AccountsClient.prototype`, and are thus available only on the client: These methods are defined on `AccountsServer.prototype`, and are thus available only on the server: This can be called multiple times. If any of the functions return `false` or throw an error, the new user creation is aborted. To set a specific error message (which will be displayed by [`accounts-ui`](../packages/accounts-ui.md)), throw a new [`Meteor.Error`](./meteor#meteor-api). Example: ```js import { Accounts } from "meteor/accounts-base"; // Validate username, sending a specific error message on failure. Accounts.validateNewUser((user) => { if (user.username && user.username.length >= 3) { return true; } else { throw new Meteor.Error(403, "Username must have at least 3 characters"); } }); // Validate username, without a specific error message. Accounts.validateNewUser((user) => { return user.username !== "root"; }); ``` If the user is being created as part of a login attempt from a client (eg, calling [`Accounts.createUser`](#Accounts-createUser) from the client, or [logging in for the first time with an external service](#meteor_loginwithexternalservice)), these callbacks are called _before_ the [`Accounts.validateLoginAttempt`](#Accounts-validateLoginAttempt) callbacks. If these callbacks succeed but those fail, the user will still be created but the connection will not be logged in as that user. Use this when you need to do more than simply accept or reject new user creation. With this function you can programatically control the contents of new user documents. The function you pass will be called with two arguments: `options` and `user`. The `options` argument comes from [`Accounts.createUser`](#Accounts-createUser) for password-based users or from an external service login flow. `options` may come from an untrusted client so make sure to validate any values you read from it. The `user` argument is created on the server and contains a proposed user object with all the automatically generated fields required for the user to log in, including a temporary `_id` (the final _id is generated upon document insertion and not available in this function). The function should return the user document (either the one passed in or a newly-created object) with whatever modifications are desired. The returned document is inserted directly into the [`Meteor.users`](#Meteor-users) collection. The default create user function simply copies `options.profile` into the new user document. Calling `onCreateUser` overrides the default hook. This can only be called once. Example: ```js import { Accounts } from "meteor/accounts-base"; // Support for playing D&D: Roll 3d6 for dexterity. Accounts.onCreateUser((options, user) => { const customizedUser = Object.assign( { dexterity: _.random(1, 6) + _.random(1, 6) + _.random(1, 6), }, user ); // We still want the default hook's 'profile' behavior. if (options.profile) { customizedUser.profile = options.profile; } return customizedUser; }); ``` Call `validateLoginAttempt` with a callback to be called on login attempts. It returns an object with a single method, `stop`. Calling `stop()` unregisters the callback. When a login attempt is made, the registered validate login callbacks are called with a single argument, you can check the example: ```js import { AccountsServer } from "meteor/accounts-base"; const options = { //... }; const accountsServer = new AccountsServer(options); accountsServer.validateLoginAttempt( ({ type, // String allowed, // Boolean error, // Error user, // Object connection, // Object collection, // Object methodName, // String methodArguments, // Array }) => { console.log(type); // ˆˆˆˆˆˆ The service name, such as "password" or "twitter". console.log(allowed); // ˆˆˆˆˆˆ Whether this login is allowed and will be successful (if not aborted // by any of the validateLoginAttempt callbacks). False if the login // will not succeed (for example, an invalid password or the login was // aborted by a previous validateLoginAttempt callback). console.log(error); // ˆˆˆˆˆˆ When `allowed` is false, the exception describing why the login // failed. It will be a `Meteor.Error` for failures reported to the // user (such as invalid password), and can be a another kind of // exception for internal errors. console.log(user); // ˆˆˆˆˆˆ When it is known which user was attempting to login, // the Meteor user object. This will always be present for successful logins. console.log(connection); // ˆˆˆˆˆˆ The `connection` object the request came in on. See // [`Meteor.onConnection`](#meteor_onconnection) for details. console.log(collection); // ˆˆˆˆˆˆ The `collection` The name of the Mongo.Collection or the // Mongo.Collection object to hold the users. console.log(methodName); // ˆˆˆˆˆˆ The name of the Meteor method being used to login. // For example, "login", "loginWithPassword", or "loginWith". console.log(methodArguments); // ˆˆˆˆˆˆ An array of the arguments passed to the login method. // For example, `["username", "password"]` } ); ``` A validate login callback must return a truthy value for the login to proceed. If the callback returns a falsy value or throws an exception, the login is aborted. Throwing a `Meteor.Error` will report the error reason to the user. All registered validate login callbacks are called, even if one of the callbacks aborts the login. The later callbacks will see the `allowed` field set to `false` since the login will now not be successful. This allows later callbacks to override an error from a previous callback; for example, you could override the "Incorrect password" error with a different message. Validate login callbacks that aren't explicitly trying to override a previous error generally have no need to run if the attempt has already been determined to fail, and should start with ```js if (!attempt.allowed) { return false; } ``` Use this hook if you need to validate that user from an external service should be allowed to login or create account. ```js import { AccountsServer } from "meteor/accounts-base"; const options = { //... }; const accountsServer = new AccountsServer(options); accountsServer.beforeExternalLogin(({ type, data, user }) => { console.log(type); // ˆˆˆˆˆˆ The service name, such as "google" or "twitter". Is a String console.log(data); // ˆˆˆˆˆˆ Data retrieved from the service (eg: email, name, etc) // Is an Object. console.log(user); // ˆˆˆˆˆˆ If user was found in the database that matches the criteria from the service, // their data will be provided here. Is an Object. }); ``` You should return a `Boolean` value, `true` if the login/registration should proceed or `false` if it should terminate. In case of termination the login attempt will throw an error `403`, with the message: `Login forbidden`. When allowing your users to authenticate with an external service, the process will eventually call `Accounts.updateOrCreateUserFromExternalService`. By default, this will search for a user with the `service..id`, and if not found will create a new user. As that is not always desirable, you can use this hook as an escape hatch to look up a user with a different selector, probably by `emails.address` or `username`. Note the function will only be called if no user was found with the `service..id` selector. The function will be called with a single argument, the info object: ```js import { AccountsServer } from "meteor/accounts-base"; const options = { //... }; const accountsServer = new AccountsServer(options); accountsServer.setAdditionalFindUserOnExternalLogin( ({ serviceName, serviceData, options }) => { // serviceName: String // The external service name, such as "google" or "twitter". // serviceData: Object // The data returned by the service oauth request. // options: Object // An optional arugment passed down from the oauth service that may contain // additional user profile information. As the data in `options` comes from an // external source, make sure you validate any values you read from it. } ); ``` The function should return either a user document or `undefined`. Returning a user will result in the populating the `service.` in your user document, while returning `undefined` will result in a new user account being created. If you would prefer that a new account not be created, you could throw an error instead of returning. Example: ```js // If a user has already been created, and used their Google email, this will // allow them to sign in with the Meteor.loginWithGoogle method later, without // creating a new user. Accounts.setAdditionalFindUserOnExternalLogin( ({ serviceName, serviceData }) => { if (serviceName === "google") { // Note: Consider security implications. If someone other than the owner // gains access to the account on the third-party service they could use // the e-mail set there to access the account on your app. // Most often this is not an issue, but as a developer you should be aware // of how bad actors could play. return Accounts.findUserByEmail(serviceData.email); } } ); ``` Use this to register your own custom authentication method. This is also used by all of the other inbuilt accounts packages to integrate with the accounts system. There can be multiple login handlers that are registered. When a login request is made, it will go through all these handlers to find its own handler. The registered handler callback is called with a single argument, the `options` object which comes from the login method. For example, if you want to login with a plaintext password, `options` could be `{ user: { username: }, password: }`,or `{ user: { email: }, password: }`. The login handler should return `undefined` if it's not going to handle the login request or else the login result object.

Rate Limiting

By default, there are rules added to the [`DDPRateLimiter`](./DDPRateLimiter.md) that rate limit logins, new user registration and password reset calls to a limit of 5 requests per 10 seconds per session. These are a basic solution to dictionary attacks where a malicious user attempts to guess the passwords of legitimate users by attempting all possible passwords. These rate limiting rules can be removed by calling `Accounts.removeDefaultRateLimit()`. Please see the [`DDPRateLimiter`](./DDPRateLimiter.md) docs for more information. ## Passwords {#passwords} The `accounts-password` package contains a full system for password-based authentication. In addition to the basic username and password-based sign-in process, it also supports email-based sign-in including address verification and password recovery emails. ### Password encryption and security Starting from `accounts-passwords:4.0.0`, you can choose which algorithm is used by the Meteor server to store passwords : either [bcrypt](http://en.wikipedia.org/wiki/Bcrypt) or [Argon2](http://en.wikipedia.org/wiki/Argon2) algorithm. Both are robust and contribute to protect against embarrassing password leaks if the server's database is compromised. Before version 4.0.0, `bcrypt` was the only available option. argon2 has been introduced because it is considered the most secure option. This algorithm is specifically designed to resist GPU-based brute force attacks. For more details, see the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html). As of January 2025, **`bcrypt` is still the default option** to enable a smooth transition. In the future, `argon2` will replace `bcrypt` as default and `bcrypt` option will be deprecated. Passwords are hashed on the client using **SHA-256** algorithm before being sent to the server. This ensures that sensitive data is never transmitted in plain text. Once received by the server, the hashed value is further encrypted and securely stored in the `Meteor.users` collection. **About the migration process from `bcrypt` to `argon2`** The transition from `bcrypt` to `argon2` happens automatically upon user login. If Argon2 encryption is enabled in an existing application, each user's password is re-encrypted during their next successful login. - Step 1: The password is first validated against the existing `bcrypt` hash. - Step 2: If authentication succeeds, the password is re-encrypted using `Argon2`. - Step 3: The new `Argon2` hash replaces the old `bcrypt` hash in the database. To monitor the migration progress, you can count users still using bcrypt: ```js const bcryptUsers = await Meteor.users.find({ "services.password.bcrypt": { $exists: true } }).countAsync(); const totalUsers = await Meteor.users.find({ "services.password": { $exists: true } }).countAsync(); console.log("Remaining users to migrate:", bcryptUsers, "/", totalUsers); ``` Once `bcryptUsers` reaches 0, the migration is complete. **Enabling Argon2 encryption** To enable Argon2 encryption, you need a small configuration change on the server: ```js Accounts.config({ argon2Enabled: true, }); ``` **Configuring `argon2` parameters** One enabled, the `accounts-password` package allows customization of Argon2's parameters. The configurable options include: - `type`: `argon2id` (provides a blend of resistance against GPU and side-channel attacks) - `timeCost` (default: 2) – This controls the computational cost of the hashing process, affecting both the security level and performance. - `memoryCost`: 19456 (19 MiB) - The amount of memory used by the algorithm in KiB per thread - `parallelism`: 1 - The number of threads used by the algorithm To update the values, use the following configuration: ```js Accounts.config({ argon2Enabled: true, argon2Type: "argon2id", argon2TimeCost: 2, argon2MemoryCost: 19456, argon2Parallelism: 1, }); ``` Other Argon2 parameters, such as `hashLength`, are kept to default values: - `hashLength`: 32 bytes - The length of the hash output in bytes The default values are the minimum [OWASP recommendations for Argon2 parameters](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#introduction). When updating these values, consider the trade-offs between security and performance on the target infrastructure. For more information about Argon2's parameters, refer to the [argon2 options documentation](https://github.com/ranisalt/node-argon2/wiki/Options). ### Using passwords To add password support to your application, run this command in your terminal: ```bash meteor add accounts-password ``` > In addition to configuring the [`email`](./email.md) package's `MAIL_URL`, it is critical that you set proper values (specifically the `from` address) in [`Accounts.emailTemplates`](#Accounts-emailTemplates) to ensure proper delivery of e-mails! You can construct your own user interface using the functions below, or use the [`accounts-ui` package](../packages/accounts-ui.md) to include a turn-key user interface for password-based sign-in. On the client, this function logs in as the newly created user on successful completion. On the server, it returns the newly created user id. On the client, you must pass `password` and at least one of `username` or `email` — enough information for the user to be able to log in again later. If there are existing users with a username or email only differing in case, `createUser` will fail. The callback's `error.reason` will be `'Username already exists.'` or `'Email already exists.'` In the latter case, the user can then either [login](accounts.html#Meteor-loginWithPassword) or [reset their password](#Accounts-resetPassword). On the server, you do not need to specify `password`, but the user will not be able to log in until it has a password (eg, set with [`Accounts.setPasswordAsync`](#Accounts-setPasswordAsync)). To create an account without a password on the server and still let the user pick their own password, call `createUser` with the `email` option and then call [`Accounts.sendEnrollmentEmail`](#Accounts-sendEnrollmentEmail). This will send the user an email with a link to set their initial password. By default the `profile` option is added directly to the new user document. To override this behavior, use [`Accounts.onCreateUser`](#Accounts-onCreateUser). This function is only used for creating users with passwords. The external service login flows do not use this function. Instead of modifying documents in the [`Meteor.users`](#Meteor-users) collection directly, use these convenience functions which correctly check for case insensitive duplicates before updates. By default, an email address is added with `{ verified: false }`. Use [`Accounts.sendVerificationEmail`](#Accounts-sendVerificationEmail) to send an email with a link the user can use to verify their email address. If the user trying to verify the email has 2FA enabled, this error will be thrown: - "Email verified, but user not logged in because 2FA is enabled [2fa-enabled]": No longer signing in the user automatically if the user has 2FA enabled. This function accepts tokens passed into the callback registered with [`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink). Use the below functions to initiate password changes or resets from the server or the client. This triggers a call to [`Accounts.sendResetPasswordEmail`](#Accounts-sendResetPasswordEmail) on the server. When the user visits the link in this email, the callback registered with [`Accounts.onResetPasswordLink`](#Accounts-onResetPasswordLink) will be called. If you are using the [`accounts-ui` package](../packages/accounts-ui.md), this is handled automatically. Otherwise, it is your responsibility to prompt the user for the new password and call `resetPassword`. This function accepts tokens passed into the callbacks registered with [`AccountsClient#onResetPasswordLink`](#Accounts-onResetPasswordLink) and [`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink). If the user trying to reset the password has 2FA enabled, this error will be thrown: - "Changed password, but user not logged in because 2FA is enabled [2fa-enabled]": No longer signing in the user automatically if the user has 2FA enabled. When the user visits the link in this email, the callback registered with [`AccountsClient#onResetPasswordLink`](#Accounts-onResetPasswordLink) will be called. To customize the contents of the email, see [`Accounts.emailTemplates`](#Accounts-emailTemplates). When the user visits the link in this email, the callback registered with [`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink) will be called. To customize the contents of the email, see [`Accounts.emailTemplates`](#Accounts-emailTemplates). When the user visits the link in this email, the callback registered with [`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink) will be called. To customize the contents of the email, see [`Accounts.emailTemplates`](#Accounts-emailTemplates). This is an `Object` with several fields that are used to generate text/html for the emails sent by `sendResetPasswordEmail`, `sendEnrollmentEmail`, and `sendVerificationEmail`. Set the fields of the object by assigning to them: - `from`: (**required**) A `String` with an [RFC5322](http://tools.ietf.org/html/rfc5322) From address. By default, the email is sent from `no-reply@example.com`. **If you want e-mails to send correctly, this should be changed to your own domain as most e-mail providers will reject mail sent from `example.com`.** - `siteName`: The public name of your application. Defaults to the DNS name of the application (eg: `awesome.meteor.com`). - `headers`: An `Object` for custom email headers as described in [`Email.send`](./email.md#Email-send). - `resetPassword`: An `Object` with the fields: - `from`: A `Function` used to override the `from` address defined by the `emailTemplates.from` field. - `subject`: A `Function` that takes a user object and returns a `String` for the subject line of a reset password email. - `text`: An optional `Function` that takes a user object and a url, and returns the body text for a reset password email. - `html`: An optional `Function` that takes a user object and a url, and returns the body html for a reset password email. - `enrollAccount`: Same as `resetPassword`, but for initial password setup for new accounts. - `verifyEmail`: Same as `resetPassword`, but for verifying the users email address. Example: ```js import { Accounts } from "meteor/accounts-base"; Accounts.emailTemplates.siteName = "AwesomeSite"; Accounts.emailTemplates.from = "AwesomeSite Admin "; Accounts.emailTemplates.enrollAccount.subject = (user) => { return `Welcome to Awesome Town, ${user.profile.name}`; }; Accounts.emailTemplates.enrollAccount.text = (user, url) => { return ( "You have been selected to participate in building a better future!" + " To activate your account, simply click the link below:\n\n" + url ); }; Accounts.emailTemplates.resetPassword.from = () => { // Overrides the value set in `Accounts.emailTemplates.from` when resetting // passwords. return "AwesomeSite Password Reset "; }; Accounts.emailTemplates.verifyEmail = { subject() { return "Activate your account now!"; }, text(user, url) { return `Hey ${user}! Verify your e-mail by following this link: ${url}`; }, }; ```

Enable 2FA for this package

You can add 2FA to your login flow by using the package [accounts-2fa](../packages/accounts-2fa.md). You can find an example showing how this would look like [here](../packages/accounts-2fa.md#working-with-accounts-password). --- --- url: /packages/accounts-2fa.md --- # accounts-2fa This package allows you to provide a way for your users to enable 2FA on their accounts, using an authenticator app such as Google Authenticator, or 1Password. When the user is logged in on your app, they will be able to generate a new QR code and read this code on the app they prefer. After that, they'll start receiving their codes. Then, they can finish enabling 2FA on your app, and every time they try to log in to your app, you can redirect them to a place where they can provide a code they received from the authenticator. To provide codes that are exactly compatible with all other Authenticator apps and services that implements TOTP, this package uses [node-2fa](https://www.npmjs.com/package/node-2fa) which works on top of [notp](https://github.com/guyht/notp), **that** implements TOTP ([RFC 6238](https://www.ietf.org/rfc/rfc6238.txt)) (the Authenticator standard), which is based on HOTP ([RFC 4226](https://www.ietf.org/rfc/rfc4226.txt)). > This package is meant to be used with [`accounts-password`](../api/accounts.md#passwords) or [`accounts-passwordless`](./accounts-passwordless.md), so if you don't have either of those in your project, you'll need to add one of them. In the future, we want to enable the use of this package with other login methods, our oauth methods (Google, GitHub, etc...). ## 2FA Activation Flow {#activating-2fa} The first step, in order to enable 2FA, is to generate a QR code so that the user can scan it in an authenticator app and start receiving codes. Receives an `appName` which is the name of your app that will show up when the user scans the QR code. Also, a callback called, on success, with a QR code in SVG format, a QR secret, and the URI that can be used to activate the 2FA in an authenticator app, or a single `Error` argument on failure. On success, this function will also add an object to the logged user's services object containing the QR secret: ```js services: { ... twoFactorAuthentication: { secret: "***" } } ``` Here it's an example on how to call this function: ```js import { Buffer } from "buffer"; import { Accounts } from 'meteor/accounts-base'; // component const [qrCode, setQrCode] = useState(null); ``` This method can fail throwing the following error: - "The 2FA is activated. You need to disable the 2FA first before trying to generate a new activation code [2fa-activated]" if trying to generate an activation when the user already have 2FA enabled. At this point, the 2FA won't be activated just yet. Now that the user has access to the codes generated by their authenticator app, you can call the function `Accounts.enableUser2fa`: It should be called with a code that the users will receive from the authenticator app once they read the QR code. The callback is called with a single `Error` argument on failure. If the code provided is correct, a `type` will be added to the user's `twoFactorAuthentication` object and now 2FA is considered enabled: ```js services: { ... twoFactorAuthentication: { type: "otp", secret: "***", } } ``` To verify whether or not a user has 2FA enabled, you can call the function `Accounts.has2faEnabled`: This function must be called when the user is logged in. ## Disabling 2FA {#disabling-2fa} To disable 2FA for a user use this method: To call this function the user must be already logged in. ## Log in with 2FA {#log-in-with-2fa} Now that you have a way to allow your users to enable 2FA on their accounts, you can create a login flow based on that. As said at the beginning of this guide, this package is currently working with two other packages: `accounts-password` and `accounts-passwordless`. Below there is an explanation on how to use this package with them. ## Working with accounts-password {#working-with-accounts-password} When calling the function `Meteor.loginWithPassword`, if the 2FA is enabled for the user, an error will be returned to the callback, so you can redirect the user to a place where they can provide a code. As an example: ```js ``` If the 2FA is not enabled, the user will be logged in normally. The function you will need to call now to allow the user to login is `Meteor.loginWithPasswordAnd2faCode`: Now you will be able to receive a code from the user and this function will verify if the code is valid. If it is, the user will be logged in. So the call of this function should look something like this: ```js ``` This method can fail throwing one of the following errors: - "2FA code must be informed [no-2fa-code]" if a 2FA code was not provided. - "Invalid 2FA code [invalid-2fa-code]" if the provided 2FA code is invalid. ## Working with accounts-passwordless {#working-with-accounts-passwordless} Following the same logic from the previous package, if the 2FA is enabled, an error will be returned to the callback of the function [`Meteor.passwordlessLoginWithToken`](./accounts-passwordless.md#Meteor-passwordlessLoginWithToken), then you can redirect the user to a place where they can provide a code. Here is an example: ```js ``` Now you can call the function `Meteor.passwordlessLoginWithTokenAnd2faCode` that will allow you to provide a selector, token, and 2FA code: This method can fail throwing one of the following errors: - "2FA code must be informed [no-2fa-code]" if a 2FA code was not provided. - "Invalid 2FA code [invalid-2fa-code]" if the provided 2FA code is invalid. ## Integrating an Authentication Package with accounts-2fa {#integrating-auth-package} To integrate this package with any other existing Login method, it's necessary following two steps: 1 - For the client, create a new method from your current login method. So for example, from the method `Meteor.loginWithPassword` we created a new one called `Meteor.loginWithPasswordAnd2faCode`, and the only difference between them is that the latest one receives one additional parameter, the 2FA code, but we call the same function on the server side. 2 - For the server, inside the function that will log the user in, you verify if the function `Accounts._check2faEnabled` exists, and if yes, you call it providing the user object you want to check if the 2FA is enabled, and if either of these statements are false, you proceed with the login flow. This function exists only when the package `accounts-2fa` is added to the project. If both statements are true, and the login validation succeeds, you verify if a code was provided: if not, throw an error; if it was provided, verify if the code is valid by calling the function `Accounts._isTokenValid`. if `Accounts._isTokenValid` returns false, throw an error. Here it's an example: ```js const result = validateLogin(); if (!result.error && Accounts._check2faEnabled?.(user)) { if (!code) { Accounts._handleError("2FA code must be informed."); } if ( !Accounts._isTokenValid(user.services.twoFactorAuthentication.secret, code) ) { Accounts._handleError("Invalid 2FA code."); } } return result; ``` --- --- url: /packages/accounts-ui.md --- # accounts-ui A turn-key user interface for Meteor Accounts. To add Accounts and a set of login controls to an application, add the `accounts-ui` package and at least one login provider package: `accounts-password`, `accounts-facebook`, `accounts-github`, `accounts-google`, `accounts-twitter`, or `accounts-weibo`. Then simply add the `{{> loginButtons}}` helper to an HTML file. This will place a login widget on the page. If there is only one provider configured and it is an external service, this will add a login/logout button. If you use `accounts-password` or use multiple external login services, this will add a "Sign in" link which opens a dropdown menu with login options. If you plan to position the login dropdown in the right edge of the screen, use `{{> loginButtons align="right"}}` in order to get the dropdown to lay itself out without expanding off the edge of the screen. To configure the behavior of `{{> loginButtons}}`, use [`Accounts.ui.config`](../api/accounts.md#loggingIn). `accounts-ui` also includes modal popup dialogs to handle links from [`sendResetPasswordEmail`](../api/accounts.md#Accounts-sendResetPasswordEmail), [`sendVerificationEmail`](../api/accounts.md#Accounts-sendVerificationEmail), and [`sendEnrollmentEmail`](../api/accounts.md#Accounts-sendEnrollmentEmail). These do not have to be manually placed in HTML: they are automatically activated when the URLs are loaded. If you want to control the look and feel of your accounts system a little more, we recommend reading the [useraccounts](http://guide.meteor.com/accounts.html#useraccounts) section of the Meteor Guide. --- --- url: /packages/appcache.md --- # AppCache > This package has been deprecated since [applicationCache](https://developer.mozilla.org/en-US/docs/Web/API/Window/applicationCache), which this package relies on, has been deprecated and is not available on the latest browsers. Plaese consider using [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) as an replacement. The `appcache` package stores the static parts of a Meteor application (the client side Javascript, HTML, CSS, and images) in the browser's [application cache](https://en.wikipedia.org/wiki/AppCache). To enable caching simply add the `appcache` package to your project. * Once a user has visited a Meteor application for the first time and the application has been cached, on subsequent visits the web page loads faster because the browser can load the application out of the cache without contacting the server first. * Hot code pushes are loaded by the browser in the background while the app continues to run. Once the new code has been fully loaded the browser is able to switch over to the new code quickly. * The application cache allows the application to be loaded even when the browser doesn't have an Internet connection, and so enables using the app offline. (Note however that the `appcache` package by itself doesn't make *data* available offline: in an application loaded offline, a Meteor Collection will appear to be empty in the client until the Internet becomes available and the browser is able to establish a DDP connection). To turn AppCache off for specific browsers use: ```js Meteor.AppCache.config({ chrome: false, firefox: false }); ``` The supported browsers that can be enabled or disabled include, but are not limited to, `android`, `chrome`, `chromium`, `chromeMobileIOS`, `firefox`, `ie`, `mobileSafari` and `safari`. Browsers limit the amount of data they will put in the application cache, which can vary due to factors such as how much disk space is free. Unfortunately if your application goes over the limit rather than disabling the application cache altogether and running the application online, the browser will instead fail that particular *update* of the cache, leaving your users running old code. Thus it's best to keep the size of the cache below 5MB. The `appcache` package will print a warning on the Meteor server console if the total size of the resources being cached is over 5MB. Starting from `appcache@1.2.5`, if you need more advanced logic to enable/disable the cache, you can use the `enableCallback` option that is evaluated on a per-request basis. For example: ```js // Enable offline mode using a value from database and certificate validation Meteor.AppCache.config({ // This option is available starting from appcache@1.2.4 enableCallback: () => { if (!getSettingsFromDb("public.appcache_enabled")) { return false; } const validation = validateClientCert({ clientCertPayload: req.headers["x-client-cert"], url: req.url.href, }); return validation.passed; }, }); ``` If you have files too large to fit in the cache you can disable caching by URL prefix. For example, ```js Meteor.AppCache.config({ onlineOnly: ['/online/'] }); ``` causes files in your `public/online` directory to not be cached, and so they will only be available online. You can then move your large files into that directory and refer to them at the new URL: ```html ``` If you'd prefer not to move your files, you can use the file names themselves as the URL prefix: ```js Meteor.AppCache.config({ onlineOnly: [ '/bigimage.jpg', '/largedata.json' ] }); ``` though keep in mind that since the exclusion is by prefix (this is a limitation of the application cache manifest), excluding `/largedata.json` will also exclude such URLs as `/largedata.json.orig` and `/largedata.json/file1`. For more information about how Meteor interacts with the application cache, see the [AppCache page](https://github.com/meteor/meteor/wiki/AppCache) in the Meteor wiki. --- --- url: /tutorials/security/security.md --- # Application Security for Production After reading this tutorial, you'll know: 1. The security surface area of a Meteor app. 2. How to secure Meteor Methods, publications, and source code. 3. Where to store secret keys in development and production. 4. How to follow a security checklist when auditing your app. 5. How App Protection works in Galaxy Hosting. [[toc]] ## Introduction Securing a web application is all about understanding security domains and understanding the attack surface between these domains. In a Meteor app, things are pretty simple: 1. Code that runs on the server can be trusted. 2. Everything else: code that runs on the client, data sent through Method and publication arguments, etc, can't be trusted. In practice, this means that you should do most of your security and validation on the boundary between these two domains. In simple terms: 1. Validate and check all inputs that come from the client. 2. Don't leak any secret information to the client. ### Concept: Attack surface Since Meteor apps are often written in a style that puts client and server code together, it's extra important to be aware what is running on the client, what is running on the server, and what the boundaries are. Here's a complete list of places security checks need to be done in a Meteor app: 1. **Methods**: Any data that comes in through Method arguments needs to be validated, and Methods should not return data the user shouldn't have access to. 2. **Publications**: Any data that comes in through publication arguments needs to be validated, and publications should not return data the user shouldn't have access to. 3. **Served files**: You should make sure none of the source code or configuration files served to the client have secret data. Each of these points will have their own section below. #### Avoid allow/deny In this guide, we're going to take a strong position that using [allow](/api/collections.html#Mongo-Collection-allow) or [deny](/api/collections.html#Mongo-Collection-deny) to run MongoDB queries directly from the client is not a good idea. The main reason is that it is hard to follow the principles outlined above. It's extremely difficult to validate the complete space of possible MongoDB operators, which could potentially grow over time with new versions of MongoDB. There have been several articles about the potential pitfalls of accepting MongoDB update operators from the client, in particular the [Allow & Deny Security Challenge](https://web.archive.org/web/20220705130732/https://www.discovermeteor.com/blog/allow-deny-security-challenge/) and its [results](https://web.archive.org/web/20220819163744/https://www.discovermeteor.com/blog/allow-deny-challenge-results/), both on the Discover Meteor blog. Given the points above, we recommend that all Meteor apps should use Methods to accept data input from the client, and restrict the arguments accepted by each Method as tightly as possible. Here's a code snippet to add to your server code which disables client-side updates on a collection. This will make sure no other part of your app can use `allow` with the collection `Lists`: ```js // Deny all client-side updates on the Lists collection Lists.deny({ insert() { return true; }, update() { return true; }, remove() { return true; }, }); ``` ## Methods Methods are the way your Meteor server accepts inputs and data from the outside world, so it's natural that they are the most important topic for security. If you don't properly secure your Methods, users can end up modifying your database in unexpected ways - editing other people's documents, deleting data, or messing up your database schema causing the app to crash. ### Validate all arguments It's much easier to write clean code if you can assume your inputs are correct, so it's valuable to validate all Method arguments before running any actual business logic. You don't want someone to pass a data type you aren't expecting and cause unexpected behavior. Consider that if you are writing unit tests for your Methods, you would need to test all possible kinds of input to the Method; validating the arguments restricts the space of inputs you need to unit test, reducing the amount of code you need to write overall. It also has the extra bonus of being self-documenting; someone else can come along and read the code to find out what kinds of parameters a Method is looking for. Just as an example, here's a situation where not checking arguments can be disastrous: ```js Meteor.methods({ removeWidget(id) { if (!this.userId) { throw new Meteor.Error('removeWidget.unauthorized'); } Widgets.remove(id); } }); ``` If someone comes along and passes a non-ID selector like `{}`, they will end up deleting the entire collection. ### jam:method To help you write good Methods that exhaustively validate their arguments, you can use a community package for Methods that enforces argument validation. Read more about how to use it in the [documentation for jam:method](/community-packages/jam-method.html). The rest of the code samples in this article will assume that you are using this package. If you aren't, you can still apply the same principles but the code will look a little different. ### Never pass userId from the client The `this` context inside every Meteor Method has some useful information about the current connection, and the most useful is [`this.userId`](/api/meteor.html#methods-userId). This property is managed by the DDP login system, and is guaranteed by the framework itself to be secure following widely-used best practices. Given that the user ID of the current user is available through this context, you should never pass the ID of the current user as an argument to a Method. This would allow any client of your app to pass any user ID they want. Let's look at an example: ```js // #1: Bad! The client could pass any user ID and set someone else's name async run({ userId, newName }) { await Meteor.users.updateAsync(userId, { $set: { name: newName } }); } // #2: Good, the client can only set the name on the currently logged in user async run({ newName }) { await Meteor.users.updateAsync(this.userId, { $set: { name: newName } }); } ``` The _only_ times you should be passing any user ID as an argument are the following: 1. This is a Method only accessible by admin users, who are allowed to edit other users. See the section about [user roles](/packages/roles) to learn how to define roles and check that a user is in a certain role. 2. This Method doesn't modify the other user, but uses it as a target; for example, it could be a Method for sending a private message, or adding a user as a friend. ### One Method per action The best way to make your app secure is to understand all of the possible inputs that could come from an untrusted source, and make sure that they are all handled correctly. The easiest way to understand what inputs can come from the client is to restrict them to as small of a space as possible. This means your Methods should all be specific actions, and shouldn't take a multitude of options that change the behavior in significant ways. The end goal is that you can look at each Method in your app and validate or test that it is secure. Here's a secure example Method from the Todos example app: ```js export const makePrivate = createMethod({ name: 'lists.makePrivate', schema: new SimpleSchema({ listId: { type: String } }), async run({ listId }) { if (!this.userId) { throw new Meteor.Error('lists.makePrivate.notLoggedIn', 'Must be logged in to make private lists.'); } const list = await Lists.findOneAsync(listId); if (list.isLastPublicList()) { throw new Meteor.Error('lists.makePrivate.lastPublicList', 'Cannot make the last public list private.'); } await Lists.updateAsync(listId, { $set: { userId: this.userId } }); Lists.userIdDenormalizer.set(listId, this.userId); } }); ``` You can see that this Method does a _very specific thing_ - it makes a single list private. An alternative would have been to have a Method called `setPrivacy`, which could set the list to private or public, but it turns out that in this particular app the security considerations for the two related operations - `makePrivate` and `makePublic` - are very different. By splitting our operations into different Methods, we make each one much clearer. It's obvious from the above Method definition which arguments we accept, what security checks we perform, and what operations we do on the database. However, this doesn't mean you can't have any flexibility in your Methods. Let's look at an example: ```js Meteor.users.methods.setUserData = createMethod({ name: 'Meteor.users.methods.setUserData', schema: new SimpleSchema({ fullName: { type: String, optional: true }, dateOfBirth: { type: Date, optional: true }, }), async run(fieldsToSet) { return (await Meteor.users.updateAsync(this.userId, { $set: fieldsToSet })); } }); ``` The above Method is great because you can have the flexibility of having some optional fields and only passing the ones you want to change. In particular, what makes it possible for this Method is that the security considerations of setting one's full name and date of birth are the same - we don't have to do different security checks for different fields being set. Note that it's very important that the `$set` query on MongoDB is generated on the server - we should never take MongoDB operators as-is from the client, since they are hard to validate and could result in unexpected side effects. #### Refactoring to reuse security rules You might run into a situation where many Methods in your app have the same security checks. This can be simplified by factoring out the security into a separate module, wrapping the Method body, or extending the `Mongo.Collection` class to do security inside the `insert`, `update`, and `remove` implementations on the server. However, implementing your client-server communication via specific Methods is still a good idea rather than sending arbitrary `update` operators from the client, since a malicious client can't send an `update` operator that you didn't test for. ### Rate limiting Like REST endpoints, Meteor Methods can be called from anywhere - a malicious program, script in the browser console, etc. It is easy to fire many Method calls in a very short amount of time. This means it can be easy for an attacker to test lots of different inputs to find one that works. Meteor has built-in rate limiting for password login to stop password brute-forcing, but it's up to you to define rate limits for your other Methods. In the Todos example app, we use the following code to set a basic rate limit on all Methods: ```js // Get list of all method names on Lists const LISTS_METHODS = _.pluck([ insert, makePublic, makePrivate, updateName, remove, ], 'name'); // Only allow 5 list operations per connection per second if (Meteor.isServer) { DDPRateLimiter.addRule({ name(name) { return _.contains(LISTS_METHODS, name); }, // Rate limit per connection ID connectionId() { return true; } }, 5, 1000); } ``` This will make every Method only callable 5 times per second per connection. This is a rate limit that shouldn't be noticeable by the user at all, but will prevent a malicious script from totally flooding the server with requests. You will need to tune the limit parameters to match your app's needs. If you're using `jam:method`, it comes with built-in [rate-limiting](https://github.com/jamauro/method#rate-limiting). ## Publications Publications are the primary way a Meteor server can make data available to a client. While with Methods the primary concern was making sure users can't modify the database in unexpected ways, with publications the main issue is filtering the data being returned so that a malicious user can't get access to data they aren't supposed to see. ### You can't do security at the rendering layer In a server-side-rendered framework like Ruby on Rails, it's sufficient to not display sensitive data in the returned HTML response. In Meteor, since the rendering is done on the client, an `if` statement in your HTML template is not secure; you need to do security at the data level to make sure that data is never sent in the first place. ### Rules about Methods still apply All of the points above about Methods apply to publications as well: 1. Validate all arguments using `check` or npm `simpl-schema`. 1. Never pass the current user ID as an argument. 1. Don't take generic arguments; make sure you know exactly what your publication is getting from the client. 1. Use rate limiting to stop people from spamming you with subscriptions. ### Always restrict fields [`Mongo.Collection#find` has an option called `projection`](/api/collections.html#Mongo-Collection-find) which lets you filter the fields on the fetched documents. You should always use this in publications to make sure you don't accidentally publish secret fields. For example, you could write a publication, then later add a secret field to the published collection. Now, the publication would be sending that secret to the client. If you filter the fields on every publication when you first write it, then adding another field won't automatically publish it. ```js // #1: Bad! If we add a secret field to Lists later, the client // will see it Meteor.publish('lists.public', function () { return Lists.find({userId: {$exists: false}}); }); // #2: Good, if we add a secret field to Lists later, the client // will only publish it if we add it to the list of fields Meteor.publish('lists.public', function () { return Lists.find({userId: {$exists: false}}, { projection: { name: 1, incompleteCount: 1, userId: 1 } }); }); ``` If you find yourself repeating the fields often, it makes sense to factor out a dictionary of public fields that you can always filter by, like so: ```js // In the file where Lists is defined Lists.publicFields = { name: 1, incompleteCount: 1, userId: 1 }; ``` Now your code becomes a bit simpler: ```js Meteor.publish('lists.public', function () { return Lists.find({userId: {$exists: false}}, { projection: Lists.publicFields }); }); ``` ### Publications and userId The data returned from publications will often be dependent on the currently logged in user, and perhaps some properties about that user - whether they are an admin, whether they own a certain document, etc. Publications are not reactive, and they only re-run when the currently logged in `userId` changes, which can be accessed through `this.userId`. Because of this, it's easy to accidentally write a publication that is secure when it first runs, but doesn't respond to changes in the app environment. Let's look at an example: ```js // #1: Bad! If the owner of the list changes, the old owner will still see it Meteor.publish('list', async function (listId) { check(listId, String); const list = await Lists.findOneAsync(listId); if (list.userId !== this.userId) { throw new Meteor.Error('list.unauthorized', 'This list doesn\'t belong to you.'); } return Lists.find(listId, { projection: { name: 1, incompleteCount: 1, userId: 1 } }); }); // #2: Good! When the owner of the list changes, the old owner won't see it anymore Meteor.publish('list', function (listId) { check(listId, String); return Lists.find({ _id: listId, userId: this.userId }, { projection: { name: 1, incompleteCount: 1, userId: 1 } }); }); ``` In the first example, if the `userId` property on the selected list changes, the query in the publication will still return the data, since the security check in the beginning will not re-run. In the second example, we have fixed this by putting the security check in the returned query itself. Unfortunately, not all publications are as simple to secure as the example above. For more tips on how to use `reywood:publish-composite` to handle reactive changes in publications, see the [data loading article](https://guide.meteor.com/data-loading#complex-auth). ### Passing options For certain applications, for example pagination, you'll want to pass options into the publication to control things like how many documents should be sent to the client. There are some extra considerations to keep in mind for this particular case. 1. **Passing a limit**: In the case where you are passing the `limit` option of the query from the client, make sure to set a maximum limit. Otherwise, a malicious client could request too many documents at once, which could raise performance issues. 2. **Passing in a filter**: If you want to pass fields to filter on because you don't want all of the data, for example in the case of a search query, make sure to use MongoDB `$and` to intersect the filter coming from the client with the documents that client should be allowed to see. Also, you should whitelist the keys that the client can use to filter - if the client can filter on secret data, it can run a search to find out what that data is. 3. **Passing in fields**: If you want the client to be able to decide which fields of the collection should be fetched, make sure to intersect that with the fields that client is allowed to see, so that you don't accidentally send secret data to the client. In summary, you should make sure that any options passed from the client to a publication can only restrict the data being requested, rather than extending it. ## Served files Publications are not the only place the client gets data from the server. The set of source code files and static assets that are served by your application server could also potentially contain sensitive data: 1. Business logic an attacker could analyze to find weak points. 1. Secret algorithms that a competitor could steal. 1. Secret API keys. ### Secret server code While the client-side code of your application is necessarily accessible by the browser, every application will have some secret code on the server that you don't want to share with the world. Secret business logic in your app should be located in code that is only loaded on the server. This means it is in a `server/` directory of your app, in a package that is only included on the server, or in a file inside a package that was loaded only on the server. If you have a Meteor Method in your app that has secret business logic, you might want to split the Method into two functions - the optimistic UI part that will run on the client, and the secret part that runs on the server. Most of the time, putting the entire Method on the server doesn't result in the best user experience. Let's look at an example, where you have a secret algorithm for calculating someone's MMR (ranking) in a game: ```js // In a server-only file, for example /imports/server/mmr.js export const MMR = { updateWithSecretAlgorithm(userId) { // your secret code here } } ``` ```js // In a file loaded on client and server Meteor.users.methods.updateMMR = new createMethod({ name: 'Meteor.users.methods.updateMMR', validate: null, run() { if (this.isSimulation) { // Simulation code for the client (optional) } else { const { MMR } = require('/imports/server/mmr.js'); MMR.updateWithSecretAlgorithm(this.userId); } } }); ``` ::: warning Note that while the Method is defined on the client, the actual secret logic is only accessible from the server and the code will **not** be included in the client bundle. Keep in mind that code inside `if (Meteor.isServer)` and `if (!this.isSimulation)` blocks is still sent to the client, it is just not executed. So don't put any secret code in there. ::: Secret API keys should never be stored in your source code at all, the next section will talk about how to handle them. ## Securing API keys Every app will have some secret API keys or passwords: 1. Your database password. 1. API keys for external APIs. These should never be stored as part of your app's source code in version control, because developers might copy code around to unexpected places and forget that it contains secret keys. You can keep your keys separately in [Dropbox](https://www.dropbox.com/), [LastPass](https://lastpass.com), or another service, and then reference them when you need to deploy the app. You can pass settings to your app through a _settings file_ or an _environment variable_. Most of your app settings should be in JSON files that you pass in when starting your app. You can start your app with a settings file by passing the `--settings` flag: ```sh # Pass development settings when running your app locally meteor --settings development.json # Pass production settings when deploying your app to Galaxy meteor deploy myapp.com --settings production.json ``` Here's what a settings file with some API keys might look like: ```js { "facebook": { "appId": "12345", "secret": "1234567" } } ``` In your app's JavaScript code, these settings can be accessed from the variable `Meteor.settings`. [Read more about managing keys and settings in the Deployment article.](https://guide.meteor.com/deployment) ### Settings on the client In most normal situations, API keys from your settings file will only be used by the server, and by default the data passed in through `--settings` is only available on the server. However, if you put data under a special key called `public`, it will be available on the client. You might want to do this if, for example, you need to make an API call from the client and are OK with users knowing that key. Public settings will be available on the client under `Meteor.settings.public`. ### Never store valuable information in public property in settings file It's ok if you want to make some properties of your settings file accessible to the client but never put any valuable information inside the `public` property. Either explicity store it under `private` property or in its own `property`. Any property that's not under `public` is treated as private by default in Meteor. ```javascript { "public": {"publicKey": "xxxxx"}, "private": {"privateKey": "xxxxx"} } ``` or ```javascript { "public": {"publicKey": "xxxxx"}, "privateKey": "xxxxx" } ``` #### Example: API keys for OAuth For the `accounts-facebook` package to pick up these keys, you need to add them to the service configuration collection in the database. Here's how you do that: First, add the `service-configuration` package: ```sh meteor add service-configuration ``` Then, upsert into the `ServiceConfiguration` collection using private settings: ```js ServiceConfiguration.configurations.upsert({ service: "facebook" }, { $set: { appId: Meteor.settings.private.facebook.appId, loginStyle: "popup", secret: Meteor.settings.private.facebook.secret } }); ``` Now, `accounts-facebook` will be able to find that API key and Facebook login will work properly. ## SSL This is a very short section, but it deserves its own place in the table of contents. **Every production Meteor app that handles user data should run with SSL.** Yes, Meteor does hash your password or login token on the client before sending it over the wire, but that only prevents an attacker from figuring out your password - it doesn't prevent them from logging in as you, since they could send the hashed password to the server to log in! No matter how you slice it, logging in requires the client to send sensitive data to the server, and the only way to secure that transfer is by using SSL. Note that the same issue is present when using cookies for authentication in a normal HTTP web application, so any app that needs to reliably identify users should be running on SSL. #### Setting up SSL * On [Galaxy](https://guide.meteor.com/deployment#galaxy), configuration of SSL is automatic. [See the help article about SSL on Galaxy](https://help.galaxycloud.app/en/article/encryption-pt8wbl/). * If you are running on your own [infrastructure](https://guide.meteor.com/deployment#custom-deployment), there are a few options for setting up SSL, mostly through configuring a proxy web server. See the articles: [Josh Owens on SSL and Meteor](http://joshowens.me/ssl-and-meteor-js/), [SSL on Meteorpedia](http://www.meteorpedia.com/read/SSL), and [Digital Ocean tutorial with an Nginx config](https://www.digitalocean.com/community/tutorials/how-to-deploy-a-meteor-js-application-on-ubuntu-14-04-with-nginx). #### Forcing SSL Generally speaking, all production HTTP requests should go over HTTPS, and all WebSocket data should be sent over WSS. It's best to handle the redirection from HTTP to HTTPS on the platform which handles the SSL certificates and termination. * On [Galaxy](https://guide.meteor.com/deployment#galaxy), enable the "Force HTTPS" setting on a specific domain in the "Domains & Encryption" section of the application's "Settings" tab. * Other deployments *may* have control panel options or may need to be manually configured on the proxy server (e.g. HAProxy, nginx, etc.). The articles linked above provide some assistance on this. In the event that a platform does not offer the ability to configure this, the `force-ssl` package can be added to the project and Meteor will attempt to intelligently redirect based on the presence of the `x-forwarded-for` header. ## HTTP Headers HTTP headers can be used to improve the security of apps, although these are not a silver bullet, they will assist users in mitigating more common attacks. ### Recommended: Helmet Although there are many great open source solutions for setting HTTP headers, Meteor recommends [Helmet](https://helmetjs.github.io/). Helmet is a collection of 12 smaller middleware functions that set HTTP headers. First, install helmet. ```js meteor npm install helmet --save ``` By default, Helmet can be used to set various HTTP headers (see link above). These are a good starting point for mitigating common attacks. To use the default headers, users should use the following code anywhere in their server side meteor startup code. > Note: Meteor has not extensively tested each header for compatibility with Meteor. Only headers listed below have been tested. ```js // With other import statements import helmet from "helmet"; // Within server side Meter.startup() WebApp.handlers.use(helmet()) ``` At a minimum, Meteor recommends users to set the following headers. Note that code examples shown below are specific to Helmet. ### Content Security Policy > Note: Content Security Policy is not configured using Helmet's default header configuration. From MDN, Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. It is recommended that users use CSP to protect their apps from access by third parties. CSP assists to control how resources are loaded into your application. By default, Meteor recommends unsafe inline scripts and styles are allowed, since many apps typically use them for analytics, etc. Unsafe eval is disallowed, and the only allowable content source is same origin or data, except for connect which allows anything (since meteor apps make websocket connections to a lot of different origins). Browsers will also be told not to sniff content types away from declared content types. ```js // With other import statements import helmet from "helmet"; // Within server side Meter.startup() WebApp.handlers.use( helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], connectSrc: ["*"], imgSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], } }) ); ``` Helmet supports a large number of directives, users should further customise their CSP based on their needs. For more detail please read the following guide: [Content Security Policy](https://helmetjs.github.io/docs/csp/). CSP can be complex, so in addition there are some excellent tools out there to help, including [Google's CSP Evaluator](https://csp-evaluator.withgoogle.com/), [Report-URI's CSP Builder](https://report-uri.com/home/generate), [CSP documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) from Mozilla and [CSPValidator](https://cspvalidator.org/). The following example presents a potential CSP and other Security Headers used in a Production Meteor Application. This configuration may require customization, depending on your setup and use-cases. ```javascript /* global __meteor_runtime_config__ */ import { Meteor } from 'meteor/meteor' import { WebApp } from 'meteor/webapp' import { Autoupdate } from 'meteor/autoupdate' import { check } from 'meteor/check' import crypto from 'crypto' import helmet from 'helmet' const self = '\'self\'' const data = 'data:' const unsafeEval = '\'unsafe-eval\'' const unsafeInline = '\'unsafe-inline\'' const allowedOrigins = Meteor.settings.allowedOrigins // create the default connect source for our current domain in // a multi-protocol compatible way (http/ws or https/wss) const url = Meteor.absoluteUrl() const domain = url.replace(/http(s)*:\/\//, '').replace(/\/$/, '') const s = url.match(/(?!=http)s(?=:\/\/)/) ? 's' : '' const usesHttps = s.length > 0 const connectSrc = [ self, `http${s}://${domain}`, `ws${s}://${domain}` ] // Prepare runtime config for generating the sha256 hash // It is important, that the hash meets exactly the hash of the // script in the client bundle. // Otherwise the app would not be able to start, since the runtimeConfigScript // is rejected __meteor_runtime_config__ is not available, causing // a cascade of follow-up errors. const runtimeConfig = Object.assign(__meteor_runtime_config__, Autoupdate, { // the following lines may depend on, whether you called Accounts.config // and whether your Meteor app is a "newer" version accountsConfigCalled: true, isModern: true }) // add client versions to __meteor_runtime_config__ Object.keys(WebApp.clientPrograms).forEach(arch => { __meteor_runtime_config__.versions[arch] = { version: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].version(), versionRefreshable: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].versionRefreshable(), versionNonRefreshable: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].versionNonRefreshable(), // comment the following line if you use Meteor < 2.0 versionReplaceable: Autoupdate.autoupdateVersion || WebApp.clientPrograms[arch].versionReplaceable() } }) const runtimeConfigScript = `__meteor_runtime_config__ = JSON.parse(decodeURIComponent("${encodeURIComponent(JSON.stringify(runtimeConfig))}"))` const runtimeConfigHash = crypto.createHash('sha256').update(runtimeConfigScript).digest('base64') const helpmentOptions = { contentSecurityPolicy: { blockAllMixedContent: true, directives: { defaultSrc: [self], scriptSrc: [ self, // Remove / comment out unsafeEval if you do not use dynamic imports // to tighten security. However, if you use dynamic imports this line // must be kept in order to make them work. unsafeEval, `'sha256-${runtimeConfigHash}'` ], childSrc: [self], // If you have external apps, that should be allowed as sources for // connections or images, your should add them here // Call helmetOptions() without args if you have no external sources // Note, that this is just an example and you may configure this to your needs connectSrc: connectSrc.concat(allowedOrigins), fontSrc: [self, data], formAction: [self], frameAncestors: [self], frameSrc: ['*'], // This is an example to show, that we can define to show images only // from our self, browser data/blob and a defined set of hosts. // Configure to your needs. imgSrc: [self, data, 'blob:'].concat(allowedOrigins), manifestSrc: [self], mediaSrc: [self], objectSrc: [self], // these are just examples, configure to your needs, see // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox sandbox: [ // allow-downloads-without-user-activation // experimental 'allow-forms', 'allow-modals', // 'allow-orientation-lock', // 'allow-pointer-lock', // 'allow-popups', // 'allow-popups-to-escape-sandbox', // 'allow-presentation', 'allow-same-origin', 'allow-scripts', // 'allow-storage-access-by-user-activation ', // experimental // 'allow-top-navigation', // 'allow-top-navigation-by-user-activation' ], styleSrc: [self, unsafeInline], workerSrc: [self, 'blob:'] } }, // see the helmet documentation to get a better understanding of // the following configurations and settings strictTransportSecurity: { maxAge: 15552000, includeSubDomains: true, preload: false }, referrerPolicy: { policy: 'no-referrer' }, expectCt: { enforce: true, maxAge: 604800 }, frameguard: { action: 'sameorigin' }, dnsPrefetchControl: { allow: false }, permittedCrossDomainPolicies: { permittedPolicies: 'none' } } // We assume, that we are working on a localhost when there is no https // connection available. // Run your project with --production flag to simulate script-src hashing if (!usesHttps && Meteor.isDevelopment) { delete helpmentOptions.contentSecurityPolicy.blockAllMixedContent; helpmentOptions.contentSecurityPolicy.directives.scriptSrc = [ self, unsafeEval, unsafeInline, ]; } // finally pass the options to helmet to make them apply helmet(helpmentOptions) ``` ### X-Frame-Options > Note: The X-Frame Options header is configured using Helmet's default header configuration. From MDN, the X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a ``, ` ## Contributing to this documentation We welcome contributions to the documentation! Since documentation is always evolving, your help is really valuable to ensure content is accurate and up-to-date. ### How to Contribute #### Identify elements that need improvement Are you new here? Please check our [documentation issues](https://github.com/meteor/meteor/issues?q=is%3Aissue%20state%3Aopen%20label%3AProject%3ADocs). If in doubt about the best way to implement something, please create additional conversation on the issue. Issues are not assigned, you don’t need to wait for approval before contributing. Jump right in and open a PR — this increases your chances of getting your work merged, since issues can be claimed fast. #### Do the changes and share them Documentation live in the `v3-docs/docs` directory of the [Meteor GitHub repository](https://github.com/meteor/meteor/tree/devel/v3-docs/docs) For small changes, such as fixing typos or formatting, you can simply click the "Edit this page on GitHub" button in the footer to edit the file and submit a PR. For larger changes, you need to fork meteor repo and start your work from the `devel` branch. You must test your contribution locally before submitting a pull request. To do so, here are the steps: 1. `cd v3-docs/docs && npm run docs:dev` will run the docs locally at [http://localhost:5173/](http://localhost:5173/) 2. Make your changes and verify them in the browser. 3. run `npm run docs:build` to ensure the build works correctly. 4. Push your work and submit a documented pull request to the `devel` branch. If you add a new page to the documentation, please make sure the configuration creates a link to access it (see [.vitepress/config.mts](https://github.com/meteor/meteor/blob/devel/v3-docs/docs/.vitepress/config.mts)). --- --- url: /CONTRIBUTING.md --- --- --- url: /api/index.md --- --- --- url: /generators/changelog/versions/3.3.2.md --- ## v3.3.2, 01-09-2025 ### Highlights - Async-compatible account URLs and email-sending coverage [#13740](https://github.com/meteor/meteor/pull/13740) - Move `findUserByEmail` method from `accounts-password` to `accounts-base` [#13859](https://github.com/meteor/meteor/pull/13859) - Return `insertedId` on client `upsert` to match Meteor 2.x behavior [#13891](https://github.com/meteor/meteor/pull/13891) - Unrecognized operator bug fixed [#13895](https://github.com/meteor/meteor/pull/13895) - Security fix for `sha.js` [#13908](https://github.com/meteor/meteor/pull/13908) All Merged PRs@[GitHub PRs 3.3.2](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3.2) #### Breaking Changes N/A ##### Cordova Upgrade - Enable modern browser support for Cordova unless explicitly disabled [#13896](https://github.com/meteor/meteor/pull/13896) #### Internal API changes - lodash.template dependency was removed [#13898](https://github.com/meteor/meteor/pull/13898) #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.3.2 ``` --- If you find any issues, please report them to the [Meteor issues tracker](https://github.com/meteor/meteor). #### Bumped Meteor Packages - accounts-base@3.1.2 - accounts-password@3.2.1 - accounts-passwordless@3.0.2 - meteor-node-stubs@1.2.24 - babel-compiler@7.12.2 - boilerplate-generator@2.0.2 - ecmascript@0.16.13 - minifier@3.0.4 - minimongo@2.0.4 - mongo@2.1.4 - coffeescript-compiler@2.4.3 - npm-mongo@6.16.1 - shell-server@0.6.2 - typescript@5.6.6 #### Bumped NPM Packages - meteor-node-stubs@1.2.23 #### Special thanks to ✨✨✨ - [@italojs](https://github.com/italojs) - [@nachocodoner](https://github.com/nachocodoner) - [@graemian](https://github.com/graemian) - [@Grubba27](https://github.com/Grubba27) - [@copleykj](https://github.com/copleykj) ✨✨✨ --- --- url: /generators/changelog/versions/3.3.1.md --- ## v3.3.1, 05-08-2025 ### Highlights - **MongoDB Driver Upgrades** - Upgraded core MongoDB driver to `6.16.0` to address latest issues reported [#13710](https://github.com/meteor/meteor/pull/13710) - Introduced `npm-mongo-legacy` to maintain compatibility with MongoDB 3.6 via `mongodb@6.9.0` [#13736](https://github.com/meteor/meteor/pull/13736) - Mitigated a cursor leak issue by synchronizing `next()` and `close()` operations [#13786](https://github.com/meteor/meteor/pull/13786) - **Improved SWC integration** - Fixed edge cases in config cache invalidation [#13809](https://github.com/meteor/meteor/pull/13809) - Ensured `@swc/helpers` is consistently used for better bundle size and performance [#13820](https://github.com/meteor/meteor/pull/13820) - Updated to SWC `1.12.14` [#13851](https://github.com/meteor/meteor/pull/13851) - **Tooling and Build System** - Fixed regression affecting rebuild behavior [#13810](https://github.com/meteor/meteor/pull/13810) - Addressed issues getting performance profiles in mounted volumes [#13827](https://github.com/meteor/meteor/pull/13827) - Fallback to Babel parser when Acorn fails to parse source code [#13844](https://github.com/meteor/meteor/pull/13844) - **Mobile Support** - Upgraded Cordova platform to version 14 [#13837](https://github.com/meteor/meteor/pull/13837) - **Developer Experience** - Added TypeScript types for `isModern` and `getMinimumBrowserVersions` functions [#13704](https://github.com/meteor/meteor/pull/13704) - Enhanced CLI help output and documented admin commands [#13826](https://github.com/meteor/meteor/pull/13826) - **Vite Tooling** - Updated official Meteor + Vite skeletons [#13835](https://github.com/meteor/meteor/pull/13835) - **Runtime & Dependencies** - Updated to Node.js `22.18.0` and NPM `10.9.3` [#13877](https://github.com/meteor/meteor/pull/13877) - Bumped `meteor-node-stubs` to `1.2.21` [#13825](https://github.com/meteor/meteor/pull/13825) All Merged PRs@[GitHub PRs 3.3.1](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3.1) #### Breaking Changes ##### MongoDB Driver Upgrades If you're using MongoDB 3.6 or earlier, install the new legacy package: ```bash meteor add npm-mongo-legacy ``` This will pin the MongoDB driver to 6.9.0 for compatibility. If you’re on MongoDB 4+, the default [MongoDB driver 6.16.0](https://github.com/mongodb/node-mongodb-native/releases/tag/v6.16.0) is applied automatically. Please migrate your database as soon as possible to MongoDB 5 onward, as [MongoDB driver 6.17.0](https://github.com/mongodb/node-mongodb-native/releases/tag/v6.17.0) will drop MongoDB 4 support. We’ll keep offering `npm-mongo-legacy` so you can keep getting Meteor updates with your existing MongoDB legacy version. ##### Cordova Upgrade The Cordova platform has been upgraded to version 14. Refer to the [Cordova Changelog](https://cordova.apache.org/announcements/2025/03/26/cordova-android-14.0.0.html) for more details on the changes and migration steps. #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.3.1 ``` --- While this is a patch release, Meteor 3.3, a recent minor update, introduced a modern build stack that’s now the default for new apps. Here’s how you can migrate to it. **Add this to your `package.json` to enable the new modern build stack:** ```json "meteor": { "modern": true } ``` Check the docs for help with the SWC migration, especially if your project uses many Babel plugins. [Modern Transpiler: SWC docs](https://docs.meteor.com/about/modern-build-stack/transpiler-swc.html) If you find any issues, please report them to the [Meteor issues tracker](https://github.com/meteor/meteor). #### Bumped Meteor Packages - babel-compiler@7.12.1 - callback-hook@1.6.1 - ecmascript@0.16.12 - minifier-js@3.0.3 - minimongo@2.0.3 - modern-browsers@0.2.3 - mongo@2.1.3 - npm-mongo-legacy@6.9.0 - npm-mongo@6.16.0 - standard-minifier-js@3.1.1 - tinytest@1.3.2 - typescript@5.6.5 - meteor-tool@3.3.1 #### Bumped NPM Packages - meteor-node-stubs@1.2.21 #### Special thanks to ✨✨✨ - [@nachocodoner](https://github.com/nachocodoner) - [@italojs](https://github.com/italojs) - [@StorytellerCZ](https://github.com/StorytellerCZ) - [@JorgenVatle](https://github.com/JorgenVatle) - [@welkinwong](https://github.com/welkinwong) - [@Saksham-Goel1107](https://github.com/Saksham-Goel1107) ✨✨✨ --- --- url: /generators/changelog/versions/3.3.0.md --- ## v3.3.0, 2025-06-11 ### Highlights - Support SWC transpiler and minifier for faster dev and builds [PR#13657](https://github.com/meteor/meteor/pull/13657), [PR#13715](https://github.com/meteor/meteor/pull/13715) - Switch to `@parcel/watcher` for improved native file watching [PR#13699](https://github.com/meteor/meteor/pull/13699), [#13707](https://github.com/meteor/meteor/pull/13707) - Default to modern architecture, skip legacy processing [PR#13665](https://github.com/meteor/meteor/pull/13665), [PR#13698](https://github.com/meteor/meteor/pull/13698) - Optimize SQLite for faster startup and better performance [PR#13702](https://github.com/meteor/meteor/pull/13702) - Support CPU profiling in Meteor 3 bundler [PR#13650](https://github.com/meteor/meteor/pull/13650) - Improve `meteor profile`: show rebuild steps and total, support `--build` [PR#16](https://github.com/meteor/performance/pull/16), [PR#13694](https://github.com/meteor/meteor/pull/13694) - Improve `useFind` and `useSubscribe` React hooks - Add `replaceEmailAsync` helper to Accounts [PR#13677](https://github.com/meteor/meteor/pull/13677) - Fix user agent detection and oplog collection filtering - Refine type definitions for Meteor methods and SSR's ServerSink - Allow opting out of usage stats with `DO_NOT_TRACK` - Update Node to 22.16.0 and Express to 5.1.0 All Merged PRs@[GitHub PRs 3.3](https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.3) React Packages Changelog: [react-meteor-data@4.0.0](https://github.com/meteor/react-packages/tree/master/packages/react-meteor-data/CHANGELOG.md#v400-2025-06-11) #### Breaking Changes - File watching strategy switched to `@parcel/watcher` - Most setups should be fine, but if issues appear, like when using WSL with host, volumes, or remote setups—switch to polling. - Set `METEOR_WATCH_FORCE_POLLING=true` to enable polling. - Set `METEOR_WATCH_POLLING_INTERVAL_MS=1000` to adjust the interval. - `react-meteor-data@4.0.0` - Independent from the core, only applies if upgraded manually. - useFind describes no deps by default [PR#431](https://github.com/meteor/react-packages/pull/431) #### Internal API changes - `express@5.1.0` - Depends on Meteor’s `webapp` package. - Deprecates non-native promise usage [#154](https://github.com/pillarjs/router/pull/154) - Use `async/await` or `Promise.resolve` when defining endpoints to avoid deprecation warnings. #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.3 ``` To apply react-meteor-data changes: ```bash meteor add react-meteor-data@4.0.0 ``` **Add this to your `package.json` to enable the new modern build stack:** ```json "meteor": { "modern": true } ``` > These settings are on by default for new apps. On activate `modern` your app will be updated to use SWC transpiler. It will automatically fallback to Babel if your code can't be transpiled wit SWC. Check the docs for help with the SWC migration, especially if your project uses many Babel plugins. [Modern Transpiler: SWC docs](https://docs.meteor.com/about/modern-build-stack/transpiler-swc.html) If you find any issues, please report them to the [Meteor issues tracker](https://github.com/meteor/meteor). #### Bumped Meteor Packages - accounts-base@3.1.1 - accounts-password@3.2.0 - autoupdate@2.0.1 - babel-compiler@7.12.0 - boilerplate-generator@2.0.1 - ddp-client@3.1.1 - ecmascript@0.16.11 - ejson@1.1.5 - meteor@2.1.1 - minifier-js@3.0.2 - modern-browsers@0.2.2 - mongo@2.1.2 - server-render@0.4.3 - socket-stream-client@0.6.1 - standard-minifier-js@3.1.0 - typescript@5.6.4 - webapp@2.0.7 - meteor-tool@3.3.0 #### Bumped NPM Packages - meteor-node-stubs@1.2.17 #### Special thanks to ✨✨✨ - [@nachocodoner](https://github.com/nachocodoner) - [@italojs](https://github.com/italojs) - [@Grubba27](https://github.com/Grubba27) - [@zodern](https://github.com/zodern) - [@9Morello](https://github.com/9Morello) - [@welkinwong](https://github.com/welkinwong) - [@Poyoman39](https://github.com/Poyoman39) - [@PedroMarianoAlmeida](https://github.com/PedroMarianoAlmeida) - [@harryadel](https://github.com/harryadel) - [@ericm546](https://github.com/ericm546) - [@StorytellerCZ](https://github.com/StorytellerCZ) ✨✨✨ --- --- url: /generators/changelog/versions/3.2.2.md --- ## v3.2.2, 2025-05-02 ### Highlights - Improved parsing of `x-forwarded-for` headers in Session._clientAddress: - Changed header splitting method to handle comma-separated values more reliably - Added explicit trimming of IP addresses with map function - Modified validation check to require exact match for httpForwardedCount #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.2.2 ``` #### Bumped Meteor Packages - ddp-server@3.1.1 #### Bumped NPM Packages #### Special thanks to ✨✨✨ - [@italojs](https://github.com/italojs) - [@ShiyuBanzhou](https://github.com/ShiyuBanzhou) ✨✨✨ --- --- url: /generators/changelog/versions/3.2.0.md --- ## v3.2.0, 2025-03-18 ### Highlights - Upgrade to Node 22.14.0 & Mongo 7.0.16. - Implement `meteor profile` command to show performance metrics of Meteor apps. - Fix Meteor profiler to handle promises and display metrics correctly. - Support for argon2 to improve password security over bcrypt. [PR #13554](https://github.com/meteor/meteor/pull/13554) - Improve and fix package resolution logic. [PR #13604](https://github.com/meteor/meteor/pull/13604) - Fix rare oplog issue that could cause data loss for clients. [PR #13603](https://github.com/meteor/meteor/pull/13603) All detailed PRs can be found here: https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.2 #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.2 ``` #### Bumped Meteor Packages - accounts-base@3.1.0 - accounts-password@3.1.0 - ecmascript-runtime-client@0.12.3 - facebook-oauth@1.11.6 - fetch@0.1.6 - logging@1.3.6 - logic-solver@3.1.0 - minifier-css@2.0.1 - meteor-tool@3.2.0 - modern-browsers@0.2.1 - mongo@2.1.1 - oauth@3.0.2 - test-helpers@2.0.3 #### Bumped NPM Packages - meteor-node-stubs@1.2.13 #### Special thanks to ✨✨✨ - [@nachocodoner](https://github.com/nachocodoner) - [@Grubba27](https://github.com/Grubba27) - [@vparpoil](https://github.com/vparpoil) - [@cunneen](https://github.com/cunneen) - [@gbuerk](https://github.com/gbuerk) - [@julio-rocketchat](https://github.com/julio-rocketchat) - [@StorytellerCZ](https://github.com/StorytellerCZ) - [@dr-dimitru](https://github.com/dr-dimitru) - [@Seb-Dion](https://github.com/Seb-Dion) ✨✨✨ --- --- url: /generators/changelog/versions/3.1.2.md --- ## v3.1.2, 2025-02-06 ### Highlights - Node upgrade: v22.13.1 - Updated Meteor skeletons for Vue and Solid - Improved browser detection and related documentation - Security updates: nodemailer-openpgp and others in Meteor packages #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.1.2 ``` #### Bumped Meteor Packages - email@3.1.2 - meteor-tool@3.1.2 - modern-browsers@0.2.0 - webapp@2.0.5 #### Bumped NPM Packages N/A #### Special thanks to ✨✨✨ - [@nachocodoner](https://github.com/nachocodoner) - [@Grubba27](https://github.com/Grubba27) - [@perbergland](https://github.com/perbergland) - [@StorytellerCZ](https://github.com/StorytellerCZ) - [@julio-rocketchat](https://github.com/julio-rocketchat) ✨✨✨ --- --- url: /generators/changelog/versions/3.1.1.md --- ## v3.1.1, 2025-01-15 ### Highlights - Node upgrade: v22.13.0 - Real-time Performance Boost: Refactored AsynchronousQueue for parallel processing and optimized MongoDB observers for faster initial document handling. - Allow/Deny Rules Update: Deprecated async rules and updated documentation and types. - Mongo Driver Downgrade: Reverted to a stable version to prevent issues in Meteor. - Support for Meteor.deprecate: Added deprecation warnings with stack trace for outdated code usage. - OAuth Fixes: Resolved issues with cancel popup event and callback. - Windows Fixes: Fixed issues with running from folders with spaces and post-npm install build errors on Windows. - Security Updates: Upgraded json5 and semver from babel compiler. #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.1.1 ``` #### Bumped Meteor Packages - accounts-base@3.0.4 - accounts-oauth@1.4.6 - accounts-passwordless@3.0.1 - allow-deny@2.1.0 - babel-compiler@7.11.3 - ddp-client@3.1.0 - ddp-server@3.1.0 - meteor-tool@3.1.1 - facebook-oauth@1.11.5 - meteor@2.1.0 - meteor-tool@3.1.1 - mongo@2.1.0 - npm-mongo@6.10.2 - oauth@3.0.1 - roles@@1.0.1 - socket-stream-client@0.6.0 #### Bumped NPM Packages N/A #### Special thanks to ✨✨✨ - [@leonardoventurini](https://github.com/leonardoventurini) - [@nachocodoner](https://github.com/nachocodoner) - [@Grubba27](https://github.com/Grubba27) - [@9Morello](https://github.com/9Morello) - [@perbergland](https://github.com/perbergland) - [@StorytellerCZ](https://github.com/StorytellerCZ) - [@MrSpark2591](https://github.com/MrSpark2591) - [@jstarpl](https://github.com/jstarpl) - [@minhna](https://github.com/minhna) - [@svolkomorov](https://github.com/svolkomorov) - [@quyetdgroup](https://github.com/quyetdgroup) ✨✨✨ --- --- url: /generators/changelog/versions/3.1.0.md --- ## v3.1.0, 2024-11-20 ### Highlights - Upgrade to Node v22 - Upgrade Mongo Driver to v6 - Upgrade Express to v5 - HMR performance improvements - Bring the `roles` package into the core - Solve remaining issues with Top Level Await and Reify - Refactor part of the real-time code in the `mongo` package into TypeScript - Deprecate `underscore` and eliminate it from packages - Fix Cordova dependency issues - Simplify Meteor.EnvironmentVariable and make sure it works in all cases - Stop using Rosetta for development MongoDB on Apple Silicon - Test improvements - Bump `http` package to 3.0.0 #### Breaking Changes - Express is now at v5 and some it's APIs have changed. #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.1 ``` Make sure to migrate userland code to use the new Express APIs: https://expressjs.com/en/guide/migrating-5.html The MongoDB Node.js driver implemented a significant breaking change regarding operation handling. Applications leveraging `rawCollection()` or `rawDatabase()` methods must now utilize Promise-based syntax exclusively, as the callback-pattern API has been deprecated and removed. This modification affects all direct MongoDB operations previously using callback functions. #### Bumped Meteor Packages - accounts-password@3.0.3 - babel-compiler@7.11.2 - boilerplate-generator-tests@1.5.3 - ddp-client@3.0.3 - ddp-server@3.0.3 - http@3.0.0 - ecmascript@0.16.10 - email@3.1.1 - meteor-tool@3.1.0 - meteor@2.0.2 - minifier-js@3.0.1 - minimongo@2.0.2 - modules@0.20.3 - mongo@2.0.3 - coffeescript-compiler@2.4.2 - coffeescript@2.7.2 - mongo-decimal@0.2.0 - npm-mongo@6.10.0 - shell-server@0.6.1 - test-helpers@2.0.2 - test-in-console@2.0.1 - tinytest@1.3.1 - typescript@5.6.3 - url@1.3.5 - webapp@2.0.4 #### Bumped NPM Packages - meteor-babel@7.20.1 - meteor-installer@3.1.0 - meteor-node-stubs@1.2.12 #### Special thanks to ✨✨✨ - [@denihs](https://github.com/denihs) - [@nachocodoner](https://github.com/nachocodoner) - [@leonardoventurini](https://github.com/leonardoventurini) - [@StorytellerCZ](https://github.com/StorytellerCZ) - [@zodern](https://github.com/zodern) - [@harry97](https://github.com/harry97) - [@permb](https://github.com/permb) - [@9Morello](https://github.com/9Morello) - [@wreiske](https://github.com/wreiske) - [@MarcoTribuz](https://github.com/MarcoTribuz) - [@MeteorCoder](https://forums.meteor.com/u/meteorcoder/summary) ✨✨✨ --- --- url: /generators/changelog/versions/3.0.4.md --- ## v3.0.4, 2024-10-15 ### Highlights - Node 20.18.0 & Typescript 5.6.2 - Updated webapp dependencies. - DDP-server and DDP-client removed underscore - Remove dependencies on Blaze packages when using static-html - Fix Cordova on Windows - Fix Cordova build on using plugins describing dependencies - Various Windows specific fixes #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.0.4 ``` #### Meteor Version Release * `Bumped packages`: - accounts-base@3.0.3 - babel-compiler@7.11.1 - caching-compiler@2.0.1 - check@1.4.4 - ddp-client@3.0.2 - ddp-server@3.0.2 - ecmascript-runtime@0.8.3 - modules@0.20.2 - static-html-tools@1.0.0 - static-html@1.4.0 - url@1.3.4 - webapp@2.0.3 - meteor-tool@3.0.4 #### Special thanks to N/A --- --- url: /generators/changelog/versions/3.0.3.md --- ## v3.0.3, 2024-09-11 ### Highlights - Fixed `Meteor.userId` only being invoked with `bindEnvironment`. - Updated to Node `20.17.x`. - Fixed an issue where `meteor --open` opens the browser before the app is started. - Investigated and addressed the error when installing the `jam:method` package. - Improved the message for new available versions when running an app. - Updated the documentation link inside `install.sh`. - Resolved the issue where subscriptions stopped after a parameter change. - Added MongoDB connection telemetry. - Bumped the `email` package to prevent update errors. - Cordova package updates #### Breaking Changes N/A #### Internal API changes - Some internal changes to how async contexts are handled, ensuring better performance and garbage collection. #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.0.3 ``` If you've had your Meteor installation for over a year, we suggest reinstalling it to avoid any package installation issues. You can do this by running a few quick commands: ```bash npx meteor uninstall // or rm -rf ~/.meteor npx meteor ``` #### Meteor Version Release * `Bumped packages`: - accounts-base@3.0.2 - accounts-password@3.0.2 - email@3.1.0 - mongo@2.0.2 #### Special thanks to - [@ayewo](https://github.com/ayewo). - [@denihs](https://github.com/denihs). - [@harryadel](https://github.com/harryadel). - [@kbarr1212](https://github.com/kbarr1212). - [@leonardoventurini](https://github.com/leonardoventurini). - [@nachocodoner](https://github.com/nachocodoner). --- --- url: /generators/changelog/versions/3.0.2.md --- ## v3.0.2, 2024-08-14 ### Highlights * Bump the patch for some packages, so we publish them using Meteor 3 tooling. [PR #13231] * Fix subscription still resetting documents [PR #13236] * Fix auth sub sending ready twice on load [PR #13247] * Remove version constraints from http package in accounts-twitter [PR #13268] * Deprecate Meteor.user() usage on server side [PR #13288] #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.0.2 ``` #### Meteor Version Release * `Bumped packages`: - accounts-2fa@3.0.1 - accounts-base@3.0.1 - accounts-password@3.0.1 - accounts-twitter@1.5.2 - ddp-client@3.0.1 - ddp-common@1.4.4 - ddp-server@3.0.1 - email@3.0.1 - meteor@2.0.1 - minimongo@2.0.1 - mongo@2.0.1 - npm-mongo@4.17.4 - test-helpers@2.0.1 - webapp@2.0.1 #### Special thanks to - [@leonardoventurini](https://github.com/leonardoventurini). - [@StorytellerCZ](https://github.com/StorytellerCZ). For making this great framework even better! --- --- url: /generators/changelog/versions/3.0.1.md --- ## v3.0.1, 2024-07-16 ### Highlights * Bump the patch for some packages, so we publish them using Meteor 3 tooling. [PR #13231] #### Breaking Changes N/A #### Internal API changes N/A #### Migration Steps Please run the following command to update your project: ```bash meteor update --release 3.0.1 ``` #### Meteor Version Release * `Bumped packages`: - accounts-ui-unstyled@1.7.2 - crosswalk@1.7.3 - facebook-oauth@1.11.4 - npm-mongo@4.17.3 - package-version-parser@3.2.3 - twitter-config-ui@1.0.2 #### Special thanks to - [@denihs](https://github.com/denihs). For making this great framework even better! --- --- url: /generators/changelog/versions/3.0.0.md --- ## v3.0, 2024-07-15 ### Highlights #### Breaking Changes - Meteor tool - 'meteor reset' command only affects the local project cache. Use `--db` option to also remove the local database. - `accounts-2fa@3.0.0`: - Some methods are now async. See below: - `Accounts._is2faEnabledForUser` - `(Meteor Method) - generate2faActivationQrCode` - `(Meteor Method) - enableUser2fa` - `(Meteor Method) - disableUser2fa` - `(Meteor Method) - has2faEnabled` - `accounts-base@3.0.0`: - `methods.removeOtherTokens` is now async - `Accounts.destroyToken` is now async - `Accounts.insertUserDoc` is now async - `Accounts.updateOrCreateUserFromExternalService` is now async - `Accounts.expirePasswordToken` is now async - `Accounts.setupUsersCollection` is now async - `Meteor.user` is now async in server - `accounts-facebook@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-github@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-google@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-meetup@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-meteor-developer@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-password@3.0.0`: - Some server methods are now async: - `Accounts.sendResetPasswordEmail` - `Accounts.sendEnrollmentEmail` - `Accounts.sendVerificationEmail` - `Accounts.addEmail` - `Accounts.removeEmail` - `Accounts.replaceEmailAsync` - `Accounts.verifyEmail` - `Accounts.createUserVerifyingEmail` - `Accounts.createUser` - `Accounts.generateVerificationToken` - `Accounts.generateResetToken` - `Accounts.forgotPassword` - `Accounts.setPassword` -- no longer exists, you should use Accounts.setPasswordAsync [PR](https://github.com/meteor/meteor/pull/13030) - `Accounts.changePassword` - `Accounts.setUsername` - `Accounts.findUserByEmail` - `Accounts.findUserByUsername` - `accounts-passwordless@3.0.0`: - `Accounts.sendLoginTokenEmail` is now async. - `accounts-twitter@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-ui-unstyled@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `accounts-weibo@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `allow-deny@2.0.0`: - Updated to accept async functions. - `appcache@2.0.0`: - Updated internal api to use `handlers` - `audit-argument-checks@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `autopublish@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `autoupdate@2.0.0`: - Updated api to be async, with asyncronous queueing. - `babel-compiler@8.0.0`: - Removed `Promise.await` default transform. - Added top-level-await to packages. - `babel-runtime@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `base64@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `binary-heap@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - Build system: - removed `Assets.getText` - removed `Assets.getBinary` - `blaze@3.0.0`: - Todo - `boilerplate-generator-tests@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `boilerplate-generator@2.0.0`: - `toHTML` is no longer available (it was already deprecated). Use `toHTMLStream` instead. - Updated to use `handlers` - `browser-policy-common@2.0.0`: - Updated to use `handlers` - `browser-policy-content@2.0.0`: - Some methods are now async. See below: - `BrowserPolicy.content.setPolicy` - `BrowserPolicy.content.allowInlineScripts` - `BrowserPolicy.content.disallowInlineScripts` - `BrowserPolicy.content.disallowAll` - `BrowserPolicy.setDefaultPolicy` - `browser-policy-framing@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `browser-policy@2.0.0`: Updated to use async methods from `browser-policy-common` and `browser-policy-content`. - `caching-compiler@2.0.0`: - `afterLink` is now async. - Updated to use now async API. - `callback-hook@2.0.0`: - Added `forEachAsync` method. - `check@2.0.0`: - Removed `fibers` related tests. - `constraint-solver@2.0.0`: - Some methods are now async. See below: - `ConstraintSolver.getVersionCostSteps` - `ConstraintSolver.analyze` - `ConstraintSolver.resolve` - Updated tests to be async. - Removed a few underscore usage. - Added updated to use async methods - `context@1.0.0`: - Removed `fibers` from package. - `core-runtime@2.0.0`: - Created package to load packages and the app. - This is the pakcages that sets up the Runtime. - `crosswalk@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `ddp-client@3.0.0`: - Added `isAsyncCall` method to know if call is being made by a async method. - Removed `fibers` from package. - Updated tests to use async methods. - `ddp-common@2.0.0`: - Added `.fence` option. - `ddp-rate-limiter@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `ddp-server@3.0.0`: - Updated to use async methods. - Removed `fibers` from package. - Updated tests to use async methods. - Turned server implementation to async. - `ddp@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `diff-sequence@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `disable-oplog@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `ecmascript-runtime-client@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `ecmascript-runtime-server@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `ecmascript-runtime@1.0.0`: - Added dependency to `@babel/runtime`. - `ecmascript@1.0.0`: - Added dependency to `@babel/runtime`. - Moved runtime tests. - `ejson@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `email@3.0.0`: - `Email.send` is no longer available. Use `Email.sendAsync` instead. - Updated types to reflext async methods and `Email.send` depracation. - `es5-shim@5.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `facebook-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `facebook-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `facts-base@2.0.0`: - turned unorderd deps on `ddp` to false. - `facts-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `fetch@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `force-ssl-common@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `force-ssl@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `geojson-utils@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `github-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `github-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `google-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `google-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `hot-code-push@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `http@`: - Updated handlers to use `handlers` - `id-map@2.0.0`: - Added `forEachAsync` method. - `insecure@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `inter-process-messaging@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `launch-screen@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `localstorage@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `logging@2.0.0`: - Added dependency to `@babel/runtime`. - `logic-solver@3.0.0`: `Logic.disablingAssertions` is now async. `minMaxWS` is now async. - `meetup-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `meetup-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `meteor-base@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `meteor-developer-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `meteor-developer-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `meteor-tool@3.0.0`: - Changes to how meteor apps are being created [PR](https://github.com/meteor/meteor/pull/12697) - `meteor@2.0.0`: - Async local storage was added to help deal with async methods. - Added `promiseEmmiter` to help with async methods. - Removed `fibers` from package. - `Meteor.absoluteUrl` in localhost uses `127.0.1` by default. - `minifier-css@2.0.0`: - `minifyCss` is now async. - Removed `fibers` from package. - `minifier-js@3.0.0`: - `minifyJs` is now async. - `terserMinify` no longer takes callbacks - Removed `fibers` from package. * `minimongo@2.0.0`: - `cursor.observe` now returns `isReady` and `isReadyPromise` wich indicates if the cursor is ready and if the callbacks are have been called. If you only use it in the `Client` or as a `LocalCollection` things have not changed. - `mobile-experience@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `mobile-status-bar@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `modern-browsers@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `modules-runtime@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `modules@1.0.0`: - Updated `reify` version. - `mongo-decimal@`: - Updated to use `async` methods. - `mongo-dev-server@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `mongo-id@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `mongo-livedata@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `mongo@2.0.0`: - Updated to unify methods, `update`,`insert`,`remove`, `fetch` are now async, they are the same as their `*Async` counterpart. - `ensureIndex` and `createIndex` are now async. - `npm-mongo@5.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `oauth-encryption@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `oauth@`: - `_endOfPopupResponseTemplate` and `_endOfRedirectResponseTemplate` are no longer a property but now a function that returns a promise of the same value as before - the following server methods are now async: - `OAuth._renderOauthResults` - `OAuth._endOfLoginResponse` - `OAuth.renderEndOfLoginResponse` - `OAuth._storePendingCredential` - `OAuth._retrievePendingCredential` - `ensureConfigured` - `_cleanStaleResults` - `oauth@3.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `oauth1@`: - the following server methods are now async: - `OAuth._storeRequestToken` - `OAuth._retrieveRequestToken` - `oauth1@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `oauth2@`: - `OAuth._requestHandlers['2']` is now async. - `oauth2@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `ordered-dict@2.0.0`: - Added `forEachAsync` method. - `package-stats-opt-out@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `package-version-parser@4.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `promise@1.0.0`: - Removed `fibers` usage - `random@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `rate-limit@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `reactive-dict@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `reactive-var@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `reload-safetybelt@2.0.0`: - Added `ecmascript` package to `package.js` - `reload@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `retry@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `routepolicy@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `server-render@1.0.0`: - Updated usage with `getBoilerplate` that are now `async`. - `service-configuration@2.0.0`: - Updated to use `createIndexAsync`. - `session@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `sha@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `shell-server@1.0.0`: - Updated to handle promises results. - `socket-stream-client@1.0.0`: - Updated tests to handle `async` code. - `spiderable@`: - Updated handlers to use `handlers` that are now using express - removed `fibers` usage if flag is set to `true` - `standard-minifier-css@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `standard-minifier-js@3.0.0`: - `processFilesForBundle` is now `async`. - `standard-minifiers@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `static-html@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `test-helpers@2.0.0`: - Updated to use `async` methods. - Removed `fibers` usage. - Added possibliy to use `async` tests. - `test-in-browser@2.0.0`: - Updated css to be in dark mode. - `test-in-console@2.0.0`: - Updated log identation. - `test-server-tests-in-console-once@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `tinytest-harness@1.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `tinytest@2.0.0`: - Added `test name` to logs. - Removed `fibers` usage. - `twitter-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `twitter-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `typescript@5.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `underscore-tests@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `underscore@2.0.0`: - Removed dependency in meteor package. - `url@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `webapp-hashing@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `webapp@2.0.0`: - These methods are now async: - `WebAppInternals.reloadClientPrograms()` - `WebAppInternals.pauseClient()` - `WebAppInternals.generateClientProgram()` - `WebAppInternals.generateBoilerplate()` - `WebAppInternals.setInlineScriptsAllowed()` - `WebAppInternals.enableSubresourceIntegrity()` - `WebAppInternals.setBundledJsCssUrlRewriteHook()` - `WebAppInternals.setBundledJsCssPrefix()` - `WebAppInternals.getBoilerplate` - Changed engine from connect to express and changed api naming to match express. See below: - `WebApp.connectHandlers.use(middleware)` is now `WebApp.handlers.use(middleware)` - `WebApp.rawConnectHandlers.use(middleware)` is now `WebApp.rawHandlers.use(middleware)` - `WebApp.connectApp` is now `WebApp.expressApp` - `WebApp.addRuntimeConfigHook` accepts async callbacks - `weibo-config-ui@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. - `weibo-oauth@2.0.0`: - Package was bumped due to a dependency update. No code changes were made. #### New Public API - `Build system` [PR](https://github.com/meteor/meteor/pull/13030): - Assets.getTextAsync - Assets.getBinaryAsync - `accounts-base`: (2.9+) - `Meteor.userAsync()` - `callback-hook`:forEachAsync - `forEachAsync` - `ddp-server`: (2.8+) - `Meteor.callAsync()` - `minifier-css`: (2.9+) - `CssTools.minifyCssAsync()` - `mongo`: - `Mongo.Collection`: (2.8+) - `createCappedCollectionAsync` - `createIndexAsync` - `dropCollectionAsync` - `dropIndexAsync` - `findOneAsync` - `insertAsync` - `removeAsync` - `updateAsync` - `upsertAsync` - `Collection.Cursor`: (2.8+) - `countAsync` - `fetchAsync` - `forEachAsync` - `mapAsync` - `[Symbol.asyncIterator]` so this code should work: ```js for await (const document of collection.find(query, options)) /* ... */ ``` #### Internal API changes `accounts-base`: - `_attemptLogin` - `_loginMethod` - `_runLoginHandlers` * Upgraded `node-gyp` to v9.4.0 * Upgraded `node-pre-gyp` to `@mapbox/node-pre-gyp` v1.0.11 #### New Internal API `accounts-password`: - `Accounts._checkPasswordAsync` #### Special thanks to - [@StorytellerCZ](https://github.com/sponsors/StorytellerCZ/) For making this great framework even better! --- --- url: /generators/changelog/README.md --- ## Changelog Generator This is a generator for the changelog, you must create a file with the name of the version that you are generating the changelog for. The script will take care of the rest. In this file you should follow the EXAMPLE.md file that is within this directory. The script will generate a file called `history.gen.md` that will be used by the `changelog.md` file to generate the changelog page. To get which branches were merged into release you can search in the GitHub repo by using this query: ``` is:pr base: is:merged ``` or in GH Cli: ```bash gh pr list --state merged --base ``` note that it may not be as useful as the first one, since it will not show the Authors and other related information. ## Why? Computers with lower memory/ IDEs with high memory usage can have problems with the changelog file(~10k lines). This is a way to reduce the memory usage of the changelog, also creating a more organized changelog, since all the files will be reflecting at least one version. ## Update ordering. If you want to make sure that the changelog is correctly ordered, take a look at the `order-packages.js` file. to use it, run the command below: ```bash node order-packages.js versions/3.0.md ``` or ```bash node order-packages.js versions/.md ``` it will update the file with the correct ordering(this will override the file). --- --- url: /generators/meteor-versions/README.md --- ## Meteor version generator for docs This is a generator for the meteor versions for the docs, this is used to generate the links in the docs to the correct version of the meteor release and docs version. ## Why? This is a way to ensure that the links in the docs are always pointing to the correct version of the docs and release. In an automated way. ## How to use To use this generator you must run the following command: ```bash node script.js ``` and it will check in the `changelog` dir for every version and generate a `versions.generated.json` file that will be used by the docs to generate the links to the correct version of the docs. --- --- url: /packages/3.writing-atmosphere-packages.md --- ## Writing Atmosphere packages To get started writing a package, use the Meteor command line tool: ```bash meteor create --package my-package ``` > It is required that your `my-package` name take the form of `username:my-package`, where `username` is your Meteor Developer username, if you plan to publish your package to Atmosphere. If you run this inside an app, it will place the newly generated package in that app's `packages/` directory. Outside an app, it will just create a standalone package directory. The command also generates some boilerplate files for you: ```txt my-package ├── README.md ├── package.js ├── my-package-tests.js └── my-package.js ``` The `package.js` file is the main file in every Meteor package. This is a JavaScript file that defines the metadata, files loaded, architectures, npm packages, and Cordova packages for your Meteor package. In this tutorial, we will go over some important points for building packages, but we won't explain every part of the `package.js` API. To learn about all of the options, [read about the `package.js` API in the Meteor docs.](https://docs.meteor.com/api/package.html) > Don't forget to run [`meteor add [my-package]`](https://docs.meteor.com/cli/#meteor-add) once you have finished developing your package in order to use it; this applies if the package is a local package for internal use only or if you have published the package to Atmosphere. ### Adding files and assets The main function of an Atmosphere package is to contain source code (JS, CSS, and any transpiled languages) and assets (images, fonts, and more) that will be shared across different applications. ### Adding JavaScript To add JavaScript files to a package, specify an entrypoint with [`api.mainModule()`](https://docs.meteor.com/packages/modules.html#modular-package-structure) in the package's `onUse` block (this will already have been done by `meteor create --package` above): ```js Package.onUse(function(api) { api.mainModule('my-package.js'); }); ``` From that entrypoint, you can `import` other files within your package, [just as you would in an application](https://docs.meteor.com/packages/modules.html). If you want to include different files on the client and server, you can specify multiple entry points using the second argument to the function: ```js Package.onUse(function(api) { api.mainModule('my-package-client.js', 'client'); api.mainModule('my-package-server.js', 'server'); }); ``` You can also add any source file that would be compiled to a JS file (such as a CoffeeScript file) in a similar way, assuming you [depend](#dependencies) on an appropriate build plugin.

Adding CSS

To include CSS files with your package you can use [`api.addFiles()`](https://docs.meteor.com/api/package.html#PackageAPI-addFiles): ```js Package.onUse(function(api) { api.addFiles('my-package.css', 'client'); }); ``` The CSS file will be automatically loaded into any app that uses your package. ### Adding Sass, Less, or Stylus mixins/variables Just like packages can export JavaScript code, they can export reusable bits of CSS pre-processor code. You can also have a package that doesn't actually include any CSS, but just exports different bits of reusable mixins and variables. To get more details see Meteor [build tool CSS pre-processors](https://guide.meteor.com/build-tool.html#css): ```js Package.onUse(function(api) { api.addFiles('my-package.scss', 'client'); }); ``` This Sass file will be eagerly evaluated and its compiled form will be added to the CSS of the app immediately. ```js Package.onUse(function(api) { api.addFiles([ 'stylesheets/_util.scss', 'stylesheets/_variables.scss' ], 'client', {isImport: true}); }); ``` These two Sass files will be lazily evaluated and only included in the CSS of the app if imported from some other file. ### Adding other assets You can include other assets, such as fonts, icons or images, to your package using [`api.addAssets`](https://docs.meteor.com/api/package.html#PackageAPI-addAssets): ```js Package.onUse(function(api) { api.addAssets([ 'font/OpenSans-Regular-webfont.eot', 'font/OpenSans-Regular-webfont.svg', 'font/OpenSans-Regular-webfont.ttf', 'font/OpenSans-Regular-webfont.woff', ], 'client'); }); ``` You can then access these files from the client from a URL `/packages/username_my-package/font/OpenSans-Regular-webfont.eot` or from the server using the [Assets API](https://docs.meteor.com/api/assets.html#Assets-getTextAsync). ### Exporting While some packages exist just to provide side effects to the app, most packages provide a reusable bit of code that can be used by the consumer with `import`. To export a symbol from your package, use the ES2015 `export` syntax in your `mainModule`: ```js // in my-package.js: export const myName = 'my-package'; ``` Now users of your package can import the symbol with: ```js import { myName } from 'meteor/username:my-package'; ``` ### Dependencies Chances are your package will want to make use of other packages. To ensure they are available, you can declare dependencies. Atmosphere packages can depend both on other Atmosphere packages, as well as packages from npm. #### Atmosphere dependencies To depend on another Atmosphere package, use [`api.use`](https://docs.meteor.com/api/package.html#PackageAPI-use): ```js Package.onUse(function(api) { // This package depends on 1.2.0 or above of validated-method api.use('mdg:validated-method@1.2.0'); }); ``` One important feature of the Atmosphere package system is that it is single-loading: no two packages in the same app can have dependencies on conflicting versions of a single package. Read more about that in the section about version constraints below. #### Depending on Meteor version Note that the Meteor release version number is mostly a marketing artifact - the core Meteor packages themselves typically don't share this version number. This means packages can only depend on specific versions of the packages inside a Meteor release, but can't depend on a specific release itself. We have a helpful shorthand api called [`api.versionsFrom`](https://docs.meteor.com/api/package.html#PackageAPI-versionsFrom) that handles this for you by automatically filling in package version numbers from a particular release: ```js // Use versions of core packages from Meteor 1.2.1 api.versionsFrom('1.2.1'); api.use([ // Don't need to specify version because of versionsFrom above 'ecmascript', 'check', // Still need to specify versions of non-core packages 'mdg:validated-method@1.2.0', 'mdg:validation-error@0.1.0' ]); ``` The above code snippet is equivalent to the code below, which specifies all of the version numbers individually: ```js api.use([ 'ecmascript@0.1.6', 'check@1.1.0', 'mdg:validated-method@1.2.0', 'mdg:validation-error@0.1.0' ]); ``` Additionally, you can call `api.versionsFrom()` multiple times, or with an array (eg `api.versionsFrom([, ])`. Meteor will interpret this to mean that the package will work with packages from all the listed releases. ```js api.versionsFrom('1.2.1'); api.versionsFrom('1.4'); api.versionsFrom('1.8'); // or api.versionsFrom(['1.2.1', '1.4', '1.8']); ``` This usually isn't necessary, but can help in cases where you support more than one major version of a core package. #### Semantic versioning and version constraints Meteor's package system relies heavily on [Semantic Versioning](http://semver.org/), or SemVer. When one package declares a dependency on another, it always comes with a version constraint. These version constraints are then solved by Meteor's industrial-grade Version Solver to arrive at a set of package versions that meet all of the requirements, or display a helpful error if there is no solution. The mental model here is: 1. **The major version must always match exactly.** If package `a` depends on `b@2.0.0`, the constraint will only be satisfied if the version of package `b` starts with a `2`. This means that you can never have two different major versions of a package in the same app. 2. **The minor and patch version numbers must be greater or equal to the requested version.** If the dependency requests version `2.1.3`, then `2.1.4` and `2.2.0` will work, but `2.0.4` and `2.1.2` will not. The constraint solver is necessary because Meteor's package system is **single-loading** - that is, you can never have two different versions of the same package loaded side-by-side in the same app. This is particularly useful for packages that include a lot of client-side code, or packages that expect to be singletons. Note that the version solver also has a concept of "gravity" - when many solutions are possible for a certain set of dependencies, it always selects the oldest possible version. This is helpful if you are trying to develop a package to ship to lots of users, since it ensures your package will be compatible with the lowest common denominator of a dependency. If your package needs a newer version than is currently being selected for a certain dependency, you need to update your `package.js` to have a newer version constraint. If your package supports multiple major versions of a dependency, you can supply both versions to `api.use` like so: ```js api.use('blaze@1.0.0 || 2.0.0'); ``` Meteor will use whichever major version is compatible with your other packages, or the most recent of the options given. #### npm dependencies Meteor packages can include [npm packages](https://www.npmjs.com/) to use JavaScript code from outside the Meteor package ecosystem or to include JavaScript code with native dependencies. Use [Npm.depends](https://docs.meteor.com/api/package.html#Npm-require) at the top level of your `package.js` file. For example, here's how you would include the `github` npm package: ```js Npm.depends({ github: '0.2.4' }); ``` If you want to use a local npm package, for example during development, you can give a directory instead of a version number: ```js Npm.depends({ my-package: 'file:///home/user/npms/my-package' }); ``` You can import the dependency from within you package code in the same way that you would inside an application: ```js import github from 'github'; ``` #### Peer npm dependencies `Npm.depends()` is fairly rigid (you can only depend on an exact version), and will typically result in multiple versions of a package being installed if many different Atmosphere packages depend on the same npm package. This makes it less than ideal to use on the client, where it's impractical to ship multiple copies of the same package code to the browser. Client-side packages are also often written with the assumption that only a single copy will be loaded. For example, React will complain if it is included more than once in an application bundle. To avoid this problem as a package author, you can request that users of your package have installed the npm package you want to use at the application level. This is similar to a [peer dependency](https://nodejs.org/en/blog/npm/peer-dependencies/) of an npm package (although with less support in the tool). You can use the [`tmeasday:check-npm-versions`](https://atmospherejs.com/tmeasday/check-npm-versions) package to ensure that they've done this, and to warn them if not. For instance, if you are writing a React package, you should not directly depend on [`react`](https://www.npmjs.com/package/react), but instead use `check-npm-versions` to check the user has installed it: ```js import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions'; checkNpmVersions({ 'react': '0.14.x' }, 'my:awesome-package'); // If you are using the dependency in the same file, you'll need to use require, otherwise // you can continue to `import` in another file. const React = require('react'); ``` > Note that `checkNpmVersions` will only output a warning if the user has installed a incompatible version of the npm package. So your `require` call may not give you what you expect. This is consistent with npm's handling of [peer dependencies](http://blog.npmjs.org/post/110924823920/npm-weekly-5). ### Cordova plugins Atmosphere packages can include [Cordova plugins](http://cordova.apache.org/plugins/) to ship native code for the Meteor mobile app container. This way, you can interact with the native camera interface, use the gyroscope, save files locally, and more. Include Cordova plugins in your Meteor package by using [Cordova.depends](https://docs.meteor.com/api/package.html#PackageCordova-depends). Read more about using Cordova in the [mobile guide](https://guide.meteor.com/mobile.html). ### Testing packages Meteor has a test mode for packages called `meteor test-packages`. If you are in a package's directory, you can run ```bash meteor test-packages ./ --driver-package meteortesting:mocha ``` This will run a special app containing only a "test" version of your package and start a Mocha [test driver package](https://guide.meteor.com/testing#driver-packages). When your package starts in test mode, rather than loading the `onUse` block, Meteor loads the `onTest` block: ```js Package.onTest(function(api) { // You almost definitely want to depend on the package itself, // this is what you are testing! api.use('my-package'); // You should also include any packages you need to use in the test code api.use(['ecmascript', 'random', 'meteortesting:mocha']); // Finally add an entry point for tests api.mainModule('my-package-tests.js'); }); ``` From within your test entry point, you can import other files as you would in the package proper. You can read more about testing in Meteor in the [Testing article](https://guide.meteor.com/testing). ### Publishing your package To publish your package to Atmosphere, run [`meteor publish`](https://docs.meteor.com/cli/#meteorpublish) from the package directory. To publish a package the package name must follow the format of `username:my-package` and the package must contain a [SemVer version number](#semantic-versionning-and-version-constraints). > Note that if you have a local `node_modules` directory in your package, remove it before running `meteor publish`. While local `node_modules` directories are allowed in Meteor packages, their paths can collide with the paths of `Npm.depends` dependencies when published. ## Cache format If you've ever looked inside Meteor's package cache at `~/.meteor/packages`, you know that the on-disk format of a built Meteor package is completely different from the way the source code looks when you're developing the package. The idea is that the target format of a package can remain consistent even if the API for development changes. ## Local packages As an alternative to publishing your package on Atmosphere, if you want to keep your package private, you can place it in your Meteor app in the `packages/` directory, for instance `packages/foo/`, and then add it to your app with `meteor add foo`. Using `git submodules` to handle private packages is a common pattern. ### Overriding published packages with a local version If you need to modify an Atmosphere package to do something that the published version doesn't do, you can edit a local version of the package on your computer. A Meteor app can load Atmosphere packages in one of three ways, and it looks for a matching package name in the following order: 1. Package source code in the `packages/` directory inside your app. 2. Package source code in directories indicated by setting a `METEOR_PACKAGE_DIRS` environment variable before running any `meteor` command. You can add multiple directories by separating the paths with a `:` on OSX or Linux, or a `;` on Windows. For example: `METEOR_PACKAGE_DIRS=../first/directory:../second/directory`, or on Windows: `set PACKAGE_DIRS=..\first\directory;..\second\directory`. > Note: Prior to Meteor 1.4.2, `METEOR_PACKAGE_DIRS` was `PACKAGE_DIRS`. For compatibility reasons, developers should use `METEOR_PACKAGE_DIRS` going forward. 3. Pre-built package from Atmosphere. The package is cached in `~/.meteor/packages` on Mac/Linux or `%LOCALAPPDATA%\.meteor\packages` on Windows, and only loaded into your app as it is built. You can use (1) or (2) to override the version from Atmosphere. You can even do this to load patched versions of Meteor core packages - just copy the code of the package from [Meteor's GitHub repository](https://github.com/meteor/meteor/tree/devel/packages), and edit away. --- --- url: /packages/2.using-atmosphere-packages.md --- ### Package naming All packages on Atmosphere have a name of the form `prefix:package-name`. The prefix is the Meteor Developer username of the organization or user that published the package. Meteor uses such a convention for package naming to make sure that it's clear who has published a certain package, and to avoid an ad-hoc namespacing convention. Meteor platform packages do not have any `prefix:` or may use `mdg:`. ### Installing Atmosphere Packages To install an Atmosphere package, you use `meteor add` inside your app directory: ```bash meteor add ostrio:flow-router-extra ``` This will add the newest version of the desired package that is compatible with the other packages in your app and your Meteor app version. If you want to specify a particular version, you can specify it by adding a suffix to the package name like: `meteor add ostrio:flow-router-extra@3.12.0`. Regardless of how you add the package to your app, its actual version will be tracked in the file at `.meteor/versions`. This means that anybody collaborating with you on the same app is guaranteed to have the same package versions as you. If you want to update to a newer version of a package after installing it, use `meteor update`. You can run `meteor update` without any arguments to update all packages and Meteor itself to their latest versions, or pass a specific package to update just that one, for example `meteor update ostrio:flow-router-extra`. If your app is running when you add a new package, Meteor will automatically download it and restart your app for you. > The actual files for a given version of an Atmosphere package are stored in your local `~/.meteor/packages` directory. To see all the Atmosphere packages installed run: ```bash meteor list ``` To remove an unwanted Atmosphere package run: ```bash meteor remove ostrio:flow-router-extra ``` You can get more details on all the package commands in the [Meteor Command line documentation](https://docs.meteor.com/cli/#meteorhelp). ### Using Atmosphere Packages inside your app To use an Atmosphere Package in your app you can import it with the `meteor/` prefix: ```js import { Mongo } from "meteor/mongo"; ``` Typically a package will export one or more symbols, which you'll need to reference with the destructuring syntax. You can find these exported symbols by either looking in that package's `package.js` file for [`api.export`](http://docs.meteor.com/api/package.html#PackageAPI-export) calls or by looking in that package's main JavaScript file for ES2015 `export ` calls like `export const packageName = 'package-name';`. Sometimes a package will have no exports and have side effects when included in your app. In such cases you don't need to import the package at all after installing. > For backwards compatibility with Meteor 1.2 and early releases, Meteor by default makes available directly to your app all symbols referenced in `api.export` in any packages you have installed. However, it is recommended that you import these symbols first before using them. #### Importing styles from Atmosphere packages Using any of Meteor's supported CSS pre-processors you can import other style files using the `{package-name}` syntax as long as those files are designated to be lazily evaluated as "import" files. To get more details on how to determine this see [CSS source versus import](https://guide.meteor.com/build-tool#css-source-vs-import) files. ```less @import '{prefix:package-name}/buttons/styles.import.less'; ``` > CSS files in an Atmosphere package are declared with `api.addFiles`, and therefore will be eagerly evaluated by default, and then bundled with all the other CSS in your app. #### Peer npm dependencies Atmosphere packages can ship with contained [npm dependencies](#npm-dependencies), in which case you don't need to do anything to make them work. However, some Atmosphere packages will expect that you have installed certain "peer" npm dependencies in your application. Typically the package will warn you if you have not done so. For example, if you install the [`react-meteor-data`](https://atmospherejs.com/meteor/react-meteor-data) package into your app, you'll also need to install the [`react`](https://www.npmjs.com/package/react) and the [`react-addons-pure-render-mixin`](https://www.npmjs.com/package/react-addons-pure-render-mixin) packages: ```bash meteor npm install --save react react-addons-pure-render-mixin meteor add react-meteor-data ``` ### Atmosphere package namespacing Each Atmosphere package that you use in your app exists in its own separate namespace, meaning that it sees only its own global variables and any variables provided by the packages that it specifically uses. When a top-level variable is defined in a package, it is either declared with local scope or package scope. ```js /** * local scope - this variable is not visible outside of the block it is * declared in and other packages and your app won't see it */ const alicePerson = {name: "alice"}; /** * package scope - this variable is visible to every file inside of the * package where it is declared and to your app */ bobPerson = {name: "bob"}; ``` Notice that this is just the normal JavaScript syntax for declaring a variable that is local or global. Meteor scans your source code for global variable assignments and generates a wrapper that makes sure that your globals don't escape their appropriate namespace. In addition to local scope and package scope, there are also package exports. A package export is a "pseudo global" variable that a package makes available for you to use when you install that package. For example, the `email` package exports the `Email` variable. If your app uses the `email` package (and _only_ if it uses the `email` package!) then your app can access the `Email` symbol and you can call `Email.send`. Most packages have only one export, but some packages might have two or three (for example, a package that provides several classes that work together). > It is recommended that you use the `ecmascript` package and first call `import { Email } from 'meteor/email';` before calling `Email.send` in your app. It is also recommended that package developers now use ES2015 `export` from their main JavaScript file instead of `api.export`. Your app sees only the exports of the packages that you use directly. If you use package A, and package A uses package B, then you only see package A's exports. Package B's exports don't "leak" into your namespace just because you used package A. Each app or package only sees their own globals plus the APIs of the packages that they specifically use and depend upon. --- --- url: /packages/1.when-to-use-meteor-packages.md --- ## Using Atmosphere packages ### When to use Atmosphere packages Atmosphere packages are packages written specifically for Meteor and have several advantages over npm when used with Meteor. In particular, Atmosphere packages can: - Depend on core Meteor packages, such as `ddp`, `mongo` or `accounts` - Explicitly include non-javascript files including CSS, Less or static assets - Have a well defined way to ship different code for client and server, enabling different behavior in each context - Get direct access to Meteor's [package namespacing](#atmosphere-package-namespacing) and package global exports without having to explicitly use ES2015 `import` - Enforce exact version dependencies between packages using Meteor's [constraint resolver](#semantic-versionning-and-version-constraints) - Include [build plugins](https://docs.meteor.com/api/package.html#build-plugin-api) for Meteor's build system - Include pre-built binary code for different server architectures, such as Linux or Windows If your package depends on another Atmosphere package, or needs to take advantage of Meteor's [build system](https://docs.meteor.com/about/modern-build-stack.html#modern-build-stack), writing an Atmosphere package might be the best option for now. --- --- url: /index.md --- --- --- url: /tutorials/react/8.deploying.md --- ## 8: Deploying Deploying a Meteor application is similar to deploying any other Node.js app that uses websockets. You can find deployment options in [our guide](https://guide.meteor.com/deployment), including Meteor Up, Docker, and our recommended method, Galaxy. In this tutorial, we will deploy our app on [Galaxy](https://www.meteor.com/hosting), which is our own cloud solution. Galaxy offers a free plan, so you can deploy and test your app. Pretty cool, right? ### 8.1: Create your account You need a Meteor account to deploy your apps. If you don’t have one yet, you can [sign up here](https://cloud.meteor.com/?isSignUp=true). With this account, you can access our package manager, [Atmosphere](https://atmospherejs.com/), [Forums](https://forums.meteor.com/) and more. ### 8.2: Set up MongoDB (Optional) As your app uses MongoDB the first step is to set up a MongoDB database, Galaxy offers MongoDB hosting on a free plan for testing purposes, and you can also request for a production ready database that allows you to scale. In any MongoDB provider you will have a MongoDB URL which you must use it. If you use the free option provided by Galaxy, the initial setup is done for you. Galaxy MongoDB URL will be like this: `mongodb://username:@org-dbname-01.mongodb.galaxy-cloud.io` . > You can read more about Galaxy MongoDB [here](https://galaxy-support.meteor.com/en/article/mongodb-general-1syd5af/). ### 8.3: Set up settings You need to create a setting file, it’s a JSON file that Meteor apps can read configurations from. Create this file in a new folder called `private` in the root of your project. It is important to notice that `private` is a special folder that is not going to be published to the client side of your app. Make sure you replace `Your MongoDB URL` by your own MongoDB URL :) ::: code-group ```json [private/settings.json] { "galaxy.meteor.com": { "env": { "MONGO_URL": "Your MongoDB URL" } } } ``` ::: ### 8.4: Deploy it Now you are ready to deploy, run `meteor npm install` before deploying to make sure all your dependencies are installed. You also need to choose a subdomain to publish your app. We are going to use the main domain `meteorapp.com` that is free and included on any Galaxy plan. In this example we are going to use `react-meteor-3.meteorapp.com` but make sure you select a different one, otherwise you are going to receive an error. > You can learn how to use custom domains on Galaxy [here](https://galaxy-support.meteor.com/en/article/domains-16cijgc/). Custom domains are available starting with the Essentials plan. Run the deployment command: ```shell meteor deploy react-meteor-3.meteorapp.com --free --mongo ``` > If you are not using the free hosting with MongoDB on Galaxy, then remove the `--mongo` flag from the deploy script and add `--settings private/settings.json` with the proper setting for your app. Make sure you replace `react-meteor-3` by a custom name that you want as subdomain. You will see a log like this: ```shell meteor deploy react-meteor-3.meteorapp.com --settings private/settings.json Talking to Galaxy servers at https://us-east-1.galaxy-deploy.meteor.com Preparing to build your app... Preparing to upload your app... Uploaded app bundle for new app at vue-tutorial.meteorapp.com. Galaxy is building the app into a native image. Waiting for deployment updates from Galaxy... Building app image... Deploying app... You have successfully deployed the first version of your app. For details, visit https://galaxy.meteor.com/app/react-meteor-3.meteorapp.com ``` This process usually takes just a few minutes, but it depends on your internet speed as it’s going to send your app bundle to Galaxy servers. > Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, [read more](https://galaxy-support.meteor.com/en/article/container-environment-lfd6kh/). You can check your logs on Galaxy, including the part that Galaxy is building your Docker image and deploying it. ### 8.5: Access the app and enjoy Now you should be able to access your Galaxy dashboard at `https://galaxy.meteor.com/app/react-meteor-3.meteorapp.com`. You can also access your app on Galaxy 2.0 which is currently in beta at `https://galaxy-beta.meteor.com//us-east-1/apps/.meteorapp.com`. Remember to use your own subdomain instead of `react-meteor-3`. You can access the app at [react-meteor-3.meteorapp.com](https://react-meteor-3.meteorapp.com/)! Just use your subdomain to access yours! > We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list [here](https://galaxy-support.meteor.com/en/article/regions-1vucejm/). This is huge, you have your app running on Galaxy, ready to be used by anyone in the world! --- --- url: /tutorials/react/7.adding-user-accounts.md --- ## 7: Adding User Accounts ### 7.1: Password Authentication Meteor already comes with a basic authentication and account management system out of the box, so you only need to add the `accounts-password` to enable username and password authentication: ```shell meteor add accounts-password ``` > There are many more authentication methods supported. You can read more about the accounts system [here](https://v3-docs.meteor.com/api/accounts.html). We also recommend you to install `bcrypt` node module, otherwise, you are going to see a warning saying that you are using a pure-Javascript implementation of it. ```shell meteor npm install --save bcrypt ``` > You should always use `meteor npm` instead of only `npm` so you always use the `npm` version pinned by Meteor, this helps you to avoid problems due to different versions of npm installing different modules. ### 7.2: Create User Account Now you can create a default user for our app, we are going to use `meteorite` as username, we just create a new user on server startup if we didn't find it in the database. ::: code-group ```js [server/main.js] import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { TasksCollection } from '/imports/api/TasksCollection'; .. const SEED_USERNAME = 'meteorite'; const SEED_PASSWORD = 'password'; Meteor.startup(async () => { if (!(await Accounts.findUserByUsername(SEED_USERNAME))) { await Accounts.createUser({ username: SEED_USERNAME, password: SEED_PASSWORD, }); } .. }); ``` ::: You should not see anything different in your app UI yet. ### 7.3: Login Form You need to provide a way for the users to input the credentials and authenticate, for that we need a form. We can implement it using `useState` hook. Create a new file called `LoginForm.jsx` and add a form to it. You should use `Meteor.loginWithPassword(username, password);` to authenticate your user with the provided inputs. ::: code-group ```js [imports/ui/LoginForm.jsx] import { Meteor } from "meteor/meteor"; import React, { useState } from "react"; export const LoginForm = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const submit = (e) => { e.preventDefault(); Meteor.loginWithPassword(username, password); }; return (
setUsername(e.target.value)} /> setPassword(e.target.value)} />
); }; ``` ::: Ok, now you have a form, let's use it. ### 7.4: Require Authentication Our app should only allow an authenticated user to access its task management features. We can accomplish that by returning the `LoginForm` component when we don't have an authenticated user, otherwise we return the form, filter, and list component. You should first wrap the 3 components (form, filter, and list) in a ``, Fragment is a special component in React that you can use to group components together without affecting your final DOM, it means without affecting your UI as it is not going to introduce other elements in the HTML. > Read more about Fragments [here](https://react.dev/reference/react/Fragment) So you can get your authenticated user or null from `Meteor.user()`, you should wrap it in a `useTracker` hook for it to be reactive. Then you can return the `Fragment` with Tasks and everything else or `LoginForm` based on the user being present or not in the session. ::: code-group ```js [imports/ui/App.jsx] import { Meteor } from 'meteor/meteor'; import React, { useState, Fragment } from 'react'; import { useTracker } from 'meteor/react-meteor-data'; import { TasksCollection } from '/imports/api/TasksCollection'; import { Task } from './Task'; import { TaskForm } from './TaskForm'; import { LoginForm } from './LoginForm'; .. export const App = () => { const user = useTracker(() => Meteor.user()); .. return ( ..
{user ? (
    {tasks.map(task => ( ))}
) : ( )}
.. ``` ::: ### 7.5: Login Form style Ok, let's style the login form now: Wrap your pairs of label and input in `div`s so it will easier to control it on CSS. Do the same to the button tag. ::: code-group ```jsx [imports/ui/LoginForm.jsx]
setUsername(e.target.value)} />
setPassword(e.target.value)} />
``` ::: And then update the CSS: ::: code-group ```css [client/main.css] .login-form { display: flex; flex-direction: column; height: 100%; justify-content: center; align-items: center; } .login-form > div { margin: 8px; } .login-form > div > label { font-weight: bold; } .login-form > div > input { flex-grow: 1; box-sizing: border-box; padding: 10px 6px; background: transparent; border: 1px solid #aaa; width: 100%; font-size: 1em; margin-right: 16px; margin-top: 4px; } .login-form > div > input:focus { outline: 0; } .login-form > div > button { background-color: #62807e; } ``` ::: Now your login form should be centralized and beautiful. ### 7.6: Server startup Every task should have an owner from now on. So go to your database, as you learn before, and remove all the tasks from there: `db.tasks.remove({});` Change your `server/main.js` to add the seed tasks using your `meteorite` user as owner. Make sure you restart the server after this change so `Meteor.startup` block will run again. This is probably going to happen automatically anyway as you are going to make changes in the server side code. ::: code-group ```js [server/main.js] import { Meteor } from "meteor/meteor"; import { Accounts } from "meteor/accounts-base"; import { TasksCollection } from "/imports/api/TasksCollection"; const insertTask = (taskText, user) => TasksCollection.insertAsync({ text: taskText, userId: user._id, createdAt: new Date(), }); const SEED_USERNAME = "meteorite"; const SEED_PASSWORD = "password"; Meteor.startup(async () => { if (!(await Accounts.findUserByUsername(SEED_USERNAME))) { await Accounts.createUser({ username: SEED_USERNAME, password: SEED_PASSWORD, }); } const user = await Accounts.findUserByUsername(SEED_USERNAME); if ((await TasksCollection.find().countAsync()) === 0) { [ "First Task", "Second Task", "Third Task", "Fourth Task", "Fifth Task", "Sixth Task", "Seventh Task", ].forEach((taskText) => insertTask(taskText, user)); } }); ``` ::: See that we are using a new field called `userId` with our user `_id` field, we are also setting `createdAt` field. ### 7.7: Task owner First, let's change our publication to publish the tasks only for the currently logged user. This is important for security, as you send only data that belongs to that user. ::: code-group ```js [/imports/api/TasksPublications.js] Meteor.publish("tasks", function () { const userId = this.userId; if (!userId) { return this.ready(); } return TasksCollection.find({ userId }); }); ``` ::: Now let's check if we have a `user` before trying to fetch any data: ::: code-group ```js [imports/ui/App.jsx] .. const tasks = useTracker(() => { if (!user) { return []; } return TasksCollection.find( hideCompleted ? hideCompletedFilter : {}, { sort: { createdAt: -1 }, } ).fetch(); }); const pendingTasksCount = useTracker(() => { if (!user) { return 0; } .. }); .. ``` ::: Also, update the `tasks.insert` method to include the field `userId` when creating a new task: ::: code-group ```js [imports/api/tasksMethods.js] .. Meteor.methods({ "tasks.insert"(doc) { return TasksCollection.insertAsync({ ...doc, userId: this.userId, }); }, .. ``` ::: ### 7.8: Log out We also can better organize our tasks by showing the username of the owner below our app bar. You can include a new `div` right after our `Fragment` start tag. On this, you can add an `onClick` handler to logout the user as well. It is very straightforward, just call `Meteor.logout()` on it. ::: code-group ```js [imports/ui/App.jsx] .. const logout = () => Meteor.logout(); return ( ..
{user.username} 🚪
.. ``` ::: Remember to style your username as well. ::: code-group ```css [client/main.css] .user { display: flex; align-self: flex-end; margin: 8px 16px 0; font-weight: bold; cursor: pointer; } ``` ::: Phew! You have done quite a lot in this step. Authenticated the user, set the user in the tasks, and provided a way for the user to log out. Your app should look like this: In the next step, we are going to learn how to deploy your app! --- --- url: /tutorials/react/6.filter-tasks.md --- ## 6: Filter tasks In this step, you will filter your tasks by status and show the number of pending tasks. ### 6.1: useState First, you are going to add a button to show or hide the completed tasks from the list. The `useState` function from React is the best way to keep the state of this button. It returns an array with two items, where the first element is the value of the state, and the second is a setter function that is how you are going to update your state. You can use _array destructuring_ to get these two back and already declare a variable for them. Bear in mind that the names used for the constants do not belong to the React API, you can name them whatever you like. Also, add a `button` below the task form that will display a different text based on the current state. ::: code-group ```js [imports/ui/App.jsx] import React, { useState } from 'react'; .. export const App = () => { const [hideCompleted, setHideCompleted] = useState(false); ..
.. ``` ::: You can read more about the `useState` hook [here](https://react.dev/reference/react/useState). We recommend that you add your hooks always in the top of your components, so it will be easier to avoid some problems, like always running them in the same order. ### 6.2: Button style You should add some style to your button so it does not look gray and without a good contrast. You can use the styles below as a reference: ::: code-group ```css [client/main.css] .filter { display: flex; justify-content: center; } .filter > button { background-color: #62807e; } ``` ::: ### 6.3: Filter Tasks Now, if the user wants to see only pending tasks you can add a filter to your selector in the Mini Mongo query, you want to get all the tasks that are not `isChecked` true. ::: code-group ```js [imports/ui/App.jsx] .. const hideCompletedFilter = { isChecked: { $ne: true } }; const tasks = useTracker(() => TasksCollection.find(hideCompleted ? hideCompletedFilter : {}, { sort: { createdAt: -1 }, }).fetch() ); .. ``` ::: ### 6.4: Meteor Dev Tools Extension You can install an extension to visualize the data in your Mini Mongo. [Meteor DevTools Evolved](https://chrome.google.com/webstore/detail/meteor-devtools-evolved/ibniinmoafhgbifjojidlagmggecmpgf) will help you to debug your app as you can see what data is on Mini Mongo. You can also see all the messages that Meteor is sending and receiving from the server, this is useful for you to learn more about how Meteor works. Install it in your Google Chrome browser using this [link](https://chrome.google.com/webstore/detail/meteor-devtools-evolved/ibniinmoafhgbifjojidlagmggecmpgf). ### 6.5: Pending tasks Update the App component in order to show the number of pending tasks in the app bar. You should avoid adding zero to your app bar when there are no pending tasks. ::: code-group ```js [imports/ui/App.jsx] .. const pendingTasksCount = useTracker(() => TasksCollection.find(hideCompletedFilter).count() ); const pendingTasksTitle = pendingTasksCount ? ` (${pendingTasksCount})` : ''; ..

📝️ To Do List {pendingTasksTitle}

.. ``` ::: You could do both finds in the same `useTracker` and then return an object with both properties but to have a code that is easier to understand, we created two different trackers here. Your app should look like this: In the next step we are going to include user access in your app. --- --- url: /tutorials/react/5.styles.md --- ## 5: Styles ### 5.1: CSS Our user interface up until this point has looked quite ugly. Let's add some basic styling which will serve as the foundation for a more professional looking app. Replace the content of our `client/main.css` file with the one below, the idea is to have an app bar at the top, and a scrollable content including: - form to add new tasks; - list of tasks. ::: code-group ```css [client/main.css] body { font-family: sans-serif; background-color: #315481; background-image: linear-gradient(to bottom, #315481, #918e82 100%); background-attachment: fixed; position: absolute; top: 0; bottom: 0; left: 0; right: 0; padding: 0; margin: 0; font-size: 14px; } button { font-weight: bold; font-size: 1em; border: none; color: white; box-shadow: 0 3px 3px rgba(34, 25, 25, 0.4); padding: 5px; cursor: pointer; } button:focus { outline: 0; } .app { display: flex; flex-direction: column; height: 100vh; } .app-header { flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .main { display: flex; flex-direction: column; flex-grow: 1; overflow: auto; background: white; } .main::-webkit-scrollbar { width: 0; height: 0; background: inherit; } header { background: #d2edf4; background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%); padding: 20px 15px 15px 15px; position: relative; box-shadow: 0 3px 3px rgba(34, 25, 25, 0.4); } .app-bar { display: flex; justify-content: space-between; } .app-bar h1 { font-size: 1.5em; margin: 0; display: inline-block; margin-right: 1em; } .task-form { display: flex; margin: 16px; } .task-form > input { flex-grow: 1; box-sizing: border-box; padding: 10px 6px; background: transparent; border: 1px solid #aaa; width: 100%; font-size: 1em; margin-right: 16px; } .task-form > input:focus { outline: 0; } .task-form > button { min-width: 100px; height: 95%; background-color: #315481; } .tasks { list-style-type: none; padding-inline-start: 0; padding-left: 16px; padding-right: 16px; margin-block-start: 0; margin-block-end: 0; } .tasks > li { display: flex; padding: 16px; border-bottom: #eee solid 1px; align-items: center; } .tasks > li > span { flex-grow: 1; } .tasks > li > button { justify-self: flex-end; background-color: #ff3046; } ``` ::: > If you want to learn more about this stylesheet check this article about [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/), and also this free [video tutorial](https://flexbox.io/) about it from [Wes Bos](https://twitter.com/wesbos). > > Flexbox is an excellent tool to distribute and align elements in your UI. ### 5.2: Applying styles Now you need to add some elements around your components. You are going to add a `className` to your main div in the `App`, also a `header` element with a few `divs` around your `h1`, and a main `div` around your form and list. Check below how it should be, pay attention to the name of the classes, they need to be the same as in the CSS file: ::: code-group ```js [imports/ui/App.jsx] .. return (

Welcome to Meteor!

    {tasks.map((task) => ( ))}
); ``` ::: > In React we use `className` instead of `class` as React uses Javascript to define the UI and `class` is a reserved word in Javascript. Also, choose a better title for your app, Meteor is amazing but you don't want to see `Welcome to Meteor!` in your app top bar all the time. You could choose something like: ::: code-group ```js [imports/ui/App.jsx] ..

📝️ To Do List

.. ``` ::: Your app should look like this: In the next step, we are going to make this task list more interactive, for example, providing a way to filter tasks. --- --- url: /tutorials/react/4.update-and-remove.md --- ## 4: Update and Remove Up until now, you have only inserted documents into our collection. Let's take a look at how you can update and remove them by interacting with the user interface. ### 4.1: Add Checkbox First, you need to add a `checkbox` element to your `Task` component. > Be sure to add the `readOnly` attribute since we are not using `onChange` to update the state. > > We also have to force our `checked` prop to a `boolean` since React understands that an `undefined` value as inexistent, therefore causing the component to switch from uncontrolled to a controlled one. > > You are also invited to experiment and see how the app behaves for learning purposes. You also want to receive a callback, a function that will be called when the checkbox is clicked. ::: code-group ```js [imports/ui/Task.jsx] import React from "react"; export const Task = ({ task, onCheckboxClick }) => { return (
  • onCheckboxClick(task)} readOnly /> {task.text}
  • ); }; ``` ::: ### 4.2: Toggle Checkbox Now you can update your task document by toggling its `isChecked` field. First, create a new method called `tasks.toggleChecked` to update the `isChecked` property. ::: code-group ```javascript [imports/api/tasksMethods.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "./TasksCollection"; Meteor.methods({ .. "tasks.toggleChecked"({ _id, isChecked }) { return TasksCollection.updateAsync(_id, { $set: { isChecked: !isChecked }, }); }, }); ``` ::: Now, create a function to change your document and pass it along to your `Task` component. ::: code-group ```js [imports/ui/App.jsx] .. export const App = () => { const handleToggleChecked = ({ _id, isChecked }) => Meteor.callAsync("tasks.toggleChecked", { _id, isChecked }); ..
      { tasks.map(task => ) }
    .. ``` ::: Your app should look like this: ### 4.3: Remove tasks You can remove tasks with just a few lines of code. First, add a button after text in your `Task` component and receive a callback function. ::: code-group ```js [imports/ui/Task.jsx] import React from 'react'; export const Task = ({ task, onCheckboxClick, onDeleteClick }) => { return ( .. {task.text} .. ``` ::: Now add the removal logic in the `App`, you need to have a function to delete the task and provide this function in your callback property in the `Task` component. For that, let's create a new method called `tasks.delete`: ::: code-group ```javascript [imports/api/tasksMethods.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "./TasksCollection"; Meteor.methods({ .. "tasks.delete"({ _id }) { return TasksCollection.removeAsync(_id); }, }); ``` ::: Then, let's call this method inside a `handleDelete` function: ::: code-group ```js [imports/ui/App.jsx] export const App = () => { .. const handleDelete = ({ _id }) =>// [!code highlight] Meteor.callAsync("tasks.delete", { _id });// [!code highlight] ..
      { tasks.map(task => ) }
    .. } ``` ::: Your app should look like this: In the next step, we are going to improve the look of your app using CSS with Flexbox. --- --- url: /tutorials/react/3.forms-and-events.md --- ## 3: Forms and Events All apps need to allow the user to perform some sort of interaction with the data that is stored. In our case, the first type of interaction is to insert new tasks. Without it, our To-Do app wouldn't be very helpful. One of the main ways in which a user can insert or edit data on a website is through forms. In most cases, it is a good idea to use the `
    ` tag since it gives semantic meaning to the elements inside it. ### 3.1: Create Task Form First, we need to create a simple form component to encapsulate our logic. As you can see we set up the `useState` React Hook. Please note the _array destructuring_ `[text, setText]`, where `text` is the stored value which we want to use, which in this case will be a _string_; and `setText` is a _function_ used to update that value. Create a new file `TaskForm.jsx` in your `ui` folder. ::: code-group ```js [imports/ui/TaskForm.jsx] import React, { useState } from "react"; export const TaskForm = () => { const [text, setText] = useState(""); return (
    ); }; ``` ::: ### 3.2: Update the App component Then we can simply add this to our `App` component above your list of tasks: ::: code-group ```js [imports/ui/App.jsx] import React from "react"; import { useTracker, useSubscribe } from "meteor/react-meteor-data"; import { TasksCollection } from "/imports/api/TasksCollection"; import { Task } from "./Task"; import { TaskForm } from "./TaskForm"; export const App = () => { const isLoading = useSubscribe("tasks"); const tasks = useTracker(() => TasksCollection.find({}).fetch()); if (isLoading()) { return
    Loading...
    ; } return (

    Welcome to Meteor!

      {tasks.map((task) => ( ))}
    ); }; ``` ::: ### 3.3: Update the Stylesheet You also can style it as you wish. For now, we only need some margin at the top so the form doesn't seem off the mark. Add the CSS class `.task-form`, this needs to be the same name in your `className` attribute in the form component. ::: code-group ```css [client/main.css] .task-form { margin-top: 1rem; } ``` ::: ### 3.4: Add Submit Handler Now let's create a function to handle the form submit and insert a new task into the database. To do it, we will need to implement a Meteor Method. Methods are essentially RPC calls to the server that let you perform operations on the server side securely. You can read more about Meteor Methods [here](https://guide.meteor.com/methods.html). To create your methods, you can create a file called `tasksMethods.js`. ::: code-group ```javascript [imports/api/tasksMethods.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "./TasksCollection"; Meteor.methods({ "tasks.insert"(doc) { return TasksCollection.insertAsync(doc); }, }); ``` ::: Remember to import your method on the `main.js` server file and the `main.jsx` client file. ::: code-group ```javascript [server/main.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "../imports/api/tasksCollection"; import "../imports/api/TasksPublications"; import "../imports/api/tasksMethods"; // [!code highlight] ``` ```javascript [client/main.jsx] import React from "react"; import { createRoot } from "react-dom/client"; import { Meteor } from "meteor/meteor"; import { App } from "/imports/ui/App"; import "../imports/api/tasksMethods"; // [!code highlight] ``` ::: Now you can attach a submit handler to your form using the `onSubmit` event, and also plug your React Hook into the `onChange` event present in the input element. As you can see you are using the `useState` React Hook to store the `value` of your `` element. Note that you also need to set your `value` attribute to the `text` constant as well, this will allow the `input` element to stay in sync with our hook. > In more complex applications you might want to implement some `debounce` or `throttle` logic if there are many calculations happening between potentially frequent events like `onChange`. There are libraries which will help you with this, like [Lodash](https://lodash.com/), for instance. ::: code-group ```js [imports/ui/TaskForm.jsx] import React, { useState } from "react"; export const TaskForm = () => { const [text, setText] = useState(""); const handleSubmit = async (e) => { e.preventDefault(); if (!text) return; await Meteor.callAsync("tasks.insert", { text: text.trim(), createdAt: new Date(), }); setText(""); }; return (
    setText(e.target.value)} />
    ); }; ``` ::: Inside the function, we are adding a task to the `tasks` collection by calling `Meteor.callAsync()`. The first argument is the name of the method we want to call, and the second argument is the text of the task. We are also trimming the text to remove any extra spaces. Also, insert a date `createdAt` in your `task` document so you know when each task was created. ### 3.5: Show Newest Tasks First Now you just need to make a change that will make users happy: we need to show the newest tasks first. We can accomplish this quite quickly by sorting our [Mongo](https://guide.meteor.com/collections.html#mongo-collections) query. ::: code-group ```js [imports/ui/App.jsx] .. export const App = () => { const tasks = useTracker(() => TasksCollection.find({}, { sort: { createdAt: -1 } }).fetch()); .. ``` ::: Your app should look like this: In the next step, we are going to update your tasks state and provide a way for users to remove tasks. --- --- url: /tutorials/react/2.collections.md --- ## 2: Collections Meteor already sets up MongoDB for you. In order to use our database, we need to create a _collection_, which is where we will store our _documents_, in our case our `tasks`. > You can read more about collections [here](https://v3-docs.meteor.com/api/collections.html). In this step, we will implement all the necessary code to have a basic collection for our tasks up and running using React hooks. ### 2.1: Create Tasks Collection {#create-tasks-collection} We can create a new collection to store our tasks by creating a new file at `imports/api/TasksCollection.js` which instantiates a new Mongo collection and exports it. ::: code-group ```js [imports/api/TasksCollection.js] import { Mongo } from "meteor/mongo"; export const TasksCollection = new Mongo.Collection("tasks"); ``` ::: Notice that we stored the file in the `imports/api` directory, which is a place to store API-related code, like publications and methods. You can name this folder as you want, this is just a choice. You can delete the `links.js` file in this folder as we are not going to use this collection. > You can read more about app structure and imports/exports [here](http://guide.meteor.com/structure.html). ### 2.2: Initialize Tasks Collection {#initialize-tasks-collection} For our collection to work, you need to import it in the server so it sets some plumbing up. You can either use `import "/imports/api/TasksCollection"` or `import { TasksCollection } from "/imports/api/TasksCollection"` if you are going to use on the same file, but make sure it is imported. Now it is easy to check if there is data or not in our collection, otherwise, we can insert some sample data easily as well. You don't need to keep the old content of `server/main.js`. ::: code-group ```js [server/main.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "/imports/api/TasksCollection"; const insertTask = (taskText) => TasksCollection.insertAsync({ text: taskText }); Meteor.startup(async () => { if ((await TasksCollection.find().countAsync()) === 0) { [ "First Task", "Second Task", "Third Task", "Fourth Task", "Fifth Task", "Sixth Task", "Seventh Task", ].forEach(insertTask); } }); ``` ::: So you are importing the `TasksCollection` and adding a few tasks to it iterating over an array of strings and for each string calling a function to insert this string as our `text` field in our `task` document. ### 2.3: Render Tasks Collection {#render-tasks-collection} Now comes the fun part, you will render the tasks using a React Function Component and a Hook called `useTracker` from a package called [react-meteor-data](https://atmospherejs.com/meteor/react-meteor-data). > Meteor works with Meteor packages and NPM packages, usually, Meteor packages are using Meteor internals or other Meteor packages. This package is already included in the React skeleton (`meteor create yourproject`) so you don't need to add it but you can always add Meteor packages running `meteor add package-name`: ```shell meteor add react-meteor-data ``` Now you are ready to import code from this package, when importing code from a Meteor package the only difference from NPM modules is that you need to prepend `meteor/` in the from part of your import. The `useTracker` function exported by `react-meteor-data` is a React Hook that allows you to have reactivity in your React components. Every time the data changes through reactivity your component will re-render. Cool, right? > For more information about React Hooks read [here](https://reactjs.org/docs/hooks-faq.html). ::: code-group ```javascript [imports/ui/App.jsx] import React from "react"; import { useTracker } from "meteor/react-meteor-data"; import { TasksCollection } from "/imports/api/TasksCollection"; import { Task } from "./Task"; export const App = () => { const tasks = useTracker(() => TasksCollection.find({}).fetch()); return (

    Welcome to Meteor!

      {tasks.map((task) => ( ))}
    ); }; ``` ::: But wait! Something is missing. If you run your app now, you'll see that you don't render any tasks. That's because we need to publish our data to the client. First, create a publication for our tasks: `imports/api/TasksPublications.js` ```javascript import { Meteor } from "meteor/meteor"; import { TasksCollection } from "./TasksCollection"; Meteor.publish("tasks", () => { return TasksCollection.find(); }); ``` Now, we need to import this file in our server: ::: code-group ```js [server/main.js] ... import { TasksCollection } from '/imports/api/TasksCollection'; import "../imports/api/TasksPublications"; // [!code highlight] const insertTask = taskText => TasksCollection.insertAsync({ text: taskText }); ... ``` ::: The only thing left is subscribe to this publication: `imports/ui/App.jsx` ```javascript import React from 'react'; import { useTracker, useSubscribe } from 'meteor/react-meteor-data'; // [!code highlight] import { TasksCollection } from '/imports/api/TasksCollection'; import { Task } from './Task'; export const App = () => { const isLoading = useSubscribe("tasks"); // [!code highlight] const tasks = useTracker(() => TasksCollection.find({}).fetch()); if (isLoading()) { return
    Loading...
    ; } ... } ``` As you can see, when subscribing to a publication using `useSubscribe` you'll get a `isLoading` function, that you can use to render some loading component before the data is ready. > For more information on Publications/Subscriptions, please check our [docs](https://v3-docs.meteor.com/api/meteor.html#pubsub). See how your app should look like now: You can change your data on MongoDB in the server and your app will react and re-render for you. You can connect to your MongoDB running `meteor mongo` in the terminal from your app folder or using a Mongo UI client, like [NoSQLBooster](https://nosqlbooster.com/downloads). Your embedded MongoDB is running in port `3001`. See how to connect: See your database: You can double-click your collection to see the documents stored on it: In the next step, we are going to create tasks using a form. --- --- url: /tutorials/react/1.creating-the-app.md --- ## 1: Creating the app ### 1.1: Install Meteor {#install-meteor} First, we need to install Meteor. If you don't have Meteor installed, you can install it by running: ```shell npx meteor ``` ### 1.2: Create Meteor Project {#create-meteor-project} The easiest way to setup Meteor with React is by using the command `meteor create` with the option `--react` and your project name (you can also omit the `--react` option since it is the default): ```shell meteor create simple-todos-react ``` Meteor will create all the necessary files for you. The files located in the `client` directory are setting up your client side (web), you can see for example `client/main.jsx` where Meteor is rendering your App main component into the HTML. Also, check the `server` directory where Meteor is setting up the server side (Node.js), you can see the `server/main.js` is initializing your MongoDB database with some data. You don't need to install MongoDB as Meteor provides an embedded version of it ready for you to use. You can now run your Meteor app using: ```shell meteor run ``` Don't worry, Meteor will keep your app in sync with all your changes from now on. Your React code will be located inside the `imports/ui` directory, and `App.jsx` file is the root component of your React To-do app. Take a quick look at all the files created by Meteor, you don't need to understand them now but it's good to know where they are. ### 1.3: Create Task Component {#create-task-component} You will make your first change now. Create a new file called `Task.jsx` in your `ui` folder. This file will export a React component called `Task` that will represent one task in your To-Do list. ::: code-group ```js [imports/ui/Task.jsx] import React from "react"; export const Task = ({ task }) => { return
  • {task.text}
  • ; }; ``` ::: As this component will be inside a list you are returning a `li` element. ### 1.4: Create Sample Tasks {#create-sample-tasks} As you are not connecting to your server and your database yet let's define some sample data which will be used shortly to render a list of tasks. It will be an array, and you can call it `tasks`. ::: code-group ```js [imports/ui/App.jsx] import React from 'react'; const tasks = [ {_id: 1, text: 'First Task'}, {_id: 2, text: 'Second Task'}, {_id: 3, text: 'Third Task'}, ]; export const App = () => ... ``` ::: You can put anything as your `text` property on each task. Be creative! ### 1.5: Render Sample Tasks {#render-sample-tasks} Now we can implement some simple rendering logic with React. We can now use our previous `Task` component to render our list items. In React you can use `{` `}` to write Javascript code between them. See below that you will use a `.map` function from the `Array` object to iterate over your sample tasks. ::: code-group ```js [imports/ui/App.jsx] import React from 'react'; import { Task } from './Task'; const tasks = ..; export const App = () => (

    Welcome to Meteor!

      { tasks.map(task => ) }
    ); ``` ::: Remember to add the `key` property to your task, otherwise React will emit a warning because it will see many components of the same type as siblings. Without a key, it will be hard for React to re-render one of them if necessary. > You can read more about React and Keys [here](https://reactjs.org/docs/lists-and-keys.html#keys). Remove the `Hello` and `Info` from your `App` component, remember to also remove the imports for them at the top of the file. Remove the `Hello.jsx` and `Info.jsx` files as well. ### 1.6: Hot Module Replacement {#hot-module-replacement} Meteor by default when using React is already adding for you a package called `hot-module-replacement`. This package updates the javascript modules in a running app that were modified during a rebuild. Reduces the feedback cycle while developing so you can view and test changes quicker (it even updates the app before the build has finished). You are also not going to lose the state, your app code will be updated and your state will be the same. In the next step, we are going to work with our MongoDB database to store our tasks. --- --- url: /tutorials/blaze/9.next-steps.md --- ## 9: Next Steps You have completed the tutorial! By now, you should have a good understanding of working with Meteor and Blaze. ::: info You can find the final version of this app in our [GitHub repository](https://github.com/meteor/meteor3-blaze). ::: Here are some options for what you can do next: - Check out the complete [documentation](https://v3-docs.meteor.com/) to learn more about Meteor 3. - Read the [Galaxy Guide](https://galaxy-support.meteor.com/en/article/deploy-to-galaxy-18gd6e2/) to learn more about deploying your app. - Join our community on the [Meteor Forums](https://forums.meteor.com/) and the [Meteor Lounge on Discord](https://discord.gg/hZkTCaVjmT) to ask questions and share your experiences. We can't wait to see what you build next! --- --- url: /tutorials/blaze/8.deploying.md --- ## 8: Deploying Deploying a Meteor application is similar to deploying any other Node.js app that uses websockets. You can find deployment options in [our guide](https://guide.meteor.com/deployment), including Meteor Up, Docker, and our recommended method, Galaxy. In this tutorial, we will deploy our app on [Galaxy](https://galaxycloud.app/), which is our own cloud solution. Galaxy offers a free plan, so you can deploy and test your app. Pretty cool, right? ### 8.1: Create your account You need a Meteor account to deploy your apps. If you don’t have one yet, you can [sign up here](https://cloud.meteor.com/?isSignUp=true). With this account, you can access our package manager, [Atmosphere](https://atmospherejs.com/), [Forums](https://forums.meteor.com/) and more. ### 8.2: Set up MongoDB (Optional) As your app uses MongoDB the first step is to set up a MongoDB database, Galaxy offers MongoDB hosting on a free plan for testing purposes, and you can also request for a production ready database that allows you to scale. In any MongoDB provider you will have a MongoDB URL which you must use. If you use the free option provided by Galaxy, the initial setup is done for you. Galaxy MongoDB URL will be like this: `mongodb://username:@org-dbname-01.mongodb.galaxy-cloud.io` . > You can read more about Galaxy MongoDB [here](https://galaxy-support.meteor.com/en/article/mongodb-general-1syd5af/). ### 8.3: Set up settings If you are not using the free option, then you need to create a settings file. It’s a JSON file that Meteor apps can read configurations from. Create this file in a new folder called `private` in the root of your project. It is important to notice that `private` is a special folder that is not going to be published to the client side of your app. Make sure you replace `Your MongoDB URL` by your own MongoDB URL :) ::: code-group ```json [private/settings.json] { "galaxy.meteor.com": { "env": { "MONGO_URL": "Your MongoDB URL" } } } ``` ::: ### 8.4: Deploy it Now you are ready to deploy, run `meteor npm install` before deploying to make sure all your dependencies are installed. You also need to choose a subdomain to publish your app. We are going to use the main domain `meteorapp.com` that is free and included on any Galaxy plan. In this example we are going to use `blaze-meteor-3.meteorapp.com` but make sure you select a different one, otherwise you are going to receive an error. > You can learn how to use custom domains on Galaxy [here](https://galaxy-support.meteor.com/en/article/domains-16cijgc/). Custom domains are available starting with the Essentials plan. Run the deployment command: ```shell meteor deploy blaze-meteor-3.meteorapp.com --free --mongo ``` > If you are not using the free hosting with MongoDB on Galaxy, then remove the `--mongo` flag from the deploy script and add `--settings private/settings.json` with the proper setting for your app. Make sure you replace `blaze-meteor-3` by a custom name that you want as subdomain. You will see a log like this: ```shell meteor deploy blaze-meteor-3.meteorapp.com --settings private/settings.json Talking to Galaxy servers at https://us-east-1.galaxy-deploy.meteor.com Preparing to build your app... Preparing to upload your app... Uploaded app bundle for new app at blaze-meteor-3.meteorapp.com. Galaxy is building the app into a native image. Waiting for deployment updates from Galaxy... Building app image... Deploying app... You have successfully deployed the first version of your app. For details, visit https://galaxy.meteor.com/app/blaze-meteor-3.meteorapp.com ``` This process usually takes just a few minutes, but it depends on your internet speed as it’s going to send your app bundle to Galaxy servers. > Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, [read more](https://galaxy-support.meteor.com/en/article/container-environment-lfd6kh/). You can check your logs on Galaxy, including the part that Galaxy is building your Docker image and deploying it. ### 8.5: Access the app and enjoy Now you should be able to access your Galaxy dashboard at `https://galaxy.meteor.com/app/blaze-meteor-3.meteorapp.com`. You can also access your app on Galaxy 2.0 which is currently in beta at `https://galaxy-beta.meteor.com//us-east-1/apps/.meteorapp.com`. Remember to use your own subdomain instead of `blaze-meteor-3`. You can access the app at [blaze-meteor-3.meteorapp.com](https://blaze-meteor-3.meteorapp.com/)! Just use your subdomain to access yours! > We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list [here](https://galaxy-support.meteor.com/en/article/regions-1vucejm/). This is huge, you have your app running on Galaxy, ready to be used by anyone in the world! --- --- url: /tutorials/blaze/7.adding-user-accounts.md --- ## 7: Adding User Accounts ### 7.1: Password Authentication Meteor already comes with a basic authentication and account management system out of the box, so you only need to add the `accounts-password` to enable username and password authentication: ```shell meteor add accounts-password ``` > There are many more authentication methods supported. You can read more about the accounts system [here](https://v3-docs.meteor.com/api/accounts.html). We also recommend you to install `bcrypt` node module, otherwise, you are going to see a warning saying that you are using a pure-Javascript implementation of it. ```shell meteor npm install --save bcrypt ``` > You should always use `meteor npm` instead of only `npm` so you always use the `npm` version pinned by Meteor, this helps you to avoid problems due to different versions of npm installing different modules. ### 7.2: Create User Account Now you can create a default user for our app, we are going to use `meteorite` as username, we just create a new user on server startup if we didn't find it in the database. ::: code-group ```js [server/main.js] import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; import { TasksCollection } from '/imports/api/TasksCollection'; import "../imports/api/TasksMethods"; const SEED_USERNAME = 'meteorite'; const SEED_PASSWORD = 'password'; Meteor.startup(async () => { if (!(await Accounts.findUserByUsername(SEED_USERNAME))) { await Accounts.createUser({ username: SEED_USERNAME, password: SEED_PASSWORD, }); } ... }); ``` ::: You should not see anything different in your app UI yet. ### 7.3: Login Form You need to provide a way for the users to input the credentials and authenticate, for that we need a form. Our login form will be simple, with just two fields (username and password) and a button. You should use `Meteor.loginWithPassword(username, password)`; to authenticate your user with the provided inputs. ::: code-group ```html [imports/ui/Login.html] ``` ::: ::: code-group ```js [imports/ui/Login.js] import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; import './Login.html'; Template.login.events({ 'submit .login-form'(e) { e.preventDefault(); const target = e.target; const username = target.username.value; const password = target.password.value; Meteor.loginWithPassword(username, password); } }); ``` ::: Be sure also to import the login form in `App.js`. ::: code-group ```js [imports/ui/App.js] import { Template } from 'meteor/templating'; import { ReactiveDict } from 'meteor/reactive-dict'; import { TasksCollection } from "../api/TasksCollection"; import '/imports/api/TasksMethods.js'; // this import in this client UI allows for optimistic execution import './App.html'; import './Task'; import "./Login.js"; ... ``` ::: Ok, now you have a form, let's use it. ### 7.4: Require Authentication Our app should only allow an authenticated user to access its task management features. We can accomplish that by rendering the `Login` from the template when we don’t have an authenticated user. Otherwise, we return the form, filter, and list component. To achieve this, we will use a conditional test inside our main div on `App.html`: ::: code-group ```html [imports/ui/App.html] ...
    {{#if isUserLoggedIn}} {{> form }}
      {{#each tasks}} {{> task}} {{/each}}
    {{else}} {{> login }} {{/if}}
    ... ``` ::: As you can see, if the user is logged in, we render the whole app (`isUserLoggedIn`). Otherwise, we render the Login template. Let’s now create our helper `isUserLoggedIn`: ::: code-group ```js [imports/ui/App.js] ... const getUser = () => Meteor.user(); const isUserLoggedInChecker = () => Boolean(getUser()); ... Template.mainContainer.helpers({ ..., isUserLoggedIn() { return isUserLoggedInChecker(); }, }); ... ``` ::: ### 7.5: Login Form style Ok, let's style the login form now: ::: code-group ```css [client/main.css] .login-form { display: flex; flex-direction: column; height: 100%; justify-content: center; align-items: center; } .login-form > div { margin: 8px; } .login-form > div > label { font-weight: bold; } .login-form > div > input { flex-grow: 1; box-sizing: border-box; padding: 10px 6px; background: transparent; border: 1px solid #aaa; width: 100%; font-size: 1em; margin-right: 16px; margin-top: 4px; } .login-form > div > input:focus { outline: 0; } .login-form > div > button { background-color: #62807e; } ``` ::: Now your login form should be centralized and beautiful. ### 7.6: Server startup Every task should have an owner from now on. So go to your database, as you learn before, and remove all the tasks from there: `db.tasks.remove({});` Change your `server/main.js` to add the seed tasks using your `meteorite` user as owner. Make sure you restart the server after this change so `Meteor.startup` block will run again. This is probably going to happen automatically anyway as you are going to make changes in the server side code. ::: code-group ```js [server/main.js] ... const user = await Accounts.findUserByUsername(SEED_USERNAME); if ((await TasksCollection.find().countAsync()) === 0) { [ "First Task", "Second Task", "Third Task", "Fourth Task", "Fifth Task", "Sixth Task", "Seventh Task", ].forEach((taskName) => { Meteor.callAsync("tasks.insert", { text: taskName, createdAt: new Date(), userId: user._id }); }); } ... ``` ::: See that we are using a new field called `userId` with our user `_id` field, we are also setting `createdAt` field. ### 7.7: Task owner First, let's change our publication to publish the tasks only for the currently logged user. This is important for security, as you send only data that belongs to that user. ::: code-group ```js [/imports/api/TasksPublications.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "./TasksCollection"; Meteor.publish("tasks", function () { let result = this.ready(); const userId = this.userId; if (userId) { result = TasksCollection.find({ userId }); } return result; }); ``` ::: Now let's check if we have a `user` before trying to fetch any data: ::: code-group ```js [imports/ui/App.js] ... Template.mainContainer.helpers({ tasks() { let result = []; if (isUserLoggedInChecker()) { const instance = Template.instance(); const hideCompleted = instance.state.get(HIDE_COMPLETED_STRING); const hideCompletedFilter = { isChecked: { $ne: true } }; result = TasksCollection.find(hideCompleted ? hideCompletedFilter : {}, { sort: { createdAt: -1, _id: -1 }, }).fetch(); } return result; }, hideCompleted() { return Template.instance().state.get(HIDE_COMPLETED_STRING); }, incompleteCount() { result = ''; if (isUserLoggedInChecker()) { const incompleteTasksCount = TasksCollection.find({ isChecked: { $ne: true } }).count(); result = incompleteTasksCount ? `(${incompleteTasksCount})` : ''; } return result; }, isUserLoggedIn() { return isUserLoggedInChecker(); }, }); ... ``` ::: Also, update the `tasks.insert` method to include the field `userId` when creating a new task: ::: code-group ```js [imports/api/TasksMethods.js] ... Meteor.methods({ "tasks.insert"(doc) { const insertDoc = { ...doc }; if (!('userId' in insertDoc)) { insertDoc.userId = this.userId; } return TasksCollection.insertAsync(insertDoc); }, ... ``` ::: ### 7.8: Log out We can also organize our tasks by showing the owner’s username below our app bar. Let’s add a new `div` where the user can click and log out from the app: ::: code-group ```html [imports/ui/App.html] ...
    {{#if isUserLoggedIn}}
    {{getUser.username}} 🚪
    {{> form }} ... ``` ::: Now, let’s create the `getUser` helper and implement the event that will log out the user when they click on this `div`. Logging out is done by calling the function `Meteor.logout()`: ::: code-group ```js [imports/ui/App.js] ... Template.mainContainer.events({ ..., 'click .user'() { Meteor.logout(); }, }); ... Template.mainContainer.helpers({ ..., getUser() { return getUser(); }, }); ... ``` ::: Remember to style your username as well. ::: code-group ```css [client/main.css] .user { display: flex; align-self: flex-end; margin: 8px 16px 0; font-weight: bold; cursor: pointer; } ``` ::: Phew! You have done quite a lot in this step. Authenticated the user, set the user in the tasks, and provided a way for the user to log out. Your app should look like this: In the next step, we are going to learn how to deploy your app! --- --- url: /tutorials/blaze/5.styles.md --- ## 5: Styles ### 5.1: CSS Our user interface up until this point has looked quite ugly. Let's add some basic styling which will serve as the foundation for a more professional looking app. Replace the content of our `client/main.css` file with the one below, the idea is to have an app bar at the top, and a scrollable content including: - form to add new tasks; - list of tasks. ::: code-group ```css [client/main.css] body { font-family: sans-serif; background-color: #315481; background-image: linear-gradient(to bottom, #315481, #918e82 100%); background-attachment: fixed; position: absolute; top: 0; bottom: 0; left: 0; right: 0; padding: 0; margin: 0; font-size: 14px; } button { font-weight: bold; font-size: 1em; border: none; color: white; box-shadow: 0 3px 3px rgba(34, 25, 25, 0.4); padding: 5px; cursor: pointer; } button:focus { outline: 0; } .app { display: flex; flex-direction: column; height: 100vh; } .app-header { flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .main { display: flex; flex-direction: column; flex-grow: 1; overflow: auto; background: white; } .main::-webkit-scrollbar { width: 0; height: 0; background: inherit; } header { background: #d2edf4; background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%); padding: 20px 15px 15px 15px; position: relative; box-shadow: 0 3px 3px rgba(34, 25, 25, 0.4); } .app-bar { display: flex; justify-content: space-between; } .app-bar h1 { font-size: 1.5em; margin: 0; display: inline-block; margin-right: 1em; } .task-form { display: flex; margin: 16px; } .task-form > input { flex-grow: 1; box-sizing: border-box; padding: 10px 6px; background: transparent; border: 1px solid #aaa; width: 100%; font-size: 1em; margin-right: 16px; } .task-form > input:focus { outline: 0; } .task-form > button { min-width: 100px; height: 95%; background-color: #315481; } .tasks { list-style-type: none; padding-inline-start: 0; padding-left: 16px; padding-right: 16px; margin-block-start: 0; margin-block-end: 0; } .tasks > li { display: flex; padding: 16px; border-bottom: #eee solid 1px; align-items: center; } .tasks > li > label { flex-grow: 1; } .tasks > li > button { justify-self: flex-end; background-color: #ff3046; } ``` ::: > If you want to learn more about this stylesheet check this article about [Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/), and also this free [video tutorial](https://flexbox.io/) about it from [Wes Bos](https://twitter.com/wesbos). > > Flexbox is an excellent tool to distribute and align elements in your UI. ### 5.2: Applying styles Now you need to add some elements around your components. You are going to add a `class` to your main div in the `App`, also a `header` element with a few `divs` around your `h1`, and a main `div` around your form and list. Check below how it should be, pay attention to the name of the classes, they need to be the same as in the CSS file: ::: code-group ```html [imports/ui/App.html] ... ... ``` ::: Your app should look like this: In the next step, we are going to make this task list more interactive, for example, providing a way to filter tasks. --- --- url: /tutorials/blaze/4.update-and-remove.md --- ## 4: Update and Remove Up until now, you have only inserted documents into our collection. Let's take a look at how you can update and remove them by interacting with the user interface. ### 4.1: Add Checkbox First, you need to add a `checkbox` element to your `Task` component. Next, let’s create a new file for our `task` template in `imports/ui/Task.html`, so we can start to separate the logic in our app. ::: code-group ```html [imports/ui/Task.html] ``` ::: Don’t forget to remove the template named `task` in `imports/ui/App.html`. You must also add the following import: ::: code-group ```js [imports/ui/App.js] ... import './Task'; ... ``` ::: ### 4.2: Toggle Checkbox Now you can update your task document by toggling its `isChecked` field. First, create a new method called `tasks.toggleChecked` to update the `isChecked` property. ::: code-group ```js [imports/api/TasksMethods.js] import { Meteor } from "meteor/meteor"; import { TasksCollection } from "./TasksCollection"; Meteor.methods({ .. "tasks.toggleChecked"({ _id, isChecked }) { return TasksCollection.updateAsync(_id, { $set: { isChecked: !isChecked }, }); }, }); ``` ::: Create a new file called `Task.js` so we can have our handlers to the `task` template: ::: code-group ```js [imports/ui/Task.js] import { Template } from 'meteor/templating'; import { TasksCollection } from "../api/TasksCollection"; import '/imports/api/TasksMethods.js'; // this import in this client UI allows for optimistic execution import './Task.html'; Template.task.events({ 'click .toggle-checked'() { // Set the checked property to the opposite of its current value let taskID = this._id; let checkedValue = Boolean(this.isChecked); Meteor.callAsync("tasks.toggleChecked", { _id: taskID, isChecked: checkedValue }); }, }); ``` ::: Toggling checkboxes should now persist in the DB even if you refresh the web browser. Your app should look like this: If your computer is fast enough, it's possible that when it sets up the default tasks a few will have the same date. That will cause them to non-deterministically "jump around" in the UI as you toggle checkboxes and the UI reactively updates. To make it stable, you can add a secondary sort on the `_id` of the task: ::: code-group ```js [imports/ui/App.js] ... Template.mainContainer.helpers({ tasks() { return TasksCollection.find({}, { sort: { createdAt: -1, _id: -1 } }); }, ... ``` ::: ### 4.3: Remove tasks You can remove tasks with just a few lines of code. First, add a button after text `label` in your `Task` component. ::: code-group ```html [imports/ui/Task.html]