mongoose query return array instead of object - express

I faced a strange problem with the mongoose query. when I do db.collection.find() it should be return a object as expected. And I got so in mongo shell
When I do a similar query in my express router endpoint I got array instead of an object. Like
[
{
"dishes": [
"5eca615117611c0480320c12",
"5eca615117611c0480320c15"
],
"_id": "5ecae7eb2e746b312cfdf59e",
"user": "5ec644d06715633270d0414d",
...
}
]
which causes error in my frontend react application. Here is my schema in favorite model:
var favoriteSchema = new Schema(
{
dishes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Dish',
unique: true,
},
],
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
},
{
timestamps: true,
}
);
And here is my express endpoint:
.get((req, res, next) => {
Favorites.find({})
.then(
(favorite) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(favorite);
console.log(favorite);
},
(err) => next(err)
)
.catch((err) => next(err));
})
I heartily thank if anyone helps me to figure out this.

You might want to use findOne with mongoose, if you are looking for a single result or null. If you use find you expect more than one row as result.
Bare in mind you should handle the case where "favourite" is null (when you can't find the row you are looking for). At that point you might want to return a different response.

.find({parameter}) returns all the objects from database by the given parameter.

Related

Keystone.js 6 access denied adminMeta

i want to seed data onConnect, but i have access denied, using this query :
{
keystone: keystone {
adminMeta {
lists {
key
description
label
singular
plural
path
fields {
path
}
}
}
}
i have this error even iam using sudo, context.sudo().graphql.raw :
[
Error: Access denied
at /Users/sidalitemkit/work/web/yet/wirxe/wirxe-app/node_modules/#keystone-next/admin-ui/system/dist/admin-ui.cjs.dev.js:552:19
at processTicksAndRejections (node:internal/process/task_queues:94:5)
at async Promise.all (index 0)
at async Promise.all (index 0) {
locations: [ [Object] ],
path: [ 'keystone', 'adminMeta' ]
}
]
here my config :
export default auth.withAuth(
config({
db: {
adapter: 'prisma_postgresql',
url:
'postgres://admin:aj093bf7l6jdx5hm#wirxe-app-database-do-user-9126376-0.b.db.ondigitalocean.com:25061/wirxepool?schema=public&pgbouncer=true&sslmode=require',
onConnect: initialiseData,
},
ui: {
isAccessAllowed: (context) => !!context.session?.data,
},
lists,
session: withItemData(
statelessSessions({
maxAge: sessionMaxAge,
secret: sessionSecret,
}),
{ User: 'email' },
),
}),
);
i figured out that when i do :
isAccessAllowed: (context) => true
it's working
any advice here
context.sudo() disabled access control. there could be some issue with your query. isAccessAllowed: (context) => true is related to admin-ui and not to the backend implementation of graphql. This could be a bug please open a bug in the repo. They whould be able to fix it quickly.
I do not see sample initialiseData to try myself. Also the graphql is designed as such if you try to access some non existing item then it may give you access denied error even though there is not access control (all access set to true).
There is also another api which is easier in creating the initial items. You should use new list api, available as context.sudo().lists.<ListName>.createOne or createMany like this
const user = await context.sudo().lists.User.createOne({
data: {
name: 'Alice',
posts: { create: [{ title: 'My first post' }] },
},
query: 'id name posts { id title }',
});
or
const users = await context.lists.User.createOne({
data: [
{
data: {
name: 'Alice',
posts: [{ create: { title: 'Alices first post' } }],
},
},
{
data: {
name: 'Bob',
posts: [{ create: { title: 'Bobs first post' } }],
},
},
],
query: 'id name posts { id title }',
});
for more details see List Items API and Database Items API in their preview documentation.
You can find a working example in keystonejs repository (blog)
You have to await and pass context to the initialiseData() method. The onConnect hook already provides this context for you
also, you can look for an argument like '--seed-data' so it's only run once
and run the code as:
keystone --seed-data
export default auth.withAuth(
config({
db: {
adapter: 'prisma_postgresql',
url:
'postgres://admin:aj093bf7l6jdx5hm#wirxe-app-database-do-user-9126376-0.b.db.ondigitalocean.com:25061/wirxepool?schema=public&pgbouncer=true&sslmode=require',
async onConnect(context) {
if (process.argv.includes('--seed-data')) {
await initialiseData(context);
}
},
},
ui: {
isAccessAllowed: (context) => !!context.session?.data,
},
lists,
session: withItemData(
statelessSessions({
maxAge: sessionMaxAge,
secret: sessionSecret,
}),
{ User: 'email' },
),
}),
);

Validate request body separately from request as a whole

I have a question for validating a PUT request. The body of the request is an array of objects. I want the request to succeed if the body contains an array of at least length one, but I also need to do a separate validation on each object in the array and pass that back in the response. So my put body would be:
[1, 2, {id: "thirdObject"}]
The response should be 200 even though the first two items are not even objects. The request just needs to succeed if an array of length 1 is passed in the body. The response needs to be something like:
[{id: firstObject, status: 400, error: should be object}, {id: secondObject, status: 400, error: should be object}, { id: thirdObject, status: 204 }]
Currently I am validating the body as such with fluent schema:
body: S.array().items(myObjectSchema)
.minItems(1)
Which will result in a 400 if any of the items in the body don’t match the myObjectSchema. Was wondering if you have any idea how to achieve this?
The validation doesn't tell you if a schema is successful (eg { id: thirdObject, status: 204 }), so you need to manage it by yourself.
To do that, you need to create an error handler to read the validation error and merge with the request body:
const fastify = require('fastify')()
const S = require('fluent-schema')
fastify.put('/', {
handler: () => { /** this will never executed if the schema validation fail */ },
schema: {
body: S.array().items(S.object()).minItems(1)
}
})
const errorHandler = (error, request, reply) => {
const { validation, validationContext } = error
// check if we have a validation error
if (validation) {
// here the validation error
console.log(validation)
// here the body
console.log(request.body)
reply.send(validation)
} else {
reply.send(error)
}
}
fastify.setErrorHandler(errorHandler)
fastify.inject({
method: 'PUT',
url: '/',
payload: [1, 2, { id: 'thirdObject' }]
}, (_, res) => {
console.log(res.json())
})
This will log:
[
{
keyword: 'type',
dataPath: '[0]',
schemaPath: '#/items/type',
params: { type: 'object' },
message: 'should be object'
},
{
keyword: 'type',
dataPath: '[1]',
schemaPath: '#/items/type',
params: { type: 'object' },
message: 'should be object'
}
]
[ 1, 2, { id: 'thirdObject' } ]
As you can see, thanks to validation[].dataPath you are able to understand which elements of the body array is not valid and merge the data to return your info.
Consider that the handler will be not executed in this scenario. If you need to execute it regardless the validation, you should do the validation job in a preHandler hook and avoid the default schema validation checks (since it is blocking)
edit
const fastify = require('fastify')()
const S = require('fluent-schema')
let bodyValidator
fastify.decorateRequest('hasError', function () {
if (!bodyValidator) {
bodyValidator = fastify.schemaCompiler(S.array().items(S.object()).minItems(1).valueOf())
}
const valid = bodyValidator(this.body)
if (!valid) {
return bodyValidator.errors
}
return true
})
fastify.addHook('preHandler', (request, reply, done) => {
const errors = request.hasError()
if (errors) {
console.log(errors)
// show the same errors as before
// you can merge here or set request.errors = errors to let the handler read them
reply.send('here merge errors and request.body')
return
}
done() // needed to continue if you don't reply.send
})
fastify.put('/', { schema: { body: S.array() } }, (req, reply) => {
console.log('handler')
reply.send('handler')
})
fastify.inject({
method: 'PUT',
url: '/',
payload: [1, 2, { id: 'thirdObject' }]
}, (_, res) => {
console.log(res.json())
})
I don't know the schema syntax you are using, but using draft 7 of the JSON Schema (https://json-schema.org/specification-links.html, and see also https://json-schema.org/understanding-json-schema for some reference material), you can do:
{
"type": "array",
"minItems": 1
}
If you want to ensure that at least one, but not necessarily all items match your object type, then add the "contains" keyword:
{
...,
"contains": ... reference to your object schema here
}

Database error handling with Sequelize when column doesn't exist?

I'm trying to handle different errors that might show up when inserting into a MYSQL database.
Using Sequelize with express.
My foo.js model file looks like this:
module.exports = (sequelize, type) => {
return sequelize.define('event', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: type.STRING,
}
},{
freezeTableName: true,
rejectOnEmpty: true,
})
}
and my route file (or whatever you wanna call it), looks like this.
const Sequelize = require('sequelize')
const fooModel = require('../../models/Foo')
const router = require('express').Router();
const auth = require('../auth');
const bodyParser = require('body-parser');
const sequelize = new Sequelize('username', 'password', 'db', {
host: 'localhost',
dialect: 'mysql'
})
const Foo = fooModel(sequelize, Sequelize);
router.use(bodyParser.json({limit: '100mb'}));
router.use(bodyParser.urlencoded({ extended: true, limit: '100mb', parameterLimit: 1000000 }));
sequelize.sync({force: true})
.then(() => {
console.log('Worked');
});
router.post('/', (req,res,next) => {
if(Object.keys(req.body).length > 0){
return Foo.create({
Name: req.body.Name
}).then((result) => {
if(result){
return res.status(200).json(result);
}else{
return res.status(400).json({'error': 'Could not create record.'});
}
}).catch(Sequelize.DatabaseError, function(err){
return res.status(400).json(err);
}).catch(function(err){
res.send(err);
})
}else{
return res.status(400).json({'error': 'error'});
}
});
module.exports = router;
Whenever I try to post to the route with something like:
{
"name": "test",
"foo": "bar"
}
Sequelize accepts the body and puts "test" in the ”name” column, and ignores the "foo" column, because the "foo" column does not exist. Meaning, all I get back once it's posted is:
{"id": "123",
createdAt: 2020-01-23 13:337:00
updatedAt: 2020-01-23 13:337:00
}
And not an error as I expect.
What Im trying to do, is catch that error (that I today ain't recieving) whenever I try to post to a column that doens't exist, basically replicate a normal MYSQL error behaviour.
Could someone point me in the right direction on what I'm missing?
In my experience, it would be better to avoid this particular problem by validating the fields on the client side.
But, you can trap such a condition in js. You'll not get a DB exception because Sequelize isn't sending your unrecognized attributes to the database.
if (!Foo.attributes.hasOwnProperty('foo')) {
// some error handing here, for invalid field.
}
You could write a utility function to iterate through the attributes of req.body and send an appropriate error to the response.
FWIW, you'll find that Name is also invalid, because your model specifies(lower case) name
hth

Prevent graphql query in vue-apollo if input variable is null

I've been doing some reading and have come up with a query setup for a contact input field. I would like to avoid running this query at component startup with null input. I could manually run the queries through computed methods maybe, but is there a simple way to prevent this?
apollo: {
search: {
query: () => contactSearchGQL,
variables() {
return {
searchText: this.searchText,
};
},
debounce: 300,
update(data) {
console.log("received data: " + JSON.stringify(data));
},
result({ data, loading, networkStatus }) {
console.log("We got some result!")
},
error(error) {
console.error('We\'ve got an error!', error)
},
prefetch() {
console.log("contact search, in prefetch");
if ( this.searchText == null ) return false;
return true;
},
},
},
I think I'm not understanding something about prefetch, or if it's even applicable here?
You should utilize the skip option for that, as shown in the docs:
apollo: {
search: {
query: () => contactSearchGQL,
variables() {
return {
searchText: this.searchText,
};
},
skip() {
return !this.searchText;
},
...
},
},
Anytime searchText updates, skip will reevaluate -- if it evaluates to false, the query will be ran. You can also set the skip property directly if you need to control this logic elsewhere in your component:
this.$apollo.queries.search.skip = true
The prefetch option is specific to SSR. By default, vue-apollo will prefetch all queries in server-side rendered components. Setting prefetch to false disables this functionality for a specific query, which means that particular query won't run until the component is rendered on the client. It does not mean the query is skipped. See here for more details about SSR in vue-apollo.
Apollo Client
const { loading, error, data } = useQuery(GET_SOME_DATA_QUERY, {
variables: { param1 },
skip: param1 === '',
})
This query will be fired only when param1 is not empty.

adding a query to an axios get request

I am trying to restrict what gets returned by my axios get request.
I have a firebase backend and in my data i have a field called case_name, I have added a few records with the name Test and thought i could run the following in a get request to restrict my results to just those where case_name is equal to test, but it still returns all records
loadCase ({ commit, context }) {
return axios.get('http', {
params: {
case_name: 'Test'
}
})
.then(res => {
const convertcase = []
for (const key in res.data) {
convertcase.push({ ...res.data[key], id: key })
}
commit('listcase', convertcase)
})
.catch(e => context.error(e));
},
Can anyone tell me what im doing wrong please as cant find anything to help me at the moment
my returned object is
data: {…}
​​
"-LFXvk9yY5c-O8yIdf8k": Object { case_name: "Test", case_status: "live", case_summary: "This is some summary content", … }
​​
"-LFXwmv6eHqZs8jndNay": Object { case_name: "case 2", case_status: "live", case_summary: "dasdasdasdasd\nasd\ndasd\na\nsdasdasd", … }
​​
"-LFc2t9V7LVqnLAlIjoU": Object { case_name: "Test", case_status: "live", case_summary: "this is just another summary", … }
Thanks