I'm having trouble with foreign-key in Knex.JS - javascript

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.

Related

React Native: Convert the value stored in AsyncStorage into an object

I am making a simple CRUD app using React Native with MongoDB, NodeJS and Express. When the user registers, the _id is saved inside AsyncStorage to check if the user is already authenticated or not every time he/she opens the app.
const response = await axios.post('API_LINK', {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
})
const data = await response.data
console.log(data)
await AsyncStorage.setItem('userId', data.user._id)
console.log(data) returns
Object {
"user": Object {
"__v": 0,
"_id": "62e42244b7bc9e7f9de5131b",
"email": "testing5#gmail.com",
"name": "Test 5",
"password": "$2a$10$SE7Eq46mBXGw7FYBtOtqqO49wvIMYDl0LfHknrrp.rWdrYNr6Dk8.",
"projects": Array [],
},
}
The projects [] contains all the projects created by that particular user. So, in the Project Model and Controller, the userId is also passed, so that the specific project is stored in the projects array of that specific user whose userId is passed.
ProjectModel
const projectSchema = new Schema({
title: {
~~~~~~~~~~~~~~~~~~~~~~~~~~~
},
description: {
~~~~~~~~~~~~~~~~~~~~~~~~~~~
},
user: {
type: mongoose.Types.ObjectId,
ref: 'User',
required: true
}
})
Project Controller
export const createProject = async (req, res, next) => {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let userExists
try {
userExists = await User.find(user)
} catch (err) {
return console.log(err)
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const project = new Project({
title,
description,
user
})
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
CreateProject
const userId = await AsyncStorage.getItem('userId')
const response = await axios.post('http://IP_ADDRESS:8000/api/project/create', {
title: title.trim(),
description: description.trim(),
image: image.trim(),
user: userId
})
But on clicking Create button, this error is shown
ObjectParameterError: Parameter "filter" to find() must be an object, got 62e42244b7bc9e7f9de5131b, linking to the createProject inside Project Controller.
Then, I saw that _id stored as userId is string, so I tried to convert it to object using JSON.parse().
const userId = await AsyncStorage.getItem('userId')
const userId2 = JSON.parse(userId)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
user: userId2
But, now I get this error JSON Parse error: Unable to parse JSON string.
So, how to convert the userId stored in AsyncStorage into object? And, if there are any other better methods to perform this task, please suggest.

Dynamically change database with Knex queries

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('*')
}

Prisma throws an error "TypeError: cannot read property findmany of undefined"

I wanted to make a chat app to practice working with graphql and node, for database I used prisma. I was doing everything like in this tutorial.
https://www.howtographql.com/graphql-js/0-introduction/
I just changed variable names.
so I have this code
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
const resolvers = {
Query: {
history: async (parent, args, context) => {
return context.prisma.Messages.findMany()
},
},
Mutation: {
post: (parent, args, context) => {
const newMessage = context.prisma.Messages.create({
data: {
username: args.username,
message: args.message,
},
})
return newMessage
},
},
}
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
context: {
prisma,
}
})
server.start(() => console.log(`Server is running on http://localhost:4000`))
as my index.js
this is my schema.prisma
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model Message {
id Int #id #default(autoincrement())
sendedAt DateTime #default(now())
message String
username String
}
script.js
const { PrismaClient } = require("#prisma/client")
const prisma = new PrismaClient()
async function main() {
const newMessage = await prisma.Messages.create({
data: {
message: 'Fullstack tutorial for GraphQL',
username: 'www.howtographql.com',
},
})
const allMessages = await prisma.Messages.findMany()
console.log(allMessages)
}
main()
.catch(e => {
throw e
})
// 5
.finally(async () => {
await prisma.disconnect()
})
and schema.graphql
type Query {
history: [Message!]!
}
type Mutation {
post(username: String!, message: String!): Message!
}
type Message {
id: ID!
message: String!
username: String!
}
and that is what i got in my playground
"data": null,
"errors": [
{
"message": "Cannot read property 'findMany' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"history"
]
}
]
}
please help
I managed to fix that. Actually, all I needed was to use the same name but lowercased as in schema.prisma
It should be noted it is not actually lowercase but camel case.
Example:
Model name: Message -> Prisma client name: message
Model name: MessagePerUser -> Prisma client name: messagePerUser
Would like to add on to the answer
Basically when calling await prisma.User.create, prisma allows the User model to be in caps, but when calling other models such as prisma.messages.create the m has to be in lowercase. Essentially,for all prisma calls, the models should be in lowercase. Hopefully this answers your question, prisma does not flag this error out
use Table name as camelCase in prisma Client to make Queries
Example:
Table Name is: StudentData
then use camelCase according to table name
prisma.studentData.findMany();

What should I do to make TypeORM, Type GraphQL, Apollo Server, and Azure Functions compatible

I have the following Azure Function written in typescript
import { createConnection, getConnectionManager } from 'typeorm';
import { ApolloServer, gql } from 'apollo-server-azure-functions';
import { buildSchemaSync, buildSchema } from 'type-graphql';
import { GraphQLSchema } from 'graphql';
import { AzureFunction, Context, HttpRequest } from '#azure/functions';
import 'reflect-metadata';
import ProjectResolver from './data-layer/project/project.resolver';
import TaskResolver from './data-layer/task/task.resolver';
import { Project } from './models/entity/project/project.model';
import { Task } from './models/entity/task/task.model';
const typeDefs = gql`
type Project {
projectId: ID!
name: String
projectHandler: String
documentFolderId: Int
tasks: [Task]
}
type Task {
taskId: ID!
title: String
primarySearchEntityId: String
project: Project
}
type Query {
projects: [Project]
tasks: [Task]
}
`;
let ready = false;
// first promise
let schema: GraphQLSchema;
buildSchema({
resolvers: [
ProjectResolver,
TaskResolver
]
}).then(success => {
schema = success;
ready = true;
}).catch(() => {
throw "Something failed"
});
while(!ready) {
}
ready = false;
//second promise
createConnection({
type: "mssql",
host: "xxx",
port: xxxx,
username: "xxxx",
password: "xxxx",
database: "xxxx",
entities: [
Project,
Task
],
extra: {
options: {
encrypt: true
},
},
synchronize: false,
logging: false
}).then(() => {
ready = true;
})
.catch(() => {
throw "Something failed"
});
while(!ready) {
}
const server = new ApolloServer({ typeDefs, schema, debug: true });
export default server.createHandler();
My Apollo Server in this case needs the schema to exist to be exported. The schema can't exist until the promise in "buildSchema" resolves, and the resolvers won't work until the database connection is established in "createConnection". Here is my struggle, async-await would fix this, but I can't await the promises because they are in a top-level module. I tried the while loop, but that is apparently a locking operation, so the promises won't resolve until the while loop is finished executing (sort of a catch 22 here). And that seems like a huge hack anyways. So is there a way at the top level, I can ensure both promises resolve BEFORE exporting my Apollo handler? There is a synchronous way to build the schema with the TypeGraphQL library I can use, but I'm a little hosed here waiting for my database connection to succeed? It seems that all of these technologies are compatible with all of the others, just not all 4 simultaneously.
I was able to get around with using an asynchronous context:
const server = new ApolloServer({
typeDefs,
resolvers,
context: async () => ({
db: await createConnection(...),
})
}));
Wrap your logic in a top-level async function.
Make await calls as needed.
Return a promise from the top-level function which resolves with ApolloServer's response.
...
let server: ApolloServer
const httpTrigger: AzureFunction = async function (context: Context) {
if (server === undefined) {
const conn = await createConnection(...)
const schema = await buildSchema(...)
server = new ApolloServer({
schema,
})
}
const apolloHandler = server.createHandler()
return new Promise((resolve, reject) => {
const originalDone = context.done
context.done = (error, result) => {
originalDone(error, result)
if (error) {
reject(error)
}
resolve(result)
}
apolloHandler(context, context.req!)
})
}
export default httpTrigger
Here's a starter repo with a full example of using Apollo Server, TypeGraphQL, TypeORM and Azure together: azure-function-graphql-typescript-starter (I'm the author)

Unexpected behavior with knex's select

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',
);

Categories