# Building a Trello - Backend from Scratch

### Core Features

1.  **Authentication System (Sign Up / Sign In)**  
    Every user starts here:
    
    *   Users can **sign up** with credentials
        
    *   Users can **log in securely**
        
    *   Authentication is handled via tokens (JWT)
        
    
    This ensures every action is tied to an authenticated user.
    
2.  **First-Time Onboarding (Organization Creation)**
    
    After logging in for the first time:
    
    *   User can **create an organization**
        
    *   Organization includes:
        
        *   Name of organisation
            
        *   Description
            
    
    This makes the user the **admin of that organization**
    
3.  **Multi-User Organization System**
    
    Admins can:
    
    *   add users to the organization based on username
        
    *   Manage members
        
    *   Users can:  
        Be part of **multiple organizations**
        
4.  **Dashboard View**  
    Once logged in:
    
    *   The user first sees a **list of all organizations** they are part of.
        
    *   Inside the selected organization, the user can see **all boards related to that specific organization**
        
    
    If a user is part of 2 organizations → they will see select organisation
    
5.  **Board-Level Task Management**  
    When user click any board inside a board, users can:
    
    *   Create tasks
        
    *   Move tasks across stages:
        
        *   **Todo**
            
        *   **In Progress**
            
        *   **Done**
            
6.  **Task Lifecycle**
    
    Each task follows a simple lifecycle:
    
    1.  Created in **Todo**
        
    2.  Moved to **In Progress**
        
    3.  Completed in **Done**
        
    4.  Option to **delete/remove** task
        

* * *

### **Step 1: Setup Express Server**

First, I created a basic Express server:

```javascript
const express = require("express");
const app = express();

app.use(express.json());

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});
```

* * *

### **Step 2: Setup MongoDB**

1.  Go to [**https://cloud.mongodb.com/**](https://cloud.mongodb.com/)
    
2.  Create account & login
    
3.  Create a **Cluster**
    
4.  Go to **Database Access**
    
    *   Create username & password
        
5.  Go to **Network Access**
    
    *   Add IP: `0.0.0.0/0` (for development)
        
6.  Click **Connect**
    
7.  Copy connection string
    

Example:

```plaintext
mongodb+srv://username:password@cluster.mongodb.net/dbname
```

* * *

### **MongoDB Connection Code**

Before connecting MongoDB, first we need to install **mongoose**.

**Step 1: Install Mongoose**

Run this command in your project:

```javascript
npm install mongoose
```

* * *

**Step 2: Connect to MongoDB**

Now write the connection code:

```javascript
const mongoose = require("mongoose");

async function connectDB() {
    try {
        await mongoose.connect("mongodb+srv://<username>:<password>@todo.dtrpx.mongodb.net/todo");
        console.log("MongoDB connected");
    } catch (err) {
        console.error("Connection error:", err);
    }
}

connectDB();
```

Initially, I tried connecting to **MongoDB (cloud)** but faced a DNS issue

**Problem:**

MongoDB connection was failing due to DNS resolution.

**Fix:**

After watching a YouTube video: , I added this:

```javascript
const dns = require("dns");
dns.setServers(["1.1.1.1", "8.8.8.8"]);
```

This fixed the issue and I was able to connect to cloud MongoDB.

* * *

### **Step 3: Create Schema using Mongoose**

Before writing any backend or frontend logic, I first designed how my data should look.

This step is very important.

**Learning:**  
Always define your **schema first** before building APIs or UI.

Because:

*   It gives clarity on what data you need
    
*   Helps structure your database properly
    
*   Makes backend logic easier to write
    

```javascript
const userSchema = new mongoose.Schema({
    username: 'String',
    password: "String"
})

const orgSchema = new mongoose.Schema({
    orgName: "String",
    description: "String",
    admin: mongoose.Types.ObjectId, 
    member: [mongoose.Types.ObjectId]
})

const boardSchema = new mongoose.Schema({
    boardName: "String",
    organisationId: mongoose.Types.ObjectId
})
const taskSchema = new mongoose.Schema({
    description: String,
    status: String, // Todo | In Progress | Done
    userId: mongoose.Types.ObjectId,
    boardId: mongoose.Types.ObjectId
})

const userModel = mongoose.model("users", userSchema);
const orgModel = mongoose.model("organisations", orgSchema);
const boardModel = mongoose.model("boards", boardSchema);
const taskModel = mongoose.model("tasks", taskSchema);

module.exports = {
    userModel,
    orgModel,
    boardModel
}
```

* * *

### **Step 4: Signup Route**

Before writing code, let’s understand the goal.

What we want to achieve:

*   Allow a new user to **register**
    
*   Store user details in database
    
*   Prevent duplicate users
    

```javascript
app.post('/signup', async (req,res)=>{
    const username = req.body.username;
    const password = req.body.password;

    const userExists = await userModel.findOne({
        username: username
    })

    if(userExists){
        return res.status(403).json({
            message: "User already exists"
        })
    }

    const newUser = await userModel.create({
        username: username,
        password: password
    })

    res.status(200).json({
        id: newUser._id,
        message: "user get created"
    })
})
```

* * *

### **Step 5: Signin + JWT Token**

Before writing code, let’s understand the goal.

What we want to achieve:

*   Verify user credentials
    
*   If valid → allow login
    
*   Generate a **JWT token** for authentication
    

```javascript
app.post("/signin", async (req, res) => {
  const { userName, password } = req.body;

  const userExist = await userModel.findOne({ userName });

  if (!userExist) {
    return res.status(404).json({
      message: "User not found",
    });
  }

  const token = jwt.sign(
    { userId: userExist.id },
    "shubham123"
  );

  res.json({ token });
});
```

* * *

### **Step 6: Auth Middleware**

This ensures only logged-in users can access routes.

```javascript
function authMiddleware(req, res, next) {
    const token = req.headers.token;

    if (!token) {
        return res.status(401).json({
            message: "Token not provided"
        });
    }

    try {
        const decode = jwt.verify(token, "shubham123");
        req.userId = decode.userId;
        next();
    } catch (err) {
        return res.status(401).json({
            message: "Invalid or expired token"
        });
    }
}
```

* * *

### **Step 7: Create Organisation**

**Requirement**

Now that authentication is done, we need to allow users to:

*   Create an organization after login
    
*   Automatically assign the creator as **admin**
    
*   Ensure organization names are unique
    

Only **authenticated users** should be able to create organizations.

**Implementation**

```js
app.post("/create-organisation", authMiddleware, async (req,res)=>{
    const orgName = req.body.orgName;
    const description = req.body.description;

    const orgExist = await orgModel.findOne({
        orgName: orgName
    })

    if(orgExist){
        return res.status(403).json({
            message: "Organisation already exists"
        })
    }

    const newOrg = await orgModel.create({
        orgName: orgName,
        description: description,
        admin: req.userId,
        member: []
    })
    
    res.status(200).json({
        orgId: newOrg.id
    })
})
```

* * *

### Step 8: Add Member to Organization

**Requirement**

Now that organizations are created, the next step is:

*   Allow **admin to add users** to their organization
    
*   Users are added using their **username**
    
*   Only **existing users** can be added
    
*   Prevent adding the same user twice
    

**Implementation**

```javascript
app.post('/add-member-to-organisation', authMiddleware, async (req,res)=>{
    const newMember = req.body.member;

    const newMemberUser = await userModel.findOne({
        username: newMember
    })

    if(!newMemberUser){
        return res.status(404).json({
            message: "User does not exist"
        })
    }

    const orgDetails = await orgModel.findOne({
        admin: req.userId
    })

    if(!orgDetails){
        return res.status(404).json({
            message: "Organization not found"
        })
    }

    const memberExists = orgDetails.member.includes(newMemberUser._id);

    if(memberExists){
        return res.status(400).json({
            message: "Member already exists in organization"
        })
    }

    orgDetails.member.push(newMemberUser._id);

    await orgDetails.save()

    res.status(200).json({
        message: "Member added successfully"
    })
})
```

* * *

### Step 9: Delete Member from Organization

**Requirement**

Admins should also be able to:

*   Remove users from the organization
    

**Implementation**

```javascript
app.delete('/delete-member-from-organisation', authMiddleware, async (req, res)=>{
    const deleteUser = req.body.username;

    const deleteUserData = await userModel.findOne({
        username: deleteUser
    })

    if(!deleteUserData){
        return res.status(404).json({
            message: "User does not exist"
        })
    }

    const orgDetails = await orgModel.findOne({
        admin: req.userId
    })

    if(!orgDetails){
        return res.status(404).json({
            message: "Organization not found"
        })
    }

    orgDetails.member = orgDetails.member.filter(
        e => e.toString() !== deleteUserData._id.toString()
    );

    await orgDetails.save()

    res.status(200).json({
        message: "Member removed successfully"
    })
})
```

* * *

### Step 10: Create Board

**Requirement**

Now comes the core feature of Trello:

*   Each organization should have **boards**
    
*   Only authorized users (admin) can create boards
    
*   Each board belongs to a specific organization
    

**Implementation**

```js
app.post("/create-board", authMiddleware, async (req,res)=>{
    const boardName = req.body.boardName;

    const orgDetails = await orgModel.findOne({
        admin: req.userId
    });

    if(!orgDetails){
        return res.status(404).json({
            message: "Organization not found"
        })
    }

    const newBoard = await boardModel.create({
        boardName,
        organisationId: orgDetails._id
    })

    res.status(200).json({
        boardId: newBoard._id,
        message: "Board created successfully"
    })
})
```

* * *

### Step 11: Task Management (Create, Update, Delete)

Now comes the most important part of any Trello-like application — **Tasks**.

This is where users actually track their work.

* * *

**Requirement:**

We want to allow users to:

*   Create tasks inside a board
    
*   Update task status (Todo → In Progress → Done)
    
*   Delete tasks when completed or no longer needed
    
*   Ensure tasks are tied to the **logged-in user**
    

This forms the core **task lifecycle system**

* * *

**Create Task**

**Logic :**

When a user creates a task:

1.  Take `description`, `status`, and `boardId`
    
2.  Validate:
    
    *   Board exists
        
    *   Status is valid
        
3.  Check duplicate **inside same board**
    
4.  Create task linked to board
    

* * *

**Implementation**

```javascript
app.post("/create-task", authMiddleware, async (req, res) => {
    const { description, status, boardId } = req.body;
 
    if (!description || !status || !boardId) {
        return res.status(400).json({
            message: "All fields are required"
        });
    }

    const allowedStatus = ["Todo", "In Progress", "Done"];
    if (!allowedStatus.includes(status)) {
        return res.status(400).json({
            message: "Invalid status"
        });
    }
 
    const board = await boardModel.findById(boardId);
    if (!board) {
        return res.status(404).json({
            message: "Board not found"
        });
    }

    const taskExists = await taskModel.findOne({
        userId: req.userId,
        description,
        boardId
    });

    if (taskExists) {
        return res.status(403).json({
            message: "Task already exists in this board"
        });
    }

    const newTask = await taskModel.create({
        userId: req.userId,
        description,
        status,
        boardId
    });

    res.status(200).json({
        message: "Task created successfully",
        taskId: newTask._id
    });
});
```

* * *

**Update Task**

**Logic :**

When updating a task:

1.  Identify task using:
    

*   taskId
    

1.  If task exists:
    
    *   Update status
        
    *   Save changes
        

This enables moving tasks across stages

* * *

**Implementation**

```javascript
app.put("/update-task", authMiddleware, async (req, res) => {
    const { taskId, status } = req.body;

    const allowedStatus = ["Todo", "In Progress", "Done"];
    if (!allowedStatus.includes(status)) {
        return res.status(400).json({
            message: "Invalid status"
        });
    }

    const task = await taskModel.findOne({
        _id: taskId,
        userId: req.userId
    });

    if (!task) {
        return res.status(404).json({
            message: "Task not found"
        });
    }

    task.status = status;
    await task.save();

    res.status(200).json({
        message: "Task updated successfully"
    });
});
```

* * *

**Delete Task**

**Logic :**

When deleting a task:

1.  Find task using:
    
    *   taskId
        
2.  If exists → delete it from database
    

* * *

**Implementation**

```javascript
app.delete("/delete-task", authMiddleware, async (req, res) => {
    const { taskId } = req.body;

    const task = await taskModel.findOne({
        _id: taskId,
        userId: req.userId
    });

    if (!task) {
        return res.status(404).json({
            message: "Task not found"
        });
    }

    await taskModel.deleteOne({ _id: taskId });

    res.status(200).json({
        message: "Task deleted successfully"
    });
});
```

* * *

### Final Thoughts

If you're learning backend development, I highly recommend building something like this — it teaches you far more than just theory.

* * *

### **Complete Source Code**

I’ve uploaded the complete project on GitHub. You can check it here:

**GitHub Repo:**  
[https://github.com/shubhamsinghbundela/Trello](https://github.com/shubhamsinghbundela/Trello)
