How come I can't use environment variables like this? - javascript

I have set up a monorepo for my project using lerna, and the website package is written using Next.
I have another package called omega=lib which acts as a library, and contains models/schemas for my mongo database. It also has a function that connects to it.
In my website, I am trying to fetch some data from the Channel schema (which exists in omega-lib), and the code for that looks like this:
Channel.ts
import { Schema, model, Document } from "mongoose";
import dbConnect from "../utils/dbConnect";
export interface IChannel extends Document {
name: string
logging_enabled: boolean
}
const ChannelSchema = new Schema({
name: { type: String, index: { unique: true } },
logging_enabled: Boolean
});
const Channel = model<IChannel>('Channel', ChannelSchema);
export const getChannel = async (username: string) => {
await dbConnect();
return Channel.findOne({ name: username });
}
export default Channel;
I am trying to use this code in one of my pages, using getServerSideProps:
export const getServerSideProps = async (context: GetServerSidePropsContext<ParsedUrlQuery>) => {
console.log("DB", process.env.MONGO_DB_URI);
const userInfo: User = await getUser(context);
const moderators: User[] = await getModerators(context);
const channelInfo = await getChannel(userInfo.login);
console.log("CHANNEL INFO", channelInfo);
return {
props: {
userInfo,
moderators,
channelInfo
}
}
}
IT IS WORTH NOTING, THAT ON LINE 2, THIS VALUE GETS LOGGED TO THE CONSOLE CORRECTLY!
Here's where the problem starts, when this code rus, I get this error:
MongooseError: The uri parameter to openUri() must be a string, got "undefined". Make sure the first parameter to mongoose.connect() or mongoose.createConnection() is a string.
This is coming from when the dbConnect() function is called. Here is the code for that:
import mongoose from 'mongoose';
const dbConnect = (uri?: string) => {
if (mongoose.connection.readyState >= 1) {
return;
}
return mongoose.connect(process.env.MONGO_DB_URI ?? uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
})
}
export default dbConnect;
I do not see how MONGO_DB_URI can be null, it is defined in my next.config.js:
module.exports = {
env: {
PRIVATE_KEY: "xxx",
CLIENT_ID: "xxx",
MONGO_DB_URI: "mongodb://localhost:27017/omegalogs",
REDIRECT_URI: "http://localhost:3000/login",
SCOPES: "user:read:email,moderation:read",
WEBSOCKET_PORT: 9393
},
};
And it is worth noting, that the correct value gets printed in the example code above, however it is undefined when being used in dbConnect().
This dbConnect() function is defined in a the omega-lib package, but is used as a dependency. I don't think this is the problem, because it works fine in another project that has the same MONGO_DB_URI defined in the environment file.
This only works if I actually pass the MONGO_DB_URI environment variable through as a parameter to dbConnect(), which is why it is an optional string.
Can anyone tell me why the dbConnect() function is not picking up the environment variable? It should be doing this server side, so I don't see why it wouldn't.

you already try to create a .env file and store the variables there for the process can see? you can install dotenv with:
npm i dotenv
//add dotenv to your index file
require('dotenv').config()
//this will in you .env file in the root of the project
PRIVATE_KEY: "xxx",
CLIENT_ID: "xxx",
MONGO_DB_URI: "mongodb://localhost:27017/omegalogs",
REDIRECT_URI: "http://localhost:3000/login",
SCOPES: "user:read:email,moderation:read",
WEBSOCKET_PORT: 9393
//now you can use in the same way
process.env.MONGO_DB_URI
is a easy way to do in my opinion

Related

Can't pass request body through next-http-proxy-middleware

I've been trying to create a frontend using nextjs for a backend I have running in Java.
I'm using the npm package next-http-proxy-middleware and it seems that either my request body is being dropped or I'm using the package incorrectly.
Here's the entirety of my ./src/api/[...all].ts file
import type { NextApiRequest, NextApiResponse } from 'next'
import httpProxyMiddleware from 'next-http-proxy-middleware'
type Data = {
name: string
}
export const config = {
api: {
externalResolver: true,
},
}
export default (req: NextApiRequest, res: NextApiResponse) => {
httpProxyMiddleware(req, res, {
target: `http://${process.env.REACT_APP_URL}`,
changeOrigin: true,
})
}
and here's the snippet of code where I attempt to login, the form that supplies this data is built using react-hook-form
const onSubmit: SubmitHandler<Inputs> = async (data: any) => {
//console.log('username', watch('username'), 'password', watch('password'))
const response = await axios.post(
'/api/session',
new URLSearchParams({
email: data.username,
password: data.password,
})
)
}
I do see that it's connecting to my backend since I'm getting an error that's generated by my Java program and not by next.js, but it just tells me that the email is null which leads me to believe that my args are being lost in translation
"Cannot invoke \"String.trim()\" because \"email\" is null - NullPointerException (DataManager:149 < PermissionsManager:508 < SessionResource:111 < ...)"
I am able to get it working using vanilla react so I'm sure it's something I'm doing wrong in the implementation of the next-http-proxy-middleware but I've been bashing my head against the wall a little too long on this. I appreciate all the help, thanks!
I figured out the solution and I feel a bit silly for not realizing earlier:
https://github.com/vercel/next.js/discussions/11036
Nextjs comes with an automatic body parser and I had to disable it.
export const config = {
api: {
externalResolver: true,
bodyParser: false,
},
}

How can I copy pouchdb 0000003.log file to Ionic 5 and retrieve the data?

My scenario is to use pouch db data in ionic and I successfully added pouch db package to ionic and created a sample and it worked fine. Now I have a scenario I have the below file
000003.log in which I have all the data, but in ionic it is storing in the indexdb so how can I use this 000003.log data and copy it to indexeddb or is there any way copy the contents ?
Below is my app code
import { Injectable } from '#angular/core';
import PouchDB from 'pouchdb';
#Injectable({
providedIn: 'root'
})
export class DataService {
private database: any;
private myNotes: any;
constructor() {
this.database = new PouchDB('my-notes');
}
public addNote(theNote: string): Promise<string> {
const promise = this.database
.put({
_id: ('note:' + (new Date()).getTime()),
note: theNote
})
.then((result): string => (result.id));
return (promise);
}
getMyNotes() {
return new Promise(resolve => {
let _self = this;
this.database.allDocs({
include_docs: true,
attachments: true
}).then(function (result) {
// handle result
_self.myNotes = result.rows;
console.log("Results: " + JSON.stringify(_self.myNotes));
resolve(_self.myNotes);
}).catch(function (err) {
console.log(err);
});
});
}
How to export/import the existing database in ionic app? Do I have to store in file system or indexeddb?
By default PouchDb will use IndexDb, so its doing it correctly. If you want to change storage you need to setup a different adapter.
I don't see where you set up the options for the local adapter, so I think you are missing the local & adapter setup options to support it
Now use the correct adapter you want PouchDB here
I've created an Ionic 5/Angular repo that demonstrates how to take a local pouchdb as described in the OP and load it as a default canned database in the app.
https://github.com/ramblin-rose/canned-pouch-db
The hurdles were not huge, but I encountered some problems along the way, mainly some wrangling with respect to pouchdb's es modules and module default exports.
Specifically, the documentation for pouchdb-replication-stream is not helpful for incorporation for Ionic5/Angular. I assumed the import
import ReplicationStream from 'pouchdb-replication-stream';
Would just work, but unfortunately at runtime this dread error would popup
Type Error: Promise is not a constructor
Ouch! That's a show stopper. However I came across the pouchdb-replication-stream issue es modules
Which prompted the solution:
import ReplicationStream from 'pouchdb-replication-stream/dist/pouchdb.replication-stream.min.js';
Anyway the highlights of the repo are 'can-a-pouchdb.js' and 'data.service.ts'.
can-a-pouchdb.js
This script will create a local node pouchdb and then serialize that db to app/assets/db, which is later loaded by the ionic app.
The important bits of code:
// create some trivial docs
const docs = [];
const dt = new Date(2021, 6, 4, 12, 0, 0);
for (let i = 0; i < 10; i++, dt.setMinutes(dt.getMinutes() + i)) {
docs[i] = {
_id: "note:" + dt.getTime(),
note: `Note number ${i}`,
};
}
// always start clean - remove database dump file
fs.rmdirSync(dbPath, { recursive: true });
PouchDB.plugin(replicationStream.plugin);
PouchDB.adapter(
"writableStream",
replicationStream.adapters.writableStream
);
const db = new PouchDB(dbName);
console.log(JSON.stringify(docs));
await db.bulkDocs(docs);
//
// dump db to file.
//
fs.mkdirSync(dumpFileFolder, { recursive: true });
const ws = fs.createWriteStream(dumpFilePath);
await db.dump(ws);
To recreate the canned database run the following from the CL:
$ node can-a-pouchdb.js
data.service.ts
Here's how the app's pouchdb is hydrated from the canned database. Take note the db is using the memory adapter, because as a demo app not persisting the db is desirable.
public async init(): Promise<void> {
if (this.db === undefined) {
PouchDB.plugin(PouchdbAdapterMemory);
PouchDB.plugin(ReplicationStream.plugin);
this.db = new PouchDB(DataService.dbName, { adapter: 'memory' });
// if the db is empty, hydrate it with the canned db assets/db
const info = await this.db.info();
if (info.doc_count === 0) {
//load the asset into a string
const cannedDbText = await this.http
.get('/assets/db/mydb.dump.txt', {
responseType: 'text',
})
.toPromise();
// hydrate the db
return (this.db as any).load(
MemoryStream.createReadStream(cannedDbText)
);
}
}

Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object

I'm following Firebase's instructions and my functions is as follows:
import { DataSource, DataSourceConfig } from "apollo-datasource";
import { KeyValueCache } from "apollo-server-caching";
import firebase from "firebase";
import admin from "firebase-admin";
import "#firebase/firestore";
import { Request } from "apollo-server-env";
export class FirebaseDataSource<TContext = any> extends DataSource {
context!: TContext;
db: firebase.firestore.Firestore;
constructor({
firebaseConfig,
serviceAccount,
databaseURL,
}: {
serviceAccount: any;
firebaseConfig: firebase.app.App;
databaseURL: string;
}) {
super();
this.context;
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL,
});
}
if (!this.db) {
this.db = firebase.firestore();
}
}
async initialize(config: DataSourceConfig<TContext & { request: Request }>) {
this.context = config.context;
}
async user_updateRestaurantFavorites(data: {
uid: string;
someId: string;
add: boolean;
}) {
const collectionRef = this.db.collection("users");
const documentRef = collectionRef.doc(data.uid);
let favorites;
if (data.add) {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayUnion(
data.someId
),
});
} else {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayRemove(
data.someId
),
});
}
return favorites;
}
}
export default FirebaseDataSource;
I dubugged it and I do pass the uid, add, and someId correctly.
someId is a string and add is a boolean (true)
When I run this, I get:
Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object (found in field favorites in document users/XXXXXXX)
I am just running their own function with a simple string.
Below is an image of my firestore showing the user record does indeed have an empty array ready to accept strings
What am I doing wrong?
You're mixing up the web client and admin client SDKs. You can't use the FieldValue objects exported by firebase-admin when calling methods exported by firebase. The error message is coming from the web client SDK, and it's effectively telling you that you passed an object that it doesn't understand (from the Admin SDK).
You should pick one or the other, and completely remove the one you aren't using in order to avoid problems. If this runs on a backend, you should only use the Firebase Admin SDK, and skip the web client altogether. If you do this, you will need to assign this.db using the admin SDK, probably as this.db = admin.firestore().
Firebase can only store primitive types, maps and array of same. In your case you are saving the result of admin.firestore.FieldValue.arrayUnion(...) for the property favorites.
My guess is that the result is not returning a supported type. I have not used FieldValue before ... is that the correct way to use the API?
It simply means that you need to send exact data which was received by the query. Partial object not allowed
db.collection("users").where("name", "==", somename).limit(1).get().then(query => {
console.log(query);
const thing = query.docs[0];
console.log(thing.data());
let tmp = thing.data();
tmp.current_game_play = tmp.current_game_play + 1;
console.log(tmp);
thing.ref.update(tmp);
});

Typescript Error code: 2366, Function lacks ending return statement and return type does not include 'undefined'

I am currently working on the server/database of my project. It is currently composed of Javascript, Typescript, MongoDB, Apollo-Server, and Express. The error above keeps coming up and I am not sure how to solve it. Here is the code I have on my index.ts file for my database folder.
import { MongoClient } from "mongodb";
import { Database, Listing, Booking, User } from '../lib/types';
const url = `mongodb+srv://${process.env.DB_USER}:${process.env.DB_USER_PASSWORD}#${process.env.DB_CLUSTER}.mongodb.net`;
export const connectDatabase = async (): Promise<Database> => {
try {
const client = await MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true });
const db = client.db("main");
return {
bookings: db.collection<Booking>("bookings"),
listings: db.collection<Listing>("listings"),
users: db.collection<User>("users"),
};
} catch (error) {
console.log(error);
}
};
Any help would be greatly appreciated.
You're catching the error but then you're not returning anything from the function. That is why it's complaining. Either remove the try/catch and handle the error in the function calling this one or return something usable to the caller.
Set "noImplicitReturns" to false in tsconfig.json.

info argument is empty in Apollo GraphQL resolver type signature

I'm working on this library https://github.com/ilyaskarim/wertik-js called Wertik JS to make GraphQL + Rest API more easily, In resolvers, when I console log info, it shows undefined. For each module, I have created dynamic resolvers to make things more easy for developers who will use this library.
let object = {
create: async (_:any, args:any, context:any,info: any) => {
console.log(info); // This will be undefined
let v = await validate(validations.create,args.input);
let {success} = v;
if (!success) {
throw new ApolloError("Validation error",statusCodes.BAD_REQUEST.number,{list: v.errors})
}
try {
let createModel = await model.create(args.input);
pubsub.publish(`${camelCase(moduleName)}Created`, { [`${camelCase(moduleName)}Created`]: createModel });
return createModel;
} catch (e) {
return internalServerError(e);
}
},
}
Line: https://github.com/ilyaskarim/wertik-js/blob/ec813f49a14ddd6a04680b261ae4ef2aadc2b1a5/src/framework/dynamic/resolvers.ts#L102
The info is described in Apollo Server Documentation https://www.apollographql.com/docs/apollo-server/essentials/data/#resolver-type-signature, Which says: This argument contains information about the execution state of the query, including the field name, the path to the field from the root, and more. For me, unfortunately, it is getting undefined.
To reproduce the issue:
Download https://github.com/ilyaskarim/wertik-js/tree/development
Yarn install
Go to examples/demo
Run node index.js
Now go to http://localhost:1209/
Enter this mutation for example:
mutation {
createRole(input: {name: "Asd"}) {
name
}
}
This line executes on this mutation https://github.com/ilyaskarim/wertik-js/blob/ec813f49a14ddd6a04680b261ae4ef2aadc2b1a5/src/framework/dynamic/resolvers.ts#L102
And returns undefined on the console.
This is how I setup the application:
const { ApolloServer } = require('apollo-server');
import mutations from "./loadAllMutations";
import queries from "./loadAllQueries";
import resolvers from "./loadAllResolvers";
import subscriptions from "./loadAllSubscriptions";
import schemas from "./loadAllSchemas";
import generalSchema from "./../helpers/generalSchema";
export default function (rootDirectory: string,app: any,configuration: object) {
let allMutations = mutations(rootDirectory);
let allQueries= queries(rootDirectory);
let allSchemas = schemas(rootDirectory);
let allResolvers = resolvers(rootDirectory);
let allSubscriptions = subscriptions(rootDirectory);
let {validateAccessToken} = require(`${rootDirectory}/framework/predefinedModules/user/auth`).default;
let mainSchema = `
${generalSchema}
${allSchemas}
type Subscription {
${allSubscriptions}
}
type Mutation {
${allMutations}
}
type Query {
${allQueries}
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
`;
const server = new ApolloServer({
typeDefs: mainSchema,
resolvers: allResolvers,
context: async (a: any) => {
await validateAccessToken(a.req);
}
});
server.listen(1209).then(({ url, subscriptionsUrl }) => {
console.log(`Server ready at ${url}`);
console.log(`Subscriptions ready at ${subscriptionsUrl}`);
});
}
What could be a possible reason?
You're truncating the parameters received by the resolvers inside this module. If you need to assign a function to some object property, it's much better to just do it like this:
mutations: {
[`create${moduleName}`]: mutations[`create${moduleName}`],
},
This is not only more succinct, but it also means you don't risk accidentally leaving off a parameter, which is what happened here.

Categories

Resources