# Advanced Tutorial: Using the user meta field

In this tutorial, we will create a plugin that allows an admin to check the number of trades a user has made. Once checked, the plugin will store the timestamp of the last trade completed by the user in `meta`. The next time the plugin checks the user's trade amount, it will only count trades made after the user's stored trade timestamp.

We will focus on the `script` section for this plugin.

{% hint style="info" %}
The user `meta` field is a JSON object in the User DB model that we can use to add additional fields for users.&#x20;
{% endhint %}

## Script

```javascript
'use strict';

const {
	app,
	loggerPlugin,
	toolsLib
} = this.pluginLibraries;
const { query, validationResult } = require('express-validator');
const moment = require('moment');

const USER_META_FIELD_NAME = 'trade_count_plugin-latest_trade';

const init = async () => {
	loggerPlugin.info(
		'PLUGIN TRADE COUNT initializing...'
	);

	if (!toolsLib.getKitConfig().user_meta[USER_META_FIELD_NAME]) {
		loggerPlugin.verbose(
			'PLUGIN TRADE COUNT',
			`User meta field ${USER_META_FIELD_NAME} not found. Creating field.`
		);

		await toolsLib.addKitUserMeta(
			USER_META_FIELD_NAME,
			'date-time',
			'Last trade timestamp used for trade-count plugin',
			false
		);
	}

	loggerPlugin.info(
		'PLUGIN TRADE COUNT initialized'
	);
};

init()
	.then(() => {
		app.get(
			'/plugins/trade-count/check',
			[
				toolsLib.security.verifyBearerTokenExpressMiddleware(['admin']),
				query('user_id').isInt({ min: 1 }).toInt().optional()
			],
			async (req, res) => {
				const errors = validationResult(req);
				if (!errors.isEmpty()) {
					return res.status(400).json({ errors: errors.array() });
				}

				const { user_id } = req.query;

				loggerPlugin.verbose(
					req.uuid,
					'GET /plugins/trade-count/check auth',
					req.auth.sub,
					'user_id:',
					user_id
				);

				try {
					const user = await toolsLib.user.getUserByKitId(user_id, false);

					if (!user) {
						throw new Error('User not found');
					}

					const lastTradeTimestamp = user.meta[USER_META_FIELD_NAME];
					const queryStartDate = toolsLib.isDatetime(lastTradeTimestamp)
						? moment(lastTradeTimestamp).add(1, 'ms').toISOString()
						: null;

					loggerPlugin.verbose(
						req.uuid,
						'GET /plugins/trade-count/check',
						'user last trade timestamp',
						lastTradeTimestamp,
						'trades query start date',
						queryStartDate
					);

					const trades = await toolsLib.order.getAllUserTradesByKitId(
						user.id,
						null,
						1,
						1,
						'timestamp',
						'desc',
						queryStartDate
					);

					loggerPlugin.verbose(
						req.uuid,
						'GET /plugins/trade-count/check',
						'trade count',
						trades.count
					);

					if (trades.count === 0) {
						return res.json({
							count: 0
						});
					}

					const lastTrade = trades.data[0];

					loggerPlugin.verbose(
						req.uuid,
						'GET /plugins/trade-count/check',
						'last trade timestamp',
						lastTrade.timestamp
					);

					await user.update({
						meta: {
							...user.meta,
							[USER_META_FIELD_NAME]: lastTrade.timestamp
						}
					});

					return res.json({
						count: trades.count
					});

				} catch (err) {
					loggerPlugin.error(
						req.uuid,
						'GET /plugins/trade-count/check err',
						err.message
					);

					return res.status(400).json({ message: err.message });
				}
			}
		);
	})
	.catch((err) => {
		loggerPlugin.error(
			'PLUGIN TRADE COUNT err during initialization',
			err.message
		);
	});

```

## Breakdown

### Import Requirements

```javascript
const {
	app,
	loggerPlugin,
	toolsLib
} = this.pluginLibraries;
const { query, validationResult } = require('express-validator');
const moment = require('moment');
```

First, import all the libraries that are required for this plugin.

### Set meta field name

```javascript
const USER_META_FIELD_NAME = 'trade_count_plugin-latest_trade';
```

All user `meta` fields require a name. Here, we are calling the `meta` field `trade_count_plugin-latest_trade`. We recommend formatting `meta` field names for plugins as `<PLUGIN_NAME>-<FIELD_NAME>`

### Check if meta field exists in init

```javascript
const init = async () => {
	loggerPlugin.info(
		'PLUGIN TRADE COUNT initializing...'
	);

	if (!toolsLib.getKitConfig().user_meta[USER_META_FIELD_NAME]) {
		loggerPlugin.verbose(
			'PLUGIN TRADE COUNT',
			`User meta field ${USER_META_FIELD_NAME} not found. Creating field.`
		);

		await toolsLib.addKitUserMeta(
			USER_META_FIELD_NAME,
			'date-time',
			'Last trade timestamp used for trade-count plugin',
			false
		);
	}

	loggerPlugin.info(
		'PLUGIN TRADE COUNT initialized'
	);
};

init()
	.then(() => {...})
	.catch((err) => {
		loggerPlugin.error(
			'PLUGIN TRADE COUNT err during initialization',
			err.message
		);
	});
```

Next, we create an `init` function that creates the new user `meta` field if it doesn't already exist. All user `meta` fields can be found in the Kit config object `user_meta`. Each field in `user_meta` has a `type`, `required` and `description` value.

```json
// KIT CONFIG

{
    ...,
    "user_meta": {
        "trade_count_plugin-latest_trade": {
            "type": "date-time",
            "required": false,
            "description": "Last trade timestamp used for trade-count plugin"
        }
    },
    ...
}
```

We are first checking if the user `meta` field exists in the Kit config returned from `toolsLib.getKitConfig`. If not, we are creating the new field using the tools library function `toolsLib.addKitUserMeta`. Please refer to the tools library documentation for more information regarding the tools library. Once initialized, the main script will be executed.

### Create an endpoint for counting user trades

```javascript
app.get(
	'/plugins/trade-count/check',
	[
		toolsLib.security.verifyBearerTokenExpressMiddleware(['admin']),
		query('user_id').isInt({ min: 1 }).toInt().optional()
	],
	async (req, res) => {
		const errors = validationResult(req);
		if (!errors.isEmpty()) {
			return res.status(400).json({ errors: errors.array() });
		}
		
		const { user_id } = req.query;

		loggerPlugin.verbose(
			req.uuid,
			'GET /plugins/trade-count/check auth',
			req.auth.sub,
			'user_id:',
			user_id
		);
```

For this plugin, we need an endpoint `GET /plugins/trade-count/check` that returns the number of trades a user has made.&#x20;

```javascript
try {
	const user = await toolsLib.user.getUserByKitId(user_id, false);

	if (!user) {
		throw new Error('User not found');
	}

	const lastTradeTimestamp = user.meta[USER_META_FIELD_NAME];
	const queryStartDate = toolsLib.isDatetime(lastTradeTimestamp)
		? moment(lastTradeTimestamp).add(1, 'ms').toISOString()
		: null;

	loggerPlugin.verbose(
		req.uuid,
		'GET /plugins/trade-count/check',
		'user last trade timestamp',
		lastTradeTimestamp,
		'trades query start date',
		queryStartDate
	);

	const trades = await toolsLib.order.getAllUserTradesByKitId(
		user.id,
		null,
		1,
		1,
		'timestamp',
		'desc',
		queryStartDate
	);

	loggerPlugin.verbose(
		req.uuid,
		'GET /plugins/trade-count/check',
		'trade count',
		trades.count
	);

	if (trades.count === 0) {
		return res.json({
			count: 0
		});
	}

	const lastTrade = trades.data[0];

	loggerPlugin.verbose(
		req.uuid,
		'GET /plugins/trade-count/check',
		'last trade timestamp',
		lastTrade.timestamp
	);

	await user.update({
		meta: {
			...user.meta,
			[USER_META_FIELD_NAME]: lastTrade.timestamp
		}
	});

	return res.json({
		count: trades.count
	});

} catch (err) {
	loggerPlugin.error(
		req.uuid,
		'GET /plugins/trade-count/check err',
		err.message
	);

	return res.status(400).json({ message: err.message });
}
```

We are first checking to see if a user with the given `user_id` exists. If so, we check to see if the user's `meta.trade_count_plugin-latest_trade` value exists. If it doesn't that means we haven't checked the user's trade count using this plugin before. If it does, that means we have previously checked the user's trade count.

Using the found `trade_count_plugin-latest_trade` value, we can determine which date to query the user's trades from. If `trade_count_plugin-latest_trade` was `null`, then we will query all of the user's trades.

Once we get the trades response, we can read the number of trades made and the timestamp of the last trade completed. If the `count` is `0`, then we can just return `0` without updating the user's   `trade_count_plugin-latest_trade` value. Otherwise, we will get the latest trade's `timestamp`, update the user's `trade_count_plugin-latest_trade` value to that `timestamp`, and return the `count`. The next time we check the user's trade count, all trades starting from the updated `trade_count_plugin-latest_trade` will be counted.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hollaex.com/plugins/develop-plugins/advanced/advanced-tutorial-using-the-user-meta-field.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
