From 1d9b7ce07e62621f9a99128fff6d44c8c04dafd3 Mon Sep 17 00:00:00 2001 From: Zain Ali Date: Wed, 6 May 2026 18:11:06 +0500 Subject: [PATCH 1/2] Implement update book functionality with validation and error handling - Updated swagger.json --- ...book.controllers.js => book.controller.js} | 0 src/controllers/update.controller.js | 17 +++ src/routes/book.routes.js | 10 +- src/services/book.service.js | 42 ++++++- src/validations/book.validation.js | 7 ++ swagger.json | 110 ++++++++++++++++++ 6 files changed, 181 insertions(+), 5 deletions(-) rename src/controllers/{book.controllers.js => book.controller.js} (100%) create mode 100644 src/controllers/update.controller.js diff --git a/src/controllers/book.controllers.js b/src/controllers/book.controller.js similarity index 100% rename from src/controllers/book.controllers.js rename to src/controllers/book.controller.js diff --git a/src/controllers/update.controller.js b/src/controllers/update.controller.js new file mode 100644 index 0000000..34935f1 --- /dev/null +++ b/src/controllers/update.controller.js @@ -0,0 +1,17 @@ +import { bookService } from '../services/book.service.js'; + +export const updateBook = async (req, res) => { + try { + const { id } = req.params; + const result = await bookService.updateBook(id, req.body); + return res.status(200).json({ + message: 'Book updated successfully', + data: result, + }); + } catch (error) { + if (error.message === 'BOOK_NOT_FOUND') { + return res.status(404).json({ message: 'Book not found' }); + } + return res.status(400).json({ message: error.message }); + } +}; diff --git a/src/routes/book.routes.js b/src/routes/book.routes.js index 3666383..00d2784 100644 --- a/src/routes/book.routes.js +++ b/src/routes/book.routes.js @@ -1,8 +1,14 @@ import { validate } from '../middlewares/validate.middleware.js'; -import { bookSchema } from '../validations/book.validation.js'; -import { createBook } from '../controllers/book.controllers.js'; +import { + bookSchema, + updateBookSchema, +} from '../validations/book.validation.js'; +import { createBook } from '../controllers/book.controller.js'; +import { updateBook } from '../controllers/update.controller.js'; import express from 'express'; const router = express.Router(); router.post('/', validate(bookSchema), createBook); +router.put('/:id', validate(updateBookSchema), updateBook); + export default router; diff --git a/src/services/book.service.js b/src/services/book.service.js index e513393..1b8b105 100644 --- a/src/services/book.service.js +++ b/src/services/book.service.js @@ -1,5 +1,6 @@ // src/services/book.service.js import { prisma } from '../lib/prisma.js'; + export const bookService = { createBook: async (data) => { const { name, authors, price, publisher } = data; @@ -14,11 +15,46 @@ export const bookService = { price, publisher, authors: { - create: authors.map((authorName) => ({ - name: authorName, - })), + create: authors.map((authorName) => ({ name: authorName })), }, }, }); }, + + updateBook: async (id, data) => { + const bookId = Number(id); + + if (!bookId || isNaN(bookId)) { + throw new Error('Invalid book id'); + } + + const existingBook = await prisma.book.findUnique({ + where: { id: bookId }, + }); + + if (!existingBook) { + throw new Error('BOOK_NOT_FOUND'); + } + + const { name, authors, price, publisher } = data; + + if (price !== undefined && price < 0) { + throw new Error('Price cannot be negative'); + } + + return await prisma.book.update({ + where: { id: bookId }, + data: { + ...(name && { name }), + ...(price !== undefined && { price }), + ...(publisher && { publisher }), + authors: authors + ? { + deleteMany: {}, + create: authors.map((authorName) => ({ name: authorName })), + } + : undefined, + }, + }); + }, }; diff --git a/src/validations/book.validation.js b/src/validations/book.validation.js index 131f495..ed5c77d 100644 --- a/src/validations/book.validation.js +++ b/src/validations/book.validation.js @@ -6,3 +6,10 @@ export const bookSchema = z.object({ price: z.number().min(0), publisher: z.string().optional(), }); + +export const updateBookSchema = z.object({ + name: z.string().optional(), + authors: z.array(z.string()).optional(), + price: z.number().min(0).optional(), + publisher: z.string().optional(), +}); diff --git a/swagger.json b/swagger.json index 439c5f0..6012d0d 100644 --- a/swagger.json +++ b/swagger.json @@ -117,6 +117,116 @@ } } } + }, + "/books/{id}": { + "put": { + "summary": "Update an existing book", + "description": "Updates book details by ID. If book is not found, returns 404.", + + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "ID of the book to update", + "schema": { + "type": "integer", + "example": 1 + } + } + ], + + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "Atomic Habits Updated" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["Zain", "James Clear"] + }, + "price": { + "type": "number", + "example": 120 + }, + "publisher": { + "type": "string", + "example": "Updated Publisher" + } + } + } + } + } + }, + + "responses": { + "200": { + "description": "Book updated successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Book updated successfully" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "name": { + "type": "string" + }, + "price": { + "type": "number" + }, + "publisher": { + "type": "string" + } + } + } + } + } + } + } + }, + + "400": { + "description": "Invalid request data", + "content": { + "application/json": { + "example": { + "message": "Invalid input or price cannot be negative" + } + } + } + }, + + "404": { + "description": "Book not found", + "content": { + "application/json": { + "example": { + "message": "Book not found" + } + } + } + } + } + } } } } From 5e284fa93e7620ce3fd622d96bfab47ba7c19558 Mon Sep 17 00:00:00 2001 From: Zain Ali Date: Thu, 7 May 2026 10:13:13 +0500 Subject: [PATCH 2/2] Update README.md to refine tech stack section and modify example book data --- README.md | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 65b4561..1f74f84 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Includes Prisma ORM with SQLite, Swagger docs, linting, formatting, and Docker s --- -## 🛠 Tech Stack +## Tech Stack - **Node.js** - **Express** @@ -237,18 +237,7 @@ Creates a new book with multiple authors and stores it in the database. "name": "Atomic Habits", "price": 100, "publisher": "Humdard Publisher", - "authors": [{ "name": "Zain" }, { "name": "Chris" }] -} -``` - ---- - -#### 400 - Bad Request - -```json -{ - "error": "Validation failed", - "details": "Invalid input data" + "authors": [{ "name": "Anyone" }, { "name": "Chris" }] } ``` @@ -260,10 +249,3 @@ You can test the API using: - Swagger UI → `http://localhost:3000/api-docs` - Postman / Thunder Client - ---- - -## Notes - -- Ensure `express.json()` middleware is enabled -- Request body must match validation schema