So far I have a Next.js app with a form component that looks like this:
the components/Form.js
import { useState } from 'react'
import { useRouter } from 'next/router'
import { mutate } from 'swr'
const Form = ({ formId, demoForm, forNewDemo = true }) => {
const router = useRouter()
const contentType = 'application/json'
const [errors, setErrors] = useState({})
const [message, setMessage] = useState('')
const [form, setForm] = useState({
projectName: demoForm.projectName,
projectVariable: demoForm.projectVariable,
})
/* The PUT method edits an existing entry in the mongodb database. */
const putData = async (form) => {
const { id } = router.query
try {
const res = await fetch(`/api/demos/${id}`, {
method: 'PUT',
headers: {
Accept: contentType,
'Content-Type': contentType,
},
body: JSON.stringify(form),
})
// Throw error with status code in case Fetch API req failed
if (!res.ok) {
throw new Error(res.status)
}
const { data } = await res.json()
mutate(`/api/demos/${id}`, data, false) // Update the local data without a revalidation
router.push('/')
} catch (error) {
setMessage('Failed to update')
}
}
/* The POST method adds a new entry in the mongodb database. */
const postData = async (form) => {
try {
const res = await fetch('/api/demos', {
method: 'POST',
headers: {
Accept: contentType,
'Content-Type': contentType,
},
body: JSON.stringify(form),
})
// Throw error with status code in case Fetch API req failed
if (!res.ok) {
throw new Error(res.status)
}
router.push('/')
} catch (error) {
setMessage('')
}
}
const handleChange = (e) => {
const target = e.target
const value =
target.name === 'poddy_trained' ? target.checked : target.value
const name = target.name
setForm({
...form,
[name]: value,
})
}
/* Makes sure demo info is filled for demo name, owner name, species, and image url*/
const formValidate = () => {
let err = {}
if (!form.projectVariable) err.projectVariable = 'Function is required'
return err
}
const handleSubmit = (e) => {
e.preventDefault()
const errs = formValidate()
if (Object.keys(errs).length === 0) {
forNewDemo ? postData(form) : putData(form)
} else {
setErrors({ errs })
}
}
return (
<>
<form id={formId} onSubmit={handleSubmit}>
<textarea
name="projectVariable"
value={form.projectVariable}
onChange={handleChange}
/>
<input
name="projectName"
value={form.projectName}
onChange={handleChange}
/>
<button type="submit" className="btn">
Submit
</button>
</form>
<div>
{Object.keys(errors).map((err, index) => (
<li key={index}>{err}</li>
))}
</div>
</>
)
}
export default Form
the form is located at the /new page
pages/new.js
import Form from '../components/Form'
const New = () => {
const demoForm = {
projectVariable: [],
projectName: []
}
return <Form formId="add-demo-form" demoForm={demoForm} />
}
export default New
and here is my api at pages/api/demos/index.js
import dbConnect from '../../../lib/dbConnect';
import Demo from '../../../models/Demo';
import fs from 'fs';
export default async function handler(req, res) {
const {
query: { id },
method,
} = req
await dbConnect()
switch (method) {
case 'POST':
try {
const timestamp = (JSON.parse(JSON.stringify(new Date())));
const newJsonDirectory = "projects/";
const newJsonFile = newJsonDirectory + timestamp + ".json";
const activeUser = process.env.ACTIVE_USERNAME;
const newFileName = req.body.projectName;
fs.writeFileSync(newJsonFile, JSON.stringify(
{
"order": [
{
"username": activeUser,
"pages": [
{
"display": "projects",
"subpages": [
{
"route": timestamp,
"display": newFileName
}
]
}
]
}
]
}));
const demo = await Demo.create(
req.body
)
res.status(201).json({ success: true, data: demo })
} catch (error) {
res.status(400).json({ success: false })
}
break
default:
res.status(400).json({ success: false })
break
}
}
lets say I submit my values into the form input: projectName
input: hello-world
the projects folder would then contain a file called 2022-09-11T231607.396Z.json that looks like this
{
"order": [
{
"username": "projectmikey",
"pages": [
{
"display": "projects",
"subpages": [
{
"route": "2022-09-11T23:16:07.396Z",
"display": "hello-world"
}
]
}
]
}
]
}
The nested format of the JSON object is perfect however I can't seem to get the query id to show up in place of the date in the new file.
I have tried changing the timestamp variable to ${id} or id, among other things within my pages/api/demos/index.js but nothing seems to work.
the changed file
import dbConnect from '../../../lib/dbConnect';
import Demo from '../../../models/Demo';
import fs from 'fs';
export default async function handler(req, res) {
const {
query: { id },
method,
} = req
await dbConnect()
switch (method) {
case 'POST':
try {
const timestamp = (JSON.parse(JSON.stringify(new Date())));
const newJsonDirectory = "projects/";
const newJsonFile = newJsonDirectory + id + ".json";
const activeUser = process.env.ACTIVE_USERNAME;
const newFileName = req.body.projectName;
fs.writeFileSync(newJsonFile, JSON.stringify(
{
"order": [
{
"username": activeUser,
"pages": [
{
"display": "projects",
"subpages": [
{
"route": id,
"display": newFileName
}
]
}
]
}
]
}));
const demo = await Demo.create(
req.body
)
res.status(201).json({ success: true, data: demo })
} catch (error) {
res.status(400).json({ success: false })
}
break
default:
res.status(400).json({ success: false })
break
}
}
the file name shows up as undefined.json within the projects/ directory and the new contents looks are:
{
"order": [
{
"username": "projectmikey",
"pages": [
{
"display": "projects",
"subpages": [
{
"display": "hello-world"
}
]
}
]
}
]
}
I am relatively new to Node.js and have been banging my head around on this one
I am also wondering how to append to new JSON file rather that creating a new file upon each request...
If anyone could help me out with either of those 2 questions it would be greatly appreciated! Thanks y'all