ExoSnip Code Snippets

Express.js Snippets

← Back to Home

About Express.js

Express.js is a web application framework for Node.js, designed for building web applications and APIs. This guide walks you through creating a simple To-Do API using Express.js and MongoDB, with best practices for project structure and .env configuration.

Project Directory Structure

Step 1: A well-organized project structure helps maintain clean and scalable code. Here’s the recommended file structure for your To-Do API:

  • todo-api/: Root directory of the project.
  • src/: Contains source code.
    • controllers/: Contains request handlers.
      • todoController.js: Handles to-do related requests.
    • models/: Contains database models.
      • todoModel.js: Defines the to-do schema.
    • routes/: Contains route definitions.
      • todoRoutes.js: Defines routes for to-do operations.
    • app.js: Main application file.
    • config.js: Configuration file for environment variables.
  • .env: Environment variables.
  • package.json: Contains project metadata and dependencies.
  • README.md: Project documentation.
This structure helps you keep your code organized and easy to manage.

todo-api/
│── src/
│   ├── controllers/
│   │   ├── todoController.js
│   ├── models/
│   │   ├── todoModel.js
│   ├── routes/
│   │   ├── todoRoutes.js
│   ├── app.js
│   ├── config.js
│── .env
│── package.json
│── README.md

Install Node.js and Create a New Project

Step 2: Before starting, you need to install Node.js. Node.js allows you to run JavaScript on the server. After installing Node.js, you can create a new project directory and initialize it with npm (Node Package Manager). This sets up a new Node.js project and creates a `package.json` file to manage your project's dependencies.

mkdir todo-api
cd todo-api
npm init -y

Install Required Dependencies

Step 3: Install the necessary dependencies for your project. Dependencies are libraries or packages that your project needs to work. Here, we are installing Express.js (a web framework for Node.js), Mongoose (a library to interact with MongoDB), and dotenv (a module to load environment variables from a `.env` file).

npm install express mongoose dotenv

Create Environment Configuration (.env)

Step 4: Create a `.env` file in the root directory to store your environment variables. Environment variables are used to store configuration settings. This file will contain your MongoDB connection string and the port number your server will run on. Keeping these settings in a separate file makes it easier to manage and change them without modifying your code.

MONGO_URI=mongodb://localhost:27017/todoapp
PORT=3000

Setup Configuration File (config.js)

Step 5: Create a `config.js` file to load environment variables from the `.env` file. This file will read the variables from the `.env` file and make them available in your application. This way, you can easily access your configuration settings from anywhere in your code.

require('dotenv').config();

module.exports = {
  mongoURI: process.env.MONGO_URI,
  port: process.env.PORT || 3000
};

Define the To-Do Model (todoModel.js)

Step 6: Create a Mongoose model for the to-do items. A model is a blueprint for how your data should look. This model defines the schema for the to-do items in the database, specifying the fields and their types. Here, each to-do item has a `text` field (a string) and a `completed` field (a boolean).

const mongoose = require('mongoose');

const todoSchema = new mongoose.Schema({
  text: { type: String, required: true },
  completed: { type: Boolean, default: false }
});

module.exports = mongoose.model('Todo', todoSchema);

Create To-Do Controller (todoController.js)

Step 7: Create a controller to handle the logic for creating, reading, updating, and deleting to-do items. Controllers are responsible for handling requests and sending responses. Here, we define methods to get all to-do items, create a new to-do item, update an existing to-do item, and delete a to-do item. - `getTodos`: Fetches all to-do items from the database and returns them as a JSON response. - `createTodo`: Creates a new to-do item with the text provided in the request body and saves it to the database. - `updateTodo`: Finds a to-do item by its ID, updates its `completed` status with the value provided in the request body, and saves the changes to the database. - `deleteTodo`: Finds a to-do item by its ID and deletes it from the database.

const Todo = require('../models/todoModel');

exports.getTodos = async (req, res) => {
  try {
    const todos = await Todo.find();
    res.json(todos);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
};

exports.createTodo = async (req, res) => {
  const todo = new Todo({ text: req.body.text });
  try {
    const newTodo = await todo.save();
    res.status(201).json(newTodo);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
};

exports.updateTodo = async (req, res) => {
  try {
    const todo = await Todo.findById(req.params.id);
    if (!todo) return res.status(404).json({ message: 'Todo not found' });
    todo.completed = req.body.completed;
    const updatedTodo = await todo.save();
    res.json(updatedTodo);
  } catch (err) {
    res.status(400).json({ message: err.message });
  }
};

exports.deleteTodo = async (req, res) => {
  try {
    const todo = await Todo.findById(req.params.id);
    if (!todo) return res.status(404).json({ message: 'Todo not found' });
    await todo.remove();
    res.json({ message: 'Todo deleted' });
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
};

Define To-Do Routes (todoRoutes.js)

Step 8: Create routes to handle HTTP requests for the to-do items. Routes define the endpoints for your API and specify which controller methods to call for each endpoint. Here, we define routes to get all to-do items, create a new to-do item, update an existing to-do item, and delete a to-do item.

const express = require('express');
const router = express.Router();
const todoController = require('../controllers/todoController');

router.get('/todos', todoController.getTodos);
router.post('/todos', todoController.createTodo);
router.put('/todos/:id', todoController.updateTodo);
router.delete('/todos/:id', todoController.deleteTodo);

module.exports = router;

Setup Express Application (app.js)

Step 9: Create the main application file to set up the Express server, connect to MongoDB, and use the routes you defined. This file initializes the Express application, connects to the MongoDB database using Mongoose, and sets up the routes for handling requests. Finally, it starts the server and listens for incoming requests on the specified port.

const express = require('express');
const mongoose = require('mongoose');
const config = require('./config');
const todoRoutes = require('./routes/todoRoutes');

const app = express();

mongoose.connect(config.mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('MongoDB connected'))
  .catch(err => console.log(err));

app.use(express.json());
app.use('/api', todoRoutes);

app.listen(config.port, () => {
  console.log(`Server running on port ${config.port}`);
});

Run the To-Do API

Step 10: Now that your To-Do API is complete, you can start the server to see your API in action. This will allow you to interact with your To-Do API using tools like Postman or cURL. Running this command will start your server, and you can then send HTTP requests to your API endpoints to create, read, update, and delete to-do items.

node src/app.js

Best Practices for Project Structure

Step 11: Follow these best practices to keep your project clean and maintainable: 1. Use a controllers folder: Keep request handlers in `src/controllers/` for better organization. 2. Separate routes from logic: Define routes in `src/routes/` and keep logic in controllers. 3. Use meaningful file names: Avoid generic names like `controller.js`, use `todoController.js`, etc. 4. Keep functions reusable: Functions like `getTodos` should be kept modular for reusability. 5. Keep configuration separate: Use a `config.js` file and `.env` for environment variables. Following these practices will help you write clean, maintainable, and scalable code.

1. Use a controllers folder: Keep request handlers in `src/controllers/` for better organization.
2. Separate routes from logic: Define routes in `src/routes/` and keep logic in controllers.
3. Use meaningful file names: Avoid generic names like `controller.js`, use `todoController.js`, etc.
4. Keep functions reusable: Functions like `getTodos` should be kept modular for reusability.
5. Keep configuration separate: Use a `config.js` file and `.env` for environment variables.