Add field to users collection?

How would you go about adding an extra field to the default Meteor.users collection after the user has already been created?

This is assuming there are two things you want to take care of: One, adding the field to the existing user documents; Two, adding the field to all new users in the future.

For the first case, you can use the meteor mongo console to enter a command that finds all users and updates their document with the new field. For the second case, write an Accounts.onCreateUser function to add the field.

profile field could be remove too.

use the Accounts.onCreateUser to add the fields you want after creation

I just need to add/update a field on already existing accounts. Eg, a user logins for the first time (basic twitter login), and after a specific event I need to add/update a field on the collection.

I’ve tried doing Meteor.users.update({ _id: userId }, { $set: { newfield: newvalue }}); when the event is triggered but the field simply isnt added as far as i can see in the console with Meteor.users.find().fetch().

If you’re going to have the field in any user documents, you might as well have it in all user documents so you can safely query the user without having to work around the buggy possibility that the field won’t be there. So rather than adding the field at your specific event, you would be updating its value.

Instead of identifying the current user by _id: userId, try using this.userId to act on the current user’s document. Like this:

Meteor.users.update(this.userId, {$set: {blurb: newText}});
2 Likes

Should the field (or i guess it would just be a key/value pair) be put into the already made profile field? If so how would this be done?

Also what is the syntax for creating a new field using onCreateUser?

I’m sorry, I didn’t mean to actually use the profile field, since that’s a special case to avoid. I’m updating my previous reply with something slightly different.

For adding a new field with onCreateUser, here’s an example:

Accounts.onCreateUser(function(options, user) {
  user.blurb = "";
  return user;
});

And as always, it’s good to read the documentation http://docs.meteor.com/#/full/accounts_oncreateuser

Haha yea ive gone through the docs for onCreateUser, werent too helpful =\

So i’ve added:
Accounts.onCreateUser(function(options, user) { user.voted = 0; return user; });

under the Meteor.isServer check (is this right?) and then using Meteor.users.update(this.userId, {$set: {voted: 1}}); when the event is triggered. This seems to delete the entire profile field and doesnt create the new field.

That’s right, but it has nothing to do with the profile field and shouldn’t interact with profile at all. I don’t know why that doesn’t seem to create the voted field as expected. Does your publish function only return certain fields? If so, then you’ll have to modify it to publish the voted field as well if you want that information on the client.

No I’m using autopublish. Ok so apparently by default Meteor only publishes the profile, email, and username. I wrote a publish/subscribe and can atleast now see the new voted field but it isnt updated after the event. Ideas?

Maybe you also need to add an allow rule to allow this operation to happen?

@sashko how would you safely structure such an allow rule? I’m trying using Stripe Checkout and confused about how to distinguish a paid user from unpaid i.e. best practice for doing so.

After some more thought on this issue, I actually think the best thing to do might be to avoid messing with the users collection, and instead to create a new collection with custom user properties.

If you don’t want to do that, I would have a second piece of advice which is to avoid allow/deny in favor of having a very specific Method that just updates the exact field you need. It’s very easy to accidentally add a security hole to your app by writing a flawed allow/deny rule, so I wouldn’t use that especially in the case of payments.

1 Like

Thanks for the speedy reply - you’re, as always, awesome! I’ve been trying to use a method but somehow I’ve been unable to get the update on the specific field to update - I have the field in Meteor.users outside of the profile object, have the _id targeted and still the field will not $set.

It would probably be a lot easier to debug if you had a code sample or something!

Please excuse the noob nature of my code :slight_smile: Thanks so much!

CLIENT:

StripeCheckout.open({
			key: 'I HAVE MY TEST PUBLIC KEY HERE',
			amount: 1995,
			name: 'EasySGO',
			description: 'EasySGO 2015/2016 School Year.',
			panelLabel: 'Pay Here',
			token: function(res) {
				stripeToken = res.id;
				console.info(res);
				user = Meteor.userId();
				console.log(user + "STRCheckout hello!");
				Meteor.call('chargeCard', stripeToken, user);
			}
		});
	}
});

SERVER:

Meteor.methods({
	'chargeCard': function(stripeToken,user) {
		check(stripeToken, String);
		check(user, String);
		var Stripe = StripeAPI('I have my test Secret Key Here');

		Stripe.charges.create ({
			source: stripeToken,
			amount: 1995,
			currency: 'usd'
		}, Meteor.bindEnvironment(function(err, charge) {
			if(err && err.type === 'stripecarderror') {
				throw new Meteor.error("stripe-charge-error", err.message);
			}
			console.log( user + "chargeCardThis is the method sending to update method.")
			return Meteor.call('userIsPaid', user);
			//console.log(err, charge);
		}));
	},

	// Paid Status check on server - not client
	'isYearPaid': function(user) {
		var getUser = Meteor.users.findOne({'_id': user}, {fields: {"paid2016": 1}}),
			paidStat = getUser;
			console.log(paidStat + "isYearPaid!");
		if(paidStat === true) {
			return true;
		} else {
			return false;
		}
	},
	'userIsPaid': function(user, error){
		var getUser = Meteor.users.findOne({'_id': user}, {fields: {"paid2016": 1}}),
			paidStatus = getUser._id;
		console.log(paidStatus + "userIsPaid");

		return Meteor.users.update({_id: paidStatus}, {$set: {'paid2016': true}});
	}
});

After some reflection, I believe that the new collection with custom user properties is a really elegant solution to this. Thanks so much!

There’s already a huge problem with this code!

Anyone can call this with any user ID to charge a user. You should take the user ID from Meteor.userId() inside the method code.

You should only use Meteor methods for methods you want to be accessible from the client, and you should never pass the user ID as an argument.

1 Like

I see! Thanks for your patience and help. I’ll dig in some more. I’m going to scrap this and go with your separate collection suggestion.