how to fix connection issue between vercel and mongodb - mongodb

im having issues with deploying my site using vercel
this is the error:
[GET] /api/menu/menuItems
00:06:56:04
2022-09-06T05:06:56.382Z 315e3b25-b88f-4c8a-8d50-a323d2fd2b26 ERROR MongoParseError: Invalid scheme, expected connection string to start with "mongodb://" or "mongodb+srv://"
at new ConnectionString (/var/task/node_modules/mongodb-connection-string-url/lib/index.js:86:19)
at parseOptions (/var/task/node_modules/mongodb/lib/connection_string.js:213:17)
at new MongoClient (/var/task/node_modules/mongodb/lib/mongo_client.js:62:63)
at Object.9590 (/var/task/.next/server/pages/api/menu/menuItems.js:35:14)
at __webpack_require__ (/var/task/.next/server/webpack-api-runtime.js:25:42)
at __webpack_exec__ (/var/task/.next/server/pages/api/menu/menuItems.js:63:39)
at /var/task/.next/server/pages/api/menu/menuItems.js:64:28
at Object.<anonymous> (/var/task/.next/server/pages/api/menu/menuItems.js:67:3)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
RequestId: 315e3b25-b88f-4c8a-8d50-a323d2fd2b26 Error: Runtime exited with error: exit status 1
Runtime.ExitError
I have the env variable added to my vercel project already the same as the local one but when I deploy my app it'll load the app but none of the data comes through. it seems to not be able to make the api call for some reason I cant figure it out. I even tried adding the env variable with cli and still didn't work
mongodb.js
import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
}
let client
let clientPromise
if (!process.env.MONGODB_URI) {
throw new Error('Please add your Mongo URI to .env.local')
}
if (process.env.NODE_ENV === 'development') {
// In development mode, use a global variable so that the value
// is preserved across module reloads caused by HMR (Hot Module Replacement).
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// In production mode, it's best to not use a global variable.
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise
/api/menu/menuItems.js
import clientPromise from "../../../mongodb";
export default async function handler(req, res) {
const client = await clientPromise;
const db = client.db("RESTAURANT");
const menu = await db.collection("menuItems").find({}).toArray();
res.json({ status: 200, data: menu });
}
/pages/index.js
import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import mainImg from '../public/burrito.jpg'
import salsaImg from '../public/salsa.jpg'
import inteiorImg from '../public/place.jpg'
import styles from '../styles/Home.module.css'
import {motion} from 'framer-motion'
import Button from '#mui/material/Button'
import Card from '../components/specialsCard'
export default function Home({}) {
const [menuItems, setMenuItems] = useState([]);
console.log(process.env.MONGODB_URI)
useEffect(() => {
(async () => {
const res = await fetch("/api/menu/menuItems");
const menuItems = await res.json();
setMenuItems(menuItems);
})()
}, [])
return (
<div style={{ width: '100%'}}>
<Head>
<title>Mexican restaurant</title>
<meta name='keywords' content='restaurant, mexican, mexican food'/>
</Head>
<div className={styles.header}>
<Image className={styles.img} src={mainImg} layout='fill' alt='mainImage'/>
<motion.div className={styles.overlay} initial='hidden' animate='visible' variants={{
hidden: {
scale: .8,
opacity: 0
},
visible: {
scale: 1,
opacity: 1,
transition: {
delay: .6
}
}
}}>
<h3 style={{fontSize: '60px', margin:'0px',fontFamily: 'Hurricane, cursive'}}>Best Burritos In Town</h3>
<p>burritos only made with organic and fresh ingredients</p>
</motion.div>
</div>
<div className={styles.salsaContainer}>
<div className={styles.salsaContent}>
<p style={{color: '#ff8d66', fontFamily: 'Hurricane, cursive',fontWeight: 'bold'}}>FREE CHIPS AND SALSA WITH EVERY ORDER</p>
<h2 style={{marginTop: '2px'}}>TRY OUR INFAMOULSY SPICY SALSA. NO EXTRA CHARGE</h2>
<p>Made with fresh tomotaos and spicy peppers. This salsa is known to leave customers happy and THIRSTY for more</p>
<Button variant='contained' href='/menu' size='large' style={{backgroundColor: '#FFD966', marginTop: '20px'}}>Order Here</Button>
</div>
<div className={styles.salsaImg}>
<Image src={salsaImg} alt='salsa'/>
</div>
</div>
<div className={styles.subContainer}>
<Image src={inteiorImg} alt='inteior'/>
<div className={styles.interiorContent}>
<h2 style={{fontFamily: 'Hurricane, cursive'}}>19th CENTURY DECOR</h2>
<p>Come eat in and enjoy our welcoming aesthetics inspired by 19th century spanish design.</p>
</div>
</div>
<div className={styles.subSpContainer}>
<h2>popular menu items</h2>
<div className={styles.subSpItems}>
{(menuItems.data || []).map(item => {
if(item.type === 'special'){
return <Card title={item.title}
description={item.description}
imgUrl={item.imgUrl}
id={item._id}
key={item._id}/>
}
})}
<span style={{flexGrow:'2'}}><h2 style={{textAlign:'center'}}>Check these hot items and more!
<br/> <Link href='/menu'><a>See menu here</a></Link></h2></span>
</div>
</div>
</div>
)
}

Related

Next JS 13 data send from client component to server side component but show Not found

JS 13 and inside my ReadMoreButton client component i push my article data using useRouter hook of NEXT.
Not i can not use useRouter hook inside NEXT.JS server component so here i fetch searchParams and fetch that data.
here problem is before rendering i am checking if searchParams are defined or not not if i check in development everything work fine it render data but in production mode it show page not found error even if data is correctly send.
when i run next build it give me following output Output
and i am running side in production mode using next start and it show page not found when i do /article?serchParamsData.
You can check my whole code here : https://github.com/ssiwach8888/Next.JS-News-App
i also deploy production build on Vercel but it also show same error.
I am using NEXT.JS 13 with typescript
# ReadMoreButton.tsx "First Control goes here."
"use client";
type Props = {
article: NewsData;
};
import { useRouter } from "next/navigation";
//For navigate to SSC
const ReadMoreButton = ({ article }: Props) => {
const router = useRouter();
const handleClick = () => {
const queryString = Object.entries(article)
.map(([key, value]) => `${key}=${value}`)
.join("&");
const url = `/article?${queryString}`;
router.push(url);
};
return (
<button
className="bg-orange-400 h-10 rounded-b-lg dark:text-gray-900 hover:bg-orange-500"
onClick={handleClick}
>
Read More
</button>
);
};
export default ReadMoreButton;
# Article.tsx "Then we navigate to this page."
type Props = {
searchParams?: NewsData;
};
import { notFound } from "next/navigation";
import LiveTimestamp from "../Components/LiveTimestamp";
import Link from "next/link";
const ArticlePage = ({ searchParams }: Props) => {
if (
(searchParams && Object.entries(searchParams).length === 0) ||
!searchParams
) {
return notFound();
}
const article: NewsData = searchParams;
return (
<article className="mt-6">
<section className="flex flex-col lg:flex-row pb-24 px-0 lg:px-10">
<img
src={article.image === "null" ? "/no-image.jpeg" : article.image}
alt={article.title}
className="h-50 max-w-md mx-auto md:max-w-lg lg:max-w-xl object-contain rounded-lg shadow-md"
/>
<div className="px-8">
<Link legacyBehavior href={article.url || ""}>
<a target="_blank">
<h1 className="headerTitle hover:underline cursor-pointer px-0 pb-2">
{article.title}
</h1>
</a>
</Link>
<div className="flex divide-x-2 space-x-4">
<h2 className="font-bold">
By: {article.author !== "null" ? article.author : "Unknown"}
</h2>
<h2 className="font-bold pl-4">Source: {article.source}</h2>
<p className="pl-4">
<LiveTimestamp
time={
article.published_at === "null" ? "" : article.published_at
}
/>
</p>
</div>
<p className="pt-4 text-lg">{article.description}</p>
</div>
</section>
</article>
);
};
export default ArticlePage;
You just need to put the article page in [bracket] to make it dynamic so next js can fetch all pages otherwise it would display blank----
change article folder to [article]
more reference https://nextjs.org/docs/routing/dynamic-routes

Cannot read properties of undefined (reading 'label')

I am trying to implement Autocomplete using materialUI and nextJs. I encountered this error whenever I start the search.
My code is like this
client:
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import React, {useState} from 'react'
import TextField from "#mui/material/TextField"
import Autocomplete from "#mui/material/Autocomplete"
import axios from "axios"
const getString = async (str) =>{
try{
// let searchableString = str.replace(/,/g, "")
let url = "http://localhost:4000/searchpop?search=" + str;
let { data } = await axios.get(url)
return data
} catch (error){
console.log(error);
}
}
export default function Home() {
const [searchOption, setOption] = useState([]);
searchOption.map((obj)=>{
console.log(obj.population_mesh.cui_str);
})
const onChangeOne = async (e) =>{
if(e.target.value) {
let data = await getString(e.target.value)
setOption(data);
}
}
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div style={{marginTop: 50}}>
<Autocomplete
freeSolo
filterOptions={(x)=> x}
onChange={(e)=> console.log(e)}
options= {searchOption ? searchOption.map((obj)=> obj.population_mesh.cui_str): []}
// options = {hello}
renderInput={(params)=>(
<TextField
{...params}
label="Search String"
onChange={(e) => onChangeOne(e)}
/>
)}
/>
</div>
</div>
)
}
I am very new to these technologies. So please help me in resolving this issue.
I also dont understand below. here is population_mesh is the field and cui_str is is subfield in the data base which i want to print for autocomplete
options= {searchOption ? searchOption.map((obj)=> obj.population_mesh.cui_str): []}

How to upload a single image file with next.js to mongodb?

When I'm uploading form data to mongodb, in the image section it shows this:
"C:\fakepath\imageName.jpg" with no actual image in it. And when I fetch the data from the database and map them, all the other data like title and body gets shown, but not the image. Because there wasn't any image to begin with. It was just a fake path.
So, how can I upload image file not the path to MongoDB.
I'm using Next.js and the mongodb npm package.
This is the pages/api/new-post.js file:
import { MongoClient } from "mongodb";
export default async function handler(req, res) {
if (req.method === 'POST') {
const data = req.body
const client = await MongoClient.connect('mongodb://localhost:27017/blog-nextjs')
const db = client.db()
const postCollections = db.collection('posts')
const result = await postCollections.insertOne(data)
console.log(result)
client.close()
res.status(201).json({message: 'Post Inserted'})
}
}
The form code I used in pages/new-post.js are:
import { useRef } from "react";
export default function NewPostForm(props) {
const titleInputRef = useRef()
const imageInputRef = useRef()
const bodyInputRef = useRef()
function submitHandler(e) {
e.preventDefault()
const enteredTitle = titleInputRef.current.value
const enteredImage = imageInputRef.current.value
const enteredBody = bodyInputRef.current.value
const postData = {
title: enteredTitle,
image: enteredImage,
body: enteredBody
}
props.onAddPost(postData)
}
return (
<div>
<form onSubmit={submitHandler}>
<div>
<label htmlFor="title">Title</label>
<input
placeholder="Post Title"
required
type="text"
ref={titleInputRef}
/>
</div>
<div>
<label htmlFor="body">Post</label>
<textarea
placeholder="Post Body "
required
ref={bodyInputRef}
></textarea>
</div>
<div>
<label htmlFor="image">Image</label>
<input
type="file"
required
placeholder="Image"
accept="image/png, image/gif, image/jpeg"
// accept="image/*"
ref={imageInputRef}
/>
</div>
<div><button>Post</button></div>
</form>
</div>
);
}
It's a bad approach to load images directly in MongoDb
You should store it to 3d party hostings and save only link(s) to your image.
Check related question

Building error at #parcel/transformer-js "cannot access a scoped thread local variable without..."

I have a project with Parcel and Preact (to implement Algolia Autocomplete Search) and I suddenly got an error while npx parcel build theme/app-home.tsx --log-level verbose.
It worked before and I'm working with git but I can't found what changed to break the build.
The error:
Building app-home.tsx...
thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first', /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-tls-1.0.0/src/lib.rs:168:9
🚨 Build failed.
#parcel/transformer-js: cannot access a scoped thread local variable without calling `set` first
Error: cannot access a scoped thread local variable without calling `set` first
at Object.transform (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/transformer-js/lib/JSTransformer.js:365:31)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Transformation.runTransformer (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:617:5)
at async Transformation.runPipeline (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:366:36)
at async Transformation.runPipelines (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:244:40)
at async Transformation.run (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/core/lib/Transformation.js:170:19)
at async Child.handleRequest (/Users/cozarkd/Documents/GitHub/projectX/node_modules/#parcel/workers/lib/child.js:217:9)
The tsx file:
/** #jsx h */
import {
autocomplete,
AutocompleteComponents,
getAlgoliaResults,
} from '#algolia/autocomplete-js';
import algoliasearch from 'algoliasearch';
import { h, Fragment } from 'preact';
const appId = 'theappid';
const apiKey = 'theapikey';
const searchClient = algoliasearch(appId, apiKey);
/* const querySuggestionsPlugin = createQuerySuggestionsPlugin({
searchClient,
indexName: 'database_query_suggestions',
getSearchParams() {
return {
hitsPerPage: 5,
};
},
}); */
autocomplete({
// debug: true,
container: '#autocomplete',
placeholder: 'Escribe aquí, sin miedo',
openOnFocus: false,
defaultActiveItemId: 0,
autoFocus: true,
getSources({ query }) {
return [
{
sourceId: 'plantag',
getItemUrl({ item }) {
return item.url;
},
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'database',
query,
params: {
hitsPerPage: 10,
clickAnalytics: true,
attributesToSnippet: [
'name:10',
'nombre'
],
snippetEllipsisText: '…',
},
},
],
});
},
templates: {
item({ item, components }) {
return <ProductItem hit={item} components={components} />;
},
noResults() {
return 'No hay plantas coincidentes :(';
},
},
},
];
},
// Default Navigator API implementation
navigator: {
navigate({ itemUrl }) {
window.location.assign(itemUrl);
},
navigateNewTab({ itemUrl }) {
const windowReference = window.open(itemUrl, '_blank', 'noopener');
if (windowReference) {
windowReference.focus();
}
},
navigateNewWindow({ itemUrl }) {
window.open(itemUrl, '_blank', 'noopener');
},
},
});
function ProductItem({ hit, components }: ProductItemProps) {
return (
<div className="c-single-result">
<a href={hit.url} className="aa-ItemLink">
<div className="l-flex-container">
<div className="aa-ItemContent">
<div className="aa-ItemContentBody">
<div className="aa-ItemContentTitle">
<components.Snippet hit={hit} attribute="nombre" />
</div>
</div>
<div className="aa-ItemContentSubtitle">
<components.Snippet hit={hit} attribute="name" />
</div>
<div className="aa-ItemActions">
<button
className="aa-ItemActionButton aa-DesktopOnly aa-ActiveOnly"
type="button"
title="Select"
style={{ pointerEvents: 'none' }}
>
<svg viewBox="0 0 30 27" width="30" height="27" fill="currentColor">
<path d="M10.0611 23.8881C10.6469 24.4606 10.6469 25.389 10.0611 25.9615C9.47533 26.5341 8.52558 26.5341 7.9398 25.9615L0.441103 18.632C0.4374 18.6284 0.433715 18.6248 0.430051 18.6211C0.164338 18.3566 0.000457764 17.994 0.000457764 17.594C0.000457764 17.3952 0.0409356 17.2056 0.114276 17.0328C0.187475 16.8598 0.295983 16.6978 0.439798 16.5572L7.9398 9.22642C8.52558 8.65385 9.47533 8.65385 10.0611 9.22642C10.6469 9.79899 10.6469 10.7273 10.0611 11.2999L5.12178 16.1278H13.5005C20.9565 16.1278 27.0005 10.2202 27.0005 2.93233V1.46616C27.0005 0.656424 27.672 -1.90735e-06 28.5005 -1.90735e-06C29.3289 -1.90735e-06 30.0005 0.656424 30.0005 1.46616V2.93233C30.0005 11.8397 22.6134 19.0602 13.5005 19.0602H5.12178L10.0611 23.8881Z"/>
</svg>
</button>
</div>
</div>
<div className="aa-ItemIcon aa-ItemIcon--picture aa-ItemIcon--alignTop">
<img src={hit.image} alt={hit.name} width="40" height="40" />
</div>
</div>
</a>
</div>
);
}
I can provide more info if needed. Did some search but I couldn't find anything related to Parcel with that error.
I'm able to repro the problem, and it looks like this might be a bug in parcel related to the JSX pragma at the top of the file (see github discussion here). If you remove this line, it should compile fine:
/** #jsx h */ <--delete this line.
Parcel will automatically detect which JSX pragma to use for your project based on finding preact as a dependency in package.json (see documentation). You can also manually control it with a tsconfig.json file like this:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
(This bug should probably still be fixed, but hopefully this is enough to help you work around it. I filed a github issue here).

react-google-maps StandaloneSearchBox set specific country restriction?

I am trying to set a specific country restriction using react-google-maps StandaloneSearchBox.
I have tried componentRestrictions, but I'm not sure how to use it.
Sharing my code below:
export const AutoCompleteSearchBox = compose(
withProps({
googleMapURL:googleMapUrl,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px`, top:'3px' }} />,
}),
lifecycle({
componentWillMount() {
const refs = {}
this.setState({
types: ['(regions)'],
componentRestrictions: {country: "bd"},
onSearchBoxMounted:ref =>{ refs.searchBox = ref; },
onPlacesChanged:()=>{
const places = refs.searchBox.getPlaces();
this.props.onPlacesChanged(places);
},
})
const options = {
types: ['(regions)'],
componentRestrictions:{ country: 'bd' }
}
},
}),
withScriptjs
)`(props =>
<div data-standalone-searchbox="">
<StandaloneSearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
onPlacesChanged={props.onPlacesChanged}
controlPosition={ window.google.maps.ControlPosition.TOP_LEFT}
>
<TextField
className={props.inputClass}
placeholder={props.inputPlaceholder}
label={props.inputLabel}
name={props.inputName}
value={props.inputValue}
onChange={props.inputOnChange}
helperText={props.inputHelperText}
error={props.inputError}
/>
</StandaloneSearchBox>
</div>
);`
How can I solve this problem?
You can't add such restrictions for the SearchBox results, but you can specify the area towards which to bias query predictions. Predictions are biased towards, but not restricted to, queries targeting these bounds.
If you want to show only specific places, then you can Google Place Autocomplete feature. For it you don't event need to use additional React libraries for Google Maps. Here's the example:
import React, { Component } from 'react';
import Script from 'react-load-script'
class LocationMap extends Component {
handleScriptLoad() {
const inputEl = document.getElementById('address-input');
/*global google*/
var options = {
//types: ['address'],
componentRestrictions: {country: 'by'}
};
this.autocomplete = new google.maps.places.Autocomplete(inputEl, options);
this.autocomplete.addListener('place_changed', this.handlePlaceSelect.bind(this));
}
handlePlaceSelect() {
console.log(this.autocomplete.getPlace());
}
render() {
return (
<section>
<Script
url="https://maps.googleapis.com/maps/api/js?key=API_KEY&v=3.33&libraries=places&language=en&region=US"
onLoad={this.handleScriptLoad.bind(this)}
/>
<div className="form-group">
<label htmlFor="address-map">Enter address</label>
<input type="text"
autoComplete="new-password"
className="form-control"
id="address-input"
name="address"/>
</div>
</section>
);
}
}
export default LocationMap;
Don't forget to add react-load-script package: npm i react-load-script --save