Firestore where 'IN' query on google-cloud firestore - node.js

(Using typescript for better readability. Vanilla js is always welcome)
Nodejs App, using these imports:
import { FieldPath, DocumentReference } from '#google-cloud/firestore';
and this function
async getByIds(ids: DocumentReference[]) {
const collection = this.db.client.collection('authors');
const query = await collection.where(FieldPath.documentId(), 'in', ids).get();
return query.docs.map(d => ({ id: d.id, ...d.data() }));
}
returns this very specific error:
The corresponding value for FieldPath.documentId() must be a string or a DocumentReference.
The debugger confirms that ids is in fact a DocumentReference array.
Maybe the #google-cloud/firestore package isn't aligned to the firebase one?
EDIT:
as noted by Doug in it's comment, I forgot to put the code for this.db.client. Here you are:
export class DatabaseProvider {
private _db: Firestore;
get client(): Firestore {
return this._db;
}
constructor() {
this._db = new Firestore({
projectId: ...,
keyFilename: ...
});
}
}
And used as
const db = new DatabaseProvider();

It seems like what you're trying to do is a batch get, which is available via a different method: getAll(). I think you want this:
async getByIds(ids: DocumentReference[]) {
return this.db.client.getAll(...ids);
}

Related

Error: Cannot get orders: TypeError: Cannot read properties of undefined (reading 'connect')

I'm trying to build an API with express and pg. Whenever I try to access an endpoint that is related to a query to the database I get the error above.
My handler function is as follows:
import { Request, Response, Router, NextFunction } from 'express';
import { Orders } from '../models/order';
const orders = new Orders;
const index = async (_req: Request, res: Response, next: NextFunction) => {
try {
const ordersList = await orders.index();
res.json(ordersList);
} catch (err) {
next(err)
}
}
const ordersRoute = Router();
ordersRoute.get('/', index);
This handler refers to the following model:
import { Pool } from 'pg';
client = new Pool({
host: POSTGRES_HOST,
database: POSTGRES_DB,
user: POSTGRES_USER,
password: POSTGRES_PASSWORD,
port: parseInt(POSTGRES_PORT as string, 10)
export class Orders {
async index(): Promise<Order[]> {
try {
const conn = await client.connect();
const sql = 'SELECT * FROM orders';
const result = await conn.query(sql);
conn.release();
return result.rows;
} catch (err) {
throw new Error(`Cannot get orders: ${err}`);
}
}
}
anytime I try to access the endpoint I get
Error: Cannot get orders: TypeError: Cannot read properties of
undefined (reading 'connect')
in the console.
any idea how to fix ?
So how things work in your case.
All modules are read by nodejs starting from your index one by one from top to bottom.
In your script, you declare client and then export a class. In that case, your client is setup, then you export a class, and that file is completed, meaning that the only thing that remains is the exported thing. So when you try to use the exported class, you'll not have the same context as in your module.
You need to export the client too and import it where you need that class or to include client definition inside the class

Error: addReview is not a function in MERN application

I'm following along with a tutorial to create a movie review app on the MERN stack.
When calling my API using Insomnia, I'm getting the error "ReviewsDAO.addReview is not a function"
reviewsDAO.js:
import mongodb from "mongodb"
const ObjectId = mongodb.ObjectId
let reviews
export default class ReviewsDAO{
static async injectDB(conn){
if(reviews){
return
}
try { reviews = await conn.db(process.env.MOVIEREVIEWS_NS).collection('reviews')}
catch(e){
console.error(`unable to establish connection handle in reviewDAO: ${e}`)
}
}
static async addReview(movieId, user, review, date){
try{
const reviewDoc = {
name: user.name,
user_id: user._id,
date: date,
review: review,
movie_id: ObjectId(movieId)
}
return await reviews.insertOne(reviewDoc)
}
catch(e) {
console.error(`unable to post review ${e}`)
return {error: e}
}
}
reviews.controller.js:
import ReviewsDAO from '../dao/reviewsDAO.js'
export default class ReviewsController{
static async apiPostReview(req,res,next) {
const reviewsDAO = new ReviewsDAO()
try{
const movieId = req.body.movie_id
const review = req.body.review
const userInfo = {
name: req.body.name,
_id: req.body.user_id
}
const date = new Date()
const ReviewResponse = await reviewsDAO.addReview(
movieId,
userInfo,
review,
date
)
res.json({status: "success"})
}
catch(e) {
res.status(500).json({error: e.message})
}
}
The reviews collection is also not being created in MongoDB. But maybe that isn't supposed to happen until we create our first review.
Why isn't my function being called appropriately?
You need to instantiate a ReviewsDAO object in order to call its methods.
const reviewsDAO = new ReviewsDAO()
Then you will be able to access the addReview() method
You are calling the class method without initializing the object.
For example:
class Animal{
function sayhello(){
console.log("Hello")
}
}
// I can call the sayhello method by creating an object.
var dog=new Animal()
dog.sayhello() //prints "Hello"
But in your case, you are calling the method without creating the object.
Animal.sayhello()
// It will create an error
So initialise the object and call the method
const reviewsDAO = new ReviewsDAO()
reviewDAO.addreviews()
The static keyword defines static methods for classes.
Static methods are called directly on the class (Car from the example above) - without creating an instance/object (mycar) of the class
So
const Reviewsdao= new ReviewsDAO()
ReviewsDAO().addreviews()
Call the method with the class name after initialising it.

TypeORM and MongoDB and Repositories: Cannot read property 'prototype' of undefined

I'm trying implement TypeORM with MongoDB using repositories. However, when I try to make use of repositories to manage the database, using the same structure as in this repository, things go a bit sideways. I'm getting the following error:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'prototype' of undefined
I have tried the following code:
import { Request, Response } from 'express';
import { getMongoRepository } from "typeorm";
import Task from "../models/Task";
export default class TasksController {
async listAll(request: Request, response: Response): Promise<Response> {
const tasksRepository = getMongoRepository(Task);
try {
const tasks = await tasksRepository.find();
return response.status(200).json({ "items": tasks });
} catch (err) {
return response.status(400).json({
message: err.message,
});
}
}
}
I know the error refers to implementing the .find() method. I have even managed to fetch the data, using a suggestion from this post replacing:
const tasks = await tasksRepository.find();
with
const tasks = await tasksRepository.createCursor(tasksRepository.find()).toArray();
but I still get the above mentioned error.
Anyone understands what's going on?
I have also managed to save data directly to the database through the use of the following script:
server.ts
import express from 'express';
import { createConnection } from 'typeorm'
const app = express();
const port = 3333;
createConnection();
app.use(express.json());
app.post('/tasks', (async (request, response) => {
const { item } = request.body;
task.item = item;
const task = new Task();
(await connection).mongoManager.save(task);
return response.send(task);
}))
app.listen(port, () =>
console.log(`Server running on port ${port}`)
);
TypeORM is not support mongodb v4.
https://github.com/nestjs/nest/issues/7798
You can use 3.7.0 instead.
I submitted a pull requests to resolve this. https://github.com/typeorm/typeorm/pull/8412 if anyone is looking for a workaround in the meantime.

In attempting to minimize cold start time for Firebase Cloud functions, how to import a class for use inside one function? [duplicate]

This question already has an answer here:
What is the correct way to dynamically import a class inside of a Firebase Cloud function using typescript?
(1 answer)
Closed 3 years ago.
If I have a module that is only required for one of my Firebase Cloud functions, this Firebase Tutorial suggests importing that module inside just the function that needs it, in order to minimize cold start time for all other functions in a project.
This makes sense, but is it also possible to import a class which contains its own set of dependencies inside of a function?
I have a need to use Bcrypt but only in two of my functions. So I would rather not have to load it for all of my other cloud functions where it is not needed.
In my application, I have the following import:
import BcryptTool from './classes/bcrypt'; // <--- only needed in 2 functions
Here is the contents of bcrypt.ts:
import * as bcrypt from 'bcryptjs';
export default class BcryptTool {
public static hashValue(value: string, rounds: number, callback: (error: Error, hash: string) => void) : void {
bcrypt.hash(value, rounds, (error:any, hash:any) => {
callback(error, hash);
});
}
public static compare(value: string, dbHash: string, callback: (error: string | null, match: boolean | null) => void) {
bcrypt.compare(value, dbHash, (err: Error, match: boolean) => {
if(match) {
callback(null, true);
} else {
callback('Invalid value match', null);
}
});
}
}
And finally, in my Firebase Cloud functions index.ts:
const express = require('express');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({ origin: true });
admin.initializeApp();
const util = express();
const api = express();
...
import BcryptTool from './classes/bcrypt'; // <-- when i import here, calls to its methods within my functions work as expected
...
util.use(cors);
util.post('/verify', async (request: any, response: any) => {
// according to Doug's answer below i should be able to attempt to import here as a solution using a dynamic import expression like so:
const BcryptTool = await import('./classes/bcrypt');
// but the following subsequent call to .compare() fails
BcryptTool.compare(...)
// VS Code hinting shows an error: Property 'compare' does not exist on type 'typeof import('FULL/PATH/TO/CLASS/classes/bcrypt')'
});
api.use(cors);
api.post('/endpoint/foo', async (request: any, response: any) => {
// I do not need Bcrypt here
});
api.post('/endpoint/bar', async (request: any, response: any) => {
// I do not need Bcrypt here
});
Is this not possible? Am I just doing it all wrong?*
Sure, you can async (dynamic) import anywhere you want in TypeScript code. The imported symbols will be visible in the scope where you imported it, and nowhere else. It doesn't matter what the module contains.
(Posted answer on behalf of the question author, to move it from the question post).
I was not importing the class correctly. The reason along with a solution is outlined here.

Need to find the error with connecting subscription with schema stitching

I am using apollo-server-express for graphql back-end. I am going to process only mutations there, but I want to redirect query and subscription on hasura by means of schema stitching with introspection. Queries through apollo-server to hasura are working fine and returning the expected data.
But subscriptions are not working and I am getting this error: " Expected Iterable, but did not find one for field subscription_root.users".
And besides, server hasura is receiving events:
But apollo-server resents the answer from hasura. It is not the first day I suffer with this and I can not understand what the problem is.
In the editor hasura subscriptions work.
Link to full code
If you need any additional info, I will gladly provide it to you.
import {
introspectSchema,
makeExecutableSchema,
makeRemoteExecutableSchema,
mergeSchemas,
transformSchema,
FilterRootFields
} from 'graphql-tools';
import { HttpLink } from 'apollo-link-http';
import nodeFetch from 'node-fetch';
import { resolvers } from './resolvers';
import { hasRoleResolver } from './directives';
import { typeDefs } from './types';
import { WebSocketLink } from 'apollo-link-ws';
import { split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import * as ws from 'ws';
import { OperationTypeNode } from 'graphql';
interface IDefinitionsParams {
operation?: OperationTypeNode,
kind: 'OperationDefinition' | 'FragmentDefinition'
}
const wsurl = 'ws://graphql-engine:8080/v1alpha1/graphql';
const getWsClient = function (wsurl: string) {
const client = new SubscriptionClient(wsurl, {
reconnect: true,
lazy: true
}, ws);
return client;
};
const wsLink = new WebSocketLink(getWsClient(wsurl));
const createRemoteSchema = async () => {
const httpLink = new HttpLink({
uri: 'http://graphql-engine:8080/v1alpha1/graphql',
fetch: (nodeFetch as any)
});
const link = split(
({ query }) => {
const { kind, operation }: IDefinitionsParams = getMainDefinition(query);
console.log('kind = ', kind, 'operation = ', operation);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httpLink,
);
const remoteSchema = await introspectSchema(link);
const remoteExecutableSchema = makeRemoteExecutableSchema({
link,
schema: remoteSchema
});
const renamedSchema = transformSchema(
remoteExecutableSchema,
[
new FilterRootFields((operation, fieldName) => {
return (operation === 'Mutation') ? false : true; // && fieldName === 'password'
})
]
);
return renamedSchema;
};
export const createNewSchema = async () => {
const hasuraExecutableSchema = await createRemoteSchema();
const apolloSchema = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers: {
hasRole: hasRoleResolver
}
});
return mergeSchemas({
schemas: [
hasuraExecutableSchema,
apolloSchema
]
});
};
Fixed by installing graphql-tools 4th version. It tutns out the editor did not even notice that I do not have this dependency and simply took the version of node_modules, which was installed by some other package. Problem was with version 3.x. Pull request is where the bug was fixed.
I had the same problem, different cause and solution.
My subscription was working well, until I introduced the 'resolve' key in
my subscription resolver:
Here is the 'Subscription' part of My resolver:
Subscription: {
mySubName: {
resolve: (payload) => {
console.log('In mySubName resolver, payload:',payload)
return payload;
},
subscribe:() => pubSub.asyncIterator(['requestsIncomplete']),
// )
},
The console.log proved the resolve() function was being called with a well structured payload (shaped the same as my Schema definiton - specifically the an object with a key named after the graphQL Subscriber, pointing to an array (array is an iterable):
In mySubName resolver, payload: { mySubName:
[ { id: 41,
...,
},
{...},
{...}
...
...
]
Even though I was returning that same unadulterated object, it caused the error expected Iterable, but did not find one for field "Subscription.mySubName"
When I commented out that resolve function all together, the subscription worked, which is further evidence that my payload was well structured, with the right key pointing to an iterable.
I must be mis-using the resolve field. From https://www.apollographql.com/docs/graphql-subscriptions/subscriptions-to-schema/
When using subscribe field, it's also possible to manipulate the event
payload before running it through the GraphQL execution engine.
Add resolve method near your subscribe and change the payload as you wish
so I am not sure how to properly use that function, specifically don't know what shape object to return from it, but using it as above breaks the subscription in the same manner you describe in your question.
I was already using graphql-tools 4.0.0, I upgraded to 4.0.8 but it made no difference.

Resources