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('*')
}
Related
I am trying to use the revalidate function. I tried to follow the code that Vercel offers, but I keep getting an error. Here is the function that I am using:
export async function getServerSideProps() {
const client = await clientPromise;
const db = client.db("myFirstDatabase");
let users = await db.collection("users").find({}).toArray();
users = JSON.parse(JSON.stringify(users));
return {
props: {
users,
},
revalidate: 15,
};
}
And here is the mongodb file that returns the client:
import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
}
let client
let clientPromise
if (!process.env.MONGODB_URI) {
throw new Error('Please add your Mongo URI to .env.local')
}
if (process.env.NODE_ENV === 'development') {
// In development mode, use a global variable so that the value
// is preserved across module reloads caused by HMR (Hot Module Replacement).
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// In production mode, it's best to not use a global variable.
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
export default clientPromise
I have been able to connect to the database and the code works fine if I remove the revalidate part. The error that I get is :
**
Error: Additional keys were returned from getServerSideProps. Properties intended for your component must be nested under the props key, e.g.:
return { props: { title: 'My Title', content: '...' } }
Keys that need to be moved: revalidate.
Read more: https://nextjs.org/docs/messages/invalid-getstaticprops-value
**
I am not sure what I am doing wrong. I want to get data from the database and update it every 15 seconds. Any help would be greatly appreciated.
revalidate is for getStaticProps, you are using it on getServerSideProps and this does not allow
I recommend you to see this library: https://swr.vercel.app/
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.
I'm following this tutorial where we're creaing an app using next.js. We're using sqlite, and testing a database. In the tutorial we write the following 'database-test.js' file:
const sqlite = require('sqlite');
async function setup() {
const db = await sqlite.open('./mydb.sqlite');
await db.migrate({force: 'last'});
const people = await db.all('SELECT * FROM person');
console.log('ALL PEOPLE', JSON.stringify(people, null, 2));
const vehicles = await db.all('SELECT * FROM vehicle');
console.log('ALL VEHICLES', JSON.stringify(vehicles, null, 2));
}
setup();
I get the following error when I $node database-test.js:
(node:26446) UnhandledPromiseRejectionWarning: Error: sqlite: filename cannot be null / undefined
I don't really understand why we are opening a .sqlite file, and not a .db file. I've made sure I have the correct path to the .sqlite file. What is the cause of this error and how might I fix it? I can't seem to find any other documentation or examples of the .open function.
As #ANimator120 was mentioned but with some tweaks.
Use require because it runs on server side.
Install sqlite3 by npm i sqlite3.
Then add path to your migrations folder if it's not in the project root.
const sqlite3 = require('sqlite3');
const sqlite = require('sqlite');
async function openDb() {
return sqlite.open({
filename: './database.db',
driver: sqlite3.Database,
});
}
async function setup() {
const db = await openDb();
await db.migrate(
{
migrationsPath: './src/migrations', //add cutom path to your migrations
force: 'last'
}
);
const people = await db.all('SELECT * FROM Person');
console.log('all person', JSON.stringify(people, null, 2));
const vehicle = await db.all(`SELECT a.*, b.* FROM Person as a
LEFT JOIN Vehicle as b
ON a.id = b.ownerId
`);
console.log('all vehicles', JSON.stringify(vehicle, null, 2));
}
setup();
Everything working fine, at least for me.
You need to npm install both sqlite and sqlite3
The correct way to use the open() method would be.
const sqlite = require('sqlite');
const sqlite3 = require('sqlite3')
const {open} = require('sqlite')
async function openDB (){
return open({
filename : './mydb.sqlite',
driver: sqlite3.Database
})
}
async function setup(){
const db = await openDB();
await db.migrate({force : 'last'});
}
setup();
Turns out they were using sqlite 3.0.3 in the tutorial. With sqlite3, the correct way to use open() in this case is:
import { open } from 'sqlite'
import sqlite3 from 'sqlite3'
// you would have to import / invoke this in another file
export async function openDB () {
return open({
filename: './mydb.sqlite',
driver: sqlite3.Database
})
}
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',
);
I am using graphql-express to create an endpoint where I can execute graphql queries in. Although I am using Sequelize with a SQL database it feels wrong to use it directly from the server outside of my graphql resolve functions. How do I go about querying my graphql API from the same server as it was defined in?
This is how I set up my graphql endpoint:
const express = require('express');
const router = express.Router();
const graphqlHTTP = require('express-graphql');
const gqlOptions = {
schema: require('./schema')
};
router.use('/', graphqlHTTP(gqlOptions));
modules.exports = router;
Basically what I want is to be able to do something like this:
query(`
{
user(id: ${id}) {
name
}
}
`)
How would I create this query function?
GraphQL.js itself does not require a http server to run. express-graphql is just a helper to mount the query resolver to a http endpoint.
You can pass your schema and the query to graphql, it'll return a Promise that'll resolve the query to the data.
graphql(schema, query).then(result => {
console.log(result);
});
So:
const {graphql} = require('graphql');
const schema = require('./schema');
function query (str) {
return graphql(schema, str);
}
query(`
{
user(id: ${id}) {
name
}
}
`).then(data => {
console.log(data);
})
I would like to complete the answer from #aᴍɪʀ by providing the pattern for properly doing a query / mutation with parameters:
const params = {
username: 'john',
password: 'hello, world!',
userData: {
...
}
}
query(`mutation createUser(
$username: String!,
$password: String!,
$userData: UserInput) {
createUserWithPassword(
username: $username,
password: $password,
userData: $userData) {
id
name {
familyName
givenName
}
}
}`, params)
This way, you don't have to deal with the string construction bits " or ' here and there.
Thanks for the other answers, this is for Nextjs inside getServerSideProps, getStaticProps, getStaticPaths and getStaticProps, includes context for MongoDB. Need this because if you have your graphql sever in api route, when you build it wont build because your server in api route is not running.
Mongo file: plugin/zDb/index:
import {MongoClient} from "mongodb"
export const connectToDatabase = async() => {
const client = new MongoClient(process.env.MONGODB_URI, {useNewUrlParser: true, useUnifiedTopology: true})
let cachedConnection
if(cachedConnection) return cachedConnection
try {
const connection = await client.connect()
cachedConnection = connection
return connection
} catch(error) {
console.error(error)
}
}
export const mongoServer = async() => {
const connect = await connectToDatabase()
return connect.db(process.env.DB_NAME)
}
In pages folder, eg index.js file homepage:
import {graphql} from 'graphql'
import {schema} from '#/plugin/zSchema/schema'
import {mongoServer} from '#/plugin/zDb/index'
async function query(source, variableValues) {
return graphql({schema, source, contextValue: {mongo: await mongoServer()}, variableValues})
}
export async function getServerSideProps(ctx) {
const listingCurrent = await query(`query($keyField: String, $keyValue: String) {
ListingRQlistingListKeyValue(keyField: $keyField, keyValue: $keyValue) {
address
urlSlug
imageFeature {
photoName
}
}
}`, {
keyField: 'offerStatus'
, keyValue: 'CURRENT'
})
return {props: {
listingCurrent: listingCurrent.data.ListingRQlistingListKeyValue
}
}
}
Please note: the graphql call field names is from: https://github.com/graphql/graphql-js/blob/fb27b92a5f66466fd8143efc41e1d6b9da97b1f4/src/graphql.js#L62
export type GraphQLArgs = {|
schema: GraphQLSchema,
source: string | Source,
rootValue?: mixed,
contextValue?: mixed,
variableValues?: ?ObjMap<mixed>,
operationName?: ?string,
fieldResolver?: ?GraphQLFieldResolver<any, any>,
|};
And my schema file: plugin/zSchema/schema.js
import { makeExecutableSchema } from '#graphql-tools/schema'
import {resolvers} from '#/plugin/zSchema/resolvers'
import {typeDefs} from '#/plugin/zSchema/typeDefs'
export const schema = makeExecutableSchema({resolvers, typeDefs})
The #/plugin folder: I'm using this in root file called jsconfig.json, and I put all my folders inside root/plugin, and I call it with #/plugin. You can use your own folder structure importing them as how you normally do it.
{
"compilerOptions": {
"baseUrl": "."
, "paths": {
"#/*": ["./*"]
}
}
}