I am building this school project where we have to create out own API in NodeJs and free-choice frontend. I wrote the following code:
[In public map] app.js
function getAll() {
console.log("Get all")
makeRequest("/poems", "GET")
}
async function getRandomPoem() {
const ids = [1, 2, 3, 4, 5, 6, 7]
const randomId = ids[Math.floor(Math.random() * ids.length)]
const arrayofPoems = await fetch("/poems/" + randomId, {method: "GET"})
const data = await arrayofPoems.json()
const titleBox = document.getElementById("title")
const authorBox = document.getElementById("author")
const textBox = document.getElementById("text")
titleBox.innerText = data.title
authorBox.innerText = data.author
textBox.innerText = data.text
}
function addPoem() {
event.preventDefault();
let title = document.getElementById("titleInput").value
let author = document.getElementById("authorInput").value
let text = document.getElementById("textInput").value
let newPoem = [{
id: 8,
title: "Aaa",
author: "Ccc",
text: "Ddd"
}]
makeRequest("/poems/", "post", newPoem)
}
async function makeRequest(url, reqMethod, body) {
const response = await fetch(url, {
// headers = { "Content-Type": "application/json" },
method: reqMethod,
body:JSON.stringify(body)
})
console.log(response)
const data = await response.json()
console.log(data)
}
[Here the requests to local server] server.js
const express = require('express');
const { poems } = require('./Poems/poemsArray');
const app = express();
const port = 8080;
const allPoems = require('./Poems/poemsArray')
app.use(express.json())
app.use("/", express.static('public'))
app.listen(port, console.log(`App listening on port ${port}`))
// ---------------- POEMS RESOURCE, All endpoints ------------------ //
// Get all
app.get('/poems', (req, res, next) => {
res.json(allPoems)
})
// Get specific
app.get('/poems/:id', (req, res, next) => {
const id = req.params.id
const onePoem = allPoems.find((poem) => poem.id == id)
if(onePoem) {
res.json(onePoem)
} else {
res.status(404).json({ status: "Poem not found! "})
}
})
// Post a poem
app.post('/poems', (req, res, next) => {
allPoems.push(req.body)
res.json({ status: "A new poem has been posted!"})
})
[And last, the HTML with the input fields, where the values should be sent with the POST req] index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Poems from outer space</title>
<script src="app.js"></script>
</head>
<body>
<div id="container">
<div id="poem-container">
<h1 style="color:red;text-align:center;">Poems</h1>
<p style="text-align: center;">Generate a random poem about space!
<button onclick="getRandomPoem()">Generate my poem!</button>
</p>
<div id="showPoem">
<h1 id="title"><!-- Title of poem injected --></h1>
<h2 id="author"><!-- Author of poem injected --></h2>
<p id="text"><!-- Text of poem injected --></p>
</div>
<div id="image-container">
<!-- INJECTED BY EXTERNAL NASA API -->
<!-- EXAMPLE IMAGE TO TEST DELETE WHEN API WORKS -->
<img src="img/apod.jpg" alt="Test Image" width="600px" id="img">
</div>
</div>
<div id="form-container">
<form method="post" action="/poems">
<h1>Send us your poem!</h1>
<label>Your title:</label> <br>
<input type="text" requirede name="title" id="titleInput"> <br>
<label>Your name:</label> <br>
<input type="text" required name="author" id="authorInput"> <br> <br>
<label>Your poem:</label> <br>
<input type="text" required name="text" id="textInput" style="width:500px;height:500px">
<br>
<button type="submit" onclick="addPoem()">Send</button>
</form>
</div>
</div>
</body>
</html>
In the function addPoem() the let newPoem is for testing purposes. The title, author and text should be coming from the form.
Anyone can see what I did wrong?
EDIT: in the makeRequest function the header is commented out, that is because if I leave it in my code, suddenly none of the request work anymore?
Thanks to everybody!
you use headers = which is not valid . try headers : {} .
When you get empty object , try logging the request. It is also possible that the body get sended as a string,which express.json() middleware cannot parse the data. As a result, you get empty object.
async function makeRequest(url, reqMethod, body) {
const response = await fetch(url, {
headers : { "Content-Type": "application/json" },
method: reqMethod,
body:JSON.stringify(body)
})
console.log(response)
const data = await response.json()
console.log(data)
}
If you are trying to access postman after a while it can also cause issue while sending body.
In my case I had done all changes in API, added router,removed validation etc
but at last the culprit was postman as whatever data I was sending, it was showing request.body as {}(empty).
After I re-installed postman it worked!
I just could felt more joyful, it took my 3-4 hours
So you can consider this option as well.
Related
I'm getting a 500 when trying to make a POST with my Next.js Application. I can't seem to figure out why.
I'm using Next.js, and MongoDB and the GET requests are working fine.
Posts.js
import clientPromise from "../../lib/mongodb";
export default async function handler(req, res) {
const client = await clientPromise;
const db = client.db("quick_ticker_db");
switch (req.method) {
case "POST":
let bodyObject = JSON.parse(req.body);
let myPost = await db.collection("posts").insertOne(bodyObject);
res.json(myPost.ops[0]);
break;
case "GET":
const allPosts = await db.collection("posts").find({}).toArray();
res.json({ status: 200, data: allPosts });
break;
}
}
create.js
export default function CreateTicker() {
// Handles the submit event on form submit.
const handleSubmit = async (event) => {
// Stop the form from submitting and refreshing the page.
event.preventDefault()
// Get data from the form.
const data = {
ticker: event.target.ticker.value
}
// Send the data to the server in JSON format.
const JSONdata = JSON.stringify(data)
// API endpoint where we send form data.
const endpoint = '/api/posts'
// Form the request for sending data to the server.
const options = {
// The method is POST because we are sending data.
method: 'POST',
// Tell the server we're sending JSON.
headers: {
'Content-Type': 'application/json',
},
// Body of the request is the JSON data we created above.
body: JSONdata,
encodeBodyAsJSON: true
}
// Send the form data to our forms API on Vercel and get a response.
const response = await fetch(endpoint, options)
// Get the response data from server as JSON.
// If server returns the name submitted, that means the form works.
const result = await response.json()
result
}
return (
// We pass the event to the handleSubmit() function on submit.
<form onSubmit={handleSubmit}>
<label htmlFor="ticker">Ticker</label>
<input type="text" id="ticker" name="ticker" required />
<button type="submit">Submit</button>
</form>
)
}
ERROR:
POST http://localhost:3000/api/posts 500 (Internal Server Error)
Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
"message":"Unexpected number in JSON at position 1"
RESPONSE:
<!DOCTYPE html>
<html>
<head>
<style data-next-hide-fouc="true">
body {
display: none
}
</style><noscript data-next-hide-fouc="true">
<style>
body {
display: block
}
</style>
</noscript>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="next-head-count" content="2" /><noscript data-n-css=""></noscript>
<script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1664669437061"></script>
<script src="/_next/static/chunks/webpack.js?ts=1664669437061" defer=""></script>
<script src="/_next/static/chunks/main.js?ts=1664669437061" defer=""></script>
<script src="/_next/static/chunks/pages/_app.js?ts=1664669437061" defer=""></script>
<script src="/_next/static/chunks/pages/_error.js?ts=1664669437061" defer=""></script>
<script src="/_next/static/development/_buildManifest.js?ts=1664669437061" defer=""></script>
<script src="/_next/static/development/_ssgManifest.js?ts=1664669437061" defer=""></script><noscript
id="__next_css__DO_NOT_USE__"></noscript>
</head>
<body>
<div id="__next" data-reactroot=""></div>
<script src="/_next/static/chunks/react-refresh.js?ts=1664669437061"></script>
<script id="__NEXT_DATA__" type="application/json">
{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{"__NEXT_PAGE":"/api/posts"},"buildId":"development","isFallback":false,"err":{"name":"SyntaxError","source":"server","message":"Unexpected number in JSON at position 1","stack":"SyntaxError: Unexpected number in JSON at position 1\n at JSON.parse (\u003canonymous\u003e)\n at handler (webpack-internal:///(api)/./pages/api/posts.js:12:35)\n at processTicksAndRejections (internal/process/task_queues.js:95:5)\n at async Object.apiResolver (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/api-utils/node.js:366:9)\n at async DevServer.runApi (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/next-server.js:481:9)\n at async Object.fn (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/next-server.js:735:37)\n at async Router.execute (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/router.js:247:36)\n at async DevServer.run (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/base-server.js:347:29)\n at async DevServer.run (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/dev/next-dev-server.js:709:20)\n at async DevServer.handleRequest (/Users/Projects/Personal/quick-ticker-web/node_modules/next/dist/server/base-server.js:285:20)"},"gip":true,"scriptLoader":[]}
</script>
</body>
</html>
For the record GET requests are working just fine.
I think that is because your post logic throwing error:
let bodyObject = JSON.parse(req.body);
let myPost = await db.collection("posts").insertOne(bodyObject);
run this code in try/catch block.
your code is not reaching res.json(myPost.ops[0])
I have a view that accepts a file (an image, for now) and previews it on the page.
The same view also has a form that accepts two text inputs. Currently, when submitted, the text inputs are sent, via axios, to my Express JS server to be displayed on a different view. I'd like the image that is chosen by the user to, also, be sent to the server and saved statically in a folder there (I'm not using a database yet).
My current attempt simply passes the same state the user image is in to the axios post request. However, this does not work and the view reloads and clears all data. I've not noticed any errors in the console.
How can I achieve my goal?
function sendVideo() {
axios
.post(`${BASE_URL}videos`, {
title: document.getElementById("uploadTitle").value,
description: document.getElementById("uploadDesc").value,
image: this.state.file,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
class VideoUpload extends React.Component {
constructor(props) {
super(props);
this.state = {
file: null,
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
file: URL.createObjectURL(event.target.files[0]),
});
}
render() {
return (
<section className="uploadContainer">
<section className="uploadContainer__titleContainer">
<h2 className="uploadContainer__title">Upload Video</h2>
</section>
<section className="uploadContainer__detailsContainer">
<input id="qwe" type="file" onChange={this.handleChange} />
<img
type="file"
id="uploadImage"
src={this.state.file}
alt={this.state.file}
className="uploadContainer__thumbnail"
></img>
<div className="uploadContainer__formContainer">
<form action="" className="uploadContainer__form" id="uploadForm">
<h6 className="uploadContainer__text">TITLE YOUR VIDEO</h6>
<input
required
id="uploadTitle"
type="text"
className="uploadContainer__uploadTitle"
placeholder="Add a title to your video"
/>
<h6 className="uploadContainer__text">ADD A VIDEO DESCRIPTION</h6>
<input
required
id="uploadDesc"
type="text"
className="uploadContainer__uploadDesc"
placeholder="Add a description to your video"
/>
</form>
</div>
</section>
<section className="uploadContainer__buttonSection">
<div className="uploadContainer__cancelButton">CANCEL</div>
<button
form="uploadForm"
type="submit"
className="uploadContainer__uploadButton"
onClick={() => sendVideo()}
>
<img
className="uploadContainer__uploadIcon"
draggable="false"
src={uploadIcon}
alt="upload-icon"
/>
PUBLISH
</button>
</section>
</section>
);
}
}
Backend:
const router = require("express").Router();
const path = require("path");
const videoJsonFileName = path.join(__dirname, "../data/videos.json");
const videos = require(videoJsonFileName);
const utils = require("../utils/utils");
router.get("/", (_req, res) => {
res.status(200).json(videos);
});
router.get("/:id", (req, res) => {
const foundVideo = videos.find((video) => video.id === req.params.id);
if (!foundVideo) {
res
.status(404)
.json({ errorMessage: `Video with ID "${req.params.id}" was not found` });
}
res.status(200).json(foundVideo);
});
//CREATE A NEW OBJECT
router.post("/", (req, res) => {
//video title, desc, thumbnail
console.log(req.body);
if (!req.body.title || !req.body.description || !req.body.image)
return res.status(400).json({
errorMessage: "Please provde title, description, and image",
});
const newVideoObj = {
title: req.body.title,
image: req.body.image,
description: req.body.description,
id: utils.getNewId(),
};
utils.writeToJsonFile(videoJsonFileName, [...videos, newVideoObj]),
res.status(201).json({ newVideoCreated: newVideoObj });
});
module.exports = router;
In your frontend:
//formData request body is used to handle file upload
const fd = new FormData();
//the file itself should be appended before any other value.
//file should be of type file or buffer
fd.append(<property name>, <propValue>);
//repeat above line for any additional fields, e.g. name, description etc.
axios.post(url, fd, [...options])
In your backend:
Firstly you'll need to install and require a module that handles multipart, such as multer.
your expressjs method will look something like this:
var storage = multer.memoryStorage();
var upload = multer({ storage: storage });
router.post(<your-url>, upload.single("file"), <your handle function>...)
References: formData (mdn), express-multer
i am getting object response when trying to use the response from express,this is the HTML and client js i am using
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<form method="post">
<input id="names" name="names" type="text" />
</form>
<button id="send">send</button>
<p id="text"></p>
<script>
document.getElementById("send").addEventListener("click", () => {
let datos = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
names: document.getElementById("names").value,
}),
};
fetch("/myaction", datos)
.then(function (response) {
document.getElementById("text").innerHTML = response;
})
.catch(() => {
document.getElementById("text").innerHTML = "Error";
});
});
</script>
</body>
</html>
i am trying to use the response of the server.js in the "text" element, the server is
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(express.json())
//Note that in version 4 of express, express.bodyParser() was
//deprecated in favor of a separate 'body-parser' module.
app.use(bodyParser.urlencoded({ extended: true }));
//app.use(express.bodyParser());
app.get('/', function(req, res) {
res.sendFile(__dirname + "/index.html");
});
app.post('/myaction', function(req, res) {
res.send(req.body.names);
});
app.listen(8088, function() {
console.log('Server running');
});
when fetch request myaction express return the name query but i cant use it on the fetch then because it print "[object Response]" instead the name form value, what can i do ?
The global fetch function returns a Promise that resolves to a Response object. This object contains all the information about the response, like headers, status, body, etc. To get the body of the response you'll need to decode the response first.
In your case you need to read the body as a string so we'll use response.text(). This is a method on the Response object. It also returns a promise which resolves to a string.
fetch("/myaction", datos)
.then(function (response) {
return response.text();
})
.then(function (text) {
document.getElementById("text").textContent = text;
})
.catch(() => {
document.getElementById("text").textContent = "Error";
});
The "response" that comes back from the fetch is an object that has more than just the data in the response. It's a Response object which means it contains the status code (like 200) as well as the data. Typically you can get the data from the response using response.json() if it's JSON format, or response.text() if it's text. These functions are asynchronous so also return a Promise. So your code can look like this:
fetch("/myaction", datos)
.then(function (response) {
return response.text(); // a Promise that will resolve to the data
})
.then(function (text) {
document.getElementById("text").innerHTML = text;
})
.catch(() => {
document.getElementById("text").innerHTML = "Error";
});
Whenever you see a string looking like [object Object] it means you are seeing a Javascript object that doesn't have a meaningful toString() function so that's the best it can do to show you the value. If you're not sure what kind of object it is, a good debugging technique is to output it using console.log(obj) so you can see what it looks like. That usually gives you a clue about what you really are working with.
I'm currently making a to-do list using Express and Node.js with MongoDB and Mongoose. I'm trying to update the to-do list so I've tried to use patch however this just changes the data to null. (No error messages shown but the HTTP status code is 400)
router.patch('/:id', ensureAuth, async(req, res) => {
try {
const id = req.params.id;
const updatedTask = await Task.findByIdAndUpdate(id, {
task: req.body.taskbox
})
res.send(result);
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
I'm thinking the error might be to do with the "req.body.taskbox" since console.log(req.body.taskbox) shows "undefined" and console.log(req.body) prints {}. Furthermore, when I hardcode something there, the patch works. (e.g. task: "Clean window" - this would update the task successfully to say "Clean window").
The code below shows where the HTML form is generated:
static addTaskToTable(currentTask) {
const list = document.querySelector('#task-list');
const row = document.createElement('div');
row.innerHTML = `
<div class="row">
<div class="col-md-8">
<form id="task-form-for-${currentTask.taskId}">
<input type="text" name="taskbox" value="${currentTask.task}" />
</form>
</div>
<div class="col">
<form id="edit-for-${currentTask.taskId}">
<p><input type="submit" class="btn btn-sm btn-primary" value="edit" onclick="Store.editTask('${currentTask.taskId}')" /></p>
</form>
</div>
<div class="col">
<form id="delete-form-for-${currentTask.taskId}">
<p><input type="submit" class="btn btn-danger btn-sm delete" onclick="Store.deleteTask('${currentTask.taskId}')" value=" x " /></p>
</form>
</div>
</div>
`;
list.appendChild(row);
}
I loop through the tasks in the database to display them using this function: (This could be another place that could've caused this error maybe?)
static displayTasks(){
Store.getTasksArr().then(taskData => {
let tasks = [];
let i;
taskData.map((currentTasks) => {
tasks.push(currentTasks)
})
tasks.forEach((task) => UI.addTaskToTable(task));
})
}
The fetch request:
static editTask = async(id) => {
const res = await fetch('http://localhost:5500/tasks/' + id, {
method: "PATCH",
});
const json = await res.json();
console.log(json);
}
My get, post, and delete all work fine and for the post, I wrote something very similar so I'm also confused as to why the patch doesn't retrieve the data from the textbox but in the post request it does:
router.post('/', ensureAuth, async(req, res) => {
try {
const task = new Task({
task: req.body.newtask,
user: req.user.id
})
const newTask = await task.save()
res.status(201)
res.redirect('/home')
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
I also have a body-parser middleware already defined:
app.use(express.urlencoded({extended: false}));
app.use(express.json());
I am trying to retrieve data from the api I made with nodejs/fastifyjs. I am able to get data from the api without errors but when I try to post request I get the error in the browser console of character encoding although I have specified the charset meta tag. When I send post request from postman I get proper response and data gets saved. Here is the frontend code I am using ...
Also when I try to send data from postman I select the x-www-urlformencoded option because when I use form-data option I get error code "405".
The html5 document code ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue App</title>
</head>
<body>
<div id="app">
{{msg}}
<button v-on:click="getData">Get Data</button>
<div v-for="detail in details">
<br>
<span>Name : {{detail.name}}</span><br>
<span>Age : {{detail.age}}</span><br>
<span>Image : {{detail.imageUrl}}</span><br>
</div>
<br>
<br>
<br>
<div class="sendDataToServer">
<form method="post">
<span>Enter Name: <input type="text" v-model="name"></span><br>
<span>Enter Age: <input type="number" v-model="age"></span><br>
<span>Enter Image url: <input type="text" v-model="imageUrl"></span><br>
<button v-on:click="sendData">Save data</button>
</form>
</div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="index.js"></script>
</body>
</html>Screenshot from 2019-03-17 16-11-42
The Vue Code I use to get and post data with axios ...
const app = new Vue({
el:'#app',
data:{
msg: 'VueJS!',
details: [],
name:'',
age: 0,
imageUrl:'',
},
methods:{
getData: function () {
axios.get('http://localhost:3000/getData')
.then((result) => {
this.details = result.data
})
.catch((err) => {
console.log(err)
})
},
sendData: function(){
let config = {'Content-Type': 'application/x-www-form-urlencoded'}
axios.post('http"localhost:3000/saveData',{
name: this.name,
age: this.age,
imageUrl: this.imageUrl,
}, config)
.then((response) => {
console.log(response)
})
.catch((err) => {
console.log(err)
})
},
}
})
The api code is here ...
const fastify = require('fastify')({
logger:{
prettyPrint: true
}
});
// Requiring the model
const dbSch = require('./models')
// Requiring Cors
fastify.register(require('fastify-cors'))
// Requiring to get data from incoming request body
fastify.register(require('fastify-formbody'))
// ******
// Defining ROUTES
// ******
fastify.get('/', (request, reply)=>{
reply.send('Hello Fastify')
})
fastify.get('/getData', (request, reply) => {
dbSch.find({}).exec(function(err, result){
if (err) throw err
// let resultObj = JSON.parse(result)
reply.send(result)
})
})
fastify.post('/saveData', (request, reply) => {
let saveDataTodb = new dbSch({
name: request.body.name,
age: request.body.age,
imageUrl: request.body.imageUrl,
})
saveDataTodb.save(function(err, result){
if (err) {
throw err
} else {
console.log('saved !!!')
// reply.header('Content-Type', 'text/json')
reply.send('Saved to DB')
}
})
})
const port = process.env.PORT || 3000
fastify.listen(port)