I am writing a lambda function on aws. I want to send user data after they signup to my mongodb database. I triggered the lambda function using "pre authenticate" method. however, "I am getting this error " preauthentication failed with error cannot find package 'aws-sdk' imported from /var/task/index.mjs. "
my lambda function code:
import AWS from 'aws-sdk'
import {MongoClient} from 'mongodb'
exports.handler = async (event) => {
try{
// Retrieve the user data from the event object
const userData = event.request.userAttributes;
// Connect to the MongoDB database
const client = await MongoClient.connect("database url", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = client.db("Cluster0");
const collection = db.collection("users");
// Insert the user data into the collection
await collection.insertOne(userData);
// Close the database connection
client.close();
}catch(error){
callback(error, event)
}
};
my question is do I have to install mongodb and aws sdk on lambda function folder?
I am writing lambda function from aws website.
cannot find package 'aws-sdk' imported from /var/task/index.mjs
This error is usually caused by using the SDK V2 import statements while using a Lambda runtime which uses SDK V3.
I am guessing you are using the new node runtime 18 on Lambda. This runtime uses the Node SDK V3 which supports modular imports like so:
const { S3Client } = require("#aws-sdk/client-s3");
You can either change to a lower runtime or change your code to suit the Node V3 SDK imports:
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/#modularized-packages
I also don't believe Lambda runtimes have the MongoDB client built in, which means you will need to create the package locally or use a Lambda Layer
https://www.mongodb.com/developer/products/atlas/serverless-development-lambda-atlas/
If you are not tied down to MongoDB, you can use DynamoDB which has a built in client for the Lambda runtime.
Related
I'm using an AWS DynamoDB to store a string associated with a user. Photo of my dynamoDb table.
In my lambda function, I have the partition key (username) and I am trying to get the string that is associated with the username. It is returning null and not even getting to my console.log instruction.
//I don't think you need to see the top part, I'm showing it just in case
let dynamo = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async function(event){
let username = 'adminDebug'; //test parameter
return await listSongs();
}
async function listSongs(){
var params = {
ExpressionAttributeValues: {
":v1": {
S: "adminDebug"
}
},
KeyConditionExpression: "username = :v1",
TableName: "name_of_my_table"
};
console.log("about to launch"); //appears before the null return
dynamo.query(params, (err, data)=>{
console.log("function has returned"); //does NOT fire off before query ends
if(err){
console.log(err, err.stack);
}
else return data;
});
}
I have also tried to do this with promises like so
//main snippet
return await listSongs();
async function listSongs()
return dynamo.query(params).promise();
and that will make it throw an error "errorMessage": "2022-08-21T16:24:17.5Z 9740-720-48a3e-5b0d0e Task timed out after 3.01 seconds"
Additional notes:
My lambda has the AmazonDynamoDBFullAccess role
My db does not have a sort-key. I just want to link a string to a primary-key.
I am using query() because getItem() requires me to have a sort-key.
I am using the AWS lambda web-app to code this.
Lambda is hooked up to an API gw but for testing, I am using the standard 'test' feature.
Thank you so much for your help. I wouldn't ask if I hadn't spent 3+ hours on this already.
A common reason for AWS Lambda functions to time out is that the function makes an outbound network request e.g. to a website or to an AWS service such as DynamoDB or S3 but the Lambda function has no network route to the internet or to an AWS service endpoint. The attempt to connect then fails after a certain timeout or after 3 seconds when the Lambda service times out the Lambda function (because the default timeout is 3 seconds).
This typically happens because the Lambda function was incorrectly configured to attach to a VPC.
If you don't need the Lambda function in a VPC, then don't configure it to attach to any VPC.
If you do need to attach to a VPC, then attach it to a private subnet (specifically not a public subnet) and ensure an outbound network path to the internet via a route table to a NAT device/gateway and IGW. Alternatively, if your connection is to an AWS service only then you can add a VPC Endpoint.
I am facing issue while deploying my next js app on vercel with mongodb connection. I have added env variable also on vercel site where we deploy next js app. Is there something going wrong in the below file ?
next.config.js
module.exports = {
env: {
MONGODB_URI: "mongodb://localhost/tasksdb",
},
};
I have add env variable as well into my next js project
.env.local
MONGODB_URI = "mongodb://localhost/tasksdb"
I establish this mongodb connection from this doc https://mongoosejs.com/. It's provide us to connect with mongodb straightaway .
And this my mongodb connection establish code
import { connect, connection } from "mongoose";
const conn = {
isConnected: false,
};
export async function dbConnect() {
if (conn.isConected) return;
const db = await connect(process.env.MONGODB_URI);
conn.isConnected = db.connections[0].readyState;
// console.log(conn.isConnected);
// console.log(db.connection.db.databaseName)
}
connection.on("connected", () => {
console.log("Mongodb connected to db");
});
connection.on("error", (err) => {
console.error("Mongodb connected to", err.message);
});
Is there something wrong by creating the database this way because in local everything working fine ?
I don't think vercel's servers can comunicate with your localhost. You can try hosting your database in the mongodb cloud
The code seems to be fine, but you can not connect to Vercel using localhost. You can use Serverless Atlas version of MongoDB which is free you can host your database there and then connect using the link they will provide.
See : https://www.mongodb.com/pricing
I'm using firebase for push web notifications, my file firebase-messaging-ws.js is something like this:
importScripts('https://www.gstatic.com/firebasejs/7.19.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.19.0/firebase-messaging.js');
fetch('./firebase-data.json')
.then(r => r.json())
.then(fbData => {
let app = firebase.initializeApp(fbData);
firebase.messaging(app);
console.log('Firebase Service Worker loaded for projectId:', fbData.projectId);
}).catch(err => {
console.error('Error configuring firebase messaging: ', err)
});
I'm using a json file, firebase-data.json to load the firebase configuration data and It works, I can receive push notifications, however several log warnings appear in the console when I call to firebase.messaging(), If I use a local object (without fetch() command) then all works OK and there is no warning messages.
The log messages are like Event handler of 'XXX' event must be added on the initial evaluation of worker script.:
Can I avoid the warning messages when I use an external file to load the firebase configuration data ?
Basically a service worker script requires synchronous execution, meaning you can't initialize Firebase Messaging within a promise.
Spitballing here, this is untested, but try this:
importScripts('https://www.gstatic.com/firebasejs/7.19.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.19.0/firebase-messaging.js');
const response = await fetch('./firebase-data.json');
const fbData = await response.json();
let app = firebase.initializeApp(fbData);
firebase.messaging(app);
console.log('Firebase Service Worker loaded for projectId:', fbData.projectId);
Alternatively, if it doesn't like await outside of an async function, use JavaScript to export your config data and use importScripts to expose it to your service worker:
firebase-data.js
const fbData = {...};
firebase-messaging-ws.js
importScripts('https://www.gstatic.com/firebasejs/7.19.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.19.0/firebase-messaging.js');
importScripts('./firebase-data.js');
let app = firebase.initializeApp(fbData);
firebase.messaging(app);
console.log('Firebase Service Worker loaded for projectId:', fbData.projectId);
As the other answer explains, moving the fbData to a js file allow the variable to be used inside the Service Worker script.
Just for the record, I did all of this to share the Firebase data between the Service Worker scope and the Angular application scope, The firebase-data.js is something like this:
const FIREBASE_DATA = {
"apiKey": "***********",
"authDomain": "***********.firebaseapp.com",
"databaseURL": "https://***********.firebaseio.com",
"projectId": "***********",
"storageBucket": "***********.appspot.com",
"messagingSenderId": "***********",
"appId": "1:***************"
}
if (typeof module !== 'undefined') {
module.exports = { ...FIREBASE_DATA }
}
With this implementation I can use self.FIREBASE_DATA inside the service worker file and I can also import the module in my angualr app, por instance mi environment.ts is something like:
import FIREBASE_DATA from "../firebase-data";
export const environment = {
production: true,
url_base: '/api',
firebase: FIREBASE_DATA
};
I'm using mongoose and node.js (express), and I wish to insert seed data using script. Like when I do node scripts/createNotifications.js I can insert a data into my db.
My code
//createNotifications.js
const mongoose = require('mongoose')
const Notification = require('../api/models/notificationModel')
mongoose.Promise = global.Promise
module.exports = (async () => {
try {
const new_notification = await new Notification({
"userId" : mongoose.Types.ObjectId("5a3e76ce914e1d1bd854451d"),
"msg" : "Something"
}).save()
} catch(e) {
console.log('Error creating notifications. ', e)
}
})()
When I run the code I don't see any data been inserted. I have my server started in port 3000, do I have to connect to mongodb too in this file? since this file has nothing to do with my express app, it's just a separated file.
If you want to see this module running make sure the following
Make sure you've made connection with the database like mongoose.connect('mongodb://IP/DBName')
What you've posted above is just a module definition. It won't execute on its own. You'll have to require this module in your mail file, the file you're running with node for example node server.js and call the method. Something like
var notification = require(path/to/createNotifications);
notification();
I'm using multiple databases in a Firebase project. Cloud functions for the main (default) database work great, however, I cannot make them work for a secondary database. For example I want to make a read request on a node with admin privileges:
//this works
admin.database().ref(nodePath).once('value')...
This works in the main database, however, if I want to execute the command on another database, it doesn't work:
//this doesn't work
admin.database(secondaryDatabaseUrl).ref(nodePath).once('value')...
Although the functions are deployed, I get an error on the console when trying to execute the cloud function.
Here's the code for the cloud function with an https trigger:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const secureCompare = require('secure-compare');
exports.testFunction= functions.https.onRequest((req, res) => {
const key = req.query.key;
// Exit if the keys don't match
if (!secureCompare(key, functions.config().cron.key)) {
console.error('keys do not match');
res.status(403).send('error1');
return;
}
//test read request
//the line below crashes the function
return admin.database('https://secondary_db_url.firebaseio.com').ref(`/testNode`).once('value').then(dataSnapshot=> {
console.log('value', dataSnapshot.val());
return;
}).catch(er => {
console.error('error', er);
res.status(403).send('error2');
});
});
Below is the error log in the Firebase console:
TypeError: ns.ensureApp(...).database is not a function
at FirebaseNamespace.fn (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:251:42)
at exports.testFunction.functions.https.onRequest (/user_code/index.js:16:16)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:26:41)
at /var/tmp/worker/worker.js:671:7
at /var/tmp/worker/worker.js:655:9
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickDomainCallback (internal/process/next_tick.js:128:9)
If I don't specify the secondary database URL, the function will make the read request on my main database which works great:
//this works
return admin.database().ref(`/testNode`).once('value').then(dataSnapshot=> {
...
I'm using the latest SDK versions: "firebase-admin": "^5.5.1" and "firebase-functions": "^0.7.3"
So, how do I get an instance of a secondary database in cloud functions using admin privileges?
Here's how to access database by URL using Admin SDK:
let app = admin.app();
let ref = app.database('https://secondary_db_url.firebaseio.com').ref();
Here's an example from Admin SDK integration tests: https://github.com/firebase/firebase-admin-node/blob/master/test/integration/database.js#L52
With cloud functions > 1.1 now, here is the documentation link that saved my life on this issue.
https://firebase.google.com/docs/database/usage/sharding#connect_your_app_to_multiple_database_instances
So, it looks like this at the top of my my cloud function index.js :
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const dev = admin.initializeApp({
databaseURL: "https://appdev.firebaseio.com"
}, 'dev');
const v2 = admin.initializeApp({
databaseURL: "https://appv2.firebaseio.com"
}, 'v2');
and then, in my clond functions functions code I can do :
//will change stuff on default database
admin.database().ref().child(`stuff/${stuffId}`).set(myStuff)
//will change stuff on my dev database
admin.database(dev).ref().child(`stuff/${stuffId}`).set(myStuff)
//will change stuff on my v2 database
admin.database(v2).ref().child(`stuff/${stuffId}`).set(myStuff)
So it looks like you are trying to access multiple databases using the javascript web client API. Passing the URL of the database to the API like this doesn't work with the Admin SDK:
admin.database('https://secondary_db_url.firebaseio.com').ref(`/testNode`)
Instead, you have to initialize a second app, give it a name, and pass that app around to the Admin SDK APIs. Here's a complete sample that writes the same data to two different database instances in the same project:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
const otherConfig = Object.assign({}, functions.config().firebase)
otherConfig.databaseURL = 'https://your-other-db.firebaseio.com/'
const otherApp = admin.initializeApp(otherConfig, 'otherAppName')
exports.foo = functions.https.onRequest((req, res) => {
const data = { foo: 'bar' }
const p1 = admin.database().ref('data').set(data)
const p2 = admin.database(otherApp).ref('data').set(data)
Promise.all([p1, p2]).then(() => {
res.send("OK")
})
.catch(error => {
res.status(500).send(error)
})
})
Updating this while on Firebase Functions v3.14.0. None of this answers worked for me so I implemented this solution
instance Registers a function that triggers on events from a specific Firebase Realtime Database instance
functions.database.instance('my-app-db-2').ref('/foo/bar')
Use the name of the database instance and it works, no need for the url. functions.database.ref used without instance watches the default instance for events.
So if both the answers doesn't work.
What happened with me is both the method worked without any error but second instance of database was not getting updated.
I updated npm and firebase CLI it worked.
Also #Dough Stevenson you Passing the URL of the database to the API like this **does** work with the Admin SDK
And this is a good blog from Firebase about the same
Firebase Blog : Easier scaling with multi-database support!