For the following code, I get a result that is sometimes an array and other times an object. I want to receive the array, even if its empty.
export const GetByPId = async (userId, pId) => knex('table1').where({ userId, pId }).select(
'userId',
'pId',
'id',
'created',
'updated',
);
In my Business object, I await the response
static async LoadByPId(userId, pId) {
const data = await GetByPId(userId, pId);
console.log(`result ${JSON.stringify(data)}`);
}
Once it returned
[{ userId: 1, id: 1 ... - I want this
and the next time it returned
{ userId: 1, id: 1 ... - Don't want this
Whats going on and how can I get it to always return an array?
Update #1
Now it only returns a single result.
Update #2
It went from bad to worse.
Now my other basic functions don't work properly. Knex only works on the first set of parameters and fails for everything else.
For example, if the express server was restarted and send a request for userId: 1 and pId: 1, it works. If I repeat the same request with same parameters, it works. But if I change the parameters (ie userId or pId) to another valid set, it fails. I have to restart the express server before trying any other parameters. I tested this on my app and postman.
My express code looks like follows
router.post('/list', auth, async (req, res) => {
try {
const biz= await BizObj.LoadByPId(req.user.id, req.body.pId);
res.json(biz);
} catch (ex) {
console.log(ex);
res.status(400).json('Unauthorized');
}
});
Update #4
In case my knex config is the problem
development: {
client: 'postgresql',
connection: {
database: 'somedb',
user: 'SOMEUSER',
password: '',
timezone: 'UTC',
},
pool: {
min: 2,
max: 10,
},
migrations: {
tableName: 'knex_migrations',
},
},
Update #5
Single page of code (only missing express setup)
in pg db / SomeObj
id userId
1 1
2 2
3 2
code sample
import knex from 'knex';
import express from 'express';
const config = {
development: {
client: 'pg',
connection: {
database: 'somedb',
user: 'SOMEUSER',
password: '',
timezone: 'UTC',
},
pool: {
min: 2,
max: 10,
},
migrations: {
tableName: 'knex_migrations',
},
},
};
const knexed = knex(config.development);
const SQL = knexed('SomeObj');
const GetAll = async userId => SQL.where({ userId }).select(
'id',
'userId',
);
const GetById = async (userId, id) => SQL.where({ userId, id }).first(
'id',
'userId',
);
class SomeObj {
constructor(data, userId) {
this.userId = userId;
this.id = data.id;
}
static async LoadAll(userId) {
const data = await GetAll(userId);
if (!data || data.length === 0) return null;
return data.map(r => new SomeObj(r, userId));
}
static async Load(userId, id) {
const data = await GetById(userId, id);
if (!data) return null;
return new SomeObj(data, userId);
}
}
const router = express.Router();
router.post('/list', async (req, res) => {
try {
const res1 = await SomeObj.LoadAll(req.body.id); // works and returns array
const res2 = await SomeObj.Load(req.body.id, 2); // fails and returns undefined
res.json({ res1, res2 });
} catch (ex) {
res.status(401).json(ex);
}
});
No second query can be ran. Don't know if I'm missing something simple to close the connection.
Update #6
I swear knex is messing with me. Every time I try something (and revert back to confirm changes are due my new inputs), there are different responses. Now, both res1 and res2 return the correct result for the first request but the second request fails.
Update #7
Runkit example: https://runkit.com/tristargod/runkit-npm-knex
It runs for the first request but fails for all other requests on express server.
Update #8
Refer to https://github.com/tgriesser/knex/issues/2346#issuecomment-346757344 for further details. Thanks Mikael!
knex('table1')
.where({ userId, pId })
.select('userId', 'pId', 'id', 'created', 'updated')
Should return always an array of results. You are doing something else wrong that is not shown in the example.
Example code: https://runkit.com/embed/kew7v2lwpibn
RESPONSE TO UPDATE #7
tldr; Knex query builders are mutable so when re-using them .clone() is necessary. https://runkit.com/mikaelle/5a17c6d99cd063001284a20a
Nice example, from that it was easy to spot the problem đź‘Ť
You are reusing the same query builder multiple times without cloning it between the queries. If you would run your code with DEBUG=knex:* environment variable set, you would see that constructed queries are not correct after the first call.
const GetAll = async userId => SQL.clone().where({ userId }).select(
'id',
'userId',
);
const GetById = async (userId, id) => SQL.clone().where({ userId, id }).first(
'id',
'userId',
);
Related
As the title says my fetch isn't doing what I want it to. Im trying to implement stripe into my grocery store website. I'm using a node.js server with express, and ejs to integrate with the front end. My client side js uses a fetch method to send a POST request to the server with all the information from the client side. The client side pulls data from a JSON file to access the store items. Those items are to be restructured as objects in the fetch and sent to the server to initiate the stripe checkout.
However, the fetch fails at the get go with a status 500. It claims that an unidentified was passed instead of a JSON. As a result, I tried to send back a hard coded object to see where the error was occurring but it also came back as undefined. I'm stumped and need any help I can get.
I'm new at coding/programming so I'm sure there is a lot wrong with my code. Thank you for your time. My code is below.
Client side JS
let payButton = document.getElementsByClassName("pay")[0].addEventListener("click", function() {
// alert("Payment recieved");
// let totalItemsInCart = document.getElementsByClassName("shopping-cart-item-div");
//
// let x = 0;
//
// while (x < totalItemsInCart.length){
// totalItemsInCart[x].remove();
// }
// updateCartTotal();
let items = [];
let cartCollection = document.getElementsByClassName("shopping-cart-basket")[0];
let cartItems = cartCollection.getElementsByClassName("shopping-cart-items");
for (let x = 0; x < cartItems.length; x++) {
let cartItem = cartItems[x];
let cartItemQuantity = cartItem.parentElement.getElementsByClassName("shop-item-input")[0];
let quantity = cartItemQuantity.value;
let id = cartItem.parentElement.dataset.itemId;
let nameText = cartItem.innerText;
let name = nameText.replace(/per lb|per item|per bundle/g, "").replace("$", "").replace(":", "");
let cartTotal = document.getElementsByClassName("shopping-cart-number")[0].innerText;
let price = parseFloat(cartTotal.replace("$", "")) * 100;
items.push({
id: id,
quantity: quantity,
name: name,
price: price
});
}
fetch("/create-checkout-session", {
method: "POST",
header: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: 1,
quantity: 2,
name: "test",
price: 500})
}).then(function(res) {
if (res.ok) {
return res.json();
} else {
return res.json().then(function(json) {
Promise.reject(json)
});
}
}).then(({url}) => {
console.log();
window.location = url;
}).catch(function(e) {
console.error("Error: " + e.error)
});
});
Sever side JS
app.post("/create-checkout-session", async function(req, res) {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
mode: 'payment',
line_items: req.body.items.map(function(item) {
return {
price_data: {
currency: 'usd',
product_data: {
name: item.name
},
unit_amount: item.price
},
quantity: item.quantity
}
}),
success_url: `${process.env.SERVER_URL}/success.ejs`,
cancel_url: `${process.env.SERVER_URL}/cancel.ejs`
})
res.json({
url: session.url
})
res.redirect(303, session.url)
} catch (e) {
res.status(500).json({
error: e.message
})
}
});
app.get("/success", function(req, res) {
res.render('success');
});
app.get("/cancel", function(req, res) {
res.render('cancel');
});
Server Side dependencies
require("dotenv").config();
const express = require('express');
const ejs = require('ejs');
const bodyParser = require('body-parser');
const path = require('path');
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(express.static('public'));
app.set('view engine', 'ejs');
EDIT: included my entire client side code for the checkout button. I originally left it out due to the code not being called anymore as I try to resolve the undefined issue. However, I got a few comments mentioning that items was not defined so I decided to add this in for clarity.
items is an empty array that has has objects pushed into it according to what ids were left in the shopping cart.
From MDN Docs
The Promise returned from fetch() won’t reject on HTTP error
status even if the response is an HTTP 404 or 500. Instead, as soon as
the server responds with headers, the Promise will resolve normally
(with the ok property of the response set to false if the response
isn’t in the range 200–299), and it will only reject on network
failure or if anything prevented the request from completing.
So what's happening here is that you're getting an error 500, so property res.ok is set to false, then it's not going through the conditional hence it's going to this part
return res.json().then(function(json) {
Promise.reject(json)
});
but you're not returning Promise.reject(json) because you're using an anonymous function, not an arrow function so you're returning undefinded, you must explicitly return the promise like this
return res.json().then(function(json) {
return Promise.reject(json)
});
^ Same goes for every anonymous function (you must return explicitly), instead of doing that I recommend you to use arrow functions and async-await
I use node.js to build the backend and persist the data in MongoDB. When I do a patch request, I can change the values of all the fields of other types except for the one of date type.
This is the backend code for the patch request.
router.patch('/:id', isLoggedIn, async (req, res) => {
try {
const updatedBooking = await Booking.updateOne(
{_id: req.params.id},
{
$set: {userEmail: req.body.userEmail},
$set: {shiftDate: req.body.shiftDate},
$set: {isMorningShift: req.body.isMorningShift}
}
);
res.json(updatedBooking);
} catch (err) {
res.send({message: err});
}
});
This is the database scheme:
const BookingSchema=mongoose.Schema({
userEmail:{
type:String,
required:true
},
shiftDate:{
type:Date,
required:true
},
isMorningShift:{
type: Boolean,
required: true
}
});
The objects in MongoDB look like this:
{
"_id": "61787183e67b6822180175f9",
"userEmail": "admin2#parantion.nl",
"isMorningShift": false,
"__v": 0,
"shiftDate": "2066-06-23T00:00:00.000Z"
}
What might be the problem?
Instead of multiple $set, update all the keys in one,
const updatedBooking = await Booking.updateOne(
{_id: req.params.id},
{
$set: {
userEmail: req.body.userEmail,
shiftDate: new Date(req.body.shiftDate),
isMorningShift: req.body.isMorningShift
}
}
);
#fractal397's answer will work fine. If you want a more cleaner code, you can use this.
const bookingId = req.params.id;
const payload =
userEmail: req.body.userEmail,
shiftDate: new Date(req.body.shiftDate),
isMorningShift: req.body.isMorningShift
}
const booking = await Booking.findByIdAndUpdate(bookingId, payload);
P.S. - After Mongoose 4.0, new value for findByIdAndUpdate has been changed to false by default. So in this operation, data will be updated in the database but it will return the old value booking. To get updated value in response too, you will have to do -
const booking = await Booking.findByIdAndUpdate(bookingId, payload, { new : true });
Change the line:
$set: {shiftDate: req.body.shiftDate}
to
$set: {shiftDate: new Date(req.body.shiftDate)}
or
$set: {shiftDate: new Date()} //for todays date in your local format
This works:
I tested this with express like so:
app.get('/updateOne', async (req, res) => {
//get data through query params in url
const id = req.query.id;
const date = req.query.date;
//connect to db and collection
//1 connect
//2 set db and collection
const client = await MongoClient.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const collection = client.db("sample_airbnb").collection("listingsAndReviews");
//update function for date field
try {
const updatedBooking = await collection.updateOne(
{_id: id},
{
$set: {name: new Date(date)} //2066-06-23T00:00:00.000Z
}
);
res.json(updatedBooking);
} catch (err) {
res.send({'message': err});
}
})
Response:
{
"acknowledged": true,
"modifiedCount": 1,
"upsertedId": null,
"upsertedCount": 0,
"matchedCount": 1
}
And updated data in Mongoscloud:
_id
:
"100009690"
name
:
2066-06-23T00:00:00.000+00:00
The I called the endpoint like so:
http://localhost:5000/updateOne?id=100009690&date=2066-06-23T00:00:00.000Z and you see it's the same date format you say you expect.
Can you update your OP and show us the exact format you are passing in?? DO a console.log(req.body.shiftDate) on line 7 just before you pass it. I suspect here is where the issue is.
Obviously I shouldn't add dates to names field but this is purely for a quick test.
If updating multiple fields I'd with:
//update function
try {
const updatedBooking = await collection.updateOne(
{_id: id},
{
$set: {
name: name,
email: email,
lastShift: new Date(date)
}
}
);
res.json(updatedBooking);
} catch (err) {
res.send({'message': err});
}
I have an issue when I am trying to set Knex database dynamically. I have multiple database, which increments in every hour. ( e.g db-1-hour, db-2-hour). When we switched into a new hour I wan to use the next database. I created a sample function which returns a new Knex function based on the new database but I got a deprecated warning.
My config
import knex from 'knex';
const knexConfig = {
client: 'pg',
connection: {
host: host,
port: port,
user: user,
database: '',
password: password,
},
pool: {
min: 2,
max: 10,
},
timezone: 'UTC',
};
exports const getCurrentDb = async () => {
const hourDb = await getLatestDbName()
cons knexConfig.connection.database = hourDb; // I update the database name
return knex(knexConfig);
}
Usage
import { getCurrentDb } from "./config"
const getSomething = async () => {
const db = await getCurrentDb()
return db.select().from("something")
}
The code is working but I always get this waring message:
calling knex without a tableName is deprecated. Use knex.queryBuilder() instead.
How could I connect to a database dynamically? Thank you in advance!
The warning message is not related to the DB switch mechanism.
Try to change your select statement to something like:
import { getCurrentDb } from "./config"
const getSomething = async () => {
const db = await getCurrentDb()
return db("something").columns('*')
}
I'm rewriting a project of mine and was wondering how would I post an array of data where I reuse the return value of a previous post request as their ID. Here's a rough detail of the data structure
Checklist A
[ChecklistItem 1, ChecklistItem 2, ChecklistItem 3] has their ID set as Checklist A
So my current setup is I send Checklist A, get the return value from FaunaDB(which is its unique ID)
then plug it in the array using array.map then resend the array to FaunaDB.
But i don't know how to save the array since the request paramater is already used up.
so i was wondering what's the normal way to do this.
here's a code snippet of the function
app.post('/checklists', (req,res) =>{
const checklist = {
dateCreated: Date.now(),
user: Call(Fn('getUser'),'10049'),
equipmentid: 'PM160'
};
const _checklistItems = [{
componentid: 'AIRLK',
conditionid: 'OK',
equipmentid: 'PM160',
remarks: 'test'
}]
const ckdoc = client.query(
Crt('checklists',checklist))
.then((ret) => {
//would like to catch this ret and plug it into _checklistitems as its ID
//then send the _checklistitems to faunaDB
});
res.send(ckdoc);
});
function Crt(collection,data){
return Create(
Collection(collection),
{data}
)
}
UPDATE
after #eskwayrd pointed out that you can chain client queries within a single express js request. i chained another client query where i save the checklist items collection along with the return reference from a previous query. though i had problems sending the it as an Array, saving it through array.map still worked.
app.post('/checklists', async (req,res) =>{
const checklist = {
dateCreated: Date.now(),
user: Call(Fn('getUser'),'10049'),
equipmentid: 'PM160'
};
const _checklistItems = [{
componentid: 'AIRLK',
conditionid: 'OK',
equipmentid: 'PM160',
remarks: 'test'
}]
var _ref;
console.log(checklist)
await client.query(
Crt('checklists',checklist)
)
.then((ret) => {
_ref = ret.ref
})
_checklistItems.map(item => {
item.checklist = _ref
console.log(item)
client.query(
Crt('checklist_items', item)
)
})
});
Using the Fauna JavaScript driver, the client object that you create is quite reusable; it is not "used up".
Generally, you can chain dependent queries like this:
client.query( ... your FQL query ... )
.then((result) => {
const ref = result.ref
client.query( ... another FQL query involving the ref ...)
.then((result2) => {
console.log(result2)
})
})
Using async and await, you can avoid nesting with something like:
;(async () => {
const result = await client.query( ... FQL query 1 ...)
.then((res) => res)
.catch((err) => console.log(`query 1 failed: ${err}`))
const ref = result.ref
const result2 = await client.query( ... FQL query 2 ...)
.then((res) => res)
.catch((err) => console.log(`query 2 failed: ${err}`))
console.log(result2)
})()
Note that both examples are, effectively, equivalent, and also demonstrates how to extract a value from the reponse.
Im try to insert data on my database with Knex.JS, in some tables I have a foreign key, and I guess the trouble is occurring with this part of project.
I'm having this error message:
error: insert into "classes" ("cost", "subject", "user_id") values ($1, $2, DEFAULT) - null value in column "user_id" violates not-null constraint
MY knexfile:
import {resolve} from 'path'
module.exports = {
development: {
client: 'pg',
connection: {
database: "XXXX",
user: "XXXX",
password:"XXXXXXX"
},
migrations: {
directory: resolve(__dirname, 'src', 'database', 'migrations')
}
},
useNullAsDefault: true
};
and the migrations im using:
import Knex from 'knex'
export async function up(Knex: Knex) {
return await Knex.schema.createTable('users', table => {
table.increments('id').notNullable().primary();
table.string('name').notNullable();
table.string('avatar').notNullable();
table.string('whatsapp').notNullable();
table.string('bio').notNullable();
})
}
export async function down(knex: Knex) {
return await knex.schema.dropTable('users')
}
import Knex from 'knex'
export async function up(Knex: Knex) {
return await Knex.schema.createTable('classes', table => {
table.increments('id').notNullable().primary().unique();
table.string('subject').notNullable();
table.decimal('cost').notNullable();
table.integer('user_id').
notNullable().
references('id').
inTable('users').
onDelete('CASCADE').
onUpdate('CASCADE')
})
}
export async function down(knex: Knex) {
return await knex.schema.dropTable('classes')
}
I guess the trouble can occur i the moment of insert data to as some data depend of other data:
async store(req: Request, res: Response) {
const {name,
avatar,
whatsapp,
bio,
subject,
cost,
schedule
} = req.body
const trx = await knex.transaction()
try {
var created_user = await trx('users').insert({
name,
avatar,
whatsapp,
bio
})
var user_id = created_user[0]
const insertedclassesID = await trx('classes').insert({
subject,
cost,
user_id
})
I've got do this with SQLite3, but not with Postgres.
I have to change the ORM Knex to Sequelize for Exemple?
I know is a lot of questions But i'm needing help!
THANKS FOR HELP ME!
The error is telling you that you cannot insert a null value for 'user' in the table "classes".
You can fix this depending on the functionality you want.
If the users column in the classes table MAY be null, then change your migration:
table.integer('user_id').
defaultTo(null).
references('id').
inTable('users').
onDelete('CASCADE').
onUpdate('CASCADE')
else if the users column should always have data, then check if your user id is being assigned correctly
var user_id = created_user[0]
I believe that line should be replaced by:
var user_id = created_user[0].id
but can't say without running it.