Firebase 9 - how to chain 'addDoc' or similar to a 'collection'? - javascript

Previously in Firebase you could add a document like this:
const myNewDoc = await db.collection('some-collection-name').add({ //Document details here... });
With the introduction of Firebase 9 this no longer works.
Instead of .add I think I am supposed to use an imported .addDoc method.
But it seems I cannot chain .addDoc onto .collection.
If I try to do something like this:
const myNewDoc = await db.collection('some-collection-name').addDoc({ //Document details here... });
TypeScript throws this error:
Property 'addDoc' does not exist on type 'CollectionReference<DocumentData>'.ts(2339)
I could create something more verbose like this:
const someCol = collection(db, "some-collection-name");
const newDoc = await addDoc(someCol, {
//Document details here...
});
But I would rather "chain" it like before.
Is that possible? How would it be done?
And should I even be using .addDoc? Or something else?

The addDoc() is a top level function in Modular SDK. Try refactoring your code like this:
import { collection, addDoc } from "firebase/firestore";
const newDoc = await addDoc(collection(db, "some-collection-name"), {
// Document Data
});
console.log("Document written with ID: ", newDoc.id);
The documentation has examples of both name-spaced and the new syntax.
If you are using compat version to use older syntax then you would have to use add() itself.

Related

How can I get the access of a const inside my function?

How can I grant access to my const to be used inside a function? In this case I want to access my const catName inside my function fetchListings. I'm getting this error:
Question Updated:
ReferenceError: catName is not defined
<script context="module">
const fetchListings = async () => {
try {
// get reference to listings collection
const listingsRef = collection(db, 'listings');
// create a query to get all listings
const q = query(
listingsRef,
where('type', '==', catName),
orderBy(timestamp, 'desc'),
limit(10)
);
// execute query
const querySnap = await getDocs(q);
let lists = [];
querySnap.forEach((doc) => {
console.log(doc);
});
} catch (error) {
console.log(error);
}
};
fetchListings();
</script>
<script>
import { page } from '$app/stores';
// export the const catName to the function above
export const catName = $page.params.catName;
</script>
Hi {catName}!
The problem you are running into is coming from how the <script context="module"> works.
The module level script tag serves as a one-time-per-app setup script. That means it will run only one time, when your app is initialized and it will be run before any of the regular <script> tag code is run. See: https://svelte.dev/docs#component-format-script-context-module
This mean that the <script context="module"> won't have any access to what's defined or created in the normal <script> tags' code. Thus the not defined error for your constant, which is defined in the regular <script> tag.
Based on this, your code would need to be refactored (reorganized). My understanding is that you put the fetchListings in the module context because you want to pre-fetch the results and only do it once during the startup of your app.
To accomplish that you can refactor your code like this:
<script context="module">
let preFetched=false
</script>
<script>
import { page } from '$app/stores';
// export is not needed
const catName = $page.params.catName;
async function fetchListings() => {
// Your code ...
}
if(!preFetched){
fetchListings()
preFetched=true
}
</script>
Hi {catName }!
This ensures that the fetchListings function only runs once. The trick is that variables, constants, etc defined in the module context are accessible to all instances of that model. So when the first instance is being created it will run the fetchListings function, and sets the preFetched variable to false, so the subsequent instances will no do that.
This is just one possible way to do it. Depending on what exactly you want to accomplish you might want to organize things differently. But with the understanding of what does the <script context="module"> do and when it runs, you should be able to come up with your own solution best suited for your needs.

Adding Dexie.js queries in JavaScript file gives hint of missing type

If I add a Dexie.js query in a JavaScript file such as:
let friends = liveQuery(async () => {
return await db.friends
.where("age")
.between(18, 65)
.toArray();
});
I get a hint like this:
"Property 'friends' does not exist on type 'Dexie'.ts(2339)"
Why?
Putting it in a Svelte file is fine, but I would like to keep some basic queries in one place, like find, add, and delete.
I suppose you are referring to a TypeScript error. If you are not subclassing Dexie as suggested on Dexie's Svelte tutorial, you cannot use db.friends directly without TypeScript complaining. If you don't want to subclass Dexie, you can also use the table() method:
let friends = liveQuery(async () => {
return await db.table('friends')
.where("age")
.between(18, 65)
.toArray();
});

Cloud function fails with "missing ) after argument list at wrapSafe"?

I'm in the process of splitting my cloud functions in separate files, so as to lazy load some dependencies and reduce cold start.
Basically, I'm trying to replicate Doug's answer from here, but not having to create a separate file for each function.
In my index.js file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
const { createUser } = require('./user/main');
await createUser(user, admin);
});
And in my 'user/main.js' file:
const functions = require('firebase-functions');
const { Stripe } = require('stripe');
const stripe = new Stripe(functions.config().stripe.secret);
const endpointSecret = functions.config().stripe.singing;
const createStripeCustomer = async (user, admin) => {
// Do some stuff
};
module.exports = { createUser: createStripeCustomer };
The intention behind this split, is that I have some functions which require Stripe, and some which do not, hence I don't want them all to load it unnecessarily, but I get an error- "missing ) after argument list".
Any suggestions as to what has gone wrong?
Your solution does not look like the real solution...
Maybe you also fixed something which looked insignificant... Like an extra double quote.
let something = "A"
console.log("hello", something")
See the extra double quote after the variable?
It produces the same error you mentionned.
It is a common error due to code editors just adding thing for you... And if you are like me and look at the keyboard instead of the screen, it is easy not to notice.
Just in case anyone experiences a similar problem, I managed to get it working, simply by changing:
const createStripeCustomer = async (user, admin) => {
// Do some stuff
};
to:
async function createStripeCustomer(user, admin) {
// Do some stuff
};
I have no idea why that was an issue, but it seemed to resolve the problem I had before.

Evalute JS file with template strings from another file

I would like to make use of a function called executeJavaScript() from the Electron webContents API. Since it is very close to eval() I will use this in the example.
The problem:
I have a decent sized script but it is contained in a template string.
Expanding this app, the script could grow a lot as a string.
I am not sure what the best practices are for this.
I also understand that eval() is dangerous, but I am interested in the principal of my question.
Basic eval example for my question:
// Modules
const fs = require('fs');
// CONSTANTS
const EXAMPLE_1 = 'EXAMPLE_1';
const EXAMPLE_2 = 'EXAMPLE_2';
const EXAMPLE_3 = 'EXAMPLE_3';
const exampleScriptFunction = require('./exampleScriptFunction');
const exampleScriptFile = fs.readFileSync('./exampleScriptFile.js');
// using direct template string
eval(`console.log(${EXAMPLE_1})`);
// using a method from but this doesnt solve the neatness issue.
eval(exampleScriptFunction(EXAMPLE_2));
// What I want is to just use a JS file because it is neater.
eval(`${exampleScriptFile}`);
exampleScriptFunction.js
module.exports = function(fetchType) {
return `console.log(${fetchType});`;
}
This will allow me to separate the script to a new file
what if I have many more then 1 variable???
exampleScriptFile.js:
console.log(${EXAMPLE_3});
This clearly does not work, but I am just trying to show my thinking.
back ticks are not present, fs loads as string, main file has back ticks.
This does not work. I do not know how else to show what I mean.
Because I am loading this will readFileSync, I figured the es6 template string would work.
This allows me to write a plain js file with proper syntax highlighting
The issue is the variables are on the page running the eval().
Perhaps I am completely wrong here and looking at this the wrong way. I am open to suggestions. Please do not mark me minus 1 because of my infancy in programming. I really do not know how else to ask this question. Thank you.
Assuming your source is stored in exampleScriptFile:
// polyfill
const fs = { readFileSync() { return 'console.log(`${EXAMPLE_3}`);'; } };
// CONSTANTS
const EXAMPLE_1 = 'EXAMPLE_1';
const EXAMPLE_2 = 'EXAMPLE_2';
const EXAMPLE_3 = 'EXAMPLE_3';
const exampleScriptFile = fs.readFileSync('./exampleScriptFile.js');
// What I want is to just use a JS file because it is neater.
eval(exampleScriptFile);
Update
Perhaps I wasn't clear. The ./exampleScriptFile.js should be:
console.log(`${EXAMPLE_3}`);
While what you're describing can be done with eval as #PatrickRoberts demonstrates, that doesn't extend to executeJavaScript.
The former runs in the caller's context, while the latter triggers an IPC call to another process with the contents of the code. Presumably this process doesn't have any information on the caller's context, and therefore, the template strings can't be populated with variables defined in this context.
Relevant snippets from electron/lib/browsers/api/web-contents.js:
WebContents.prototype.send = function (channel, ...args) {
// ...
return this._send(false, channel, args)
}
// ...
WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) {
// ...
return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript',
// ...
}
// ...
const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
return new Promise((resolve, reject) => {
this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
// ...
})
}
Relevant snippets from electron/atom/browser/api/atom_api_web_contents.cc
//...
void WebContents::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "WebContents"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
// ...
.SetMethod("_send", &WebContents::SendIPCMessage)
// ...
}

Async.forEach iterator data formatting issue

I am having an issue with the Mongoose fixture loader and I am not sure what is wrong.
When I load my data according to docs such as:
var data = { User: [{name: 'Alex'}, {name: 'Bob'}] };
It does not load. Exploring the code I see that in this file there is an async.forEach iterator which doesn't seem to get triggered. Creating a simple file to test I still cannot get this to work as it should. Evidently the console should print 'User' but it does not. Can someone shed some light on what the issue might be? Note that while I have phrased my question about the async, ultimately I am trying to get the mongoose loader to work so I need to stay within their code structure.
var async = require('async');
var data = { User: [{name: 'Alex'}, {name: 'Bob'}] };
var iterator = function(modelName, next){
// not working
console.log(modelName);
next();
};
async.forEach(data, iterator, function() { });
The pow-mongoose-fixtures module in the NPM repository contains a bug (see bug report).
Your code contains the same bug:
async.forEach(data, ...)
forEach() operates on arrays, but data is an object. In case of the module, it was fixed by using Object.keys() to get an array of keys. You could use it too:
async.forEach(Object.keys(data), ...);
To get mongoose-fixtures working, install the GitHub version:
npm install git://github.com/powmedia/mongoose-fixtures.git
There's a couple of changed you need to make to your code as well:
var fixtures = require('mongoose-fixtures'); // renamed from 'pow-mongoose-fixtures'
var client = mongoose.connect(...);
...
fixtures.load(data, client); // need to pass the client object

Categories