React Native [iOS] Database Options - database

I am looking to create an iOS app in React Native and wondering what's the best database option to fit my app.
The app will be supported by ~300 data objects which will be fetched as json objects from a remote server. There are some variances in attributes across these 300 objects. Therefore, I am hesitant to set up an inflexible database schema.
Ideally, I would just like 1 database with 2 attributes:
1) id (for example: from 1 to 300)
2) data (for example: {"hello" : "world"})
Give the above, any suggestions on what kind of react-native database module I should use?
Thanks

According to my own experience in previous successful react-native project, you could use AsyncStorage, which is simple but yet powerful enough, you can store whatever you want!
Besides, you should also use flux or redux, which will provide you a Store solution where you can read & store the data related to AsyncStorage easily (everywhere, on every page)!
The steps are (main flow's idea on how to organise your data and structure the logics):
0/ Importing libraries:
import React, { Component } from 'react';
import {
AsyncStorage,
// ...
} from 'react-native';
1/ Get data from your API or somewhere (local files etc.), then write (save) the data to AsyncStorage:
async firstWriteFavorite() {
fetch("YOUR_API_URL", {method: "GET", headers: {'Cache-Control': 'no-cache'}})
.then((response) => response.json())
.then((responseJson) => {
try {
// make sure you write a STRING into AsyncStorage,
// and also be careful which key of the JSON should be written, the below line is just a good example:
await AsyncStorage.setItem("#PROJECT_NAME:your_favorite_array", JSON.stringify(responseJson.response));
// if you use flux or redux here, you can perform some action here, then you can load the data everywhere later:
// FavoriteActionCreators.set_favorite(responseJson.response);
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
})
.catch((error) => {
console.log("Error in first getting ajax data!", error);
}
);
}
2/ Retrieve the data from AsyncStorage:
async loadFavorite() {
try {
var fav_array_string = await AsyncStorage.getItem("#PROJECT_NAME:your_favorite_array");
// the above returned value is a STRING, then you can split it, or do whatever based on the structure you have written
var real_fav_id_array = fav_array_string.split('YOUR_SEPARATOR');
// ...
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}
3/ When you need to update the data, firstly retrieve data, then bind the data to a variable and make changes to that variable, next write the new data to AsyncStorage:
async saveFavorite() {
// loadFavorite() here,
// make sure you've got data "your_new_JSON_data" which has been converted into object, then maybe: "your_new_JSON_data.push({NEW_OBJ})";
// after that, SAVE NEW DATA now:
try {
await AsyncStorage.setItem("#PROJECT_NAME:your_favorite_array", JSON.stringify(your_new_JSON_data));
// same here, if you use flux or redux here, you can save the new data here:
// FavoriteActionCreators.set_favorite(your_new_JSON_data);
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}
The above code was copied and shortened from my real project's code, please try and tell me if you have any problem!

Related

Can URL API endpoint self-correct?

I am using fetch API inside a React application to retrieve and display some quiz questions.
This is my url endpoint: https://opentdb.com/api.php?amount=${amount}&difficulty=${difficulty}&type=multiple
I have noticed that:
-when I misspell part of the URL before "?" then the response doesn't get back.
example:https://opentdb.com/api.ph?amount=${amount}&difficulty=${difficulty}& (missing "p" of php)
-when I misspell part of the url after "?" then, sometimes I get an empty array back, sometimes I get the data back. How can I get data back with a wrong URL?
example: https://opentdb.com/api.php?amoun=${amount}&difficulty=${difficulty}&type=multiple (missing "t" in amount)
I haven't deployed the application yet, I am using vsc and run npm start to develop the application.
Is it possible that the URL auto-corrects? or maybe it gets cached?
my code:
export const fetchQuizQuestions = async (
amount: number,
difficulty: Difficulty
) => {
const endPoint = `https://opentdb.com/api.php?amount=${amount}&difficulty=${difficulty}&type=multiple`;
try {
const response = await fetch(endPoint);
console.log(response);
const data = await response.json();
console.log(data);
if (data.results.length === 0) {
throw new Error("The part after ? contains some mistake");
}
//below I create the new property "all_answers" and make sure the answers order is never the same
return data.results.map((question: Question) => ({
...question,
all_answers: shuffleArray([
...question.incorrect_answers,
question.correct_answer,
]),
}));
} catch (error: any) {
console.log(error.name);
console.log(error.message);
}
};
Before the ? It's the url. So if you make a mistake there, basically it's like sending a letter to a different adress, so you will not get any answers.
After the ? it's the query string. So you're asking for a result, with some parameters (your query)
So if you're saying like "ok, send me back answers with amount = XXX" but you misspell amount, it's just like "ok send me back answers" because you're not asking for amount anymore (but amoun which is nothing for the endpoint)

Fetch status 200 but pending endllessly, except first call

I've been searching to solve this problem for a while but couldn't find a working solution.
I'm making a simple social network website and this API returns a article data such as text, image and video url, etc, all saved in server's local MySQL Database. My front-end is React and server is Nginx reverse proxy with Node.js using Express. When I load the page, I create 5 React components that each make fetch request for given article number.
The following code snippet is the fetch API that asks the server to fetch data from database:
//server-side script
app.get('/api/getArticle/:id', (req, res) => {
const con = mysql.createConnection({
host: 'myhost_name',
user: 'myUser',
password: 'myPassword',
database: 'myDB',
});
con.connect(function (err) {
if (err) {
throw err;
}
console.log("Connected!");
})
const idInterest = req.params.id.toString();
console.log(idInterest)
let sql = 'some_sql';
con.query(sql, function (err, result) {
if (err) {
res.status(500).send("Error while getting article data");
return;
}
else {
res.set('Connection', 'close')
res.status(200).send(result);
console.log("ended")
con.end();
return;
}
})
}
//React script
//index.js
fetch('http://mywebsite.com/api/getMaxArticleId/')//Retrieve top 5 article ID
.then((response) => {
for (let i = 0; i < data.length; i++) {
nodesList.push(<Container articleId={data[i]['id']}/>)
}
ReactDOM.render(<React.StrictMode><NavBar />{nodesList}<Writer writer="tempWriter" /></React.StrictMode>, document.getElementById('root'));
})
//Container.jsx; componentDidMount
const url = "http://mywebsite.com/api/getArticle/" + this.props.articleId.toString();
fetch(url, {
method: 'GET',
credentials: "include",
}).then((response) => {
response.json().then((json) => {
console.log(json);
//processing json data
This used to work very fine, but suddenly the getArticle/:id calls started to show 200 status but 'pending' in 'time' column in Chrome network tab, endlessly, all except the first*getArticle/:idcall. This prevents my subsequent .then() in each Container from being called and thus my entire tab is frozen.
Link to image of network tab
As you see from the image, all pending fetches are missing 'Content Download' and stuck in 'Waiting(TTFB)', except the first call, which was '39'
I checked the API is working fine, both on Postman and Chrome, the server sends result from DB query as expected, and first call's Json response is intact. I also see that console.log(response.json()) in React front-end shows Promise{<pending>} with *[[PromiseStatus]]: "Resolved"* and *[[PromiseValue]]* of Array(1) which has expected json data inside.
See Image
This became problematic after I added YouTube upload functionality with Google Cloud Platform API into my server-side script, so that looks little suspicious, but I have no certain clue. I'm also guessing maybe this could be problem of my React code, probably index.js, but I have no idea which specific part got me so wrong.
I've been working on this for a few days, and maybe I need common intelligence to solve this (or I made a silly mistake XD). So, any advices are welcomed :)

API caching for next JS

I'm building an app with Next.js... we have 100k+ pages and content changes daily, so using SSR and getServerSideProps.
Some of our data is coming from a headless CMS provider that charges by the request. I'd like to cache the API responses from this server for 24hrs.
What is the best way of going about this?
Is there a common library most folks use to do this?
Just looking for suggestions of approaches I should investigate (or great examples of how to do this).
I used this npm package:
https://www.npmjs.com/package/memory-cache
And then something like this:
import cacheData from "memory-cache";
async function fetchWithCache(url, options) {
const value = cacheData.get(url);
if (value) {
return value;
} else {
const hours = 24;
const res = await fetch(url, options);
const data = await res.json();
cacheData.put(url, data, hours * 1000 * 60 * 60);
return data;
}
}
Then if you want to fetch something with using the cache just call this function. Or it can be used as a midware in the requests. It checks if the data is already in the cache and returns it, or if not - it puts the data into the cache under the key. The key can be anything, I am using the url for instance.
In addition to Tobias Lins' answer:
At least if deploying on Vercel, you can use set Cache-Control headers in getStaticProps, getServerSideProps, API routes, etc to cache responses on Vercel's edge network. This solution does not require any additional dependencies and very minimal code.
api route example - source Vercel
// pages/api/user.js
export default function handler(req, res) {
res.setHeader('Cache-Control', 's-maxage=86400');
res.status(200).json({ name: 'John Doe' });
}
Example in getServerSideProps - Source NextJS
// This value is considered fresh for ten seconds (s-maxage=10).
// If a request is repeated within the next 10 seconds, the previously
// cached value will still be fresh. If the request is repeated before 59 seconds,
// the cached value will be stale but still render (stale-while-revalidate=59).
//
// In the background, a revalidation request will be made to populate the cache
// with a fresh value. If you refresh the page, you will see the new value.
export async function getServerSideProps({ req, res }) {
res.setHeader(
'Cache-Control',
'public, s-maxage=10, stale-while-revalidate=59'
)
return {
props: {},
}
}
I believe you'd want to use:
res.setHeader('Cache-Control', 's-maxage=1440000')
Here are some other useful links for caching on Vercel:
https://vercel.com/docs/concepts/functions/edge-caching
https://vercel.com/docs/concepts/edge-network/overview
https://vercel.com/docs/concepts/edge-network/caching
https://vercel.com/docs/concepts/edge-network/headers
For your specific case, you also may want to look into using getStaticPaths with getStaticProps. You can use fallback: true on getStaticPaths to only build pages when they're visited (you can still build your post popular pages at initial build time).
https://nextjs.org/docs/basic-features/data-fetching#the-fallback-key-required
I know this is an old post, but for others googling (at least those deploying on Vercel), these solutions should help where revalidate in getStaticProps does not.
You could use getStaticProps from Next.js for SSG
They currently have a revalidate property that you can return, that defines how often the content should be re-fetched.
Take a look here:
https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration
This is how we did it without any 3rd party libraries, as in our use-case we only had to cache a relatively smaller amount of global data(header/footer menus) which was shared across the site.
The data was coming from a CMS via GraphQL.
We ran an async method getGlobalData on each page from on getStaticProps method and then returned the cached data to the page component via props.
import fs from 'fs';
import path from 'path';
// Cache files are stored inside ./next folder
const CACHE_PATH = path.join(__dirname, 'globalData.json');
export default async function getGlobalData() {
let cachedData;
// #1 - Look for cached data first
try {
cachedData = JSON.parse(fs.readFileSync(CACHE_PATH, 'utf8'));
} catch (error) {
console.log('❌ CACHE NOT INITIALIZED');
}
// #2 - Create Cache file if it doesn't exist
if (!cachedData) {
// Call your APIs to-be-cached here
const data = await fetchGlobalData();
// Store data in cache files
// this always rewrites/overwrites the previous file
try {
await fs.writeFileSync(
CACHE_PATH,
JSON.stringify(data),
err =>throw err
);
console.log('💾 CACHE FILE WRITTEN SUCCESSFULLY');
} catch (error) {
console.log('❌ ERROR WRITING MEMBERS CACHE TO FILE\n', error);
}
cachedData = data;
}
return cachedData;
}
Call getGlobalData method from getStaticProps.
export async function getStaticProps({ preview = false }) {
const globalData = await getGlobalData();
// call other page-specific/non-shared APIs here
// ...
return { props: { globalData } };
}
References
https://flaviocopes.com/nextjs-cache-data-globally/
Note if you get an error saying fs or path is unknown or invalid, then please understand that, the above code is supposed to be running or referenced "serverside" i.e only inside getStaticProps or getServerSideProps. If you import and reference it "browser-side", say somewhere inside your components or on the page (other than methods mentioned above), then you will get an error, as there is no filesystem(fs) or path modules on browser. They are only available on node.

How to have a server return a remote json object to client? react/express/web3

I'm using web3 to send a transaction to Ethereum. My express server logs the block, transactionHash, etc. data as a json object. I need the json returned to the client.
This question is running the risk of repeating a previous question, but I believe that it is more refined, to the point, and ultimately a different question. These previous threads have helped me remove several errors from the code and zero in on what is actually happening.
How to return json data to a react state?
How to await a json return value (the return takes at least 30 seconds) before logging it? javascript/react/express
How to set state of a react component with a specific item from a returned json object?
The specific code that is returning a blank value instead of the json object is:
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", console.log, res.json());
Client Code:
axios
.post("http://ec2-54-67-28-69.us-west-1.compute.amazonaws.com:3000/")
.then(response => console.log(response.data, payment))
.catch(function(error) {
console.log(error);
})
I somehow need to get the json object inside the res.json() but putting it inside the function () does not work. Any suggestions?
Thanks!
Edit. I am pretty sure I need to use something from this:
web3.eth.sendTransaction({from: '0x123...', data: '0x432...'})
.once('transactionHash', function(hash){ ... })
.once('receipt', function(receipt){ ... })
.on('confirmation', function(confNumber, receipt){ ... })
.on('error', function(error){ ... })
.then(function(receipt){
// will be fired once the receipt is mined
});
Try this:
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", res.json);
The way you did it res.json() was called without parameters.

How should we handle an error while getting initial state from store in getDataFromTree

What I am trying to do
Render a page server side with data from our graphql server with errors.
Context
Our GraphQL server speaks to multiple APIs and returns a graphQL response which is handled by Apollo UI.
Certain GraphQL errors are not considered critical, and we want to still render a page with it. E.g.
data: {
thing: {
id: "1234"
name: "Thing"
nonCriticalData: null
},
error: {
graphQLErrors: [
path: ['thing', 'nonCriticalData']
]
}
}
With the above response we get from Apollo, we want to still render a page with thing. Looking at the implementation of getDataFromTree here, any error will get thrown, without the rest of the data.
How we plan to handle our errors
Here is a snippet of how we plan to handle our code:
getDataFromTree(app)
.then(() => {})
.catch(error => {
const content = ReactDOMServer.renderToString(app);
const { data } = client.getInitialState();
console.log("data", data); <-------------- We need data here
console.log("error", error); <———————— errors are here
// render page with data
});
Please let me know if there are ways around this, or whether I have missed
something.
Extra questions:
Should getDataFromTree even throw errors? It feels like the responsibility should lie with the consumer.

Resources