I'm building a simple marketplace web app where the user posts their products. I want to get the user owner of each product. Please tell me how do I perform Firebase inner joins using observables!
productsRef: AngularFireList<any>;
///
constructor(
private db: AngularFireDatabase,
private userService: UserService
) {
this.productsRef = db.list('/products');
}
getProductsList() {
const product$ = this.productsRef.valueChanges();
// 1st observable to get the products
const productsResults$ = product$.pipe(
switchMap(products => products)
)
);
productsResults$.subscribe(
console.log,
console.error,
() => console.log('completed httpResult$')
);
// 2nd observable to get the user
const user$ = this.userService.getUserById(Id);
productsResults$.subscribe(
console.log,
console.error,
() => console.log('completed httpResult$')
);
}
i get the 2 following results.
Products
[
{
"key": "-TM2Y6vBk70rgKZ3zTUAw",
"name": "T-shirt",
"size": "M",
"user": "-NDNPe47CDTbjmwGgW_3",
},
]
User
{
"key": "-NDNPe47CDTbjmwGgW_3",
"username": "Alex199",
},
the end result i want
{
"key": "-TM2Y6vBk70rgKZ3zTUAw",
"name": "T-shirt",
"size": "M",
"user": {
"key": "-NDNPe47CDTbjmwGgW_3",
"username": "Alex199",
},
},
How can I achieve this using observable?
There is no Inner Join clause in NoSQL databases. So if you want to read some data at a location based on what you read at another location, then you have to perform two separate requests. There is no way you can get that data in a single go.
Alternatively, if you don't want to perform two separate queries, then you can save user data right inside the product node, or vice versa. This practice is called denormalization and is a common practice when it comes to NoSQL databases like Realtime Database. For a better understanding, I recommend you see this video.
If you consider at some point in time try using Cloud Firestore, then I recommend you read my answer from the following post:
What is denormalization in Firebase Cloud Firestore?
Related
A React learning project I'm working through has an outdated backend built through Node that posts a movie object as such using REST
"movies": {
"5b21ca3eeb7f6fbccd471815": {
"genre": "5b21ca3eeb7f6fbccd471818",
}
but gets
"movies": {
"5b21ca3eeb7f6fbccd471815": {
"genre": { "_id": "5b21ca3eeb7f6fbccd471818", "name": "Action" },
}
I'm using Firebase instead. The database looks like this, Firebase RT DB. Is there a way in Firebase that I can reference the genre object by it's Id and accomplish the same thing when calling Get?
I have an API response from a REST API server which I called using my REACT + Apollo with apollo-link-rest. but the response of the API looks like this
[
[
{
"name": "joe"
"address": "123 street hello city"
},
{
"name": "joe2"
"address": "1233 street2 hello2 city"
}
],
2356
]
How can I create a query with this kind of response that has an array with a number as a last item in the array and the first item of the array consist of list of users?
So far I just have this query.
const QueryTest = gql`
query people {
people #rest(type: "People", path: "/allpersons")
}
`;
1st: you need name and address props within (inside) people query - 'ask for what you need' - to get results.
2nd: Work with apollo-link-rest usually needs type patching - technique used to adapt/transform existing (REST) responses to the current needs (structures). It can be combined with response transformer but this step isn't required.
Sometimes you need to add id (calculated from other fields, unique) for nested types (apollo normalizing cache requirement) or define additional 'response type' above desired/required type - google for type patching examples (f.e. patchDeeper usage) or tutorials... define patchers, use debugger...
It's far easier when you know what to expect/what is needed - compare responses with working graphql responses.
Had the same issue, you can use responseTransformer in your link to transform the response.
const restLink = new RestLink({
uri: '...',
responseTransformer: async response =>
response.json()
.then((data: any) => {
if (Array.isArray(data)) return { data }
else return data
})
});
So you should be able to use the query like this:
const QueryTest = gql`
query people {
people #rest(type: "People", path: "/allpersons") {
data
}
}
`;
Where data would contain:
[
[
{
"name": "joe"
"address": "123 street hello city"
},
{
"name": "joe2"
"address": "1233 street2 hello2 city"
}
],
2356
]
The Community Connector feature is very new, and I have searched, there isn't much information. We are building a Community Connector to enable Data Studio to pull API data from Google My Business Insights.
the getconfig() function is described here: https://developers.google.com/datastudio/connector/reference#getconfig
We can display our configuration options to the user, that was easy, but the API reference is unclear what the next step is: how to pass the user input to the next step. Pardon me if I am not using the proper terms here.
var config = {
configParams: [
{
"type": "SELECT_SINGLE",
"name": "SELECT_SINGLE",
"displayName": "Select a Location",
"helpText": "Pick One!",
"options": [
{
"label": locationName,
"value": name
},
{
"label": "altLocationName",
"value": "altName"
}
]
},
]
};
return config;
}
The preceding code displays properly to the user and the user can make a selection from the pull-down in Data Studio when making an initial data connection. But to repeat the question another way: how do we access the selection that the user chose?
The getData(), getSchema(), and getConfig() functions are all called with a parameter (which is called "request" in the documentation). The parameter is an object containing various info at each stage.
At the getConfig() stage, it includes a property called languageCode, in my case set to 'en-GB'.
The getSchema() stage is provided a property called configParams, which is essentially the result of all the settings in getConfig() after the user has set them.
Finally, getData() gets the most info, including whether this request is for extracting sample data for google to run heuristics on, and most importantly: again the configParams.
Here's what a sample request object might look like:
{ //------ Present in:
languageCode: en-GB, //////-Only getConfig()
configParams: { //////-getSchema() + getData()
SELECT_SINGLE: altName ////-+
}, //
scriptParams: { //////-Only getData()
sampleExtraction: true ////-|
lastRefresh: 'new Date()' ////-+
}, //
fields: [ //////-Only getData()
{ name: FooAwesomeness }, ////-|
{ name: BarMagicality }, ////-|
{ name: BazPizzazz } ////-+
] //
dimensionsFilters: [ //////-Only getData()
[{ // |
fieldName: "string", ////-|
values: ["string", ...], ////-|
type: DimensionsFilterType, ////-|
operator: Operator ////-+
}] //
] //
} //------
Do note
that the name field in your code, currently set to SELECT_SINGLE, would be better suited to be called location because that it how you'll access it later on.
In this way you would
access:
request.configParams.location
rather than
request.configParams.SELECT_SINGLE
:)
Also note
that the format for specifying a configuration screen has been updated. Your configuration would now be able to be done as follows:
function getConfig(request) {
var cc = DataStudioApp.createCommunityConnector();
var config = cc.getConfig();
config
.newSelectSingle()
.setId('location') // You can call this "location"
.setName('Select a Location')
.setHelpText('Pick One!')
.addOption(config.newOptionBuilder()
.setLabel('Location Name')
.setValue('value'))
.addOption(config.newOptionBuilder()
.setLabel('Alternate Location Name')
.setValue('altValue'))
config.setDateRangeRequired(true);
config.setIsSteppedConfig(false);
return config.build();
}
See: Connector API Reference
See: Build a Connector Guide
The user selections will be passed to getSchema() and getData() requests under configParams object.
Using your example, let's assume the user selects altLocationName in the configuration screen. In your getSchema() and getData() functions, request.configParams.SELECT_SINGLE should return altName.
I'm building a single-page application with react and redux. Which of course needs a backend for its data. Now we're looking at what api architecture would be best, but it I couldn't find any consensus on which api architecture would best fit a react/redux application.
Now I know that you can basically consume data any way you like. But still there are clear market leaders, like redux over mobx for example. That doesn't make them better, but it's nice to at least know where the preference lies.
So my question is: which api architecture is used most for a react/redux app at this point in time?
From our experience, it's best not to make the API "fit" react/redux and vice versa.
Just use thunk-action-creators and handle the API calls in whatever shape or form they are.
Here is a contrived example:
export function getUserById (userId) {
return async dispatch => {
dispatch({ type: 'REQUEST_USER_BY_ID', payload: userId })
try {
const user = await api.get(`/users/${userId}`)
ga('send', 'event', 'user', 'profile', 'view')
dispatch({
type: 'LOAD_USER',
payload: user
})
dispatch({
type: 'NOTIFY_SUCCESS',
payload: `Loaded ${user.firstname} ${user.lastname}!`
})
}
catch (err) {
dispatch({
type: 'NOTIFY_ERROR',
payload: `Failed to load user: ${err.message}`,
})
}
}
}
The biggest benefit of this approach is flexibility.
The API(s) stay completely unopinionated about the consumer.
You can handle errors, add retry logic, fallback logic differently on any page.
Easy to glue together actions that require calls to several different apis in parallel or sequential.
We tried many approaches like "redux over the wire" and relays/apollos "bind component to query".
This one stuck as the most flexible and easiest to understand and refactor.
It's difficult to find authoritative information or guidelines on this subject, but it's hard to argue that if you create an API specifically for one flux/redux app, and you store the data in normalized form in the database, it's rather silly to de-normalize it in your API endpoint only to normalize it straight after in your client (using normalizr)... In that case, just leave the data normalized and pass it over the wire to your client like that.
Concretely you'd have something like this:
GET /meetings
{
"result": ["1", "2"],
"entities": {
"meetings": {
"1": { "id": 1, "date": "2016-01-01", "attendees": [1, 2, 3] },
"2": { "id": 2, "date": "2016-01-02", "attendees": [2, 3, 4] }
},
"users": {
"1": { "id": 1, "name": "User 1" },
"2": { "id": 1, "name": "User 2" },
"3": { "id": 1, "name": "User 3" },
"4": { "id": 1, "name": "User 4" }
}
}
}
Given that each of these entities correspond to a property on your state, such response is trivial to merge into your store in a reducer action, using something like Lodash merge:
return _.merge({}, state, action.entities);
If you have multiple consumers, you might opt for a normalize=true query parameter. You might also want to combine this with some kind of expand|include=entities,to,include query parameter.
Finally, note that the JSON API spec doesn't play nicely with the normalized structure of flux/redux stores.
Further reading:
https://softwareengineering.stackexchange.com/questions/323325/how-to-structure-a-rest-api-response-for-a-flux-redux-frontend
https://www.reddit.com/r/reactjs/comments/5g3rht/use_normalizr_or_just_flatten_in_the_rest_api/
Nowadays, there's so much new technology. There isn't really a consensus on what to choose to use, like it was back in the day. Don't find an architecture that would be a best fit for a react/redux application. Find an architecture that best FITS YOUR PROJECT.
I am currently trying to figure out how to setup my Backand app and its REST API. This question is related to question: Backand deep querying. However, I was hoping that I could get some best practice code examples on how to perform server side code to perform a loop and create a JSON responds with the following criteria:
I want to be able to make a REST request to Backand and get one data object back that has manipulated/merged two data objects from my database.
I have an object called "media" and another named "users". Obviously, users contain user information and media contains information on a picture that the user has uploaded. The two objects are related by the userId and by collection set in Backand. I want to make a GET request that responds with a JSON object with all pictures and a nested user object on each picture object that contains the related user information. I know that I get back "relatedObjects", and I could then make some manipulation on the client side, but I am hoping that there is another easier way to do this from the Backand administration system either on server side code or as a query.
So, my question is, what's the best way to produce a REST call that responds a database object with nested related data object through Backand?
Here's the object models (shorten for clarity):
User object model as set up in Backand
{
"name": "users",
"fields": {
"media": {
"collection": "media",
"via": "user"
},
"email": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
} }
Media object model as set up in Backand
{
"name": "media",
"fields": {
"description": {
"type": "string"
},
"thumbnail": {
"type": "string"
},
"fullImage": {
"type": "string"
},
"user": {
"object": "users"
}
}}
Final JSON response that I am looking for:
{
description: 'Blah',
thumbnail: 'someImageUrl.jpg',
fullImage: 'someImageUrl.jpg',
user: {
firstName: 'John'
lastName: 'Smith'
email: 'john#smith.com'
}
}
Just in case anybody else comes across this, I chose to do it with server-side javascript code, since my backend, SQL and NoSQL query skills are very weak. I'm guessing a noSQL query would probably be better in terms of performance. And I would still like to see how it could be done in noSQL. Anyway my server-side javascript code in a Backand action does the job. Here it is:
/* globals
$http - Service for AJAX calls
CONSTS - CONSTS.apiUrl for Backands API URL
Config - Global Configuration
socket - Send realtime database communication
files - file handler, performs upload and delete of files
request - the current http request
*/
'use strict';
function backandCallback(userInput, dbRow, parameters, userProfile) {
var response = [];
var request =
$http({
method: "GET",
url: CONSTS.apiUrl + "/1/objects/media",
headers: {"Authorization": userProfile.token},
params: {
exclude: 'metadata',
deep: true
}
});
var object = request.data;
var related = request.relatedObjects.users;
for (media in object) {
if (object.hasOwnProperty(media)) {
for (user in related) {
if (object[media].user == related[user].id) {
response.push({
id: object[media].id,
thumbnailUrl: object[media].thumbnail,
description: object[media].description,
fullName: related[user].firstName + ' ' + related[user].lastName,
email: related[user].email
});
}
}
}
}
return response;
}