My original plan for this project involved building a much more complex app, but the project was only started to learn a few things before working on a different app already in progress, and I've come to realize that at this point in time, the only two things left that I need to learn are authentication and connecting the front and back end. I might come back to the app at some point in the future, but tomorrow will most likely be the end of this project for a while.
Today I'll be working on setting up the back end API so that the front end can interact with the database.
The first step is to create some files:
Then some boilerplate and placeholder code to make sure everything is working:
// server.js
import express from "express";
import bodyParser from "body-parser";
import cors from 'cors';
import { router } from './routes.js';
import dotenv from 'dotenv';
import mongoose from 'mongoose';
dotenv.config();
const app = express();
const port = process.env.PORT;
const user = process.env.DB_USER;
const pass = process.env.DB_PASS;
const cluster = process.env.DB_CLUSTER;
const dbName = process.env.DB_NAME;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use("/", router);
mongoose.connect(`mongodb+srv://${user}:${pass}@${cluster}.58qh2.mongodb.net/${dbName}?retryWrites=true&w=majority`, {
useNewUrlParser: true
}).then(() => {
console.log("Successfully connected to the database");
}).catch(err => {
console.log('Could not connect to the database. Exiting now...', err);
process.exit();
});
app.listen(port, () => console.log(`Server is running on port ${port}`));
// routes.js
import express from 'express';
import * as api from './api.js';
export const router = express.Router();
router.get('/', (req, res) => res.redirect('/api'));
router.get('/api', api.main);
router.get('/api/all', api.getAll);
// api.js
export const main = (req, res) => {
res.send('main')
}
export const getAll = (req, res) => {
res.send('all')
}
Everything is working correctly so it's time to set up a response
schema. If this app were more complex, I would need to worry about different surveys having different fields, but since I've decided to stick to just one form for now, the model is just a translation of the survey data to a set of database fields.
I'm removing the the password fields from the form. User authentication will be handled separately so these fields serve no purpose. I'm also commenting out the file upload for now, as that requires more complex handling and I don't need to know how to do it right now.
// models/Response.js
import mongoose from "mongoose";
const responseSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
select: Number,
selectMulti: [Number],
text: String,
choices: String,
check: Boolean
});
export default mongoose.model('Response', responseSchema);
Now the controller to post the data. I added a route:
// routes.js
...
router.post('/api/add', api.addResponse);
and the controller:
// api.js
...
export const addResponse = async (req, res) => {
const multi = req.body.selectMulti ? req.body.selectMulti.map(Number) : [];
try {
const responseData = new Response({
name: req.body.name,
email: req.body.email,
select: Number(req.body.select),
selectMulti: multi,
text: req.body.text,
choices: req.body.choices,
check: req.body.check === "true"
})
await responseData.save((err) => {
if (err) return console.error(err);
});
res.json({ 'message': 'success' });
}
catch (err) {
console.log(err);
}
}
and now I need to go back to the front end to hook it up to the form:
// Survey.jsx
...
onSubmit={(values, { setSubmitting }) => {
fetch('http://localhost:5000/api/add', {
headers: { 'Content-Type': 'application/json' },
method: 'post',
body: JSON.stringify(values),
})
.then(response => response.json())
.then(data => {
console.log(data)
setSubmitting(false)
})
.catch(err => console.log(err));
}}
...
The form is now hooked up properly but 1) there's no indication to the user that the form has been submitted other than a quick disabling of the submit button and 2) selectMulti's data is not being sent.
The second one was a simple omission - I didn't add tag={Field}
.
// Survey.jsx
...
<Input
tag={Field}
multiple
id="selectMulti"
name="selectMulti"
as="select"
type="select"
>
...
The first one wasn't much more difficult to fix. As I'm just figuring out how to handle a redirect after form submission, I didn't bother setting up a success message, I'm just redirecting to the home page. This took a bit of searching because the way React Router handles it has changed over time, and older code didn't work.
// Survey.jsx
...
import { useNavigate } from "react-router-dom";
...
export default function Survey() {
let navigate = useNavigate();
...
onSubmit={(values, { setSubmitting }) => {
...
navigate("/");
}}
...
That's it for today, tomorrow is authentication and then I'll be retiring this project, probably for quite a long time.