Using knex with express, how can I access the results of a knex query?
Example:
var bots = []
response = knex.select('id', 'name').from('robots')
.then(function(robots){
console.log(robots);
bots = robots
});
console.log(bots)
This will log the robots but not not update the bots array, which is empty.
EDIT:
As a synchronous workaround, in an express route, I stuck the express block inside the knex block:
router.get('/robots', function (req, res) {
response = knex.select('id', 'name').from('robots').then(function(bots){
res.render('robots/index', {
page_title: 'All Robots',
robots: bots
}); // res.render
}); // knex.select
}); // router.get
Is this the recommended pattern?
knex uses Promises. Specifically, it uses http://bluebirdjs.com/docs/getting-started.html. console.log(bots) will not work because it is called right away, while .then( ... ) is only called after the knex query has been successfully called and run.
Your edited, "synchronous workaround" is the correct method to run the Express response, though you do not need to set that query to var response (see my comment on your question for more on that).
I'd suggest using async/await functions.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
router.get('/robots', async function (req, res) {
const bots = await knex.select('id', 'name').from('robots');
res.render('robots/index', {
page_title: 'All Robots',
robots: bots
}); // res.render
}); // router.get
Related
I have a website that will use EJS templating to display data from my database. Instead of creating a second API just to do this, I would like to use the pre-existing API that other developers can use. Is there a way to get data from my API in the most meta way possible.
main app route
app.get("/facts/random", async (req, res) => {
/* Make request to /api/random here and get JSON data */
});
API route
app.get("/api/random/", async (req, res) => {
let results = await Fact.find();
const randomResult = Math.floor(Math.random() * results.length);
return res.json(results[randomResult]);
})
I want to make a request from the first route to the second route using the most meta way possible.
If /api/random is in another microservice then you can use Axios to make an http call, but if it is in the same Express App then refactor the code and turn it into a function and call that function in both the controllers.
Usually, you don't have your own http server make a request to itself. Usually, you just factor out the functionality the other API is using into an exported function and you call the function directly.
It's definitely more efficient that way since there's really no reason to package it up into an http request, go through the network stack, parse it as an http request in your server, process it, create an http response, send that back through the network stack, parse the response, then process it when you could avoid 6 of those 8 steps by just calling a common function you factored out.
app.get("/facts/random", async (req, res) => {
let randomResult = await apiRandom();
...
});
async function apiRandom() {
let results = await Fact.find();
const randomResult = Math.floor(Math.random() * results.length);
return results[randomResult];
}
app.get("/api/random/", async (req, res) => {
let randomResult = await apiRandom();
res.json(randomResult);
});
If, for some reason, you do want to make requests to your own http server, then get a library like got() or axios() that makes that easy.
i have tried implementing my own middleware which is similar to app.use(express.json())
the code is here
const rahul_express={
json(){
return (req,res,next)=>{
let arr=[]
req.on('data',(chunk)=>{
arr.push(chunk)
})
req.on('end',(fin)=>{
const parserbody=new Buffer.concat(arr).toString()
req.body=JSON.parse(parserbody)
console.log(req.body)
})
next()
}
}
}
app.use(rahul_express.json())
i have tried parsing the JSON and adding it the req but still in other middleware i can't access the req.body data,i don't know why,any help will be appreciated!
Events are called "asynchronously," meaning that the code doesn't wait until the event is emitted. This means that next() is ran before the events are finished.
The correct approach to this problem is to use next() inside of the end event's callback, like so:
req.on('end', (fin) => {
const parserbody = new Buffer.concat(arr).toString();
req.body = JSON.parse(parserbody);
console.log(req.body);
next();
})
Let's say I have an express api with an /api/load-post endpoint. That is handled by the loadPostHandler: RequestHandler
index.ts
const app = express();
app.get("/api/load-post", loadPostHandler);
loadPostHandler.ts
Can I make a fetch request from that handler?
import fetch from "cross-fetch";
export const loadPostHandler: RequestHandler = async (req, res) => {
// HANDLE BLOGPOST LOAD
res.json({ blogPost: blogPostData }) // RES JSON THE BLOGPOST DATA
await fetch("/api/updateViewcount?id=POST_ID"); // MAKE A FETCH REQUEST
};
Is this something people usually do? Or is this an anti-pattern? Not sure if this would even work.
Short answer
Yes, you can make requests in the api call handler in general, and it depends on the requirements of that api.
Longer version
Judging by your example: you want to update view count, and since there is no use of response of it, you don't need to await for the response. You can just fire it without await.
And structurally it would be better practice to move it to a separate function that make an actual call, or fire an event and handle it in a different place.
Moreover, it looks like you are calling the same api server, in that case it will be better just to call a function instead of the api call.
const updatePostViewcount = postId => {
// HANDLE BLOGPOST VIEWCOUNT UPDATE
}
export const loadPostHandler: RequestHandler = async (req, res) => {
// HANDLE BLOGPOST LOAD
// no await here because we don't need the response
// it will still run asynchronously
updatePostViewcount(POST_ID);
res.json({ blogPost: blogPostData }) // RES JSON THE BLOGPOST DATA
};
I currently have a POST route defined in an Express Node.js application as so:
var locationService = require("../app/modules/locationservice.js");
app.post('/createstop', isLoggedIn, function(req, res) {
locationService.createStop(res, req.body);
});
(for this question, please assume the routing in & db works.. my record is created on form submission, it's the response I am struggling with)
In the locationservice.js class I then currently have
var models = require('../models');
exports.createStop = function(res, formData) {
models.location.build({ name: formData.name })
.save()
.then(function(locationObj) {
res.json({ dbResult : locationObj });
});
};
So as you can see, my route invokes the exported function CreateStop which uses the Sequelize persistent layer to insert a record asynchronously, after which I can stick the result on the response in the promised then()
So at the moment this only works by passing the response object into the locationservice.js method and then setting res.json in the then() there. This is sub-optimal to me with regards to my service classes, and doesn't feel right either.
What I would like to be able to do is "treat" my createStop method as a promise/with a callback so I can just return the new location object (or an error) and deal with it in the calling method - as future uses of this method might have a response context/parameter to pass in/be populated.
Therefore in the route I would do something more like:
var locationService = require("../app/modules/locationservice.js");
app.post('/createstop', isLoggedIn, function(req, res) {
locationService.createStop(req.body)
.then(dataBack) {
res.json(dataBack);
};
});
Which means, I could call createStop from else where in the future and react to the response in that promise handler. But this is currently beyond me. I have done my due diligence research, but some individual expert input on my specific case would be most appreciated.
Your locationservice.js could look like that
exports.createShop = function(data){
// here I have used create instead of build -> save
return models.location.create(data).then(function(location){
// here you return instance of saved location
return location;
});
}
And then your post() method should be like below
app.post('/createstop', isLoggedIn, function(req, res){
locationService.createShop(req.body).then(function(location){
// here you access the location created and saved in createShop function
res.json(location);
}).catch(function(error){
// handle the error
});
});
Wrap your createStop function with a promise like so:
exports.createStop = function(res, formData) {
return new Promise(function(resolve, reject) {
models.location.build({ name: formData.name })
.save()
.then(function(locationObj) {
resolve({ dbResult : locationObj });
});
//in case of error, call reject();
});
};
This will allow you to use the .then after the createStop within your router.
My application has several layers: middleware, controllers, managers. Controllers interface is identical to middlewares one: (req, res, next).
So my question is: how can I test my controllers without starting the server and sending 'real' requests to localhost. What I want to do is to create request, response instances as nodejs does and then just call controllers method.
Something like this:
var req = new Request()
var res = new Response()
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
Any advice is highly appreciated. Thanks!
P.S. the reason why I want to do this is that at the end I would like to test whether the response object contains correct variables for the jade views.
There's a semi decent implementation at node-mocks-http
Require it:
var mocks = require('node-mocks-http');
you can then compose req and response objects:
req = mocks.createRequest();
res = mocks.createResponse();
You can then test your controller directly:
var demoController = require('demoController');
demoController.login(req, res);
assert.equal(res.json, {})
caveat
There is at time of writing an issue in this implementation to do with the event emitter not being fired.
Since JavaScript is a dynamically typed language you can create mock objects and passing them to your controllers as follow:
var req = {};
var res = {};
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
If your controller needs a particular piece of data or functionality from your request or response object you'll need to provide such data or functionality in your mocks. For example,
var req = {};
req.url = "http://google.com"; // fake the Url
var res = {};
res.write = function(chunk, encoding) {
// fake the write method
};
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
I would try using dupertest for this. It's a node module I created for the very purpose of easy controller testing without having to spin up a new server.
It keeps the familiar syntax of node modules like request or supertest, but again, without the need to spin up a server.
It runs a lot like Hector suggested above, but integrates with a test framework like Jasmine to feel a little more seamless.
An example relating to your question may look like:
request(controller.get_user)
.params({id: user_id})
.expect(user, done);
Or the more explicit longhand version:
request(controller.get_user)
.params({id: user_id})
.end(function(response) {
expect(response).toEqual(user);
done();
});
Note: the examples assume user_id and user are defined somewhere, and that the controller grabs and returns a user based on id.
Edit: reading your response to an answer above, I will admit the downside currently is that this module does not integrate a more robust mock request or response object by default. dupertest makes it super easy to extend and add properties to both req and res, but by default they are pretty bare.
If you want to use the real req and res objects, you have to send real requests to the server. However this is much easier than you might think. There are a lot of examples at the express github repo. The following shows the tests for req.route
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.route', function(){
it('should be the executed Route', function(done){
var app = express();
app.get('/user/:id/edit', function(req, res){
// test your controllers with req,res here (like below)
req.route.method.should.equal('get');
req.route.path.should.equal('/user/:id/edit');
res.end();
});
request(app)
.get('/user/12/edit')
.expect(200, done);
})
})
})
A bit old post, but I would like to give my 2 cents. The approach you want to take depends on whether you are doing unit testing or integration testing. If you are going down the route of using supertest, that means you are running the actual implementation code and that means you are doing integration testing. If that's what you want to do this approach is fine.
But if you are doing unit testing, you would mock req and res objects (and any other dependencies involved). In the below code (non-relevant code removed for brevity), I am mocking res and giving just a mock implementation of json method, as that's the only method I need for my tests.
// SUT
kids.index = function (req, res) {
if (!req.user || !req.user._id) {
res.json({
err: "Invalid request."
});
} else {
// non-relevent code
}
};
// Unit test
var req, res, err, sentData;
describe('index', function () {
beforeEach(function () {
res = {
json: function (resp) {
err = resp.err;
sentData = resp.kids;
}
};
});
it("should return error if no user passed in request", function () {
req = {};
kidsController.index(req, res);
expect(err).to.equal("Invalid request.");
});
/// More tests....
})
Take a look at node-tdd and the useNock flag. It builds on top of mocha and nock and automatically creates and uses a recording file for each test.
We love that it's so easy to use. Basically just "enable and forget" and focus on writing requests / test cases. If requests for a test change, one still needs to delete or adjust the recording file, but at least it's entirely separate from the code.