Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mongo API #511

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions FolderStructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# project-mongo-api folder structure
├── data/
│ ├── trips.json
│ ├── users.json
├── middleware/
│ ├── validation.js # Validation middleware using express-validator
│ ├── auth.js # Placeholder for authentication logic (optional)
│ ├── errorHandler.js # Global error handling middleware
├── models/
│ ├── userModel.js # Mongoose schema for users
│ ├── tripModel.js # Mongoose schema for trips
├── routes/
│ ├── userRoutes.js # Routes for user-related API endpoints
│ ├── tripRoutes.js # Routes for trip-related API endpoints
├── controllers/
│ ├── userController.js # Handlers for user-related requests
│ ├── tripController.js # Handlers for trip-related requests
├── utils/
│ ├── seedDatabase.js # Script to seed JSON data into MongoDB
├── config/
│ ├── database.js # Database connection setup
│ ├── dateValidators.js # put all date-related utilities (validation, calculation)
├── .env # Environment variables (e.g., MONGO_URL, PORT)
├── .gitignore # Exclude sensitive and unnecessary files
├── server.js # Entry point for the API
├── app.js
├── package.json


# Implementation Plan
# # Step 1: Centralize MongoDB Connection Logic
Create config/database.js with connectDatabase function to manage MongoDB connections.
# # Step 2: Setup Database Connection
Import this file into server.js to ensure the app connects to the database at startup.
# # Step 3: Create Mongoose Models
Define User and Trip schemas in the models/ folder.
Ensure schemas align with the structure of users.json and trips.json.
# # Step 4: Seed the Database
Create utils/seedDatabase.js to populate MongoDB with users.json and trips.json
# # Step 5: Add Routes
Split routes into routes/userRoutes.js and routes/tripRoutes.js.
Implement basic GET, POST, and PUT endpoints for users and trips.
# # Step 6: Add Controllers
Create corresponding controllers in the controllers/ folder.
Each controller should handle logic for the routes.
# # Step 7: Add Middleware
Validation Middleware: Validate input data for creating or updating users and trips.
Error Handler Middleware: Add centralized error handling.
# # Step 8: Test API with Postman
Create a Postman collection for testing each endpoint.
Verify the API works as expected for GET, POST, and PUT requests.
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
# Project Mongo API
## Introduction
Welcome to the Project Mongo API, a RESTful API built with Node.js and MongoDB. This project serves as a backend solution for managing users and trips, including features such as CRUD operations, data validation, and database seeding.

Replace this readme with your own information about your project.
## Highlights
- Fully RESTful API for Users and Trips.
- MongoDB integration with Mongoose ORM.
- Robust validation middleware using express-validator.
- Centralized error handling for consistent responses.
- Database seeding for easy setup with sample data.
- Supports both local development and deployment to MongoDB Atlas.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
## Key Technologies
- Node.js: Backend runtime.
- Express.js: Web framework for building the API.
- MongoDB: NoSQL database for storing users and trips.
- Mongoose: Elegant MongoDB object modeling for Node.js.
- dotenv: Environment variable management.
- express-validator: Middleware for request validation.
- Babel: Transpilation for modern JavaScript.
- Nodemon: Development tool for auto-reloading the server.

## The problem
## Future Enhancements
- Add authentication and authorization.
- Implement advanced filtering and sorting for trips.
- Add unit and integration tests.

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
## Available Endpoints
Method Endpoint Description
GET /api/users Get all users
POST /api/users Create a new user
PUT /api/users/:id Update an existing user
GET /api/trips Get all trips
POST /api/trips Create a new trip
PUT /api/trips/:id Update an existing trip

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
https://project-mongo-api-df42.onrender.com/
30 changes: 30 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
import listEndpoints from "express-list-endpoints";

import { userRoutes } from "./routes/userRoutes.js";
import { tripRoutes } from "./routes/tripRoutes.js";
import { errorHandler } from "./middleware/errorHandler.js";

export const app = express();

// Middleware
app.use(cors());
app.use(bodyParser.json());

// Routes
app.use("/api/users", userRoutes);
app.use("/api/trips", tripRoutes);

// API documentation
app.get("/", (req, res) => {
const endpoints = listEndpoints(app);
res.send({
message: "This API returns info of users and trips",
endpoints: endpoints,
});
});

// Error Handling Middleware
app.use(errorHandler);
14 changes: 14 additions & 0 deletions config/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import mongoose from "mongoose";

export const connectDatabase = async () => {
const mongoUrl = process.env.MONGO_URL || "mongodb://127.0.0.1/triptracking"
// console.log("MongoDB Connection String:", mongoUrl); Debug line

try {
await mongoose.connect(mongoUrl);
console.log("Connected to MongoDB!")
} catch (error) {
console.error("Error connecting to MongoDB: ", error.message);
process.exit(1); // Exit process on connection failure
}
}
107 changes: 107 additions & 0 deletions controllers/tripController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Trip } from "../models/tripModel.js";

// GET all trips
export const getTrips = async (req, res) => {
try {
const trips = await Trip.find();
res.status(200).json(trips);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

// GET a specific trip by ID
export const getTripById = async (req, res) => {
try {
const trip = await Trip.findById(req.params.id).populate("creation.createdBy").populate("submission.approvedBy");
if (!trip) {
return res.status(404).json({ message: "Trip not found." });
}
res.status(200).json(trip);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

// POST a new trip
export const createTrip = async (req, res) => {
const trip = await new Trip(req.body).save();

try {
res.status(201).json({
success: true,
response: trip,
message: "Trip is created"
});
} catch (error) {
res.status(400).json({
success: false,
response: error.message,
message: "Trip can't be created"
});
}
};

// PATCH (Partial Update) an existing trip
export const updateTrip = async (req, res) => {
try {
const trip = await Trip.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true, // Ensure validation runs for updates
});

if (!trip) {
return res.status(404).json({
success: false,
message: "Trip didn't found. Please add one."
});
}

res.status(200).json({
success: true,
response: trip,
message: "Trip updated success"
});
} catch (error) {
res.status(400).json({
success: false,
response: error.message,
message: "Trip can't be updated. Please check again"
});
}
};

// PUT (Full Replace) - using findOneAndReplace
export const updateTripPut = async (req, res) => {
try {
const trip = await Trip.findOneAndReplace(
{ _id: req.params.id },
req.body,
{
new: true, // Return the updated document
overwrite: true, // Overwrite the entire document
runValidators: true, // Enforce validation rules
});

if (!trip) {
return res.status(404).json({
success: false,
message: "Trip didn't found. Please add one."
});
}

res.status(200).json({
success: true,
response: trip,
message: "Trip updated success"
});
} catch (error) {
res.status(400).json({
success: false,
response: error.message,
message: "Trip can't be updated. Please check again"
});
}
};


118 changes: 118 additions & 0 deletions controllers/userController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { User } from "../models/userModel.js";

// GET all users
export const getUsers = async (req, res) => {
try {
const users = await User.find();
res.status(200).json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

// GET a specific user by ID
export const getUserById = async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ message: "User not found." });
}
res.status(200).json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
};

// POST (create) a new user
export const createUser = async (req, res) => {
try {
const { email } = req.body;

// Double-check for existing user (optional safeguard)
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({
success: false,
response: `Email is already in use: ${existingUser.email}`,
});
}

// Create and save new user
const user = await new User(req.body).save();

res.status(201).json({
success: true,
response: user,
message: "User created successfully"
} );
} catch (error) {
res.status(400).json({
success: false,
response: error.message,
message: "User can't be created."
});
}
};

// PATCH (Partial Update) an existing user
export const updateUser = async (req, res) => {
try {
const user = await User.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true, // Ensure schema rules are enforced
});

if (!user) {
return res.status(404).json({
success: false,
response: null,
message: "User didn't found. Please add one."
});
}

res.status(200).json({
success: true,
response: user,
message: "User updated successfully"
});
} catch (error) {
res.status(400).json({
success: false,
response: error.message,
message: "User can't be updated. Please check again"
});
}
};

// PUT (Full Replace) - using findOneAndReplace
export const updateUserPut = async (req, res) => {
try {
const user = await User.findOneAndReplace(
{ _id: req.params.id }, // Find user by ID
req.body, // Replace with new data
{
new: true, // Return the updated document
overwrite: true, // Overwrite the entire document
runValidators: true, // Ensure schema rules are enforced
});

if (!user) {
return res.status(404).json({
success: false,
message: "User didn't found. Please add one."
});
}

res.status(200).json({
success: true,
response: user,
message: "User replaced successfully"
});
} catch (error) {
res.status(400).json({
success: false,
response: error.message,
message: "User can't be replaced. Please check again"
});
}
};
Loading