A comprehensive learning project demonstrating Meteor.js and MongoDB best practices through a real-world Task Management & Team Collaboration System.
This project is designed to teach you:
- CRUD Operations - Complete create, read, update, delete examples
- MongoDB Best Practices - Schema design, indexing, aggregations
- Security - Password encryption, input validation, authorization
- DDP Optimization - Efficient publications and subscriptions
- TypeScript - Full type safety across client and server
- Real-time Features - Reactive data with Meteor's DDP protocol
- Performance - Indexing strategies, query optimization, pagination
- Architecture - Proper code organization and separation of concerns
meteorjs-learning/
├── CASE_STUDY.md # Detailed case study and learning objectives
├── DDP_OPTIMIZATION_GUIDE.md # DDP performance best practices
├── REFACTORING_GUIDE.md # Feature-based architecture explanation
├── README.md # This file
│
├── docker-compose.yml # MongoDB + Mongo Express setup
├── docker/
│ └── mongo-init.js # MongoDB initialization script
│
├── imports/
│ └── api/
│ ├── users/ # User domain
│ │ ├── types.ts # User types
│ │ ├── collection.ts # Users collection + indexes + security
│ │ └── index.ts # Barrel export
│ │
│ ├── projects/ # Project domain
│ │ ├── types.ts # Project types
│ │ ├── collection.ts # Projects collection + indexes + security
│ │ ├── methods.ts # Project CRUD methods
│ │ └── index.ts # Barrel export
│ │
│ ├── tasks/ # Task domain
│ │ ├── types.ts # Task types
│ │ ├── collection.ts # Tasks collection + indexes + security
│ │ ├── methods.ts # Task CRUD methods
│ │ └── index.ts # Barrel export
│ │
│ ├── activityLogs/ # Activity log domain
│ │ ├── types.ts # Activity log types
│ │ ├── collection.ts # ActivityLogs collection + indexes + security
│ │ └── index.ts # Barrel export
│ │
│ ├── aggregations/ # MongoDB Aggregations
│ │ └── aggregations.ts
│ │
│ └── publications/ # DDP Publications
│ └── publications.ts
│
├── server/
│ ├── main.ts # Server entry point
│ └── fixtures.ts # Seed data
│
├── client/
│ └── main.tsx # Client entry point
│
├── package.json # Dependencies
└── tsconfig.json # TypeScript configuration
Note: This project follows a feature-based (domain-driven) architecture for better maintainability and scalability. See REFACTORING_GUIDE.md for details.
# Clone the repository
git clone <your-repo-url>
cd Meteor.js-Mongo-Learning
# Install dependencies
meteor npm install# Start MongoDB and Mongo Express
docker-compose up -d
# Verify MongoDB is running
docker-compose psYou should see:
- MongoDB: Running on
localhost:27017 - Mongo Express: Web UI at
http://localhost:8081- Username:
admin - Password:
admin123
- Username:
# Copy example env file
cp .env.example .env
# The default settings should work for local development# Start Meteor
meteor npm start
# Or with custom MongoDB URL
MONGO_URL=mongodb://admin:admin123@localhost:27017/meteor-learning?authSource=admin meteor npm startThe app will:
- Connect to MongoDB
- Create indexes
- Seed the database with sample data
- Start on
http://localhost:3000
The seed data creates these users:
| Username | Password | Role |
|---|---|---|
admin |
admin123 |
Admin (full access) |
manager1 |
manager123 |
Manager (can create projects) |
manager2 |
manager123 |
Manager |
member1 |
member123 |
Member (can work on tasks) |
member2 |
member123 |
Member |
member3 |
member123 |
Member |
Read CASE_STUDY.md to understand:
- System architecture
- Collection relationships
- Learning objectives
- Implementation plan
File: imports/api/collections/types.ts
- Understand TypeScript interfaces
- See document design patterns
- Learn about embedded vs referenced documents
File: imports/api/collections/collections.ts
- Collection creation with type safety
- Index strategies and why they matter
- Security (deny rules)
File: imports/api/methods/projects.methods.ts
File: imports/api/methods/tasks.methods.ts
Learn about:
- Input validation with
check() - Authorization patterns
- Business logic enforcement
- Activity logging
- Error handling
File: imports/api/publications/publications.ts
Learn when to use DDP:
- Filtering data server-side
- Field projections for security
- Pagination strategies
- Composite publications
File: imports/api/aggregations/aggregations.ts
Learn MongoDB aggregation pipeline:
$match,$group,$project$lookupfor joins- Complex analytics
- When NOT to use aggregations
Read DDP_OPTIMIZATION_GUIDE.md
- When to use DDP vs Methods
- Anti-patterns to avoid
- Subscription management
- Performance monitoring
# In a new terminal (while Meteor is running)
meteor shell
# Test a method
Meteor.call('projects.insert', {
name: 'Test Project',
description: 'Created from shell',
teamMemberIds: [],
status: 'active',
tags: ['test']
}, (err, result) => {
console.log('Project ID:', result);
});
# Test an aggregation
Meteor.call('aggregations.getUserStatistics', (err, result) => {
console.log('User stats:', result);
});
# Query collections
ProjectsCollection.find().fetch();
TasksCollection.find({ status: 'todo' }).count();// Subscribe to data
Meteor.subscribe("projects.owned");
// Query local MiniMongo
ProjectsCollection.find().fetch();
// Call a method
Meteor.call(
"tasks.insert",
{
projectId: "PROJECT_ID",
title: "New Task",
description: "Test task",
priority: "high",
tags: [],
},
(err, taskId) => {
console.log("Task created:", taskId);
}
);
// Call aggregation
Meteor.call("aggregations.getProjectStatistics", "PROJECT_ID", (err, stats) => {
console.log("Project stats:", stats);
});- Open http://localhost:8081
- Login:
admin/admin123 - Select database:
meteor-learning - Browse collections:
projects,tasks,activityLogs,users
# Connect to MongoDB
docker exec -it meteor-mongodb mongosh -u admin -p admin123 --authenticationDatabase admin
# Switch to database
use meteor-learning
# View collections
show collections
# Query examples
db.tasks.find({ status: 'todo' }).pretty()
db.projects.find({ status: 'active' })
db.users.find({}, { username: 1, 'profile.role': 1 })
# Test aggregation
db.tasks.aggregate([
{ $group: { _id: '$status', count: { $sum: 1 } } }
])
# View indexes
db.tasks.getIndexes()Password Encryption:
// NEVER store plain passwords
// Meteor's Accounts package uses bcrypt automatically
Accounts.createUser({
username: 'user',
password: 'password123', // Automatically hashed
profile: { ... }
});Input Validation:
Meteor.methods({
"tasks.insert"(taskData) {
// ALWAYS validate inputs
check(taskData, {
title: String,
description: String,
priority: Match.OneOf("low", "medium", "high"),
});
},
});Authorization:
// ALWAYS check permissions
if (!canModifyTask(this.userId, task)) {
throw new Meteor.Error("not-authorized");
}Indexing:
// Compound index for common query pattern
TasksCollection.createIndexAsync({
projectId: 1,
status: 1,
dueDate: 1,
});Field Projections:
// Only fetch needed fields
TasksCollection.find(
{ projectId },
{ fields: { title: 1, status: 1, dueDate: 1 } }
);Pagination:
const limit = 20;
const skip = (page - 1) * limit;
TasksCollection.find({}, { limit, skip });Denormalization:
// Project stores task counts for fast dashboard queries
metadata: {
totalTasks: 15,
completedTasks: 8
}References vs Embedding:
// Use references for:
// - Large data
// - Data that changes
// - Many-to-many relationships
teamMemberIds: ['userId1', 'userId2']
// Use embedding for:
// - Small data
// - Data that doesn't change
// - Data always queried together
metadata: { priority: 'high', ... }- Define types in
imports/api/collections/types.ts - Create collection in
imports/api/collections/collections.ts - Add indexes in the same file (server-side block)
- Export from
imports/api/collections/index.ts - Create methods in
imports/api/methods/yourCollection.methods.ts - Create publications in
imports/api/publications/publications.ts
// In imports/api/methods/yourCollection.methods.ts
Meteor.methods({
"yourCollection.yourAction"(params) {
check(params, Object);
if (!this.userId) {
throw new Meteor.Error("not-authorized");
}
// Validate, check permissions, perform action
// ...
return result;
},
});// In imports/api/publications/publications.ts
if (Meteor.isServer) {
Meteor.publish("yourPublication", function (params) {
check(params, String);
if (!this.userId) return this.ready();
return YourCollection.find(
{
/* filter */
},
{
fields: {
/* projection */
},
limit: 50,
}
);
});
}# Check if MongoDB is running
docker-compose ps
# View MongoDB logs
docker-compose logs mongodb
# Restart MongoDB
docker-compose restart mongodb
# Reset everything (deletes data!)
docker-compose down -v
docker-compose up -d# Clear Meteor cache
meteor reset
# Reinstall packages
rm -rf node_modules
meteor npm install
# Update Meteor
meteor update"MongoError: Authentication failed"
- Check MONGO_URL in .env
- Verify MongoDB credentials in docker-compose.yml
"Error: Match error: Expected string, got undefined"
- You're missing required parameters in a method call
- Check the method signature
"Error: not-authorized"
- You're not logged in, or don't have permission
- Check user role and ownership
- Meteor Guide - Best practices
- Meteor Docs - API reference
- Meteor Forums - Community help
- Create a new user through the Meteor shell
- Create a project using the
projects.insertmethod - Add a task to your project
- Subscribe to your tasks and display them
- Create a new publication that filters tasks by priority
- Add a method to update task priority
- Create an aggregation to count tasks by status
- Add a new field to the Task schema
- Implement a "copy project" feature (including all tasks)
- Add a "task comments" collection with references
- Create a publication that includes tasks with their assigned users
- Optimize a slow query using indexes
This is a learning project, but improvements are welcome!
- Fork the repository
- Create a feature branch
- Add tests if applicable
- Submit a pull request
MIT License - Feel free to use this project for learning!
Built with:
Happy Learning!
Questions? Check the CASE_STUDY.md or DDP_OPTIMIZATION_GUIDE.md for more details.