I have been trying to solve this but I really got stuck. I'm still learning web development so I'm really lost on why this is happening and how to solve it. There is a mysql table called ticket and I want to get all its information to display it in a page. I'm using express and react.
I'm using nodemon and it shows the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client
at new NodeError (node:internal/errors:387:5)
at ServerResponse.removeHeader (node:_http_outgoing:711:11)
at ServerResponse.send (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\response.js:214:10)
at file:///C:/Users/millenas/Documents/cxbunal/api/UserRoutes.js:19:28
at C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:223:12
at getSecret (C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:90:14)
at Object.module.exports [as verify] (C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:94:10)
at file:///C:/Users/millenas/Documents/cxbunal/api/UserRoutes.js:10:18
at Layer.handle [as handle_request] (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\router\route.js:144:13)
Error [ERR_HTTP_HEADERS_SENT]: Cannot remove headers after they are sent to the client
at new NodeError (node:internal/errors:387:5)
at ServerResponse.removeHeader (node:_http_outgoing:711:11)
at ServerResponse.send (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\response.js:214:10)
at file:///C:/Users/millenas/Documents/cxbunal/api/UserRoutes.js:19:28
at C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:223:12
at getSecret (C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:90:14)
at Object.module.exports [as verify] (C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:94:10)
at file:///C:/Users/millenas/Documents/cxbunal/api/UserRoutes.js:10:18
at Layer.handle [as handle_request] (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\router\route.js:144:13)
node:internal/errors:477
ErrorCaptureStackTrace(err);
^
listening on port: 3030
node:internal/errors:477
ErrorCaptureStackTrace(err);
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the at file:///C:/Users/millenas/Documents/cxbunal/api/TicketRoutes.js:39:26 at processTicksAndRejections (node:internal/process/task_queues:96:5) { code: 'ERR_HTTP_HEADERS_SENT'
}[nodemon] app crashed - waiting for file changes before starting...
UserRoutes.js:
import express from 'express';
import jsonwebtoken from 'jsonwebtoken';
import db from "./db.js";
const UserRoutes = express.Router();
const secret = 'hardcoded123';
UserRoutes.get('/profile', ((req, res) => {
const token = req.cookies.token;
jsonwebtoken.verify(token, secret, (err, data) => {
if(err){
res.status(403).send();
}
else {
res.json(data).send();
}
});
}));
UserRoutes.post('/login', ((req,res) => {
const {email, password} = req.body;
db.select('password')
.where({email})
.from('users')
.first()
.then(user => {
const isLoginOk = password === user.password;
isLoginOk && jsonwebtoken.sign(email, secret, (err, token) => {
if (err) {
res.status(403).send(); //forbidden - access denied
}
else {
db('users').where({email}).update({token})
.then(() => res.cookie('token', token).send('ok'))
.catch(() => res.sendStatus(422));
}
});
if (!isLoginOk) {
res.status(403).send('Incorrect email or password.');
}
})
.catch(e => {
res.status(422).send('Oops. Something went wrong.');
console.log(e);
});
}));
UserRoutes.post('/logout', ((req, res) => {
res.clearCookie('token').send('ok');
}));
export default UserRoutes;
TicketRoutes.js:
import express from 'express';
import db from './db.js';
const TicketRoutes = express.Router();
TicketRoutes.post('/tickets', (req,res) => {
const {title, content, priority} = req.body;
const {token} = req.cookies;
db.select('id').from('users').where({token}).first().then(user => {
if(user && user.id) {
db('tickets').insert({
title,
content,
priority,
parent_id: null,
author_id: user.id,
status: 'open',
}).then(() => {
res.sendStatus(201);
}).catch(() => res.sendStatus(422));
}
else {
res.sendStatus(403);
}
});
});
TicketRoutes.get('/tickets/:id', (req,res) => {
const id = req.params.id;
db.select('*')
.from('tickets')
.where({id})
.first()
.then(ticket => {
res.json(ticket).send();
})
.catch(() => res.sendStatus(422));
});
export default TicketRoutes;
TicketPage.js:
import axios from 'axios';
import React from 'react'
import styled from 'styled-components';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import gfm from 'remark-gfm';
import ReactMarkdown from 'react-markdown';
const Container = styled.div`
padding: 30px 20px;
background-color: whitesmoke;
width: 90%;
margin: 40px 40px;
border-radius: 5px;
`;
const HeaderRow = styled.div`
display: grid;
grid-template-columns: 1fr min-content min-content min-content;
grid-column-gap: 15px;
`;
const StyledHeader = styled.h1`
font-size: 1.8rem;
font-weight: bold;
margin-top: 0;
margin-bottom: 10px;
color: #032c5b;
`;
const Tag = styled.span`
display: inline-block;
margin-right: 3px;
background-color: #4c4d50;
color: whitesmoke;
padding: 4px;
border-radius: 5px;
font-size: 0.7rem;
`;
const Labels = styled.h3`
color: #032c5b;
font-weight: 500;
margin-top: 10px;
`;
const TicketBody = styled.div`
color: #032c5b;
border: 2px solid #032c5b;
border-radius: 5px;
margin-top: 30px;
padding: 15px;
`
function TicketPage() {
const [ticket, setTicket] = useState(false);
const params = useParams();
useEffect(() => {
function fetchTicket(){
axios.get('http://localhost:3030/tickets/' params.id)
.then(response => {
setTicket(response.data.ticket);
});
};
fetchTicket()
}, [params.id]);
return (
<>
<Container>
{ticket && (
<><HeaderRow>
<StyledHeader>{ticket && ticket.title}</StyledHeader>
<Labels>22/02/2022</Labels>
<Labels>Closed</Labels>
<Labels>High</Labels>
</HeaderRow>
<Labels>by Millena Freitas</Labels>
<TicketBody>
<ReactMarkdown plugins={[gfm]} children={ticket.content} />
</TicketBody>
</>
)}
</Container>
</>
);
}
export default TicketPage
If you need to see anything more please ask. Thank you so much for anyone willing to help me :)
CodePudding user response:
Remove .send()
from res.json(data).send()
.
I'm able to see the issue from the stack trace.
Canonical stack trace troubleshooting steps
Given a stack trace,
at new NodeError (node:internal/errors:387:5)
at ServerResponse.removeHeader (node:_http_outgoing:711:11)
at ServerResponse.send (C:\Users\millenas\Documents\cxbunal\api\node_modules\express\lib\response.js:214:10)
at file:///C:/Users/millenas/Documents/cxbunal/api/UserRoutes.js:19:28
at C:\Users\millenas\Documents\cxbunal\api\node_modules\jsonwebtoken\verify.js:223:12
Going from the bottom up,
verify.js:223
:return done(null, payload)
shows that the verify function completed without error.api/UserRoutes.js
gives us the code for the callback. Line 19 doesn't match up with anything significant (probably because the code has changed since the error was shown), but gives us the route. We can see there is conditional logic based on whether there was an error or not. We know there is no error. That leaves us with only one line to look at
So the problematic line is:
res.json(data).send();
Looking up the documentation for res.json()
, we can see it is not chainable. Remove .send()
.