I have the following code written. I am not sure if it works but basically am trying to create a function that inserts a new todo with a description unless that description is bad todo. Obviously this is just for testing but I want to call this function I created in my express app.
BEGIN;
CREATE OR REPLACE FUNCTION perntodo.createTodo(
description perntodo.todoDescription%Type)
RETURNS VARCHAR AS
$$
DECLARE
BEGIN
IF description == 'bad todo' THEN
RAISE EXCEPTION 'Cannot post this todo!';
ELSE
INSERT INTO TODO VALUES(description);
END IF;
END;
COMMIT;
My controller looks like this...
router.post('/', async (req, res) => {
try {
const { description } = req.body;
// const newTodo = await pool.query(
// 'INSERT INTO todo (description) VALUES($1) RETURNING *',
// [description]
// );
const newTodo = await pool.func('perntodo.createTodo', [description]);
res.json(newTodo.rows[0]);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error...');
}
});
The commented out code works. But the pool.func says it's not a function. What am I doing wrong.
This is how I connected to my database
const Pool = require('pg').Pool;
const user = './user';
const DBPASSWORD = './pass';
const pool = new Pool({
user: user,
password: DBPASSWORD,
host: 'localhost',
port: 5432,
database: 'perntodo',
});
module.exports = pool;
Related
I have a MERN stack Library Management System website.
In my app currently for admin i have given a Notify button to send emails to all user that have any books due in the library. For this an array of defaulty user gets passed as a req body to send emails. Admin gets this list of users from database on initial render of that particular component.
But i want to automate sending of emails and want my server to trigger automatic emails at 10:00 am to all the users who have due books.
On Notify button click my notifyBookDefaulties controller gets triggered.
I tried to use a setTimeout and a timer as well to call my route at 10:00 am and trigger emails but i am not able to get desired output.
Below i my notifyBookDefaulties controller:
const notifyBookDefaulties = asyncHandler(async (req, res) => {
const admin = await Auth.findById(req.user.id);
// to check if user exists by that id in the databse
// and that user is a admin (got by token)
if (!admin && admin.admin !== true) {
res.status(401);
throw new Error("Not Authorized");
}
const { users, bookID, title } = req.body; // here users is the list of user id's
let emails = "";
// to get email of each user from their user id
for (let user of users) {
try {
const defaulty = await Auth.findById(user);
emails += defaulty.email + ",";
} catch (error) {
res.status(400);
throw new Error(error);
}
}
// to get comma separated list of emails
const emailList = emails.slice(0, -1).toString();
// try block tries to send email and catch block catches any error if occured
try {
var transporter = nodemailer.createTransport({
service: process.env.SERVICE,
auth: {
user: process.env.USER,
pass: process.env.PASS,
},
});
var mailOptions = {
from: process.env.USER,
to: emailList,
subject: "Return Book",
html: `<!DOCTYPE html><html lang="en"><body>This is to remind you that the book titled ${title} and ID ${bookID} issued by you is due.</body></html>`,
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
res.status(400).json({ msg: error });
} else {
res.status(200).json({ msg: "E-Mail Successfully sent" });
}
});
} catch (error) {
console.log(error);
res.status(500).json({ msg: error });
}
});
Below is my server.js:
require("dotenv").config();
const express = require("express");
const { errorHandler } = require("./middleware/errorMiddleware");
const connectDB = require("./config/db");
const cors = require("cors");
const port = process.env.PORT || 5000;
connectDB();
const app = express();
const corsOptions = {
origin: 'http://localhost:3000',
optionsSuccessStatus: 204
};
app.use(cors(corsOptions))
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/api/admin", require("./routes/adminRoutes"));
app.use("/api/user", require("./routes/userRoutes"));
app.use("/api/actions", require("./routes/authRoute"));
app.use(errorHandler);
app.listen(port, () => {
console.log(`Running on ${port}`);
});
My controller gets called for below route:
router.post("/notify", protect, notifyBookDefaulties);
and the url is:
http://localhost:5000/api/admin/notify
Note: here i have not included my function which fetches the list of user id's, of users that have due books. To fetch defaulting users i have a separate controller and i will merge that into this controller once i get the logic to send mails at 10:00 am.
If there is any other way to implement this i would like to know. If any more clarity needed do tell. Thanks in advance.
Sounds like a cron job, check this package https://www.npmjs.com/package/node-cron
I am using Typescript, Express, PostgresDB.
Here is my code for connecting to the database cluster.
import { Pool } from "pg";
const myPool = new Pool({
host: `${process.env.DATABASE_URL}`, //somedb.abc.us-east-1.rds.amazonaws.com
database: `${process.env.DATABASE_NAME}`, //dbName
user: `${process.env.DATABASE_USER}`, //dbUser
password: `${process.env.DATABASE_PASSWORD}`, //dbPassword
port: 5432
});
myPool.connect();
Here is my post route:
const router = express.Router();
router.post("/item/new", async (request, response) =>{
try{
const { itemTitle } = request.body;
const myItem = await myPool.query(`INSERT INTO items VALUES('${itemTitle}')`), (resp, err) =>{
if(err){
return err;
}
return resp;
});
return response.status(201).json({message: myItem});
}catch(err){
return response.status(400).json({message: `${err}`});
}
});
When I send the request, I get the following response with a 201 status code, but nothing
is inserted into the database:
{
"message": {}
}
It's because you're sending the callback function with the wrong argument's order. The first argument for the callback is error, not result.
It should be like this:
client.query('SELECT NOW() as now', (error, result) => {
if (error) {
console.log(error.stack)
} else {
console.log(result.rows[0])
}
})
documentation.
You can try to print the query that you are passing to find the error.
The reason is that you are concatenating a json object with string which is wrong, instead try this:
`INSERT INTO items(col1, col2) VALUES(${itemTitle.col1}, ${itemTitle.col2})`
I need to create a mysql connection in nodejs , but the credentials for the mysql comes from a third party credential manager service. Can somebody suggest me a way to achieve this?
database.js - i am using connection from this file in all other database operations
const mysql = require("mysql");
const {env} = require('./globals')
const connection = mysql.createConnection({
host: env.DATABASE.HOST,
user: env.DATABASE.USER,
password: env.DATABASE.PASSWORD,
database: env.DATABASE.NAME,
multipleStatements: true
});
connection.connect(function (err) {
if (err) {
console.log("Error in DB connection");
console.log("err", err);
} else console.log("Connected!");
});
module.exports = connection
globals.js
const {getSecret} = require('../src/service')
require("dotenv").config();
async function getCredentials() {
const result = await getSecret()
return JSON.parse(result?.SecretString || {})
}
const credentials = getCredentials() // not working, and i can't use await here
const env = {
DATABASE: {
HOST: credentials.ip_address,
PASSWORD: credentials.password,
NAME: credentials.dbname,
USER: credentials.username,
},
SKU: process.env.SKU
}
module.exports.env = env
Your 2 main options are:
Don't export connection but export an async function that returns a connection.
Write an init() function that sets up your database connection, and ensure it's one of the first things your application calls before anything else.
Well first, you need to fix up that globals.js file. Logic that depends on an async function must be async itself. You can just move everything into the async function like so:
const {getSecret} = require('../src/service')
require("dotenv").config();
async function getCredentials() {
const result = await getSecret()
const credentials = JSON.parse(result?.SecretString || {})
return {
DATABASE: {
HOST: credentials.ip_address,
PASSWORD: credentials.password,
NAME: credentials.dbname,
USER: credentials.username,
},
SKU: process.env.SKU
}
}
module.exports = { getCredentials }
And since even your database connection in database.js depends on this async function, it will have to be async as well:
const mysql = require("mysql");
const {getCredentials} = require('./globals')
const getConnection = getSecret().then(function (env) {
const connection = mysql.createConnection({
host: env.DATABASE.HOST,
user: env.DATABASE.USER,
password: env.DATABASE.PASSWORD,
database: env.DATABASE.NAME,
multipleStatements: true
});
connection.connect(function (err) {
if (err) {
console.log("Error in DB connection");
console.log("err", err);
} else console.log("Connected!");
});
return connection;
})
module.exports = getConnection
I am creating an express app using mongoose with the intention of connecting this to React for the frontend.
I have listed some CRUD operations for a customer controller below but there are a few things I do not like about this approach.
When using Customer.findById with a valid ObjectID that is not found, it returns null with a 200 response code. I want this to return 404 if no customer was found. I realise I could change the catch response to a 404, but I want to have some generic error handling incase the server goes down during the request or an invalid ObjectId was provided, which brings me to my next item.
If I provide an invalid ObjectId I want to provide some meaningful message, is 500 the right response code?
Error handling: Am I returning errors the correct way? currently errors return a string with the error message. Should I return JSON instead? e.g. res.status(500).json({error: error.message). I am planning on connecting this to react (which I am still learning) and I assume the UI will need to display these messages to the user?
findById is repeated in getCustomerById, updateCustomer, and deleteCustomer. I feel this is bad practice and there must be a more streamlined approach?
I want to have one function that validates if the ObjectId is valid. I am aware that I can do this is the routes using router.params but I'm not sure if checking for a valid id should be in the routes file as it seems like something the controller should be handling? See routes example below from another project I did.
What are the best practices and suggested ways to improve my code, based on the above?
I have read the documentation from mongoose, mozilla, and stackoverflow Q&A but they don't seem to address these issues (at least I could not find it).
I am really after some guidance or validation that what I am doing is correct or wrong.
customer.controller.js
const Customer = require("../models/customer.model");
exports.getCustomers = async (req, res) => {
try {
const customers = await Customer.find();
res.status(200).json(customers);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.getCustomerById = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.addCustomer = async (req, res) => {
try {
const customer = new Customer(req.body);
await customer.save().then(res.status(201).json(customer));
} catch (error) {
res.status(500).send(error.message);
}
};
exports.updateCustomer = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
Object.assign(customer, req.body);
customer.save();
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
exports.deleteCustomer = async (req, res) => {
try {
const customer = await Customer.findById(req.params.id);
await customer.remove();
res.status(200).json(customer);
} catch (error) {
res.status(500).send(error.message);
}
};
Router.params example
This is a routes file (not related to my current app) and is provided as an example of how I have used router.params in the past.
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Artist = require("../models/Artist");
const loginRequired = require("../middleware/loginRequired");
const {
getArtists,
addArtist,
getArtistById,
updateArtist,
deleteArtist,
} = require("../controllers/artistController");
router
.route("/")
.get(loginRequired, getArtists) // Get all artists
.post(loginRequired, addArtist); // Create a new artist
router
.route("/:id")
.get(loginRequired, getArtistById) // Get an artist by their id
.put(loginRequired, updateArtist) // Update an artist by their id
.delete(loginRequired, deleteArtist); // Delete an artist by their id
router.param("id", async (req, res, next, id) => {
// Check if the id is a valid Object Id
if (mongoose.isValidObjectId(id)) {
// Check to see if artist with valid id exists
const artist = await Artist.findOne({ _id: id });
if (!artist) res.status(400).json({ errors: "Artist not found" });
res.locals.artist = artist;
res.locals.artistId = id;
next();
} else {
res.status(400).json({ errors: "not a valid object Id" });
}
});
module.exports = router;
i personly like to make error handeling more global so i would write something like
constPrettyError = require('pretty-error')
const pe = new PrettyError()
const errorHandler = (err, req, res, next) => {
if (process.env.NODE_ENV !== 'test') {
console.log(pe.render(err))
}
return res
.status(err.status || 500)
.json({ error: { message: err.message || 'oops something went wrong' } })
}
module.exports = errorHandler
as a handler
the in your index / server file
app.use(errorHandler)
then in your handlers just
} catch (err) {
next(err);
}
as an example
if (!artist) next({ message: "Artist not found" ,status:404 });
also, note that you can customize this error handler to switch case (or object) a custom error per status as well if you want
const errorHandler = (err, req, res, next) => {
if (process.env.NODE_ENV !== 'test') {
console.log(pe.render(err))
}
const messagePerStatus = {
404: 'not found',
401: 'no authorization'
}
const message = messagePerStatus[err.status]
return res
.status(err.status || 500)
.json({
error: { message: message || err.message || 'oops something went wrong' }
})
}
then just
if (!artist) next({status:404 });
I also agree with answer by Asaf Strilitz but still need to show what i do in my projects
Create a custom error class
AppError.js
class AppError extends Error {
constructor(statusCode, message) {
super();
// super(message);
this.statusCode = statusCode || 500 ;
this.message = message || "Error Something went wrong";
}
}
module.exports = AppError;
Create an error handling middleware
errors.js
const AppError = require("../helpers/appError");
const errors = (err, req, res, next) => {
// console.log(err);
let error = { ...err };
error.statusCode = error.statusCode;
error.message = error.message;
res.status(error.statusCode).json({
statusCode: err.statusCode,
message: err.message,
});
};
exports.errors = errors;
Create a middleware to validate object id
validateObjectId.js
const mongoose = require("mongoose");
const AppError = require("appError");
module.exports = function (req, res, next) {
const { _id } = req.params;
if (_id && !mongoose.Types.ObjectId.isValid(_id)) {
throw new AppError(422, "Invalid ID field in params");
}
next();
};
In your app.js
const { errors } = require("errors");
// At the end of all middlewares
// Error Handler Middleware
app.use(errors);
In your routes file
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Artist = require("../models/Artist");
const loginRequired = require("../middleware/loginRequired");
const validateId = require("validateObjectId");
const {
getArtists,
addArtist,
getArtistById,
updateArtist,
deleteArtist,
} = require("../controllers/artistController");
// Your routes
router
.route("/:id")
.get(validateId, loginRequired, getArtistById) // Get an artist by their id
.put(validateId, loginRequired, updateArtist) // Update an artist by their id
.delete(validateId, loginRequired, deleteArtist); // Delete an artist by their id
module.exports = router;
Now regarding findById method being repeated i dont see anything bad in that as it is specific to database call still you can introduce a staic method on model itself or create a single method on cntroller but still need to check if it returns the found object or not and handle the error on that.
I am trying to connect my application to the database using the connection pool method, its connecting fine, and data insertion is happening fine without any issues but other queries in the same file are slowing down.
I have tried with release() method also not working properly.
How can release the pool to the next query once it's executed the current query?
Below is my dbpool.js file code where I am writing a common generalized database connection,
var pg = require('pg');
var PGUSER = 'postgres';
var PGDATABASE = 'test_database';
var config = {
user: PGUSER, // name of the user account
host: 'localhost',
database: PGDATABASE, // name of the database
password: 'password#AWS',
port: 5432,
max: 10,
idleTimeoutMillis: 10000
};
const pool = new pg.Pool(config);
const DB = {
query: function(query, callback) {
pool.connect((err, client, done) => {
if(err){ return callback(err); }
client.query(query, (err, results) => {
// done();
client.release();
// if(err) { console.error("ERROR: ", err) }
if(err) { return callback(err); }
callback(null, results.rows);
})
});
}
};
module.exports = DB;
I tried with both the done() and client.release() method but no luck. If I use both then I am getting an error message client is already released.
Below is my socket.js file code:
var express = require('express');
const connection = require('./dbpool.js');
if(arData == '0022'){
const queryText = "INSERT INTO alert(alert_data) VALUES('"+arData+"')";
connection.query(queryText,(err, res) => {
if(err){
console.log(err.stack);
}
});
}
if(arData == '0011'){
const queryText = "INSERT INTO table2(alert_data) VALUES('"+arData+"')";
connection.query(queryText,(err, res) => {
if(err){
console.log(err.stack);
}
});
}
function ReverseCommunication(){
const select1 = "SELECT * FROM alert WHERE action = '0' ORDER BY alert_id ASC LIMIT 1";
connection.query(select1, (err, res) =>{
if(err) {
console.log("Error1");
res.json({"error":true});
}
else{
console.log("res==",res);
}
});
}
setInterval(function(){
ReverseCommunication();
}, 2000)
With pool you shouldn't need to close the connection. With pool it will reuse the connection pool for subsequent request so you don't have to connect to the DB each time.
(i'm not a PG expert here, sure other could expand on that way better then I )
What works for us is to set up the dbpool file you have like this
const {Pool,Client} = require('pg');
const pool = new Pool({
user: process.env.POSTGRES_USER,
host: process.env.POSTGRES_URL,
database: process.env.POSTGRES_DATABASE,
password: process.env.POSTGRES_PASSWORD,
port: process.env.POSTGRES_PORT,
keepAlive: true,
connectionTimeoutMillis: 10000, // 10 seconds
max: 10
});
pool.connect()
.then(() => console.log('pg connected'))
.catch(err => console.error(err))
module.exports = pool
Then use the pool.query like you have now with pool.connect
Also, just a side note what lib are you using for PG? Noticed your queries are dynamic, you may want to adjust those to prevent possible SQL-injection.