I am very new to both JavaScript and NodeJS and am struggling to grapple some of the concepts surrounding async, promises, and others, and especially how to structure the code properly. I am trying to encapsulate some of the functionality in one of my route controllers by extracting it into a Service class. The initial code I have is something like the below example:
import Person from "../models/person.js";
import mongoose from "mongoose";
async function getPerson(req, res) {
try{
if(!mongoose.Types.ObjectId.isValid(req.params.id)){
return res.status(400).send('Invalid ID');
}
// Query DB
const person = await Person.findById(req.params.id);
if (!person) {
res.status(404).send('Not found');
}
// Verify
if (person.Age < 18) {
res.status(403).send('Cannot query for minors');
}
// Success
res.status(200).send(person)
} catch (error) {
console.error(error);
res.status(500).send("Error retrieving media");
}
}
export { getPerson };
This is the controller for a route like "www.projectdomain/person/99" to retrieve the Person data from the person with ID 99. There are a few different failure modes with error codes 400
, 404
, 403
or 500
depending on the circumstances.
Now, I want to extract some of this into a more general Service class that isn't necessarily tied to a HTTP get request, and without access to the req
and res
objects. I have the following:
import Person from "../models/person.js";
import mongoose from "mongoose";
class PersonService {
getPerson(id) {
if(!mongoose.Types.ObjectId.isValid(id)){
throw new Error('Invalid ID');
}
const person = await Person.findById(id);
if (!person) {
throw new Error("Not found");
}
// Verify
if (person.Age < 18) {
throw new Error('Cannot query for minors');
}
// Success
return person;
}
}
export default PersonService;
I don't know of a better way to throw informed errors that can be translated to a HTTP error code in the outside context. The Controller would now look something like:
import PersonService from "../services/PersonService.js";
async function getPerson(req, res) {
const personService = new PersonService();
try{
const personData = await personService.getPerson(req.params.id);
res.status(200).send(personData)
} catch (error) {
console.error(error);
res.status(500).send("Error retrieving media");
}
}
export { getPerson };
The motivation is of course that we might want to reuse the logic for retrieving a person's data from the database in a context where we don't have res
and req
. However, any errors thrown by the Service class will now get caught by the outside Controller and returned as a status 500
. How can I implement Services with proper error handling in NodeJS? How can I abstract some functionality to Service classes without losing the ability to return informed error codes?
I am very new to both JavaScript and NodeJS and am struggling to grapple some of the concepts surrounding async, promises, and others, and especially how to structure the code properly. I am trying to encapsulate some of the functionality in one of my route controllers by extracting it into a Service class. The initial code I have is something like the below example:
import Person from "../models/person.js";
import mongoose from "mongoose";
async function getPerson(req, res) {
try{
if(!mongoose.Types.ObjectId.isValid(req.params.id)){
return res.status(400).send('Invalid ID');
}
// Query DB
const person = await Person.findById(req.params.id);
if (!person) {
res.status(404).send('Not found');
}
// Verify
if (person.Age < 18) {
res.status(403).send('Cannot query for minors');
}
// Success
res.status(200).send(person)
} catch (error) {
console.error(error);
res.status(500).send("Error retrieving media");
}
}
export { getPerson };
This is the controller for a route like "www.projectdomain/person/99" to retrieve the Person data from the person with ID 99. There are a few different failure modes with error codes 400
, 404
, 403
or 500
depending on the circumstances.
Now, I want to extract some of this into a more general Service class that isn't necessarily tied to a HTTP get request, and without access to the req
and res
objects. I have the following:
import Person from "../models/person.js";
import mongoose from "mongoose";
class PersonService {
getPerson(id) {
if(!mongoose.Types.ObjectId.isValid(id)){
throw new Error('Invalid ID');
}
const person = await Person.findById(id);
if (!person) {
throw new Error("Not found");
}
// Verify
if (person.Age < 18) {
throw new Error('Cannot query for minors');
}
// Success
return person;
}
}
export default PersonService;
I don't know of a better way to throw informed errors that can be translated to a HTTP error code in the outside context. The Controller would now look something like:
import PersonService from "../services/PersonService.js";
async function getPerson(req, res) {
const personService = new PersonService();
try{
const personData = await personService.getPerson(req.params.id);
res.status(200).send(personData)
} catch (error) {
console.error(error);
res.status(500).send("Error retrieving media");
}
}
export { getPerson };
The motivation is of course that we might want to reuse the logic for retrieving a person's data from the database in a context where we don't have res
and req
. However, any errors thrown by the Service class will now get caught by the outside Controller and returned as a status 500
. How can I implement Services with proper error handling in NodeJS? How can I abstract some functionality to Service classes without losing the ability to return informed error codes?
1 Answer
Reset to default 3First off, welcome to JavaScript and Node.js
Your current setup works, but as you’ve noticed, lumping all errors into a generic 500
response in the controller loses the granularity you had before (like 400
, 404
, 403
).
Step 1: Create Custom Error Classes
You can define custom error classes that carry extra info, like an HTTP status code. This way, your service can throw meaningful errors, and your controller can interpret them. Here’s an example:
// errors.js
class HttpError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.name = this.constructor.name;
}
}
class BadRequestError extends HttpError {
constructor(message = "Bad Request") {
super(message, 400);
}
}
class NotFoundError extends HttpError {
constructor(message = "Not Found") {
super(message, 404);
}
}
class ForbiddenError extends HttpError {
constructor(message = "Forbidden") {
super(message, 403);
}
}
export { BadRequestError, NotFoundError, ForbiddenError };
These classes inherit from Error
, and each one has a default status code tied to it. You can tweak the messages or add more properties if needed.
Step 2: Update Your Service Class
Now, use these custom errors in your PersonService
. Also, since you’re working with async
operations (like findById
), mark the method as async
and handle the promise properly. Here’s how it could look:
// services/PersonService.js
import Person from "../models/person.js";
import mongoose from "mongoose";
import { BadRequestError, NotFoundError, ForbiddenError } from "../errors.js";
class PersonService {
async getPerson(id) { // Note the 'async' here
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new BadRequestError("Invalid ID");
}
const person = await Person.findById(id);
if (!person) {
throw new NotFoundError("Person not found");
}
if (person.Age < 18) {
throw new ForbiddenError("Cannot query for minors");
}
return person;
}
}
export default PersonService;
Step 3: Update Your Controller
In your controller, catch these errors and use their statusCode
to send the right response. If an unexpected error slips through (like a database crash), fall back to 500
. Here’s the updated version:
// controllers/personController.js
import PersonService from "../services/PersonService.js";
async function getPerson(req, res) {
const personService = new PersonService();
try {
const personData = await personService.getPerson(req.params.id);
res.status(200).send(personData);
} catch (error) {
// Check if it's one of our custom HttpErrors
if (error.statusCode) {
res.status(error.statusCode).send(error.message);
} else {
// Unexpected errors (e.g., database issues)
console.error(error);
res.status(500).send("Internal server error");
}
}
}
export { getPerson };
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744997309a4605274.html
评论列表(0条)