From 3e6fdc25f9532a2ae161dbb21879088d291dae42 Mon Sep 17 00:00:00 2001 From: Olaoluwa Oyebode Date: Tue, 6 Aug 2024 19:01:59 +0100 Subject: [PATCH] Olaoluwa Oyebode_Pull Request --- credentials.env | 1 + package-lock.json | 67 ++++++-------------------- src/routers/books.js | 111 ++++++++++++++++++++++++++++++++++++++++--- src/routers/pets.js | 108 +++++++++++++++++++++++++++++++++++++++++ src/server.js | 22 +++++++-- 5 files changed, 246 insertions(+), 63 deletions(-) create mode 100644 credentials.env create mode 100644 src/routers/pets.js diff --git a/credentials.env b/credentials.env new file mode 100644 index 00000000..d8058089 --- /dev/null +++ b/credentials.env @@ -0,0 +1 @@ +PGURL="postgres://neondb_owner:2lI6UfidxAPz@ep-damp-butterfly-a5ikepwz.us-east-2.aws.neon.tech/neondb" diff --git a/package-lock.json b/package-lock.json index 37ccdff8..f08df953 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1441,12 +1441,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1740,9 +1740,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -2024,16 +2024,16 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -2064,43 +2064,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/faker": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", @@ -2128,9 +2091,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" diff --git a/src/routers/books.js b/src/routers/books.js index 1551dd87..2cb7eef7 100644 --- a/src/routers/books.js +++ b/src/routers/books.js @@ -1,9 +1,108 @@ -const express = require('express') -const router = express.Router() -const db = require("../../db"); +const router = require("express").Router(); -router.get('/', async (req, res) => { +module.exports = function (pool) { + // GET all books + router.get("/", async function (req, res) { + const client = await pool.connect(); + try { + const response = await client.query("SELECT * FROM books"); + const books = response.rows; + res.json({ books: books }); + } catch (error) { + console.error("Error fetching books: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); -}) + // GET a book by ID + router.get("/:id", async function (req, res) { + const client = await pool.connect(); + const { id } = req.params; + try { + const response = await client.query("SELECT * FROM books WHERE id = $1", [ + id, + ]); + const book = response.rows[0]; + if (book) { + res.json({ book: book }); + } else { + res.status(404).json({ error: "Book not found" }); + } + } catch (error) { + console.error("Error fetching book by ID: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); -module.exports = router + // POST a new book + router.post("/", async function (req, res) { + const client = await pool.connect(); + const { title, author, genre, year } = req.body; + try { + const response = await client.query( + "INSERT INTO books (title, author, genre, year) VALUES ($1, $2, $3, $4) RETURNING *", + [title, author, genre, year] + ); + const newBook = response.rows[0]; + res.status(201).json({ book: newBook }); + } catch (error) { + console.error("Error creating a new book: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + // PUT update an existing book by ID + router.put("/:id", async function (req, res) { + const client = await pool.connect(); + const { id } = req.params; + const { title, author, genre, year } = req.body; + try { + const response = await client.query( + "UPDATE books SET title = $1, author = $2, genre = $3, year = $4 WHERE id = $5 RETURNING *", + [title, author, genre, year, id] + ); + const updatedBook = response.rows[0]; + if (updatedBook) { + res.json({ book: updatedBook }); + } else { + res.status(404).json({ error: "Book not found" }); + } + } catch (error) { + console.error("Error updating book: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + // DELETE a book by ID + router.delete("/:id", async function (req, res) { + const client = await pool.connect(); + const { id } = req.params; + try { + const response = await client.query( + "DELETE FROM books WHERE id = $1 RETURNING *", + [id] + ); + const deletedBook = response.rows[0]; + if (deletedBook) { + res.json({ book: deletedBook }); + } else { + res.status(404).json({ error: "Book not found" }); + } + } catch (error) { + console.error("Error deleting book: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + return router; +}; diff --git a/src/routers/pets.js b/src/routers/pets.js new file mode 100644 index 00000000..66b7c112 --- /dev/null +++ b/src/routers/pets.js @@ -0,0 +1,108 @@ +const router = require("express").Router(); + +module.exports = function (pool) { + // GET all pets + router.get("/", async function (req, res) { + const client = await pool.connect(); + try { + const response = await client.query("SELECT * FROM pets"); + const pets = response.rows; + res.json({ pets: pets }); + } catch (error) { + console.error("Error fetching pets: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + // GET a pet by ID + router.get("/:id", async function (req, res) { + const client = await pool.connect(); + const { id } = req.params; + try { + const response = await client.query("SELECT * FROM pets WHERE id = $1", [ + id, + ]); + const pet = response.rows[0]; + if (pet) { + res.json({ pet: pet }); + } else { + res.status(404).json({ error: "Pet not found" }); + } + } catch (error) { + console.error("Error fetching pet by ID: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + // POST a new pet + router.post("/", async function (req, res) { + const client = await pool.connect(); + const { name, type, age, owner } = req.body; + try { + const response = await client.query( + "INSERT INTO pets (name, type, age, owner) VALUES ($1, $2, $3, $4) RETURNING *", + [name, type, age, owner] + ); + const newPet = response.rows[0]; + res.status(201).json({ pet: newPet }); + } catch (error) { + console.error("Error creating a new pet: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + // PUT update an existing pet by ID + router.put("/:id", async function (req, res) { + const client = await pool.connect(); + const { id } = req.params; + const { name, type, age, owner } = req.body; + try { + const response = await client.query( + "UPDATE pets SET name = $1, type = $2, age = $3, owner = $4 WHERE id = $5 RETURNING *", + [name, type, age, owner, id] + ); + const updatedPet = response.rows[0]; + if (updatedPet) { + res.json({ pet: updatedPet }); + } else { + res.status(404).json({ error: "Pet not found" }); + } + } catch (error) { + console.error("Error updating pet: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + // DELETE a pet by ID + router.delete("/:id", async function (req, res) { + const client = await pool.connect(); + const { id } = req.params; + try { + const response = await client.query( + "DELETE FROM pets WHERE id = $1 RETURNING *", + [id] + ); + const deletedPet = response.rows[0]; + if (deletedPet) { + res.json({ pet: deletedPet }); + } else { + res.status(404).json({ error: "Pet not found" }); + } + } catch (error) { + console.error("Error deleting pet: ", error); + res.status(500).json({ error: "Internal Server Error" }); + } finally { + client.release(); + } + }); + + return router; +}; diff --git a/src/server.js b/src/server.js index dac55e5d..a585e5c3 100644 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,19 @@ const express = require("express"); const morgan = require("morgan"); const cors = require("cors"); +const { Pool } = require("pg"); +const booksRouter = require("./routers/books.js"); +const petsRouter = require("./routers/pets.js"); + +// Set up a connection to the database +const pool = new Pool({ + user: "neondb_owner", + host: "ep-damp-butterfly-a5ikepwz.us-east-2.aws.neon.tech", + database: "neondb", + password: "2lI6UfidxAPz", + port: 5432, + ssl: true, +}); const app = express(); @@ -8,9 +21,8 @@ app.use(morgan("dev")); app.use(cors()); app.use(express.json()); -//TODO: Implement books and pets APIs using Express Modular Routers -const booksRouter = require('./routers/books.js') - -app.use('/books', booksRouter) +// Implement books and pets APIs using Express Modular Routers +app.use("/books", booksRouter(pool)); +app.use("/pets", petsRouter(pool)); -module.exports = app +module.exports = app;