Yato uses MongoDB with Mongoose for data persistence. Models are defined in src/models/ and provide schema validation and database operations.
Database connection
The MongoDB connection is established in the ready event at src/index.js:11:
const mongoose = require('mongoose');
client.on('ready', async () => {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('✔ Connected to MongoDB'))
.catch(err => console.log(`✖ Failed to connect to MongoDB\n${err}`));
});
The connection string is stored in the .env file as MONGO_URI.
Existing models
Yato currently has two database models:
User model
Stores user profiles with AniList integration:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
id: {
type: String,
required: true
},
aniProfile: {
type: mongoose.Schema.Types.Mixed,
required: true
}
});
module.exports = mongoose.model('user', userSchema, 'user');
Fields:
name - Discord username
id - Discord user ID
aniProfile - AniList profile data (flexible schema)
CSS model
Stores Counter-Strike server configurations:
const mongoose = require('mongoose');
const cssSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
cmd: {
type: String,
required: true
},
ip: {
type: String,
required: true
}
});
module.exports = mongoose.model('css', cssSchema, 'css');
Fields:
name - Server name or identifier
cmd - Command associated with this server
ip - Server IP address and port
Creating a new model
Create the model file
Create a new file in src/models/:touch src/models/guild.js
Define the schema
const mongoose = require('mongoose');
const guildSchema = new mongoose.Schema({
guildId: {
type: String,
required: true,
unique: true
},
prefix: {
type: String,
default: '!'
},
welcomeChannel: {
type: String,
default: null
},
logChannel: {
type: String,
default: null
},
muteRole: {
type: String,
default: null
},
modRoles: {
type: [String],
default: []
}
}, {
timestamps: true // Adds createdAt and updatedAt
});
module.exports = mongoose.model('guild', guildSchema, 'guilds');
Use the model in commands
src/commands/utilities/setprefix.js
const Guild = require('../../models/guild');
module.exports = {
name: 'setprefix',
description: 'Set server prefix',
userRequiredPermissions: 'MANAGE_GUILD',
expectedArgs: '<prefix:3:New prefix>',
run: async ({ guild, respond }, arrayArgs, args) => {
// Find or create guild document
let guildDoc = await Guild.findOne({ guildId: guild.id });
if (!guildDoc) {
guildDoc = new Guild({ guildId: guild.id });
}
// Update prefix
guildDoc.prefix = args.prefix;
await guildDoc.save();
respond(`Prefix updated to \`${args.prefix}\``);
}
};
Schema types
Mongoose supports various schema types:
{
stringField: String,
numberField: Number,
booleanField: Boolean,
dateField: Date,
bufferField: Buffer,
objectIdField: mongoose.Schema.Types.ObjectId
}
Field options
Configure fields with validation and defaults:
| Option | Description | Example |
|---|
type | Data type | String, Number, etc. |
required | Must have a value | required: true |
unique | Must be unique | unique: true |
default | Default value | default: 0 |
min | Minimum value (numbers) | min: 0 |
max | Maximum value (numbers) | max: 100 |
minlength | Minimum length (strings) | minlength: 3 |
maxlength | Maximum length (strings) | maxlength: 32 |
enum | Allowed values | enum: ['option1', 'option2'] |
validate | Custom validator | validate: (v) => v.length > 0 |
Common operations
Create a document
const User = require('./models/user');
const newUser = new User({
name: 'John Doe',
id: '123456789',
aniProfile: { username: 'johndoe' }
});
await newUser.save();
Find documents
const user = await User.findOne({ id: '123456789' });
if (!user) {
console.log('User not found');
}
Update documents
await User.updateOne(
{ id: '123456789' },
{ $set: { name: 'Jane Doe' } }
);
Delete documents
await User.deleteOne({ id: '123456789' });
Example: Complete model with methods
Add custom methods to your schemas:
const mongoose = require('mongoose');
const guildSchema = new mongoose.Schema({
guildId: {
type: String,
required: true,
unique: true
},
prefix: {
type: String,
default: '!'
},
warnings: {
type: Map,
of: [{
reason: String,
moderator: String,
timestamp: Date
}],
default: new Map()
}
});
// Instance method
guildSchema.methods.addWarning = function(userId, reason, moderatorId) {
const warnings = this.warnings.get(userId) || [];
warnings.push({
reason,
moderator: moderatorId,
timestamp: new Date()
});
this.warnings.set(userId, warnings);
return this.save();
};
// Static method
guildSchema.statics.findByGuildId = function(guildId) {
return this.findOne({ guildId });
};
module.exports = mongoose.model('guild', guildSchema, 'guilds');
Usage:
const Guild = require('./models/guild');
// Static method
const guild = await Guild.findByGuildId('123456789');
// Instance method
await guild.addWarning('userId', 'Spam', 'modId');
Mongoose version notes
Yato uses Mongoose v5.12.12, which is outdated. Some options like useNewUrlParser and useUnifiedTopology are deprecated in Mongoose 6+.
When upgrading Mongoose, update the connection:
// Old (v5)
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
// New (v6+)
await mongoose.connect(process.env.MONGO_URI);
Best practices
Use indexes for frequently queried fields
const userSchema = new mongoose.Schema({
id: {
type: String,
required: true,
unique: true,
index: true // Creates index
}
});
Validate data before saving
const guildSchema = new mongoose.Schema({
prefix: {
type: String,
required: true,
minlength: 1,
maxlength: 5,
validate: {
validator: (v) => !/\s/.test(v),
message: 'Prefix cannot contain spaces'
}
}
});
const schema = new mongoose.Schema({
// fields...
}, {
timestamps: true // Adds createdAt and updatedAt
});
try {
const user = await User.create(userData);
} catch (error) {
if (error.code === 11000) {
// Duplicate key error
console.log('User already exists');
} else {
console.error('Database error:', error);
}
}
Use MongoDB Compass or a similar tool to visualize and query your database during development.