Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
54 changes: 43 additions & 11 deletions shatter-backend/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,59 @@ import app from '../src/app';

const MONGODB_URI = process.env.MONGO_URI;

let cachedConnection: typeof mongoose | null = null;
let isConnected = false;

async function connectDB() {
if (cachedConnection) {
return cachedConnection;
if (isConnected && mongoose.connection.readyState === 1) {
console.log('Using existing MongoDB connection');
return;
}

if (!MONGODB_URI) {
throw new Error('MONGO_URI is not set in environment variables');
}

const conn = await mongoose.connect(MONGODB_URI, {
bufferCommands: false,
});

cachedConnection = conn;
return conn;
try {
console.log('Creating new MongoDB connection...');

await mongoose.connect(MONGODB_URI, {
bufferCommands: false,
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
});

isConnected = true;
console.log('MongoDB connected successfully');

mongoose.connection.on('error', (err) => {
console.error('MongoDB connection error:', err);
isConnected = false;
});

mongoose.connection.on('disconnected', () => {
console.log('MongoDB disconnected');
isConnected = false;
});

} catch (error) {
console.error('Failed to connect to MongoDB:', error);
isConnected = false;
throw error;
}
}

connectDB().catch((err) => {
console.error('Failed to connect to MongoDB:', err);
app.use(async (req, res, next) => {
try {
await connectDB();
next();
} catch (error: any) {
console.error('Database connection error:', error);
res.status(500).json({
error: 'Database connection failed',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
});

export default app;
15 changes: 1 addition & 14 deletions shatter-backend/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
// Express framework gives us fucnctions to creeate web server, handle routes, process HTTP req and send responses
import express from 'express';
import userRoutes from './routes/user_route'; // these routes define how to handel requests to /api/users
import userRoutes from './routes/user_route';

// Creating express application as a single "app" object, started in server.ts
// This object represents entire web server and will be used to:
// register Middleware, define routes ....
const app = express();

// Middleware setup
// responsible for parsing incoming JSON request bodies, making req.body usable in controllers
app.use(express.json());

// Defining routes
// a simple route that sends back plain text "Hello" when anyone visits the app in browser
app.get('/', (_req, res) => {
res.send('Hello');
});

// Mounts the user routes under the path 'api/users'
// any routes defined in user_route.ts will be accessible with URLs starting with /api/users
app.use('/api/users', userRoutes);


// Export the configured Express app, so that server.ts can import it
// Keeping app setup separate from server startup makes the code modular and testable
export default app;
61 changes: 26 additions & 35 deletions shatter-backend/src/controllers/user_controller.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,41 @@
// import req and res types for type safety
import { Request, Response } from 'express';
import { User } from '../models/user_model'; // imports user model created with mongoose
import { Request, Response } from "express";
import { User } from "../models/user_model";

// controller: GET /api/users
// This function handles GET reqs to /api/users
// It fetches all users from MongoDB and sends them as json

export const getUsers = async (_req: Request, res: Response) => {
try {
// retrieves all docs from "users" collection
const users = await User.find().lean(); // .lean() returns plain JS objects instead of Mongoose docs, may change incase we need extra model methods later
res.json(users); // sends list of users back as JSON response
} catch (err) { // log the error if something goes wrong
console.error('GET /api/users error:', err);
res.status(500).json({ error: 'Failed to fetch users' });
}
try {
const users = await User.find().lean();
res.json(users);
} catch (err: any) {
console.error("GET /api/users error:", err);
res.status(500).json({
error: "Failed to fetch users",
message: err.message,
});
}
};


// controller: POST /api/users
// reads data from req body, vailidates it and creates a new user
export const createUser = async (req: Request, res: Response) => {
try {
// Destructure the req body sent by the client
// The ?? {} ensures we don't get error if req.body is undefined
const { name, email } = req.body ?? {};
try {
const { name, email } = req.body ?? {};

// Basic validation to ensure both name and email are provided
// if not respond with bad request and stop further processes
if (!name || !email) {
return res.status(400).json({ error: 'name and email required' });
}
if (!name || !email) {
return res.status(400).json({ error: "name and email required" });
}

// create a new user doc in DB using Mongoose's .create()
const user = await User.create({ name, email });
// respond with "created" and send back created user as JSON
res.status(201).json(user);
} catch(err: any) {
// Handle duplicate email error
// Mongo DB rejects duplicat value since we have email marked as 'unique'
if (err?.code === 11000) {
return res.status(409).json({ error: 'email already exists' });
}

// for all other errors, log them and return generic 500 response
console.error('POST /api/users error:', err);
res.status(500).json({error: 'Failed to create user' });
const user = await User.create({ name, email });
res.status(201).json(user);
} catch (err: any) {
if (err?.code === 11000) {
return res.status(409).json({ error: "email already exists" });
}

console.error("POST /api/users error:", err);
res.status(500).json({ error: "Failed to create user" });
}
};
11 changes: 2 additions & 9 deletions shatter-backend/src/routes/user_route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
// router is like a mini-express app that allows grouping of related routes together
import { Router } from 'express';
import { getUsers, createUser } from '../controllers/user_controller';
// Importing controller functions that handle logic for each route
// These function define what happens when a req is received

// creating new router instance
const router = Router();

// Defining routes for the /api/users path
router.get('/', getUsers); // when GET req is made, run getUsers func
router.post('/', createUser); // when POST req is made, run creatUser func
router.get('/', getUsers);
router.post('/', createUser);

// Export the router so it can be used in app.ts
// app.ts imports router and mounts it under '/api/users'
export default router;
23 changes: 9 additions & 14 deletions shatter-backend/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
// This is the main entry point that starts the application

import 'dotenv/config'; // loads .env file and populates process.env (the node.js object)
import mongoose from 'mongoose'; // mongoose is an Object Data Modelling (ODM) livrary for MongoDB
import app from './app'; // Import the express app

import 'dotenv/config';
import mongoose from 'mongoose';
import app from './app';

// config
const PORT = process.env.PORT ? Number(process.env.PORT) : 4000;
const MONGODB_URI = process.env.MONGO_URI;

const PORT = process.env.PORT ? Number(process.env.PORT) : 4000; // use defined PORT in .env if present , otherwise default to 400
const MONGODB_URI = process.env.MONGO_URI; // read the mongoDB connection from env variables

// Start up function
async function start() {
try {
if (!MONGODB_URI) { // check that MOGO URI is provided in .env
if (!MONGODB_URI) {
throw new Error('MONGODB_URI is not set');
}
await mongoose.connect(MONGODB_URI); // this returns a promise, so we 'await' until it's successfully connected
await mongoose.connect(MONGODB_URI);
console.log('Successfully connected to MongoDB');

// start listening for incoming HTTP requests on chosen port
app.listen(PORT, () => {
console.log('Server running on http://localhost:${PORT}');
});
} catch (err) { // if connection goes wrong, log the error
} catch (err) {
console.error('Failed to start server:', err);
process.exit(1); // code 1 indicates failure
process.exit(1);
}
}

Expand Down