Skip to content

Commit 2a60821

Browse files
authored
Merge pull request #6 from techstartucalgary/feature/STR-80-get-and-create-users
Add basic /getUsers and /createUser endpoints
2 parents 64195ed + b0bd61d commit 2a60821

File tree

6 files changed

+145
-23
lines changed

6 files changed

+145
-23
lines changed

shatter-backend/src/app.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
1+
// Express framework gives us fucnctions to creeate web server, handle routes, process HTTP req and send responses
12
import express from 'express';
2-
import userRoutes from './routes/user_route.ts';
3+
import userRoutes from './routes/user_route.ts'; // these routes define how to handel requests to /api/users
34

5+
// Creating express application as a single "app" object, started in server.ts
6+
// This object represents entire web server and will be used to:
7+
// register Middleware, define routes ....
48
const app = express();
9+
10+
// Middleware setup
11+
// responsible for parsing incoming JSON request bodies, making req.body usable in controllers
512
app.use(express.json());
613

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

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

24+
25+
// Export the configured Express app, so that server.ts can import it
26+
// Keeping app setup separate from server startup makes the code modular and testable
1327
export default app;
Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,50 @@
1+
// import req and res types for type safety
12
import { Request, Response } from 'express';
3+
import { User } from '../models/user_model.ts'; // imports user model created with mongoose
24

3-
let users = [
4-
{ id: 1, name: 'Minh', email: '[email protected]' }
5-
];
5+
// controller: GET /api/users
6+
// This function handles GET reqs to /api/users
7+
// It fetches all users from MongoDB and sends them as json
68

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

11-
export const createUser = (req: Request, res: Response) => {
12-
const { name, email } = req.body;
13-
14-
if (!name || !email) {
15-
return res.status(400).json({ error: 'name and email required' });
16-
}
1720

18-
const newUser = { id: users.length + 1, name, email };
19-
users.push(newUser);
20-
res.status(201).json(newUser);
21+
// controller: POST /api/users
22+
// reads data from req body, vailidates it and creates a new user
23+
export const createUser = async (req: Request, res: Response) => {
24+
try {
25+
// Destructure the req body sent by the client
26+
// The ?? {} ensures we don't get error if req.body is undefined
27+
const { name, email } = req.body ?? {};
28+
29+
// Basic validation to ensure both name and email are provided
30+
// if not respond with bad request and stop further processes
31+
if (!name || !email) {
32+
return res.status(400).json({ error: 'name and email required' });
33+
}
34+
35+
// create a new user doc in DB using Mongoose's .create()
36+
const user = await User.create({ name, email });
37+
// respond with "created" and send back created user as JSON
38+
res.status(201).json(user);
39+
} catch(err: any) {
40+
// Handle duplicate email error
41+
// Mongo DB rejects duplicat value since we have email marked as 'unique'
42+
if (err?.code === 11000) {
43+
return res.status(409).json({ error: 'email already exists' });
44+
}
45+
46+
// for all other errors, log them and return generic 500 response
47+
console.error('POST /api/users error:', err);
48+
res.status(500).json({error: 'Failed to create user' });
49+
}
2150
};

shatter-backend/src/models/.gitkeep

Whitespace-only changes.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Import Schema and model from the Mongoose library.
2+
// - Schema: defines the structure and rules for documents in a collection (like a blueprint).
3+
// - model: creates a model (class) that we use in code to read/write those documents.
4+
import { Schema, model } from 'mongoose';
5+
6+
// define TS interface for type safety
7+
// This helps IDE and compiler know what fields exist on a User
8+
9+
export interface IUser {
10+
name: string;
11+
email: string;
12+
}
13+
14+
// Create the Mongoose Schema (the database blueprint)
15+
16+
// A Schema tells Mongoose what fields each document should have
17+
// and what rules apply to those fields.
18+
const UserSchema = new Schema<IUser>(
19+
{
20+
name: {
21+
type: String,
22+
required: true, // field is mandatory; Mongoose will throw error if missing
23+
trim: true // removes extra space at start and end
24+
},
25+
email: {
26+
type: String,
27+
required: true,
28+
trim: true,
29+
lowercase: true, // converts all emails to lowercase before saving for consistency
30+
unique: true // enforce uniqueness, error 11000 if duplicate is detected
31+
}
32+
},
33+
{
34+
// timestamps: true automatically adds two fields to each document:
35+
// - createdAt: Date when the document was first created
36+
// - updatedAt: Date when the document was last modified
37+
timestamps: true
38+
}
39+
);
40+
41+
42+
// create and export mongoose model
43+
// model is simply a wrapper around schema that gives access to MongoDB opeprations
44+
45+
// "User" is the model name
46+
// Mongoose will automatically use "users" as the collection name in MongoDB
47+
export const User = model<IUser>('User', UserSchema);
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
// router is like a mini-express app that allows grouping of related routes together
12
import { Router } from 'express';
23
import { getUsers, createUser } from '../controllers/user_controller.ts';
4+
// Importing controller functions that handle logic for each route
5+
// These function define what happens when a req is received
36

7+
// creating new router instance
48
const router = Router();
59

6-
router.get('/', getUsers);
7-
router.post('/', createUser);
10+
// Defining routes for the /api/users path
11+
router.get('/', getUsers); // when GET req is made, run getUsers func
12+
router.post('/', createUser); // when POST req is made, run creatUser func
813

9-
export default router;
14+
// Export the router so it can be used in app.ts
15+
// app.ts imports router and mounts it under '/api/users'
16+
export default router;

shatter-backend/src/server.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
1-
import app from './app.ts';
1+
// This is the main entry point that starts the application
22

3-
const PORT = 4000;
3+
import 'dotenv/config'; // loads .env file and populates process.env (the node.js object)
4+
import mongoose from 'mongoose'; // mongoose is an Object Data Modelling (ODM) livrary for MongoDB
5+
import app from './app.ts'; // Import the express app
46

5-
app.listen(PORT, () => {
6-
console.log(`Server running on http://localhost:${PORT}`);
7-
});
7+
8+
// config
9+
10+
const PORT = process.env.PORT ? Number(process.env.PORT) : 4000; // use defined PORT in .env if present , otherwise default to 400
11+
const MONGODB_URI = process.env.MONGO_URI; // read the mongoDB connection from env variables
12+
13+
// Start up function
14+
async function start() {
15+
try {
16+
if (!MONGODB_URI) { // check that MOGO URI is provided in .env
17+
throw new Error('MONGODB_URI is not set');
18+
}
19+
await mongoose.connect(MONGODB_URI); // this returns a promise, so we 'await' until it's successfully connected
20+
console.log('Successfully connected to MongoDB');
21+
22+
// start listening for incoming HTTP requests on chosen port
23+
app.listen(PORT, () => {
24+
console.log('Server running on http://localhost:${PORT}');
25+
});
26+
} catch (err) { // if connection goes wrong, log the error
27+
console.error('Failed to start server:', err);
28+
process.exit(1); // code 1 indicates failure
29+
}
30+
}
31+
32+
start();

0 commit comments

Comments
 (0)