Methods
Methods are remote functions that Meteor clients can invoke with Meteor.call
.
If you prefer to watch the video, click below.
Meteor.methods(methods)
Defines functions that can be invoked over the network by clients.
Arguments
- methods Object
-
Dictionary whose keys are method names and values are functions.
Example:
Meteor.methods({
foo(arg1, arg2) {
check(arg1, String);
check(arg2, [Number]);
// Do stuff...
if (/* you want to throw an error */) {
throw new Meteor.Error('pants-not-found', "Can't find my pants");
}
return 'some return value';
},
bar() {
// Do other stuff...
return 'baz';
}
});
Calling methods
on the server defines functions that can be called remotely by
clients. They should return an EJSON-able value or throw an
exception. Inside your method invocation, this
is bound to a method
invocation object, which provides the following:
isSimulation
: a boolean value, true if this invocation is a stub.unblock
: when called, allows the next method from this client to begin running.userId
: the id of the current user.setUserId
: a function that associates the current client with a user.connection
: on the server, the connection this method call was received on.
Calling methods
on the client defines stub functions associated with
server methods of the same name. You don’t have to define a stub for
your method if you don’t want to. In that case, method calls are just
like remote procedure calls in other systems, and you’ll have to wait
for the results from the server.
If you do define a stub, when a client invokes a server method it will also run its stub in parallel. On the client, the return value of a stub is ignored. Stubs are run for their side-effects: they are intended to simulate the result of what the server’s method will do, but without waiting for the round trip delay. If a stub throws an exception it will be logged to the console.
You use methods all the time, because the database mutators
(insert
, update
, remove
) are implemented
as methods. When you call any of these functions on the client, you’re invoking
their stub version that update the local cache, and sending the same write
request to the server. When the server responds, the client updates the local
cache with the writes that actually occurred on the server.
You don’t have to put all your method definitions into a single Meteor.methods
call; you may call it multiple times, as long as each method has a unique name.
If a client calls a method and is disconnected before it receives a response,
it will re-call the method when it reconnects. This means that a client may
call a method multiple times when it only means to call it once. If this
behavior is problematic for your method, consider attaching a unique ID
to each method call on the client, and checking on the server whether a call
with this ID has already been made. Alternatively, you can use
Meteor.apply
with the noRetry option set to true.
Read more about methods and how to use them in the Methods article in the Meteor Guide.
Meteor.isAsyncCall()
Tells if the method call came from a call or a callAsync.
This method can be used to determine if the current method invocation is
asynchronous. It returns true if the method is running on the server and came from
an async call(Meteor.callAsync
)
The id of the user that made this method call, or null
if no user was logged in.
The user id is an arbitrary string — typically the id of the user record
in the database. You can set it with the setUserId
function. If you’re using
the Meteor accounts system then this is handled for you.
Set the logged in user.
Arguments
- userId String or null
-
The value that should be returned by
userId
on this connection.
Call this function to change the currently logged-in user on the
connection that made this method call. This simply sets the value of
userId
for future method calls received on this connection. Pass
null
to log out the connection.
If you are using the built-in Meteor accounts system then this
should correspond to the _id
field of a document in the
Meteor.users
collection.
setUserId
is not retroactive. It affects the current method call and
any future method calls on the connection. Any previous method calls on
this connection will still see the value of userId
that was in effect
when they started.
If you also want to change the logged-in user on the client, then after calling
setUserId
on the server, call Meteor.connection.setUserId(userId)
on the
client.
Access inside a method invocation. Boolean value, true if this invocation is a stub.
Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber.
On the server, methods from a given client run one at a time. The N+1th
invocation from a client won’t start until the Nth invocation
returns. However, you can change this by calling this.unblock
. This
will allow the N+1th invocation to start running in a new fiber.
Access inside a method invocation. The connection that this method was received on. null
if the method is not associated with a connection, eg. a server initiated method call. Calls to methods made from a server method which was in turn initiated from the client share the same connection
.
new Meteor.Error(error, [reason], [details])
This class represents a symbolic error thrown by a method.
Arguments
- error String
-
A string code uniquely identifying this kind of error. This string should be used by callers of the method to determine the appropriate action to take, instead of attempting to parse the reason or details fields.
For legacy reasons, some built-in Meteor functions such as
check
throw errors with a number in this field. - reason String
-
Optional. A short human-readable summary of the error, like 'Not Found'.
- details String
-
Optional. Additional information about the error, like a textual stack trace.
If you want to return an error from a method, throw an exception. Methods can
throw any kind of exception. But Meteor.Error
is the only kind of error that
a server will send to the client. If a method function throws a different
exception, then it will be mapped to a sanitized version on the
wire. Specifically, if the sanitizedError
field on the thrown error is set to
a Meteor.Error
, then that error will be sent to the client. Otherwise, if no
sanitized version is available, the client gets
Meteor.Error(500, 'Internal server error')
.
Meteor.call(name, [arg1, arg2...], [asyncCallback])
Invokes a method with a sync stub, passing any number of arguments.
Arguments
- name String
-
Name of method to invoke
- arg1, arg2... EJSON-able Object
-
Optional method arguments
- asyncCallback Function
-
Optional callback, which is called asynchronously with the error or result after the method is complete. If not provided, the method runs synchronously if possible (see below).
This is how to invoke a method with a sync stub. It will run the method on the server. If a
stub is available, it will also run the stub on the client. (See also
Meteor.apply
, which is identical to Meteor.call
except that
you specify the parameters as an array instead of as separate arguments and you
can specify a few options controlling how the method is executed.)
If you include a callback function as the last argument (which can’t be
an argument to the method, since functions aren’t serializable), the
method will run asynchronously: it will return nothing in particular and
will not throw an exception. When the method is complete (which may or
may not happen before Meteor.call
returns), the callback will be
called with two arguments: error
and result
. If an error was thrown,
then error
will be the exception object. Otherwise, error
will be
undefined
and the return value (possibly undefined
) will be in result
.
// Asynchronous call
Meteor.call('foo', 1, 2, (error, result) => { ... });
If you do not pass a callback on the server, the method invocation will
block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the method
threw an exception. (Possibly mapped to 500 Server Error if the
exception happened remotely and it was not a Meteor.Error
exception.)
// Synchronous call
const result = Meteor.call('foo', 1, 2);
On the client, if you do not pass a callback and you are not inside a
stub, call
will return undefined
, and you will have no way to get
the return value of the method. That is because the client doesn’t have
fibers, so there is not actually any way it can block on the remote
execution of a method.
Finally, if you are inside a stub on the client and call another
method, the other method is not executed (no RPC is generated, nothing
“real” happens). If that other method has a stub, that stub stands in
for the method and is executed. The method call’s return value is the
return value of the stub function. The client has no problem executing
a stub synchronously, and that is why it’s okay for the client to use
the synchronous Meteor.call
form from inside a method body, as
described earlier.
Meteor tracks the database writes performed by methods, both on the client and
the server, and does not invoke asyncCallback
until all of the server’s writes
replace the stub’s writes in the local cache. In some cases, there can be a lag
between the method’s return value being available and the writes being visible:
for example, if another method still outstanding wrote to the same document, the
local cache may not be up to date until the other method finishes as well. If
you want to process the method’s result as soon as it arrives from the server,
even if the method’s writes are not available yet, you can specify an
onResultReceived
callback to Meteor.apply
.
Meteor.callAsync(name, [arg1, arg2...])
Invokes a method with an async stub, passing any number of arguments.
Arguments
- name String
-
Name of method to invoke
- arg1, arg2... EJSON-able Object
-
Optional method arguments
Meteor.callAsync
is just like Meteor.call
, except that it’ll return a promise that you need to solve to get the result.
Be aware that you should never call a method (async or not) after calling
Meteor.callAsync
without waiting for the promise to solve. To understand why, please read this part of our migration guide.
Meteor.apply(name, args, [options], [asyncCallback])
Invoke a method passing an array of arguments.
Arguments
- name String
-
Name of method to invoke
- args Array of EJSON-able Objects
-
Method arguments
- asyncCallback Function
-
Optional callback; same semantics as in
Meteor.call
.
Options
- wait Boolean
-
(Client only) If true, don't send this method until all previous method calls have completed, and don't send any subsequent method calls until this one is completed.
- onResultReceived Function
-
(Client only) This callback is invoked with the error or result of the method (just like
asyncCallback
) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method. - noRetry Boolean
-
(Client only) if true, don't send this method again on reload, simply call the callback an error with the error code 'invocation-failed'.
- throwStubExceptions Boolean
-
(Client only) If true, exceptions thrown by method stubs will be thrown instead of logged, and the method will not be invoked on the server.
- returnStubValue Boolean
-
(Client only) If true then in cases where we would have otherwise discarded the stub's return value and returned undefined, instead we go ahead and return it. Specifically, this is any time other than when (a) we are already inside a stub or (b) we are in Node and no callback was provided. Currently we require this flag to be explicitly passed to reduce the likelihood that stub return values will be confused with server return values; we may improve this in future.
Meteor.apply
is just like Meteor.call
, except that the method arguments are
passed as an array rather than directly as arguments, and you can specify
options about how the client executes the method.
Meteor.applyAsync(name, args, [options])
Invoke a method passing an array of arguments.
Arguments
- name String
-
Name of method to invoke
- args Array of EJSON-able Objects
-
Method arguments
Options
- wait Boolean
-
(Client only) If true, don't send this method until all previous method calls have completed, and don't send any subsequent method calls until this one is completed.
- onResultReceived Function
-
(Client only) This callback is invoked with the error or result of the method (just like
asyncCallback
) as soon as the error or result is available. The local cache may not yet reflect the writes performed by the method. - noRetry Boolean
-
(Client only) if true, don't send this method again on reload, simply call the callback an error with the error code 'invocation-failed'.
- throwStubExceptions Boolean
-
(Client only) If true, exceptions thrown by method stubs will be thrown instead of logged, and the method will not be invoked on the server.
- returnStubValue Boolean
-
(Client only) If true then in cases where we would have otherwise discarded the stub's return value and returned undefined, instead we go ahead and return it. Specifically, this is any time other than when (a) we are already inside a stub or (b) we are in Node and no callback was provided. Currently we require this flag to be explicitly passed to reduce the likelihood that stub return values will be confused with server return values; we may improve this in future.
Meteor.applyAsync
is just like Meteor.apply
, except it is an async function, and it will consider that the stub is async.
DDPRateLimiter
Customize rate limiting for methods and subscriptions to avoid a high load of WebSocket messages in your app.
Galaxy (Meteor hosting) offers additional App Protection, read more and try it with our free 30-day trial.
By default, DDPRateLimiter
is configured with a single rule. This rule
limits login attempts, new user creation, and password resets to 5 attempts
every 10 seconds per connection. It can be removed by calling
Accounts.removeDefaultRateLimit()
.
To use DDPRateLimiter
for modifying the default rate-limiting rules,
add the ddp-rate-limiter
package to your project in your terminal:
meteor add ddp-rate-limiter
DDPRateLimiter.addRule(matcher, numRequests, timeInterval, callback)
Add a rule that matches against a stream of events describing method or subscription attempts. Each event is an object with the following properties:
type
: Either "method" or "subscription"name
: The name of the method or subscription being calleduserId
: The user ID attempting the method or subscriptionconnectionId
: A string representing the user's DDP connectionclientAddress
: The IP address of the user
Returns unique ruleId
that can be passed to removeRule
and setErrorMessageOnRule
Arguments
- matcher Object
-
Matchers specify which events are counted towards a rate limit. A matcher is an object that has a subset of the same properties as the event objects described above. Each value in a matcher object is one of the following:
-
a string: for the event to satisfy the matcher, this value must be equal to the value of the same property in the event object
-
a function: for the event to satisfy the matcher, the function must evaluate to true when passed the value of the same property in the event object
Here's how events are counted: Each event that satisfies the matcher's filter is mapped to a bucket. Buckets are uniquely determined by the event object's values for all properties present in both the matcher and event objects.
-
- numRequests number
-
number of requests allowed per time interval. Default = 10.
- timeInterval number
-
time interval in milliseconds after which rule's counters are reset. Default = 1000.
- callback Function
-
function to be called after a rule is executed.
Custom rules can be added by calling DDPRateLimiter.addRule
. The rate
limiter is called on every method and subscription invocation.
A rate limit is reached when a bucket has surpassed the rule’s predefined capacity, at which point errors will be returned for that input until the buckets are reset. Buckets are regularly reset after the end of a time interval.
Here’s example of defining a rule and adding it into the DDPRateLimiter
:
// Define a rule that matches login attempts by non-admin users.
const loginRule = {
userId(userId) {
const user = Meteor.users.findOne(userId);
return user && user.type !== 'admin';
},
type: 'method',
name: 'login'
};
// Add the rule, allowing up to 5 messages every 1000 milliseconds.
DDPRateLimiter.addRule(loginRule, 5, 1000);
DDPRateLimiter.removeRule(id)
Removes the specified rule from the rate limiter. If rule had hit a rate limit, that limit is removed as well.
Arguments
- id string
-
'ruleId' returned from
addRule
DDPRateLimiter.setErrorMessage(message)
Set error message text when method or subscription rate limit exceeded.
Arguments
- message string or Function
-
Functions are passed in an object with a
timeToReset
field that specifies the number of milliseconds until the next method or subscription is allowed to run. The function must return a string of the error message.
DDPRateLimiter.setErrorMessageOnRule(ruleId, message)
Set error message text when method or subscription rate limit exceeded for a specific rule.
Arguments
- ruleId string
-
The ruleId returned from
addRule
- message string or Function
-
Functions are passed in an object with a
timeToReset
field that specifies the number of milliseconds until the next method or subscription is allowed to run. The function must return a string of the error message.
Allows developers to specify custom error messages for each rule instead of being limited to one global error message for every rule. It adds some clarity to what rules triggered which errors, allowing for better UX and also opens the door for i18nable error messages per rule instead of the default English error message.
Here is an example with a custom error message:
const setupGoogleAuthenticatorRule = {
userId(userId) {
const user = Meteor.users.findOne(userId);
return user;
},
type: 'method',
name: 'Users.setupGoogleAuthenticator',
};
// Add the rule, allowing up to 1 google auth setup message every 60 seconds
const ruleId = DDPRateLimiter.addRule(setupGoogleAuthenticatorRule, 1, 60000);
DDPRateLimiter.setErrorMessageOnRule(ruleId, function (data) {
return `You have reached the maximum number of Google Authenticator attempts. Please try again in ${Math.ceil(data.timeToReset / 1000)} seconds.`;
});
Or a more simple approach:
const ruleId = DDPRateLimiter.addRule(setupGoogleAuthenticatorRule, 1, 60000);
DDPRateLimiter.setErrorMessageOnRule(ruleId, 'Example as a single string error message');