Proper way to return JSON using node or Express
Jsonnode.jsExpressHttpresponseJson Problem Overview
So, one can attempt to fetch the following JSON object:
$ curl -i -X GET http://echo.jsontest.com/key/value/anotherKey/anotherValue
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=ISO-8859-1
Date: Wed, 30 Oct 2013 22:19:10 GMT
Server: Google Frontend
Cache-Control: private
Alternate-Protocol: 80:quic,80:quic
Transfer-Encoding: chunked
{
"anotherKey": "anotherValue",
"key": "value"
}
$
Is there a way to produce exactly the same body in a response from a server using node or express? Clearly, one can set the headers and indicate that the content-type of the response is going to be "application/json", but then there are different ways to write/send the object. The one that I have seen commonly being used is by using a command of the form:
response.write(JSON.stringify(anObject));
However, this has two points where one could argue as if they were "problems":
- We are sending a string.
- Moreover, there is no new line character in the end.
Another idea is to use the command:
response.send(anObject);
This appears to be sending a JSON object based on the output of curl similar to the first example above. However, there is no new line character in the end of the body when curl is again being used on a terminal. So, how can one actually write down something like this with a new line character appended in the end using node or node/express?
Json Solutions
Solution 1 - Json
That response is a string too, if you want to send the response prettified, for some awkward reason, you could use something like JSON.stringify(anObject, null, 3)
It's important that you set the Content-Type
header to application/json
, too.
var http = require('http');
var app = http.createServer(function(req,res){
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ a: 1 }));
});
app.listen(3000);
// > {"a":1}
Prettified:
var http = require('http');
var app = http.createServer(function(req,res){
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ a: 1 }, null, 3));
});
app.listen(3000);
// > {
// > "a": 1
// > }
I'm not exactly sure why you want to terminate it with a newline, but you could just do JSON.stringify(...) + '\n'
to achieve that.
Express
In express you can do this by changing the options instead.
> 'json replacer'
JSON replacer callback, null by default
>
> 'json spaces'
JSON response spaces for formatting, defaults to 2 in development, 0 in production
Not actually recommended to set to 40
app.set('json spaces', 40);
Then you could just respond with some json.
res.json({ a: 1 });
It'll use the 'json spaces
' configuration to prettify it.
Solution 2 - Json
Since Express.js 3x the response object has a json() method which sets all the headers correctly for you and returns the response in JSON format.
Example:
res.json({"foo": "bar"});
Solution 3 - Json
If you're using Express, you can use this:
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({key:"value"}));
or just this
res.json({key:"value"});
Solution 4 - Json
The res.json()
function should be sufficient for most cases.
app.get('/', (req, res) => res.json({ answer: 42 }));
The res.json()
function converts the parameter you pass to JSON using JSON.stringify()
and sets the Content-Type
header to application/json; charset=utf-8
so HTTP clients know to automatically parse the response.
Solution 5 - Json
If you are trying to send a json file you can use streams
var fs = require('fs');
var usersFilePath = path.join(__dirname, 'users.min.json');
apiRouter.get('/users', function(req, res){
var readable = fs.createReadStream(usersFilePath);
readable.pipe(res);
});
Solution 6 - Json
You can make a helper for that: Make a helper function so that you can use it everywhere in your application
function getStandardResponse(status,message,data){
return {
status: status,
message : message,
data : data
}
}
Here is my topic route where I am trying to get all topics
router.get('/', async (req, res) => {
const topics = await Topic.find().sort('name');
return res.json(getStandardResponse(true, "", topics));
});
Response we get
{
"status": true,
"message": "",
"data": [
{
"description": "sqswqswqs",
"timestamp": "2019-11-29T12:46:21.633Z",
"_id": "5de1131d8f7be5395080f7b9",
"name": "topics test xqxq",
"thumbnail": "waterfall-or-agile-inforgraphics-thumbnail-1575031579309.jpg",
"category_id": "5de0fe0b4f76c22ebce2b70a",
"__v": 0
},
{
"description": "sqswqswqs",
"timestamp": "2019-11-29T12:50:35.627Z",
"_id": "5de1141bc902041b58377218",
"name": "topics test xqxq",
"thumbnail": "waterfall-or-agile-inforgraphics-thumbnail-1575031835605.jpg",
"category_id": "5de0fe0b4f76c22ebce2b70a",
"__v": 0
},
{
"description": " ",
"timestamp": "2019-11-30T06:51:18.936Z",
"_id": "5de211665c3f2c26c00fe64f",
"name": "topics test xqxq",
"thumbnail": "waterfall-or-agile-inforgraphics-thumbnail-1575096678917.jpg",
"category_id": "5de0fe0b4f76c22ebce2b70a",
"__v": 0
},
{
"description": "null",
"timestamp": "2019-11-30T06:51:41.060Z",
"_id": "5de2117d5c3f2c26c00fe650",
"name": "topics test xqxq",
"thumbnail": "waterfall-or-agile-inforgraphics-thumbnail-1575096701051.jpg",
"category_id": "5de0fe0b4f76c22ebce2b70a",
"__v": 0
},
{
"description": "swqdwqd wwwwdwq",
"timestamp": "2019-11-30T07:05:22.398Z",
"_id": "5de214b2964be62d78358f87",
"name": "topics test xqxq",
"thumbnail": "waterfall-or-agile-inforgraphics-thumbnail-1575097522372.jpg",
"category_id": "5de0fe0b4f76c22ebce2b70a",
"__v": 0
},
{
"description": "swqdwqd wwwwdwq",
"timestamp": "2019-11-30T07:36:48.894Z",
"_id": "5de21c1006f2b81790276f6a",
"name": "topics test xqxq",
"thumbnail": "waterfall-or-agile-inforgraphics-thumbnail-1575099408870.jpg",
"category_id": "5de0fe0b4f76c22ebce2b70a",
"__v": 0
}
]
}
Solution 7 - Json
You can just prettify it using pipe and one of many processor. Your app should always response with as small load as possible.
$ curl -i -X GET http://echo.jsontest.com/key/value/anotherKey/anotherValue | underscore print
Solution 8 - Json
For the header half of the question, I'm gonna give a shout out to res.type
here:
res.type('json')
is equivalent to
res.setHeader('Content-Type', 'application/json')
Source: express docs:
> Sets the Content-Type HTTP header to the MIME type as determined by mime.lookup() for the specified type. If type contains the “/” character, then it sets the Content-Type to type.
Solution 9 - Json
You can use a middleware to set the default Content-Type, and set Content-Type differently for particular APIs. Here is an example:
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const server = app.listen(port);
server.timeout = 1000 * 60 * 10; // 10 minutes
// Use middleware to set the default Content-Type
app.use(function (req, res, next) {
res.header('Content-Type', 'application/json');
next();
});
app.get('/api/endpoint1', (req, res) => {
res.send(JSON.stringify({value: 1}));
})
app.get('/api/endpoint2', (req, res) => {
// Set Content-Type differently for this particular API
res.set({'Content-Type': 'application/xml'});
res.send(`<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>`);
})
Solution 10 - Json
Older version of Express use app.use(express.json())
or bodyParser.json()
read more about bodyParser middleware
On latest version of express we could simply use res.json()
const express = require('express'),
port = process.env.port || 3000,
app = express()
app.get('/', (req, res) => res.json({key: "value"}))
app.listen(port, () => console.log(`Server start at ${port}`))