I have a rest api (expressjs) which handles user personal records.
I want to create a route that will allow a user to download all of its records in a json file(the file should be generated on the fly and not be stored on the server_.I found the following gist:
app.get('/', function(req, res) {
res.contentType('text/plain');
res.send('This is the content', { 'Content-Disposition': 'attachment; filename=name.txt' });
});
Which I modified to this:
router.get('/records/export', validateToken, async (req, res, next) => {
const id = res.locals.user;
try {
const user = await User.findById(id).exec();
const {
records
} = user;
res.contentType('json');
res.statusCode = 200;
res.send(JSON.stringify(records), {
'Content-Disposition': `attachment; filename=record-backup-${user.email}`
});
} catch (e) {
next(e);
}
});
However when I try to hit that endpoint I get the following error
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code:
Any ideas what I am doing wrong?
Try giving it the status code like this:
res.status(200).send(JSON.stringify(records), {
'Content-Disposition': `attachment; filename=record-backup-${user.email}`
});
So I finally figured out what was wrong with my code.
res.send accepts only one argument.So I used res.set to set content-disposition header and my working code is this:
router.get('/records/export', validateToken, async (req, res, next) => {
const id = res.locals.user;
try {
const user = await User.findById(id).exec();
const {
records
} = user;
res.contentType('application/octet-stream');
res.set('Content-Disposition', `attachment; filename=record-backup-${user.email}`)
.status(200).send(JSON.stringify(records));
} catch (e) {
next(e);
}
});
Related
I was working with jsonwebtoken. Then I go to verify jsonwebtoken I see some tutorial. But when I am using my next () function I am not getting my expected data
I would have benefited from your help. Thanks
// This is for verifying the jsonwebtoken
function verifyJwt(req, res, next) {
const authHeaders = req.headers.authorization;
if (!authHeaders) {
return res.status(401).send({ message: "Unauthorized access" });
}
const token = authHeaders.split(" ")[1];
// verify a token symmetric
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, function (err, decoded) {
if (err) {
return res.status(403).send({ message: "Forbidden Access" });
}
req.decoded = decoded;
next();
console.log(decoded);
});
console.log(token);
}
//This one is for giving access if the email matched then it will give me the needed data
app.get("/booking", verifyJwt, async (req, res) => {
const patientEmail = req.query.patientEmail;
// Left over
const decodedEmail = req.decoded.patientEmail;
if (patientEmail === decodedEmail) {
const query = { patientEmail: patientEmail };
const services = await bookingCollection.find(query).toArray();
// const authorization = req.headers.authorization;
console.log(authorization);
return res.send(services);
} else {
return res.status(403).send({ message: "Forbidden Access" });
}
});
This next() function not working any solve ?
Please try the below code
try {
const decoded = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
req.decoded = decoded;
console.log(decoded);
next();
} catch (ex) {
return res.status(403).send({ message: 'Forbidden Access' });
}
appointments.map is not a function. Please console appointment and see what it returns.
Problem Solved
Thanks Everyone
Background
I am trying to create a simple CRUD application using NextJS along with react-redux, so what it does is that it saves peoples contacts.So when adding a contact i am trying to send some data along with a file to a NextJS API.
Issue
ContactAction.js
Make a POST request from redux action to add a contact
export const addContact = (data) => async (dispatch) => {
try {
var formData=new FormData();
formData.append('name',data.Name);
formData.append('email',data.Email);
formData.append('phone',data.Phone);
formData.append('image',data.Image);
let response= await Axios.post(`http://localhost:3000/api/contact/addContact`,formData,{
headers:{
'x-auth-token':localStorage.getItem('token')
}
});
} catch (error) {
console.log(error);
}
}
addContact.js
This is the API route in /api/contact/
const handler = async (req, res) => {
switch(req.method){
case "POST":{
await addContact(req,res)
}
}
}
const addContact = async (req, res) => {
console.log(req.body);
// do some stuff here and send response
}
this is what i get in the terminal after the log,also the file is Gibberish as well when logging req.files
Current Effort
I tried using third party packages such as formidable and formidable-serverless but got no luck. so after a day i made it work with a package called multiparty.
addContact.js
const handler = async (req, res) => {
switch(req.method){
case "POST":{
let form = new multiparty.Form();
let FormResp= await new Promise((resolve,reject)=>{
form.parse(req,(err,fields,files)=>{
if(err) reject(err)
resolve({fields,files})
});
});
const {fields,files} = FormResp;
req.body=fields;
req.files=files;
await addContact(req,res)
}
}
}
const addContact = async (req, res) => {
console.log(req.body); //Now i get an Object which i can use
// do some stuff here and send response
}
The above solution is obviously redundant and probably not the best way to go about it plus i don't want to add these 7 8 lines into each route.
so if someone could help me understand what i am doing wrong and why formData doesn't seem to work with NextJS API (when it works with the Express server) i would be grateful.
FormData uses multipart/form-data format. That is not a simple POST request with a body. It is generally used for uploading files, that's why it needs special handling. As an alternative, you could use JSON.
Here is my solution, i hope this helps anybody.
First of all you need to install next-connect and multer as your dependencies.
Now you can use this API route code.
import nextConnect from "next-connect";
import multer from "multer";
const apiRoute = nextConnect({
onError(error, req, res) {
res.status(501).json({ error: `Sorry something Happened! ${error.message}` });
},
onNoMatch(req, res) {
res.status(405).json({ error: `Method "${req.method}" Not Allowed` });
},
});
apiRoute.use(multer().any());
apiRoute.post((req, res) => {
console.log(req.files); // Your files here
console.log(req.body); // Your form data here
// Any logic with your data here
res.status(200).json({ data: "success" });
});
export default apiRoute;
export const config = {
api: {
bodyParser: false, // Disallow body parsing, consume as stream
},
};
Here is an example about uploading file with Next.js:
https://codesandbox.io/s/thyb0?file=/pages/api/file.js
The most important code is in pages/api/file.js
import formidable from "formidable";
import fs from "fs";
export const config = {
api: {
bodyParser: false
}
};
const post = async (req, res) => {
const form = new formidable.IncomingForm();
form.parse(req, async function (err, fields, files) {
await saveFile(files.file);
return res.status(201).send("");
});
};
const saveFile = async (file) => {
const data = fs.readFileSync(file.path);
fs.writeFileSync(`./public/${file.name}`, data);
await fs.unlinkSync(file.path);
return;
};
Generally speaking,in your api file,you should disable the default bodyParser,and write your own parser
Working locally, my jwt token is invalid but in jwt.io it shows verified signature. Not sure what i am missing. I am having invalid signature whenever i tried to make a call to a api whithin the app.
Link.js
const { Router } = require("express");
const Link = require("../models/Link");
const auth = require("../middleware/auth.middleware");
const router = Router();
router.get("/", auth, async (req, res) => {
try {
const links = await Link.find({ owner: req.user.userId });
res.json(links);
} catch (error) {
res.status(500).json({ message: "Something went wrong, try again" });
}
});
auth.middleware.js
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = (req, res, next) => {
if (req.method === "OPTIONS") {
return next();
}
try {
const token = req.headers.authorization; // Token
if (!token) {
return res.status(401).json({ message: "No Authorization" });
}
const decoded = jwt.verify(token, config.get("secret"));
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ message: "No Authorization" });
}
};
Links.tsx
const LinksPage: React.FC = () => {
const [links, setLinks] = useState([]);
const fetchLinks = useCallback(async () => {
try {
const fetched = await request("http://localhost:5000/api/link/", "GET", null, {
Authorization: Token,
});
setLinks(fetched);
} catch (error) {
alert(error);
}
}, []);
};
Maybe the "req.headers.authorization" was not what you looking for.
Try to console.log(req.headers.authorization) F12 in chrome, firefox.
I suggest you also POSTMAN (free software). It help me a lot for debugging the back end (server side).
I solved the problem. I had to json.parse(token) which stored in the client in order to jwt.verify(token, secret), but instead i was verifying string that contains object of token and userId.
I have the following problem, after making a mongo scheme like this:
let Books = new Schema({
video_ru: String,
name: {
ru: String,
uz: String,
en: String
},
only field video_ru comes in db.
When there were no categories, everything worked as it should.
here is my Route
const express = require('express');
const booksRoutes = express.Router();
// Require books model in our routes module
let Books = require('./books.model');
// Defined store route
booksRoutes.route('/add').post(function (req, res) {
let books = new Books(req.body);
books.save()
.then(books => {
res.status(200).json({'books': 'books is added successfully'});
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
// Defined get data(index or listing) route
booksRoutes.route('/').get(function (req, res) {
Books.find(function(err, bookses){
if(err){
console.log(err);
}
else {
res.json(bookses);
}
});
});
// Defined edit route
booksRoutes.route('/edit/:id').get(function (req, res) {
let id = req.params.id;
Books.findById(id, function (err, books){
res.json(books);
});
});
// Defined update route
booksRoutes.route('/update/:id').post(function (req, res) {
Books.findById(req.params.id, function(err, books) {
if (!books)
res.status(404).send("data is not found");
else {
books.name.ru = req.body.name_ru;
books.subname.ru = req.body.subname_ru;
books.description.ru = req.body.description_ru;
books.logo.ru = req.body.logo_ru;
books.video_ru = req.body.video_ru;
books.name.uz = req.body.name_uz;
books.subname.uz = req.body.subname_uz;
books.description.uz = req.body.description_uz;
books.logo.uz = req.body.logo_uz;
books.name.en = req.body.name_en;
books.subname.en = req.body.subname_en;
books.description.en = req.body.description_en;
books.logo.en = req.body.logo_en;
books.save().then(books => {
res.json('Update complete');
})
.catch(err => {
res.status(400).send("unable to update the database");
});
}
});
});
// Defined delete | remove | destroy route
booksRoutes.route('/delete/:id').get(function (req, res) {
Books.findByIdAndRemove({_id: req.params.id}, function(err, books){
if(err) res.json(err);
else res.json('Successfully removed');
});
});
module.exports = booksRoutes;
in the end I get in the database this json
[{"_id":"5d2d94ca6206e73ff02e920d","video_ru":"","__v":0}]
but need with name fields
Well! Your schema is right. Your schema is nested so you have to save value like below or use bodyparser to wrap forms input to body.
booksRoutes.route('/add').post(function (req, res) {
let books = new Books(req.body);
books.name = {
ru: req.body.ru,
uz: req.body.ru,
en: req.body.ru
};
books.save()
.then(books => {
res.status(200).json({'books': 'books is added successfully'});
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
I am having some trouble with get the param from the url. I use Express(4.16.3) on the server side, and using Axios to make the request. But I couldn't seem to get the param from the url in Express.
Here is my code:
on my Route.js in Express
app.get('/api/surveys/:surveyId', (req, res, next) => {
var id = req.params.surveyId;
console.log(req.params);
// it gets params {surveyId: ':surverId'}
res.send('Hello World');
});
so instead of getting the actual id, it logs params: {surveyId: ':surveyId'}. I have been researching, but seems this is the correct way to do it. I also use axios to make the request:
in actions/index.js (I use react):
export const fetchOneSurvey = () => async dispatch => {
const res = await axios.get('/api/surveys/:surveyId');
dispatch({ type: FETCH_ONE_SURVEY, payload: res.data });};
Not sure if this is relevant:
On the view page, instead of having http://localhost:3000/api/surveys/:surveyId, I have http://localhost:3000/surveys/:surveyId route set in React. When I go to http://localhost:3000/surveys/:surveyId, it does console log (req.params) like I write in express, but I only get a string ':surveyId' is the params, not the actual id on the url.
Please anyone can help me? I have tried many different ways, but nothing seem working. I thank you all very much in advance.
===== Extra section ======
Here is my index.js:
const express = require('express');
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const bodyParser = require('body-parser');
const keys = require('./config/keys');
require('./models/User');
require('./models/Survey');
require('./services/passport');
mongoose.connect(keys.mongoURI);
const app = express();
app.use(bodyParser.json());
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(passport.initialize());
app.use(passport.session());
require('./routes/authRoutes')(app);
require('./routes/billingRoutes')(app);
require('./routes/surveyRoutes')(app);
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
My survey model route js:
const _ = require('lodash');
const Path = require('path-parser');
const { URL } = require('url');
const mongoose = require('mongoose');
const requireLogin = require('../middlewares/requireLogin');
const requireCredits = require('../middlewares/requireCredits');
const Mailer = require('../services/Mailer');
const surveyTemplate = require('../services/emailTemplates/surveyTemplate');
const Survey = mongoose.model('surveys');
module.exports = app => {
app.get('/api/surveys', requireLogin, async (req, res) => {
const surveys = await Survey.find({ _user: req.user.id }).select({
recipients: false
});
res.send(surveys);
});
app.get('/api/surveys/:surveyId/:choice', (req, res) => {
res.send('thanks for voting');
});
app.get('/api/surveys/:surveyId', (req, res, next) => {
var id = req.params.surveyId;
console.log(id);
// it gets params {surveyId: ':surverId'}
res.send('Hello World');
});
app.post('/api/surveys/webhooks', (req, res) => {
// console.log(req.body);
// res.send({});
const p = new Path('/api/surveys/:surveyId/:choice');
const test = _.chain(req.body)
.map(({ email, url }) => {
const match = p.test(new URL(url).pathname);
if (match) {
return {
email,
surveyId: match.surveyId,
choice: match.choice
};
}
})
.compact()
.uniqBy('email', 'surveyId')
.each(({ surveyId, email, choice }) => {
Survey.updateOne(
{
// have to add _ to keys as mongoDB rule, mongoose doensn't need.
_id: surveyId,
recipients: {
$elemMatch: { email: email, responded: false }
}
},
{
$inc: { [choice]: 1 },
$set: { 'recipients.$.responded': true },
lastResponded: new Date()
}
).exec();
})
.value();
console.log(test);
res.send({});
});
app.post('/api/surveys', requireLogin, requireCredits, async (req, res) => {
const { title, subject, body, recipients } = req.body;
const survey = new Survey({
// map(email => ({ email }) === map(email =>{ return {email: email}})
title,
body,
subject,
recipients: recipients
.split(',')
.map(email => ({ email: email.trim() })),
_user: req.user.id,
dateSent: Date.now()
});
// send an email
const mailer = new Mailer(survey, surveyTemplate(survey));
try {
await mailer.send();
await survey.save();
req.user.credits -= 1;
const user = await req.user.save();
res.send(user);
} catch (err) {
res.status(422).send(err);
}
});
};
Posting below details for debugging the issue
Note: if you are using Windows OS, use command prompt for node project development. i have seen people using git bash for doing node project developments and it causes unnecessary issues
Below are the steps for debugging
1.Create a new directoryforexample test and initialize it using npm init
2.Install express npm install --save express
3.Create a new file for example index.js and use below code
test/index.js
var express= require("express");
var app = express();
app.get("/api/surveys/:surveyId",(req,res,next)=>{
console.log(req.params.surveyId);
res.send('Hello World');
});
var server= app.listen(3000,()=>{
console.log("port started at ",server.address().port);
})
4.Start the program node index.js
5.Trigger http request from browser http://localhost:3000/api/surveys/llads . The value llads can be accessed using the path param surveyId in the route
6.if you can see the below output in node console then the program is working as it should. And this has to work as described here.
if above steps yields expected output then i don't see any problem in your route code.
Let me know your feedback.