How to upload a single image file with next.js to mongodb? - 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

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

How to store additional fields in mongo upon user sign up using next-auth's Email Provider

I have create a login form with two fields
a field where the user can select their university
a field where the user can enter their university email address
I use next-auth's Email Provider under the hood, so when they fill out those two fields and click on "sign up", a document will automatically be created by default in my MongoDB "users" collection that looks like this
email: 'theuseremail#something.com',
emailVerified: '2022-07-16T11:54:06.848+00:00'
and the user gets an email with a magic sign link to sign in to the website.
My problem is the following:
I want to be able to store not just the user email but also the university they selected when filling out the sign up form. But the object that gets created by default in my MongoDB only has the "email" and the "emailVerified" fields. I cannot find a way to capture other data (e.g. the user's selected university) to create the user in the database.
Is there any obvious way of doing so that I am missing? I have looked around but couldn't find any working example of this! Any help is appreciated.
This is my pages/api/[...nextAuth].js file:
import NextAuth from "next-auth"
import nodemailer from 'nodemailer'
import EmailProvider from 'next-auth/providers/email'
import { MongoDBAdapter } from "#next-auth/mongodb-adapter"
import clientPromise from "../../../utils/mongoClientPromise"
const THIRTY_DAYS = 30 * 24 * 60 * 60
const THIRTY_MINUTES = 30 * 60
export default NextAuth({
secret: process.env.NEXTAUTH_SECRET,
session: {
strategy: 'jwt',
maxAge: THIRTY_DAYS,
updateAge: THIRTY_MINUTES
},
adapter: MongoDBAdapter(clientPromise),
providers: [
EmailProvider({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
from: process.env.EMAIL_FROM,
async sendVerificationRequest ({
identifier: email,
url,
provider: { server, from }
}) {
const { host } = new URL(url)
const transport = nodemailer.createTransport(server)
await transport.sendMail({
to: email,
from,
subject: `Sign in to ${host}`,
text: text({ url, host }),
html: html({ url, host, email })
})
}
})
],
pages: {
signIn: '/login',
}
})
function html ({ url, host, email }) {
const escapedEmail = `${email.replace(/\./g, '​.')}`
const escapedHost = `${host.replace(/\./g, '​.')}`
// Your email template here
return `
<body>
<h1>Your magic link! 🪄</h1>
<h3>Your email is ${escapedEmail}</h3>
<p>
Sign in to ${escapedHost}
</body>
`
}
// Fallback for non-HTML email clients
function text ({ url, host }) {
return `Sign in to ${host}\n${url}\n\n`
}
This is my Login page in pages/login.tsx:
import { Row, Col, Button, Input, Form, Space } from "antd";
import { useSession, signIn } from "next-auth/react";
import { getCsrfToken } from "next-auth/react"
import { useRouter } from 'next/router';
import { useState } from "react";
import UniversitySearchAndSelectDropdown from "../components/UniversitySearchAndSelectDropdown";
import data from '../mock_api_payload.json'
export default function LoginPage({ csrfToken }) {
const navigate = useRouter();
const { data: session } = useSession()
const [selectedUniversityId, setSelectedUniversityId] = useState('');
const [form] = Form.useForm();
if (session) {
navigate.push("/")
}
if (!session) {
return (
<>
<Row justify="center" style={{marginTop: '2rem'}}>
<Col>
<form method="post" action="/api/auth/signin/email">
<input name="csrfToken" type="hidden" defaultValue={csrfToken} />
<Row justify="center">
<Col>
<Space direction="vertical">
<Input
placeholder="Enter your university email address"
type="email"
id="email"
name="email"
/>
</Space>
</Col>
</Row>
<Row justify="center" style={{marginTop: '1rem'}}>
<Col>
<Button
htmlType="submit"
shape="round"
type="primary"
>Sign in</Button>
</Col>
</Row>
</form>
</Col>
</Row>
</>
)
}
};
export async function getServerSideProps(context: any) {
const csrfToken = await getCsrfToken(context)
return {
props: { csrfToken },
}
}
Thank you!

How to upload multiple images to Deno?

I want to know, how can we upload multiple images using Deno, I'm using mongo Database. I followed Article to Upload a single image to the server.
app.ts
import { Application, Router } from 'https://deno.land/x/oak/mod.ts';
import { viewEngine, engineFactory, adapterFactory } from 'https://deno.land/x/view_engine/mod.ts';
import { upload } from 'https://deno.land/x/upload_middleware_for_oak_framework/mod.ts';
// Setting up our view Engine
const ejsEngine = engineFactory.getEjsEngine();
const oakAdapter = adapterFactory.getOakAdapter();
// Initiate our Application and Router
const app = new Application();
const router = new Router();
app.use(viewEngine(oakAdapter, ejsEngine));
// Setting our router to handle request
router
.get('/', (ctx) => {
ctx.render('index.ejs')
})
.post('/upload', upload('uploads'), async (context: any, next: any) => {
const file = context.uploadedFiles;
console.log(file);
context.response.redirect('/');
});
// Passing Router as middleware
app.use(router.routes());
app.use(router.allowedMethods());
// Server our app
console.log('App is listening on PORT 8000');
await app.listen({ port: 8000 });
index.ejs
<body>
<form method="POST" enctype="multipart/form-data" action="/validated">
<input type="file" name="fileName" multiple> <br>
<input type="submit" value="submit">
</form>
</body>

File upload inside window.addEventListener

My brain's hurting. After my page loads, I get some HTML. This is a stripped-down version:
window.addEventListener('load', () => {
if (window.location.pathname === '/profile' && Cookies.get('token')) {
axios.get('/api/profile-info').then(res => {
const member = res.data.member
const memberInfo = `
<form enctype="multipart/form-data" id="uploadProfilePictureForm">
<input type="file"/>
<button onclick="uploadPicture(event)">Upload</button>
</form>
`;
})
}
})
I then handle the onclick event:
const uploadPicture = (event) => {
event.preventDefault()
const form = document.getElementById('uploadProfilePictureForm')
console.log(form) // Just shows the HTML form
}
This handler is placed before window.addEventListener
The file name appears on the page, but after clicking "Upload", it won't show in the console (which I plan to send to my server).
How do I allow an onclick event to handle a file upload?
Solved
Inside window.addEventListener(), I used a simple input tag:
<input type="file" id="fileUpload" onchange="uploadPicture()"/>
Then, outside this event listener, I defined the uploadPicture() function:
function uploadPicture() {
var FD = new FormData()
var fileInput = document.getElementById('fileUpload')
FD.append("pictureFile", fileInput.files[0])
const data = FD.entries().next().value
console.log('data\n', data) // This is the FormData array
}

React|Rest API: Storing form data into an object on the REST API

I've set up a react web application that's currently listing all "Employees" from a mongodb.
I'm now trying to "add" employees to the database through a react frontend form.
I've managed to pass the data from the form to the application but I'm unsure of the process I need to go through to actually get that data solidified into an object and stored in the api.
Please excuse my code, it's disgusting as this is my first week learning react(honestly with little js knowledge, that's another story) and I've just patched together like 20 tutorials....
Here's my Form class:
class Form extends React.Component {
state = {
fullname: '',
}
change = e => {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state)
this.setState({
fullname: ''
})
}
render() {
return <div>
<form>
<input name="fullname" placeholder="Full Name" value={this.state.fullname} onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
</div>
}
}
and my Listing(?) class:
class EmployeeList extends React.Component {
constructor(props) {
super(props);
this.state = {employee: []};
this.EmployeeList = this.EmployeeList.bind(this)
this.componentDidMount = this.componentDidMount.bind(this)
}
componentDidMount() {
this.EmployeeList();
}
EmployeeList() {
fetch('/api/employees').then(function(data){
return data.json();
}).then( json => {
this.setState({
employee: json
});
console.log(json);
});
}
onSubmit = fields => {
console.log('app component got: ', fields)
}
render() {
//return a mapped array of employees
const employees = this.state.employee.map((item, i) => {
return <div className="row">
<span className="col-sm-6">{item.fullname}</span>
<span className="col-sm-2" id={item.action1}></span>
<span className="col-sm-2" id={item.action2}></span>
<span className="col-sm-2" id={item.action3}></span>
</div>
});
return <div>
<Form onSubmit={fields => this.onSubmit(fields)}/>
<div className="container">
<div className="row">
<div className="col-sm-6 bg-warning"><h3>Full Name</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 1</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 2</h3></div>
<div className="col-sm-2 bg-success"><h3>Action 3</h3></div>
</div>
</div>
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">{ employees }</div>
</div>
</div>
}
}
I've managed to pass the data to the listing app evident by
onSubmit = fields => {
console.log('app component got: ', fields)
}
But how can I go about making a post request to store this data I send into an object on the db? And then also reload the page so that the new list of all employee's is shown?
Thanks so much for your time!
You can use fetch API to make POST request as well. Second parameter is the config object wherein you can pass the required request configurations.
fetch('url', {
method: 'post',
body: JSON.stringify({
name: fields.fullname
})
})
.then(response) {
response.json();
}
.then( json => {
this.setState({
employee: json
});
});
Additional Request Configs which can be used :
method - GET, POST, PUT, DELETE, HEAD
url - URL of the request
headers - associated Headers object
referrer - referrer of the request
mode - cors, no-cors, same-origin
credentials - should cookies go with the request? omit, same-origin
redirect - follow, error, manual
integrity - subresource integrity value
cache - cache mode (default, reload, no-cache)