In my api router, there is a function called generatePDF which aims to use PDFKit module to generate a PDF file in memory and send to client for download instead of displaying only.
In api.js:
var express = require('express');
var router = express.Router();
const PDFDocument = require('pdfkit');
router.get('/generatePDF', async function(req, res, next) {
var myDoc = new PDFDocument({bufferPages: true});
myDoc.pipe(res);
myDoc.font('Times-Roman')
.fontSize(12)
.text(`this is a test text`);
myDoc.end();
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-disposition': 'attachment;filename=test.pdf',
'Content-Length': 1111
});
res.send( myDoc.toString('base64'));
});
module.exports = router;
This does not work. The error message is (node:11444) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
How can I go about fixing the issue and getting it work?
Also, a relevant question would be how I can separate the business logic of PDF generation from the router and chain them up?
Complete solution.
var express = require('express');
var router = express.Router();
const PDFDocument = require('pdfkit');
router.get('/generatePDF', async function(req, res, next) {
var myDoc = new PDFDocument({bufferPages: true});
let buffers = [];
myDoc.on('data', buffers.push.bind(buffers));
myDoc.on('end', () => {
let pdfData = Buffer.concat(buffers);
res.writeHead(200, {
'Content-Length': Buffer.byteLength(pdfData),
'Content-Type': 'application/pdf',
'Content-disposition': 'attachment;filename=test.pdf',})
.end(pdfData);
});
myDoc.font('Times-Roman')
.fontSize(12)
.text(`this is a test text`);
myDoc.end();
});
module.exports = router;
First I recommend to create a service for the PDF kit. And then a Controller to the route that you want.
I used get-stream to make this easier.
It also answers your question to the accepted answer:
how I can separate the business logic of PDF generation from the
router and chain them up?
This is my professional solution:
import PDFDocument from 'pdfkit';
import getStream from 'get-stream';
import fs from 'fs';
export default class PdfKitService {
/**
* Generate a PDF of the letter
*
* #returns {Buffer}
*/
async generatePdf() {
try {
const doc = new PDFDocument();
doc.fontSize(25).text('Some text with an embedded font!', 100, 100);
if (process.env.NODE_ENV === 'development') {
doc.pipe(fs.createWriteStream(`${__dirname}/../file.pdf`));
}
doc.end();
const pdfStream = await getStream.buffer(doc);
return pdfStream;
} catch (error) {
return null;
}
}
}
And then the method of the Controller:
(...)
async show(req, res) {
const pdfKitService = new PdfKitService();
const pdfStream = await pdfKitService.generatePdf();
res
.writeHead(200, {
'Content-Length': Buffer.byteLength(pdfStream),
'Content-Type': 'application/pdf',
'Content-disposition': 'attachment;filename=test.pdf',
})
.end(pdfStream);
}
And finally the route:
routes.get('/pdf', FileController.show);
For those how don't want to waste RAM on buffering PDFs and send chunks right away to the client:
const filename = `Receipt_${invoice.number}.pdf`;
const doc = new PDFDocument({ bufferPages: true });
const stream = res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-disposition': `attachment;filename=${filename}.pdf`,
});
doc.on('data', (chunk) => stream.write(chunk));
doc.on('end', () => stream.end());
doc.font('Times-Roman')
.fontSize(12)
.text(`this is a test text`);
doc.end();
You can use blob stream like this.
reference: https://pdfkit.org/index.html
const PDFDocument = require('pdfkit');
const blobStream = require('blob-stream');
// create a document the same way as above
const doc = new PDFDocument;
// pipe the document to a blob
const stream = doc.pipe(blobStream());
// add your content to the document here, as usual
doc.font('fonts/PalatinoBold.ttf')
.fontSize(25)
.text('Some text with an embedded font!', 100, 100);
// get a blob when you're done
doc.end();
stream.on('finish', function() {
// get a blob you can do whatever you like with
const blob = stream.toBlob('application/pdf');
// or get a blob URL for display in the browser
const url = stream.toBlobURL('application/pdf');
iframe.src = url;
});
pipe all your pdf data to your blob and then write it to a file or url.
or u can store the pdf directly into cloud storage like firebase storage and send download link to client.
If you want to generate pdfs dynamically then you can also try out html-pdf library in node which allows you to create a pdf from html template and add dynamic data in it. Also it is more reliable than pdfkit
https://www.npmjs.com/package/html-pdf
Also refer this link
Generate pdf file using pdfkit and send it to browser in nodejs-expressjs
I want to stream events to localhost/czml - which works fine in the console or in the get request window. But I can't stream those variables to the page because req.query always ends up being undefined
I'm a bloody beginner in programming and most of the time I have no clue what I'm doing (that's why the code is so bad...). I got that code through trial and error and mostly through copying from somewhere
var express = require('express'),
fs = require('fs'),
morgan = require('morgan'),
path = require('path'),
os = require('os'),
http = require('http');
const app = express();
const EventEmitter = require('events');
const stream = new EventEmitter();
var czmlstream = fs.createWriteStream('czml.czml',{flags: 'a'});
app.get('/czml', function (req, res, next) {
//don't log favicon
if (req.url === '/favicon.ico'){
res.end();
return;
}
//only log GET and set to stream
if (req.method === 'GET' ) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
function createCzml() {
//get the query value from the request
var id = req.query.id;
var lon = parseInt(req.query.lon);
var lat = parseInt(req.query.lat);
var alt = parseInt(req.query.alt);
// custom json format for czml file
var entity = {
"id": id,
"position": {
"cartographicDegrees": [lat, lon, alt]
},
"point": {
"color" : {"rgba": [0,0,255,255]},
"pixelSize": 20
}
};
return entity;
}
//first 2 lines for the event stream
res.write('event: czml\n');
res.write('data:' + JSON.stringify({ "id":"document", "version":"1.0" })+
'\n\n');
//always tells me that 10 listeners are added .... ?
stream.setMaxListeners(0);
//stream.on(req) = emit event on get request?
stream.on('req', function() {
res.write('event: czml\n');
res.write('data:' +JSON.stringify(createCzml)+ '\n\n'); //this
doesn't work
});
//not sure why this is needed
stream.emit('req');
}else{
res.WriteHead(405, {'Content-Type': 'text/plain'});
res.end('No GET Request - not allowed');
}
//morgan(format, {stream: czmlstream})(req,res,next);
}).listen(8000);
console.log('Server running');
What I want to achieve:
someone sends a get request to localhost/czml/?id=1&lon=-40&lat=30&alt=5000 => those queries are parsed and sent to localhost/whatever as event-stream in the format of:
event: czml
data: {json}
I'm nearly there (even if the code is bad) - it's just the last part left where I have to write those pesky queries to localhost/whatever. Right now it loggs everything fine in the console, but undefined is written to localhost/whatever...
I would be very grateful if you can point me in the right direction - keep in mind though, that I need easy and good explanations ;)
ok I solved this on my own and just for reference for some other newcomers:
It's basically this Example, only with listeners (as I understood them) for get requests
// most basic dependencies
var express = require('express')
, http = require('http')
, os = require('os')
, path = require('path')
, url = require('url')
, fs = require('fs');
// create the app
var app = express();
// configure everything, just basic setup
//app.set('port', process.env.PORT || 8000);
app.use(function(req, resp, next) {
resp.header("Access-Control-Allow-Origin", "*");
resp.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// Serve the www directory statically
app.use(express.static('www'));
//---------------------------------------
// Handle Get request and event-stream every second
//---------------------------------------
var openConnections = [];
var id, lon, lat, alt;
app.get('/czml', function(req, res, next) {
//don't log favicon
if (req.url === '/favicon.ico'){
res.end();
return;
} else {
var queryData = url.parse(req.url, true).query;
id = queryData.id;
lon = queryData.lon;
lat = queryData.lat;
alt = queryData.alt;
req.socket.setTimeout(2 * 60 * 1000);
// send headers for event-stream connection
// see spec for more information
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
res.write('\n');
// push this res object to our global variable
openConnections.push(res);
// send document packet
res.write('event: czml\ndata:' + JSON.stringify({ "id":"document", "version":"1.0" })+ '\n\n');
// When the request is closed, e.g. the browser window
// is closed. We search through the open connections
// array and remove this connection.
req.on("close", function() {
var toRemove;
for (var j =0 ; j < openConnections.length ; j++) {
if (openConnections[j] == res) {
toRemove =j;
break;
}
}
openConnections.splice(j,1);
});
next();
}
}).listen(8000);
function createMsg() {
var entity = {
"id" : id,
"position" : {
"cartographicDegrees": [lon,lat,alt]
},
"point" : {
"color" : {
"rgba" : [0,0,255,255]
},
"pixelSize" : 15
}
};
return JSON.stringify(entity);;
}
setInterval(function() {
// we walk through each connection
openConnections.forEach(function(res) {
// send doc
res.write('event: czml\n');
res.write('data:' + createMsg() + '\n\n');
});
}, 1000);
I don't know how this works here on SO - the above isn't really the answer to my question - more of a workaround. But it works, so I guess it's fine :)
I ran into a problem and I feel that node-http-proxy is changing my target links. I got a few examples below.
I am using express as my server and using Metaweather API .
The problem is that I was able to get data from the endpoints below
https://www.metaweather.com/api/location/2487956/
https://www.metaweather.com/api/location/2487956/2013/4/30/
But when I try to call the API from https://www.metaweather.com/api/location/search/?lattlong=36.96,-122.02
It fails with status code 500 which I lead me thinking that node-http-proxy added some values after 122.02 as it was not closed with /
server.js
const express = require("express");
const next = require("next");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const httpProxy = require("http-proxy");
const proxyOptions = {
changeOrigin: true
};
const apiProxy = httpProxy.createProxyServer(proxyOptions);
const apiUrl =
"https://www.metaweather.com/api/location/search/?lattlong=36.96,-122.02";
/*
https://www.metaweather.com/api/location/search/?lattlong=36.96,-122.02 - failed with 500
https://www.metaweather.com/api/location/2487956/ - passed
https://www.metaweather.com/api/location/2487956/2013/4/30/ - passed
*/
app
.prepare()
.then(() => {
const server = express();
server.use("/api", (req, res) => {
console.log("Going to call this API " + apiUrl);
apiProxy.web(req, res, { target: apiUrl });
});
server.get("*", (req, res) => {
return handle(req, res);
});
server.listen(3000, err => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
Thanks for looking into this question.
I have reproduced where this is happening in node-http-proxy.
In common.js there is a function called urlJoin which is appending the req.url to the end of the target url.
I'm not exactly sure what the intent is, but it's a start.
Here's my test:
const urlJoin = function() {
//
// We do not want to mess with the query string. All we want to touch is the path.
//
var args = Array.prototype.slice.call(arguments),
lastIndex = args.length - 1,
last = args[lastIndex],
lastSegs = last.split('?'),
retSegs;
args[lastIndex] = lastSegs.shift();
//
// Join all strings, but remove empty strings so we don't get extra slashes from
// joining e.g. ['', 'am']
//
retSegs = [
args.filter(Boolean).join('/')
.replace(/\/+/g, '/')
.replace('http:/', 'http://')
.replace('https:/', 'https://')
];
// Only join the query string if it exists so we don't have trailing a '?'
// on every request
// Handle case where there could be multiple ? in the URL.
retSegs.push.apply(retSegs, lastSegs);
return retSegs.join('?')
};
let path = urlJoin('/api/location/search/?lattlong=36.96,-122.02', '/');
console.log(path);
// /api/location/search/?lattlong=36.96,-122.02/
Can we get the variables in the query string in Node.js just like we get them in $_GET in PHP?
I know that in Node.js we can get the URL in the request. Is there a method to get the query string parameters?
Since you've mentioned Express.js in your tags, here is an Express-specific answer: use req.query. E.g.
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('id: ' + req.query.id);
});
app.listen(3000);
In Express it's already done for you and you can simply use req.query for that:
var id = req.query.id; // $_GET["id"]
Otherwise, in NodeJS, you can access req.url and the builtin url module to url.parse it manually:
var url = require('url');
var url_parts = url.parse(request.url, true);
var query = url_parts.query;
In Express, use req.query.
req.params only gets the route parameters, not the query string parameters. See the express or sails documentation:
(req.params) Checks route params, ex: /user/:id
(req.query) Checks query string params, ex: ?id=12 Checks urlencoded body params
(req.body), ex: id=12 To utilize urlencoded request bodies, req.body should be an object. This can be done by using the _express.bodyParser middleware.
That said, most of the time, you want to get the value of a parameter irrespective of its source. In that case, use req.param('foo'). Note that this has been deprecated as of Express 4: http://expressjs.com/en/4x/api.html#req.param
The value of the parameter will be returned whether the variable was in the route parameters, query string, or the encoded request body.
Side note- if you're aiming to get the intersection of all three types of request parameters (similar to PHP's $_REQUEST), you just need to merge the parameters together-- here's how I set it up in Sails. Keep in mind that the path/route parameters object (req.params) has array properties, so order matters (although this may change in Express 4)
For Express.js you want to do req.params:
app.get('/user/:id', function(req, res) {
res.send('user' + req.params.id);
});
I learned from the other answers and decided to use this code throughout my site:
var query = require('url').parse(req.url,true).query;
Then you can just call
var id = query.id;
var option = query.option;
where the URL for get should be
/path/filename?id=123&option=456
//get query¶ms in express
//etc. example.com/user/000000?sex=female
app.get('/user/:id', function(req, res) {
const query = req.query;// query = {sex:"female"}
const params = req.params; //params = {id:"000000"}
})
If you are using ES6 and Express, try this destructuring approach:
const {id, since, fields, anotherField} = request.query;
In context:
const express = require('express');
const app = express();
app.get('/', function(req, res){
const {id, since, fields, anotherField} = req.query;
});
app.listen(3000);
You can use default values with destructuring too:
// sample request for testing
const req = {
query: {
id: '123',
fields: ['a', 'b', 'c']
}
}
const {
id,
since = new Date().toString(),
fields = ['x'],
anotherField = 'default'
} = req.query;
console.log(id, since, fields, anotherField)
There are 2 ways to pass parameters via GET method
Method 1 :
The MVC approach where you pass the parameters like /routename/:paramname
In this case you can use req.params.paramname to get the parameter value For Example refer below code where I am expecting Id as a param
link could be like : http://myhost.com/items/23
var express = require('express');
var app = express();
app.get("items/:id", function(req, res) {
var id = req.params.id;
//further operations to perform
});
app.listen(3000);
Method 2 :
General Approach : Passing variables as query string using '?' operator
For Example refer below code where I am expecting Id as a query parameter
link could be like : http://myhost.com/items?id=23
var express = require('express');
var app = express();
app.get("/items", function(req, res) {
var id = req.query.id;
//further operations to perform
});
app.listen(3000);
You should be able to do something like this:
var http = require('http');
var url = require('url');
http.createServer(function(req,res){
var url_parts = url.parse(req.url, true);
var query = url_parts.query;
console.log(query); //{Object}
res.end("End")
})
UPDATE 4 May 2014
Old answer preserved here: https://gist.github.com/stefek99/b10ed037d2a4a323d638
1) Install express: npm install express
app.js
var express = require('express');
var app = express();
app.get('/endpoint', function(request, response) {
var id = request.query.id;
response.end("I have received the ID: " + id);
});
app.listen(3000);
console.log("node express app started at http://localhost:3000");
2) Run the app: node app.js
3) Visit in the browser: http://localhost:3000/endpoint?id=something
I have received the ID: something
(many things have changed since my answer and I believe it is worth keeping things up to date)
Express specific simple ways to fetch
query strings(after ?) such as https://...?user=abc&id=123
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('id: ' + req.query.id);
});
app.listen(3000);
query params such as https://.../get/users/:id
var express = require('express');
var app = express();
app.get('/get/users/:id', function(req, res){
res.send('id: ' + req.params.id);
});
app.listen(3000);
A small Node.js HTTP server listening on port 9080, parsing GET or POST data and sending it back to the client as part of the response is:
var sys = require('sys'),
url = require('url'),
http = require('http'),
qs = require('querystring');
var server = http.createServer(
function (request, response) {
if (request.method == 'POST') {
var body = '';
request.on('data', function (data) {
body += data;
});
request.on('end',function() {
var POST = qs.parse(body);
//console.log(POST);
response.writeHead( 200 );
response.write( JSON.stringify( POST ) );
response.end();
});
}
else if(request.method == 'GET') {
var url_parts = url.parse(request.url,true);
//console.log(url_parts.query);
response.writeHead( 200 );
response.write( JSON.stringify( url_parts.query ) );
response.end();
}
}
);
server.listen(9080);
Save it as parse.js, and run it on the console by entering "node parse.js".
Whitequark responded nicely. But with the current versions of Node.js and Express.js it requires one more line. Make sure to add the 'require http' (second line). I've posted a fuller example here that shows how this call can work. Once running, type http://localhost:8080/?name=abel&fruit=apple in your browser, and you will get a cool response based on the code.
var express = require('express');
var http = require('http');
var app = express();
app.configure(function(){
app.set('port', 8080);
});
app.get('/', function(req, res){
res.writeHead(200, {'content-type': 'text/plain'});
res.write('name: ' + req.query.name + '\n');
res.write('fruit: ' + req.query.fruit + '\n');
res.write('query: ' + req.query + '\n');
queryStuff = JSON.stringify(req.query);
res.end('That\'s all folks' + '\n' + queryStuff);
});
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
})
It is so simple:
Example URL:
http://stackoverflow.com:3000/activate_accountid=3&activatekey=$2a$08$jvGevXUOvYxKsiBt.PpMs.zgzD4C/wwTsvjzfUrqLrgS3zXJVfVRK
You can print all the values of query string by using:
console.log("All query strings: " + JSON.stringify(req.query));
Output
All query strings : { "id":"3","activatekey":"$2a$08$jvGevXUOvYxKsiBt.PpMs.zgzD4C/wwTsvjz
fUrqLrgS3zXJVfVRK"}
To print specific:
console.log("activatekey: " + req.query.activatekey);
Output
activatekey: $2a$08$jvGevXUOvYxKsiBt.PpMs.zgzD4C/wwTsvjzfUrqLrgS3zXJVfVRK
You can use
request.query.<varible-name>;
You can use with express ^4.15.4:
var express = require('express'),
router = express.Router();
router.get('/', function (req, res, next) {
console.log(req.query);
});
Hope this helps.
In express.js you can get it pretty easy, all you need to do in your controller function is:
app.get('/', (req, res, next) => {
const {id} = req.query;
// rest of your code here...
})
And that's all, assuming you are using es6 syntax.
PD. {id} stands for Object destructuring, a new es6 feature.
app.get('/user/:id', function(req, res) {
res.send('user' + req.params.id);
});
You can use this or you can try body-parser for parsing special element from the request parameters.
consider this url -> /api/endpoint/:id?name=sahil
here id is param where as name is query. You can get this value in nodejs like this
app.get('/api/endpoint/:id', (req, res) => {
const name = req.query.name; // query
const id = req.params.id //params
});
There are many answers here regarding accessing the query using request.query however, none have mentioned its type quirk. The query string type can be either a string or an array, and this type is controlled by the user.
For instance using the following code:
const express = require("express");
const app = express();
app.get("/", function (req, res) {
res.send(`Your name is ${(req.query.name || "").length} characters long`);
});
app.listen(3000);
Requesting /?name=bob will return Your name is 3 characters long but requesting /?name=bob&name=jane will return Your name is 2 characters long because the parameter is now an array ['bob', 'jane'].
Express offers 2 query parsers: simple and extended, both will give you either a string or an array. Rather than checking a method for possible side effects or validating types, I personally think you should override the parser to have a consistent type: all arrays or all strings.
const express = require("express");
const app = express();
const querystring = require("querystring");
// if asArray=false only the first item with the same name will be returned
// if asArray=true all items will be returned as an array (even if they are a single item)
const asArray = false;
app.set("query parser", (qs) => {
const parsed = querystring.parse(qs);
return Object.entries(parsed).reduce((previous, [key, value]) => {
const isArray = Array.isArray(value);
if (!asArray && isArray) {
value = value[0];
} else if (asArray && !isArray) {
value = [value];
}
previous[key] = value;
return previous;
}, {});
});
app.get("/", function (req, res) {
res.send(`Your name is ${(req.query.name || "").length} characters long`);
});
app.listen(3000);
So, there are two ways in which this "id" can be received:
1) using params: the code params will look something like :
Say we have an array,
const courses = [{
id: 1,
name: 'Mathematics'
},
{
id: 2,
name: 'History'
}
];
Then for params we can do something like:
app.get('/api/posts/:id',(req,res)=>{
const course = courses.find(o=>o.id == (req.params.id))
res.send(course);
});
2) Another method is to use query parameters.
so the url will look something like ".....\api\xyz?id=1" where "?id=1" is the query part. In this case we can do something like:
app.get('/api/posts',(req,res)=>{
const course = courses.find(o=>o.id == (req.query.id))
res.send(course);
});
In case you want to avoid express, use this example:
var http = require('http');
const url = require('url');
function func111(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var q = url.parse(req.url, true);
res.end("9999999>>> " + q.query['user_name']);
}
http.createServer(func111).listen(3000);
usage:
curl http://localhost:3000?user_name=user1
by yl
you can use url module to collect parameters by using url.parse
var url = require('url');
var url_data = url.parse(request.url, true);
var query = url_data.query;
In expressjs it's done by,
var id = req.query.id;
Eg:
var express = require('express');
var app = express();
app.get('/login', function (req, res, next) {
console.log(req.query);
console.log(req.query.id); //Give parameter id
});
If you ever need to send GET request to an IP as well as a Domain (Other answers did not mention you can specify a port variable), you can make use of this function:
function getCode(host, port, path, queryString) {
console.log("(" + host + ":" + port + path + ")" + "Running httpHelper.getCode()")
// Construct url and query string
const requestUrl = url.parse(url.format({
protocol: 'http',
hostname: host,
pathname: path,
port: port,
query: queryString
}));
console.log("(" + host + path + ")" + "Sending GET request")
// Send request
console.log(url.format(requestUrl))
http.get(url.format(requestUrl), (resp) => {
let data = '';
// A chunk of data has been received.
resp.on('data', (chunk) => {
console.log("GET chunk: " + chunk);
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log("GET end of response: " + data);
});
}).on("error", (err) => {
console.log("GET Error: " + err);
});
}
Don't miss requiring modules at the top of your file:
http = require("http");
url = require('url')
Also bare in mind that you may use https module for communicating over secured domains and ssl. so these two lines would change:
https = require("https");
...
https.get(url.format(requestUrl), (resp) => { ......
do like me
npm query-string
import queryString from "query-string";
export interface QueryUrl {
limit?: number;
range?: string;
page?: number;
filed?: string;
embody?: string;
q?: string | object;
order?: number;
sort?: string;
}
let parseUri: QueryUrl = queryString.parse(uri.query);
I am using MEANJS 0.6.0 with express#4.16, it's good
Client:
Controller:
var input = { keyword: vm.keyword };
ProductAPi.getOrder(input)
services:
this.getOrder = function (input) {return $http.get('/api/order', { params: input });};
Server
routes
app.route('/api/order').get(products.order);
controller
exports.order = function (req, res) {
var keyword = req.query.keyword
...