I have been having a tough time writing form input to a seperate file in Javascript. I posted a repo with a demo I set up to show the problem I have been having! Feel free to take a look. https://github.com/projectmikey/projectmikey-cant-write-to-api-dir-stackoverflow
The app works fine locally from both “next dev” and “next start” when I end up at a http://localhost:3000 url.
My api at pages/api/demos/index.js
responds with the body from the form’s textarea at the pages/new
page and writes it as a new file at /api/newfiles/file
(which becomes essentially a script file that doesnt have an extension.) This is perfect! however, I cant seem to get the file written to the /api/newfiles
folder (or any other folder for that matter with fs.writeFileSync) when I am deployed live to Vercel.
I was also hoping to use shell-js to fire off a shell script (script.sh
)
So with this not working I have really been banging my head around trying to figure it out!
I am not an expert by any means at debugging the console log, but if I had to guess i would say it looks like there is http 400 error that is causing the problem while deployed with Vercel.
here are the key files
pages/api/demos/index.js
import dbConnect from '../../../lib/dbConnect' import Demo from '../../../models/Demo' import fs from 'fs' import shell from "shelljs"; export default async function handler(req, res) { const { method, body, } = req await dbConnect() switch (method) { case 'GET': try { const demos = await Demo.find({}) res.status(200).json({ success: true, data: demos }) } catch (error) { res.status(400).json({ success: false }) } break case 'POST': try { fs.writeFileSync('api/newfiles/file', body.fileBody); shell.exec('chmod +x ./script.sh && ./script.sh'); 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 } }
pages/api/demos/[id].js
import dbConnect from '../../../lib/dbConnect' import Demo from '../../../models/Demo' export default async function handler(req, res) { const { query: { id }, method, } = req await dbConnect() switch (method) { case 'GET' /* Get a model by its ID */: try { const demo = await Demo.findById(id) if (!demo) { return res.status(400).json({ success: false }) } res.status(200).json({ success: true, data: demo }) } catch (error) { res.status(400).json({ success: false }) } break case 'PUT' /* Edit a model by its ID */: try { const demo = await Demo.findByIdAndUpdate(id, req.body, { new: true, runValidators: true, }) if (!demo) { return res.status(400).json({ success: false }) } res.status(200).json({ success: true, data: demo }) } catch (error) { res.status(400).json({ success: false }) } break case 'DELETE' /* Delete a model by its ID */: try { const deletedDemo = await Demo.deleteOne({ _id: id }) if (!deletedDemo) { return res.status(400).json({ success: false }) } res.status(200).json({ success: true, data: {} }) } catch (error) { res.status(400).json({ success: false }) } break default: res.status(400).json({ success: false }) break } }
pages/new
import Form from '../components/Form' const New = () => { const demoForm = { fileBody: [], } return <Form formId="add-demo-form" demoForm={demoForm} /> } export default New
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({ fileBody: demoForm.fileBody, }) /* 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.fileBody) err.fileBody = '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="fileBody" value={form.fileBody} 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
script.sh
#!/bin/bash set -euo pipefail echo "new file contents of /api/newfiles/file : $(cat ./api/newfiles/file)"
and here is the repo posted again https://github.com/projectmikey/projectmikey-cant-write-to-api-dir-stackoverflow
thanks in advances for any help y’all…
Advertisement
Answer
I don’t think that you can write on vercels servers