Error opening database in expo - sqlite (react native) - javascript

I wanted to know if you could help me with the following error:
I'm trying to remove the EXPO SQLite base in React-Native and then import another base that I have in my folder.
When I do, it throws me the following error:
Error: no such table: users (code 1 SQLITE_ERROR): , while compiling:
this is my code:
import React, { useState, useEffect } from 'react';
const SQLite = require('expo-sqlite')
import * as FileSystem from 'expo-file-system';
import { Asset } from "expo-asset";
var db = null;
export default class SQLiteScreen extends React.Component {
constructor() {
super();
}
/*async openDatabase() {
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + 'SQLite')).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + 'SQLite');
}
await FileSystem.downloadAsync(
Asset.fromModule(require('./MainDB.db')).uri,
FileSystem.documentDirectory + 'SQLite/MainDB.db'
);
db = await SQLite.openDatabase(`MainDB.db`);
}
*/
async openDatabaseIShipWithApp() {
if (!(await FileSystem.getInfoAsync(`${FileSystem.documentDirectory}SQLite/MainDB.sqlite`)).exists) {
await FileSystem.makeDirectoryAsync(`${FileSystem.documentDirectory}SQLite/`, { intermediates: true });
const asset = await Asset.fromModule(require("./MainDB.db"));
await FileSystem.downloadAsync(asset.uri, `${FileSystem.documentDirectory}SQLite/MainDB.sqlite`);
}
db = await SQLite.openDatabase(`MainDB.sqlite`);
}
async deleteDatabaseWithApp(){
const internalDbName = "MainDB.sqlite"; // Call whatever you want
const sqlDir = FileSystem.documentDirectory + "SQLite/";
if ((await FileSystem.getInfoAsync(sqlDir + internalDbName)).exists){
await FileSystem.deleteAsync(sqlDir+internalDbName,
{idempotent:true})
}
}
ExecuteQuery = (sql, params = []) => new Promise(async (resolve, reject) => {
await this.openDatabaseIShipWithApp();
db.exec([{ sql: 'PRAGMA foreign_keys = ON ; ', args: [] }], false, () => { });
db.transaction((trans) => {
trans.executeSql(sql, params, (trans, results) => {
resolve(results);
},
(_, error) => {
reject(error);
});
});
});
}
I call the function as follows:
let query = `SELECT * FROM personal`;
await new SQLiteScreen().deleteDatabaseWithApp();
let selectQuery = await new SQLiteScreen().ExecuteQuery(query);

Related

How to initialize App Data in node js and access it without being undefined in jest test?

i am initializing a node js app with crucial data for the app to work from a database in index.js.
index.ts
import {getInitialData} from 'initData.ts';
export let APP_DATA: AppData;
export const initializeAppData = async () => {
try {
APP_DATA = (await getInitialData()) as AppData;
if (process.env.NODE_ENV !== 'test') {
initializeMongoose();
startServer();
}
} catch (error) {
console.log(error);
}
};
initData.ts
let dbName: string = 'initialData';
if (process.env.NODE_ENV === 'test') {
dbName = 'testDb';
}
const uri = `${process.env.MONGODB_URI}/?maxPoolSize=20&w=majority`;
export async function getInitialData() {
const client = new MongoClient(uri);
try {
await client.connect();
const database = client.db(dbName);
const configCursor = database
.collection('config')
.find({}, { projection: { _id: 0 } });
const config = await configCursor.toArray();
const aaoCursor = database
.collection('aao')
.find({}, { projection: { _id: 0 } });
const aao = await aaoCursor.toArray();
return { config, aao };
} catch {
(err: Error) => console.log(err);
} finally {
await client.close();
}
}
I'm using this array in another file and import it there.
missionCreateHandler
import { APP_DATA } from '../index';
export const addMissionResources = (
alarmKeyword: AlarmKeyword,
newMission: MissionDocument
) => {
const alarmKeywordObject = APP_DATA?.aao.find(
(el) => Object.keys(el)[0] === alarmKeyword
);
const resourceCommand = Object.values(alarmKeywordObject!);
resourceCommand.forEach((el) => {
Object.entries(el).forEach(([key, value]) => {
for (let ii = 1; ii <= value; ii++) {
newMission.resources?.push({
initialType: key,
status: 'unarranged',
});
}
});
});
};
I'm setting up a mongodb-memory-server in globalSetup.ts for Jest and copy the relevant data to the database from json-files.
globalSetup.ts
export = async function globalSetup() {
const instance = await MongoMemoryServer.create({
instance: { dbName: 'testDb' },
});
const uri = instance.getUri();
(global as any).__MONGOINSTANCE = instance;
process.env.MONGODB_URI = uri.slice(0, uri.lastIndexOf('/'));
process.env.JWT_SECRET = 'testSECRET';
const client = new MongoClient(
`${process.env.MONGODB_URI}/?maxPoolSize=20&w=majority`
);
try {
await client.connect();
const database = client.db('testDb');
database.createCollection('aao');
//#ts-ignore
await database.collection('aao').insertMany(aao['default']);
} catch (error) {
console.log(error);
} finally {
await client.close();
}
};
missionCreateHandler.test.ts
test('it adds the correct mission resources to the array', async () => {
const newMission = await Mission.create({
address: {
street: 'test',
houseNr: 23,
},
alarmKeyword: 'R1',
});
const expected = {
initialType: 'rtw',
status: 'unarranged',
};
addMissionResources('R1', newMission);
expect(newMission.resources[0].initialType).toEqual(expected.initialType);
expect(newMission.resources[0].status).toEqual(expected.status);
});
When runing the test, i get an 'TypeError: Cannot convert undefined or null to object at Function.values ()'. So it seems that the APP_DATA object is not set. I checked that the mongodb-memory-server is set up correctly and feed with the needed data.
When i hardcode the content of APP_DATA in index.ts, the test runs without problems.
So my questions are: How is the best practice to set up initial data in a node js app and where to store it (global object, simple variable and import it in the files where needed)? How can the test successfully run, or is my code just untestable?
Thank you!

I am making a project to add users in MongoDB but I am getting ERR_HTTP_HEADERS_SENT

import axios from 'axios';
// const usersUrl = 'http://localhost:3003/users';
const usersUrl = 'http://localhost:8020/users';
export const getUsers = async (id) => {
id = id || '';
return await axios.get(`${usersUrl}/${id}`);
}
export const addUser = async (user) => {
return await axios.post(`${usersUrl}/add`, user);
}
export const deleteUser = async (id) => {
return await axios.delete(`${usersUrl}/${id}`);
}
export const editUser = async (id, user) => {
return await axios.put(`${usersUrl}/${id}`, user)
}
This my client code when I try to add user it adds the user and details in back end in mongo db but cant view it in the front end when I click on the specific user.
import React, { useState, useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import axios from "axios";
const usersUrl = 'http://localhost:8020/users';
const View = () => {
const [user, setUser] = useState({
projectname: '',
projecttype: '',
numberofissuesreported: '',
retestlead: '',
progress: '',
startdate: '',
enddate: '',
});
const { id } = useParams();
useEffect(() => {
loadUser();
//getUsers();
}, []);
const loadUser = async () => {
const res = await axios.get(`http://localhost:8020/users/add`);
setUser(res.data);
};
And this is my view.jsx file.
As the error mentions clearly, if you use response.send, it sends the data to the client. You cannot modify the states once they are sent.
Corrected Code:
export const addUser = async (request, response) => {
// retreive the info of user from frontend
const user = request.body;
// Removed the line
const newUser = new User(user);
try{
await newUser.save();
response.status(201).json(newUser);
} catch (error){
return response.status(404).json({ message: error.message});
}
}
Tip: If you are debugging, just use console.log instead of response.send and see those values on the terminal.
Example:
export const addUser = async (request, response) => {
// retreive the info of user from frontend
const user = request.body;
console.log("Executing..");
console.log(user);
const newUser = new User(user);
try{
await newUser.save();
response.status(201).json(newUser);
} catch (error){
return response.status(404).json({ message: error.message});
}
}

ERROR: Module not found: Can't resolve 'node:fs' in 'D:\Codes\ToDo\todo\node_modules\write-json-file'

I am facing with this error for days: tried anything written on the internet, but still persisting the error-->
./node_modules/write-json-file/index.js
Module not found: Can't resolve 'node:fs' in 'D:\Codes\ToDo\todo\node_modules\write-json-file'.
Here is my write-json-file/index.js:
import path from 'node:path';
import fs, {promises as fsPromises} from 'node:fs';
import writeFileAtomic from 'write-file-atomic';
import sortKeys from 'sort-keys';
import detectIndent from 'detect-indent';
import isPlainObj from 'is-plain-obj';
const init = (function_, filePath, data, options) => {
if (!filePath) {
throw new TypeError('Expected a filepath');
}
if (data === undefined) {
throw new TypeError('Expected data to stringify');
}
options = {
indent: '\t',
sortKeys: false,
...options,
};
if (options.sortKeys && isPlainObj(data)) {
data = sortKeys(data, {
deep: true,
compare: typeof options.sortKeys === 'function' ? options.sortKeys : undefined,
});
}
return function_(filePath, data, options);
};
const main = async (filePath, data, options) => {
let {indent} = options;
let trailingNewline = '\n';
try {
const file = await fsPromises.readFile(filePath, 'utf8');
if (!file.endsWith('\n')) {
trailingNewline = '';
}
if (options.detectIndent) {
indent = detectIndent(file).indent;
}
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const json = JSON.stringify(data, options.replacer, indent);
return writeFileAtomic(filePath, `${json}${trailingNewline}`, {mode: options.mode, chown: false});
};
const mainSync = (filePath, data, options) => {
let {indent} = options;
let trailingNewline = '\n';
try {
const file = fs.readFileSync(filePath, 'utf8');
if (!file.endsWith('\n')) {
trailingNewline = '';
}
if (options.detectIndent) {
indent = detectIndent(file).indent;
}
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const json = JSON.stringify(data, options.replacer, indent);
return writeFileAtomic.sync(filePath, `${json}${trailingNewline}`, {mode: options.mode, chown: false});
};
export async function writeJsonFile(filePath, data, options) {
await fsPromises.mkdir(path.dirname(filePath), {recursive: true});
await init(main, filePath, data, options);
}
export function writeJsonFileSync(filePath, data, options) {
fs.mkdirSync(path.dirname(filePath), {recursive: true});
init(mainSync, filePath, data, options);
}
And here is the code where I want to use writeJsonFile, because I want to read from a json file and also write to the same file. The reading part is working, but the problem is at writing to file... The app is a simple Todo App in React.
import React, { useState } from 'react';
import TodoForm from './TodoForm';
import Todo from './Todo';
import data from './data/data.json';
import {writeJsonFile} from 'write-json-file';
function TodoList() {
const [todos, setTodos] = useState(data); // <-- initial state
const fs = require('fs');
const addTodo = (todo) => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
setTodos((todos) => [todo, ...todos]);
writeJsonFile('data.json', {id: todo.id, text: todo.text});
};
const updateTodo = (id, newTodo) => {
if (!newTodo.text || /^\s*$/.test(newTodo.text)) {
return;
}
setTodos((todos) => todos.map((todo) => (todo.id === id ? newTodo : todo)));
};
const removeTodo = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
const completeTodo = (id) => {
setTodos((todos) =>
todos.map((todo) =>
todo.id === id
? {
...todo,
isComplete: !todo.isComplete
}
: todo
)
);
};
return (
<>
<TodoForm onSubmit={addTodo} />
<Todo
todos={todos}
completeTodo={completeTodo}
removeTodo={removeTodo}
updateTodo={updateTodo}
/>
</>
);
}
export default TodoList;
It seems clear your issue comes from:
import fs, {promises as fsPromises} from 'node:fs';
But you are experiencing no issue in your second file with:
const fs = require('fs');
Why don't you use the same line of code for your first file?

React Native - Invariant Violation: Objects are not valid as a React Child (Using Node JS for backend)

here I'm trying to have the sum of orders and the sum of their quantity in which I use Node JS for my backend. The problem is whenever I run my code -- my fetch functions seems not working properly or I'm missing something that I'm not aware.
But using postman, my API is working with the expected output. Buuut if I use it in my react-native code it show some errors.
Here's my code for backend:
OrderNo.js (models) //Backend
var Task = {
Sum:function(id,callback) {
return db.query("SELECT SUM(order_amount) AS TotalAmountOrdered FROM orders where order_no=?",[id],callback);
},
}
OrderNo.js (router) //Backend
var Task = require('../models/OrderNo');
router.get('/ForSum/:id?', (req, res, next) => {
Task.Sum(req.params.id,function(err,rows) {
if(err) {
res.json(err);
}
else {
res.json(rows);
}
});
});
NumOrder.js (models) //Backend
var Task = {
NumOrder:function(id,callback) {
return db.query("SELECT SUM(order_quantity) AS TotalItemsOrdered FROM orders where order_no=?",[id],callback);
},
}
NumOrder.js (router) //Backend
var Task = require('../models/NumOrder');
router.get('/num/:id?', (req, res, next) => {
Task.NumOrder(req.params.id,function(err,rows) {
if(err) {
res.json(err);
}
else {
res.json(rows);
}
});
});
And here's my code for React-Native
export default class Settlement extends Component {
constructor(props){
super(props)
this.state = {
orderDet: this.props.navigation.state.params.orderDet,
numOrder: [],
TotalSum: [],
};
}
fetchSum = async () => {
const response = await fetch("http://192.168.254.104:3308/OrderNo/ForSum/" + this.state.orderDet)
const json = await response.json()
this.setState({ TotalSum: json })
}
fetchNumOrders = async () => {
const response = await fetch("http://192.168.254.104:3308/NumOrder/num/" + this.state.orderDet )
const json = await response.json()
this.setState({ numOrder: json })
}
componentDidMount() {
this.fetchNumOrders();
this.fetchSum();
}
render() {
return (
<View>
<Text>Number of Orders: { this.state.numOrder }</Text>
<Text>Total Amount: ₱{ this.state.TotalSum }</Text>
</View>
)
}
}
And here is my DB
**PS: **I also tried " json[0].order_no " on each of my fetch function and there's no error, but my output is empty.
Based on your response object in the Postman, you need to do the following
this.state = {
orderDet: this.props.navigation.state.params.orderDet,
numOrder: null,
TotalSum: null,
};
fetchSum = async () => {
const response = await fetch("http://192.168.254.104:3308/OrderNo/ForSum/" + this.state.orderDet)
const json = await response.json()
this.setState({ TotalSum: json[0].TotalAmountOrdered })
}
fetchNumOrders = async () => {
const response = await fetch("http://192.168.254.104:3308/NumOrder/num/" + this.state.orderDet )
const json = await response.json()
this.setState({ numOrder: json[0].TotalItemsOrdered })
}
The error means that you cannot have object or array as the child the component i.e. <Text>. You can only have string or number displayed inside the component.
<Text>Number of Orders: { this.state.numOrder[0].TotalAmountOrdered }</Text>// Inside {} value of variable should be string or number not array or object
The error is that you are setting value of this.state.numOrder an array

How to async await until I receive from database?

I am trying to make stores which can be accessed by Screens. File structure:
index.ios.js
/app/index.js
/app/store/database.js
/app/store/userStore.js
index.ios.js :
import { AppRegistry } from 'react-native';
import App from './app/index';
AppRegistry.registerComponent('AwesomeProject', () => App);
/app/index.js :
import React, { Component} from 'react';
import {
Text,
View,
} from 'react-native';
import userStore from './store/userStore';
import ViewBackground from './components/ViewBackground';
class App extends Component {
constructor(props){
super(props);
this.isLoggedIn = true;
}
componentDidMount() {
this.fetchUsers().done();
}
async fetchUsers(){
console.log('Before User Fetch');
var result = await userStore.getAllUsers();
console.log('After User Fetch');
}
render() {
if(this.isLoggedIn){
return this.loggedInView();
}else{
return this.loggedOutView();
}
}
loggedInView(){
return <ViewBackground>
</ViewBackground>
}
loggedOutView(){
return <View>
<Text>Hi, This is logged Out view </Text>
</View>
}
}
export default App;
/app/store/userStore.js :
import React from 'react';
import database from './database';
class UserStore{
async getAllUsers(){
let query = {
type: 'SELECT',
sql: 'SELECT * FROM Users',
vals: [],
}
let queries = [query];
console.log('in store before');
let dbResult = await database.runQuery(queries);
console.log('in store after');
return dbResult;
}
}
const userStore = new UserStore;
export default userStore;
/app/store/database.js :
'use strict';
import React from 'react';
import SQLite from 'react-native-sqlite-storage';
SQLite.enablePromise(true);
var database_name = "test.db";
var database_version = "1.0";
var database_displayname = "Test";
var database_size = 1024 * 1024 * 10;
class Database {
constructor(){
SQLite.openDatabase(database_name, database_version, database_displayname, database_size).then(this.dbOpenSuccess).catch(this.dbOpenError);
}
dbOpenSuccess(dbInstance){
this.conn = dbInstance;
}
dbOpenError(err) {
}
getConnection() {
return this.conn;
}
setUpDatabase(){
let queries = [
'CREATE TABLE IF NOT EXISTS Users (user_id INTEGER PRIMARY KEY, f_name VARCHAR(64), l_name VARCHAR(64), email_id VARCHAR(128), mobile VARCHAR(10))'
];
this.conn.transaction( tx => {
queries.map( q => {
tx.executeSql(q, [], res => {}, err => {});
});
});
}
async runQuery(queries) {
await this.conn.transaction(tx => {
return Promise.all(queries.map(async (q) => {
try {
let results = null;
switch(q.type){
case 'SELECT':
results = await tx.executeSql(q.sql, q.vals);
console.log('Query', q, 'Executed. results:', results);
break;
}
} catch(err) {
console.log('Something went wrong while executing query', q, 'error is', err);
}
}));
});
return false;
}
closeDatabase(){
this.conn.close().then(this.dbCloseSuccess).catch(this.dbCloseError);
}
dbCloseSuccess(res) {
}
dbCloseError(err){
}
}
const dbInstance = new Database();
dbInstance.setUpDatabase();
export default dbInstance;
In /app/index.js I am trying to fetch users from userStore.js which will call database.js to retrieve the users from database. I want to pause execution when I fetch users from database, so tried async/await. I used console.log() to check whether execution paused. None of async/await is working. I am getting response after all console.log is done.
I want it to wait until I receive data from database.
You need to wrap it in a Promise, then resolve the result when sql execution is done.
for example
async () => {
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql('SELECT * FROM users;', [], (tx, results) => {
const { rows } = results;
let users = [];
for (let i = 0; i < rows.length; i++) {
users.push({
...rows.item(i),
});
}
resolve(users);
});
});
});
}
let SQLite = require('react-native-sqlite-storage');
const DB = SQLite.openDatabase({name:'test.db',createFromLocation:'~sqlitedb.db'});
class Database{
db;
constructor(db){
this.db =db;
}
executeSql = (sql,params=[])=>new Promise((resolve , reject)=>{
this.db.transaction((trans)=>{
trans.executeSql(sql,params,(db,results)=>{
resolve(results);
},
(error)=>{
reject(error);
});
});
});
}
export default (new Database(DB));
use
import Database from 'database';
try
{
results = await DB.excuteSql("SQL",['params']);
}
catch(e)
{
}
The following worked for me:
import { openDatabase } from 'react-native-sqlite-storage'
const db = openDatabase({
name: 'app.db',
location: 'default',
})
export default function App() {
useEffect(() => {
db.then((db) => {
db.transaction((tx) => {
tx.executeSql('SELECT this from that', [])
})
})
/* Rest of the component... */
}
openDatabase returns a Promise, on resolving which we get the actual db on which we can run db.transaction().
You could use the async/await syntax too, but that would be a lot of encapsulation, and so .then() is always cleaner.
This is different from the "expo-sqlite" package, because db = openDatabase('app.db') does not return a Promise there. Instead, you can run db.transaction() directly.
just add this lines
import {enablePromise, openDatabase } from 'react-native-sqlite-storage';
enablePromise(true)
I follow this way to do DB operations
export const fetchUsers = async () => {
return new Promise((resolve, reject) => {
db.transaction(txn => {
txn.executeSql('SELECT * FROM "Users";', [], (tx, res) => (
resolve(res.rows.raw())
))
})
})
}
rows.raw() will return the data as object
(async()=>{
data= await fetchUsers()
})()

Categories