How to reduce Waiting TTFB time on GET request to database? - firebase

I am having problems with my Axios GET requests taking up to 3-7 seconds to retrieve the data from my Firebase Cloud Firestore Database. I have tried searching up page optimization techniques to speed up my page load such as cacheing resources or image optimization but nothing is helping with the actual request time. The image below is for 1 GET request which pulls an array of objects from the database. It's not as if it's a large array though, there is only 6 objects currently in it and it still takes very long to retrieve. This happens for all of my get requests and I'm not sure why.
It isn't only on initial page load, it still takes a couple seconds (generally 2-5 on subsequent loads) to load in even after refreshing the page or moving between pages on my site. It's not consistent at all and I was wondering if there was anything I can do to improve the request speed.
The Action for the "posts" request above. I am using React Redux to get the json data from Firebase Database and adding it to my store:
// get all posts
export const getPosts = () => (dispatch) => {
dispatch({ type: LOADING_DATA });
axios
.get("/posts")
.then((res) => {
dispatch({
type: SET_POSTS,
payload: res.data,
});
})
.catch(() => {
dispatch({
type: SET_POSTS,
payload: [],
});
});
};
The reducer:
export default function (state = initialState, action) {
switch (action.type) {
case SET_POSTS:
return {
...state,
posts: action.payload,
loading: false,
};
default:
return state;
}
}
Firebase functions:
// fetch all posts
exports.getAllPosts = (req, res) => {
db.collection("posts")
.orderBy("createdAt", "desc")
.get()
.then(data => {
let posts = [];
data.forEach(doc => {
posts.push({
postId: doc.id,
body: doc.data().body,
userHandle: doc.data().userHandle,
createdAt: doc.data().createdAt,
commentCount: doc.data().commentCount,
likeCount: doc.data().likeCount,
userImage: doc.data().userImage
});
});
return res.json(posts);
})
.catch(err => console.error(err));
};
Another example of my GET request to retrieve user data after login:

Related

Ngrx/effects is not working in async with angular 2

Am new to Ngrx, We got stuck with the implementation , Need some help
Architecture:
We are trying to fill the store when the User is authenticated by that time am trying to redirect.
Expectation:
Effects should be called as async and In ui we have to do the redirect
Problem:
Redirection to Homepage is happening only after the ngrx/effect api call is done.
this.userService.authenticateUser(this.userId.toUpperCase(), this.password, (user) => {
this.authenticatedUser = user;
//Call the ngrx dispatchMethod to fill the store
this.router.navigateByUrl("/home");
this.ngrxGetModule_data();
async ngrxGetModule_data() {
this.store.dispatch({ type: Affaire_Action.Affaire_Addstore_Login });
//this.store.dispatch({ type: Affaire_Action.Initial_Store });
}
#Effect()
updateAffaireStore$: Observable<Action> = this.actions$
.ofType(Affaire_Action.Affaire_Addstore_Login)
.map<Action, string>(toPayload)
.switchMap(res => this.affaireService.getAffairesSearchSuggetions('')
//.delay(10000)
.map(res => (
{
type: Affaire_Action.Affaire_on_Success, payload: ({ "parameter": SearchSuggestion, "data": res })
}
)))
.catch(error => Observable.of({ type: Affaire_Action.Affaire_on_Failure, payload: null }))
What are you actually trying with the this.userService.authenticateUser ?
Seems like you are trying to call the function but the way you are doing is wrong.
What is the return type?!!! Observable or promise.
Suggestion: You should call services on you effect and dispatch actions from effect. You can also use this.router.navigateByUrl("/home"); on your effect.

Adding a document to a collections keeps hanging

Disclaimer: I'm new to React Native and Firebase in general, so this might be me just being dumb. Anyways, I'm trying to learn something new and making a test app at the same time and I've run into an issue when registering users.
I have an action that I dispatch that should be able to Authenticate users and at the same time add the user.uid and email to a collection called users.
The problem is that every time that I call the await to add the user the app just hangs there and nothing happens. I tried to resolve the Promise or catch an error and it still gives me nothing.
Here's the full action:
export const registerUser =
(
email: string,
password: string,
): ThunkAction<void, RootState, unknown, SignUpTypes> =>
async (dispatch: Dispatch<SignUpTypes>) => {
dispatch({ type: SIGN_UP });
try {
const response = await firebase
.auth()
.createUserWithEmailAndPassword(email, password);
const { user, additionalUserInfo } = response;
const userProfile = {
uid: user?.uid,
email,
};
await usersCollection
.doc(user?.uid)
.set(userProfile)
.then(res => console.log('res', res))
.catch(error => console.log('error', error));
dispatch({
type: SIGN_UP_COMPLETE,
payload: {
isLoggedIn: true,
user: userProfile,
isNewUser: additionalUserInfo?.isNewUser,
},
});
} catch (error) {
dispatch({
type: SIGN_UP_ERROR,
payload: { error: { code: error.code, message: error.message } },
});
}
};
I got nothing from the moment that I call the await on the usersCollection, not even a console log and everything just freezes there. Not sure if it's something related to thunks but or not.
Also, it if helps I'm always having this warning:
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See https://github.com/facebook/react-native/issues/12981 for more info
Any ideas on what am I missing?
Thanks ✌
Don't combine await and then. To get your logs try this:
try {
const res = await usersCollection
.doc(user?.uid)
.set(userProfile)
console.log('res', res)
}
catch(e){
console.log('e', e)
}

Api middleware with redux-observable

I'm refactoring my react/redux app to use redux-observable instead of redux-thunk. Using thunk, I have an api middleware set up to listen for any actions with a CALL_API key and do some manipulation of the data, prepare headers, prepare full url, perform an api call using axios, and also do some additional action dispatches related to an api call.
Importantly, the api middleware dispatches a REQUEST_START action which gives the request an id and sets its status to pending in the network part of my state. When the promise from axios resolves or rejects, the middleware dispatches a REQUEST_END action, updating the state so that the current request is set to resolved or rejected. Then the response is returned to the calling action creator that initially dispatched the CALL_API action.
I have not been able to figure out how to do this with redux-observable. The part about the api middleware described above that I want to replicate is the REQUEST_START and REQUEST_END action dispatches. It's very convenient to have a centralized place where all api call related stuff is handled. I know I can effectively dispatch the REQUEST_START and REQUEST_END actions in each of my epics that does an api call, but I don't want to have to repeat the same code in many places.
I managed to partially solve this by creating an apiCallEpic which listens for actions with type CALL_API and does the above setup for api calls. However, an issue (or rather, something I don't like) is that the epic that initiates the api call (e.g. getCurrentUserEpic) essentially gives up control to apiCallEpic.
So, for example, when the api call succeeds and has a response, I may want to format that response data in some way before dispatching an action to be handled by my reducer. That is, getCurrentUserEpic should do some formatting of data returned from api call before sending to reducer. I was able to achieve something close to this by passing a payloadHandler callback function defined in getCurrentUserEpic that the apiCallEpic can call if/when it gets a successful response. However, I don't like this callback architecture and it seems like there's got to be a better way.
Here is some code that demonstrates my use of api middleware using thunk.
import axios from 'axios';
// actionCreators.js
// action types
const CALL_API = "CALL_API";
const FETCH_CURRENT_USER = "FETCH_CURRENT_USER";
const RECEIVE_CURRENT_USER = "RECEIVE_CURRENT_USER";
// action creators for request start and end
export const reqStart = (params = {}) => (dispatch) => {
const reduxAction = {
type: REQ_START,
status: 'pending',
statusCode: null,
requestId: params.requestId,
}
dispatch(reduxAction);
}
export const reqEnd = (params = {}) => (dispatch) => {
const {
requestId,
response = null,
error = null,
} = params;
let reduxAction = {}
if (response) {
reduxAction = {
type: REQ_END,
status: 'success',
statusCode: response.status,
requestId,
}
}
else if (error) {
if (error.response) {
reduxAction = {
type: REQ_END,
status: 'failed',
statusCode: error.response.status,
requestId,
}
}
else {
reduxAction = {
type: REQ_END,
status: 'failed',
statusCode: 500,
requestId,
}
}
}
dispatch(reduxAction);
}
// some api call to fetch data
export const fetchCurrentUser = (params = {}) => (dispatch) => {
const config = {
url: '/current_user',
method: 'get',
}
const apiCall = {
[CALL_API]: {
config,
requestId: FETCH_CURRENT_USER,
}
}
return dispatch(apiCall)
.then(response => {
dispatch({
type: RECEIVE_CURRENT_USER,
payload: {response},
})
return Promise.resolve({response});
})
.catch(error => {
return Promise.reject({error});
})
}
// apiMiddleware.js
// api endpoint
const API_ENTRY = "https://my-api.com";
// utility functions for request preparation
export const makeFullUrl = (params) => {
// ...prepend endpoint url with API_ENTRY constant
return fullUrl
}
export const makeHeaders = (params) => {
// ...add auth token to headers, etc.
return headers;
}
export default store => next => action => {
const call = action[CALL_API];
if (call === undefined) {
return next(action);
}
const requestId = call.requestId;
store.dispatch(reqStart({requestId}));
const config = {
...call.config,
url: makeFullUrl(call.config),
headers: makeHeaders(call.config);
}
return axios(config)
.then(response => {
store.dispatch(reqEnd({
response,
requestId,
}))
return Promise.resolve(response);
})
.catch(error => {
store.dispatch(reqEnd({
error,
requestId,
}))
return Promise.reject(error);
})
}
// reducers.js
// Not included, but you can imagine reducers handle the
// above defined action types and update the state
// accordingly. Most usefully, components can always
// subscribe to specific api calls and check the request
// status. Showing loading indicators is one
// use case.
Here's the code I've implemented to accomplish a similar thing with redux-observable.
export const fetchCurrentUserEpic = (action$, state$) => {
const requestType = FETCH_CURRENT_USER;
const successType = RECEIVE_CURRENT_USER;
const requestConfig = {
url: "/current_user",
method: "get",
}
const payload = {requestConfig, requestType, successType};
const payloadNormalizer = ({response}) => {
return {currentUser: response.data.data};
}
return action$.ofType(FETCH_CURRENT_USER).pipe(
switchMap((action) => of({
type: CALL_API,
payload: {...payload, requestId: action.requestId, shouldFail: action.shouldFail, payloadNormalizer},
})),
)
}
export const apiEpic = (action$, state$) => {
return action$.ofType(CALL_API).pipe(
mergeMap((action) => (
concat(
of({type: REQ_START, payload: {requestId: action.payload.requestId, requestType: action.payload.requestType}}),
from(callApi(action.payload.requestConfig, action.payload.shouldFail)).pipe(
map(response => {
return {
type: action.payload.successType,
payload: action.payload.payloadNormalizer({response})
}
}),
map(() => {
return {
type: REQ_END,
payload: {status: 'success', requestId: action.payload.requestId, requestType: action.payload.requestType},
}
})
)
)
).pipe(
catchError(error => {
console.log('error', error);
return of({type: REQ_END, payload: {status: 'failed', requestId: action.payload.requestId, requestType: action.payload.requestType}, error});
})
)
)
)
}
Any comments or suggestions are appreciated!
I've found redux-fetch-epic-builder (A lib for building "fetch actions" and generic epics handled by redux-observable) to be similar to what you are trying to achieve here (beware it uses rxjs 5, this guide to rescue). It uses fetch, not axios, but it's easy to replace that. Plus it has transformers for successful/failed actions.
The library is a bit old, but the base idea to overcome boilerplate code is still valid: Generic epic-builder to fetch data with calls to API(s).
I am a novice in React / Redux / RxJS, but the only problem I see with the redux-fetch-epic-builder is the way to configure the client (in axios terms). That is, I am not fully satisfied with (due to it being not FSA or RSAA):
//action creators
const getComments = (id, page = 1) => ({
type: GET_COMMENTS,
host: 'http://myblog.com',
path: `/posts/${id}/comments`,
query: {
page,
},
})
// ...
const epics = [
buildEpic(GET_COMMENTS),
]
but this may still be an elegant way. And the license allow to develop the library further. I have not converted the example from the library documentation to your user-related example, but with react-observable there is certainly no need to introduce a separate "api middleware". (Also, I like /SUBACTION better than _SUBACTION, but it's trivial to change.)

What is the suitable query for the following case?

This is my database structure:
I am trying to list all users with "locale" equal to "Cairo, Egypt" so I made the following query:
exports.calculateMatches = functions.https.onRequest((request, response) => {
// Access users' profiles that are located in the locale of the requesting user
databaseRef.child("users").orderByChild("locale").equalTo(request.query.locale).once("value")
.then(snap => {
snap.forEach(profile => {
console.log(profile);
});
});
});
Note this function is deployed to firebase cloud functions and this is what I get in the logs:
HTTPS type functions require that you send a response to the client in order to terminate the function. Without that, they will always time out, and the client will be waiting the whole time.
For example:
const databaseRef = admin.database().ref('')
exports.calculateMatches = functions.https.onRequest((request, response) => {
databaseRef.child("users").orderByChild("locale").equalTo(request.query.locale).once("value")
.then(snap => {
const profiles = []
snap.forEach(profile => {
profiles.push(profile.val())
});
response.send(profiles)
})
.catch(error => {
response.status(500).send(error)
});
});

pass Meteor user data to redux

how to pass Meteor user data to redux, on initializing react-native app.
Here is my code. I use tracker for subscribe for changes, but it works very strangely. It reacts for changes for only one time.
What is the best way to update the Meteor.user() info in redux store??
export const loadUser = () => dispatch => {
console.log('load user)')
Tracker.autorun(() => {
console.log('datachanges user')
const loginStatus = Meteor.loggingIn();
dispatch({
type: 'USER_LOGGING_IN',
payload: loginStatus
});
});
Tracker.autorun(() => {
console.log('datachanges logins')
const user = Meteor.user()
dispatch({
type: 'USER_DATA',
payload: user,
});
});
};

Resources