Store morgan logs into database - javascript

I am using morgan('dev') for logging, but I want to store this object "GET /users/get 200 195.315 ms - 393" [in console log] into database.
like
- method : get
- endpoint : /users/get
- status : 200
How can I get this object?
How to explode and insert data into field(method, endpoint, status) above on database?

You can create a small middleware that will listen on 'finish' event of Response and save the data into the database:
app.use((req, res, next) => {
const method = req.method;
const endpoint = req.originalUrl;
res.on('finish', () => {
const status = res.status;
saveRequestDataToDatabase(method, endpoint, status);
next();
});
});

There are modules already out there that provide stream-compatible interfaces to known storages, such as Mongoose-Morgan which allows you to stream your Mongoose logs directly into MongoDB. However, if you can’t find a morgan-compatible module, you can simply write a function that returns a writable stream and sends the information where you need it.
Morgan NPM Logger – The Beginner’s Guide
Create a new named format:
const morgan = require('morgan')
const Writable = require("stream").Writable
morgan.token("custom", "-method: :method -endpoint: :url -status: status")
Use the new format by name:
class MyStream extends Writable {
write(line) {
//here you send the log line to wherever you need
console.log("Logger:: ", line)
}
}
let writer = new MyStream()
app.use(morgan(‘custom’, {stream: write}))

Related

detect a request using NodeJS without express or accessing the request object outside an express middleware [duplicate]

This question already has answers here:
Access current req object everywhere in Node.js Express
(2 answers)
Closed 8 months ago.
This post was edited and submitted for review 8 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I have the following lines of code:
const express = require('express');
const app = express()
// ... defining the routes, app.get('/api/users', (req, res, next)=>{ }) ...etc
app.listen(3000, ()=> console.log('Listening on port 3000...'))
module.exports = app
I want to be able to read the request object outside an express middleware.
I have another file called mongoose_models.js, inside that file, I don't have the access to the express middleware arguments (req, res, next).
And the only option I have for reading the request body from that file is to import the app and somehow read the request Object.
NodeJs is event-driven, so there must be a way somehow to do so, for instance, inside the file mongoose_models.js I would have maybe something like this code:
// mongoose_models.js
// ... some code
const app = require('../app.js')
app.on('request', (req)=>{
// here I have the request
})
or maybe if express supports:
// mongoose_models.js
// ... some code
const { req } = require('express')
console.log(req.body) // ? maybe something like that ?
or maybe if express supports too:
// mongoose_models.js
// ... some code
const app = require('../app.js')
app.onRequest((req, res) => {
// here I have the access to the request object
})
Is there a way to reach the request object without having to be inside an express middleware in NodeJS?
edit:
Some of you asked me to provide the source code, unfortunately, I wanted to provide a stackblitz or code sandbox instance, but I didn't know how to set up the connections to the database.
Anyway, the following is the file structure of the sample project:
app.js file (full code):
const express = require('express')
const app = express()
const mongoose = require('mongoose')
const RoomModel = require('./mongoose_models')
app.use((req, res, next) => {
// this middleware is the "protect" middleware, it validates a JWT (JSON web token), decodes it, and then stores the user it finds to the req object:
// .... etc some code
// decode the JWT .. some code
// find the user in the DB const userDoc = await UserModel.findOne({ _id: decodedJWT.id )})
const userDoc = {
id: 'abc-123-edf-cds-123-321-qu5-eu4-dc9-182',
name: 'Murat',
// and some other fields ... etc
}
req.$loggedInUser = userDoc
})
app.get('/rooms', async(req, res, next) => {
const docs = await RoomModel.find({})
res.status(200).json({
message: 'here are all the rooms',
results: docs.length,
data: docs,
})
})
app.post('/rooms', async(req, res, next) => {
const doc = await RoomModel.create(req.body)
res.status(201).json({
message: 'the new room which got created:',
data: doc,
})
})
// connecting to the database:
mongoose.connect(
'mongodb+srv://USERNAME:PASSWORD#YOUR_CLUSTER.mongodb.net/?retryWrites=true&w=majority'
)
// starting the HTTP service:
app.listen(3000, () => console.log('app listening on port 3000...'))
mongoose_models.js file (full code):
const mongoose = require('mongoose')
const roomSchema = new mongoose.Schema({
name: String,
by: mongoose.Schema.ObjectId,
})
roomSchema.pre('save', function(next) {
// Here I want to make the by field be the req.$loggedInUser.id but I can't because I have no way to read the request object
const doc = this
// doc.by = req.$loggedInUser.id // < ----- 👈👈👈 HERE, I can't reach the req object
next()
})
const RoomModel = mongoose.model('Room', roomSchema, 'rooms')
module.exports = RoomModel
NodeJS is event driven, so there must be a way somehow to do so, for
instance, inside the file mongoose_models.js I would have maybe
something like this code:
// mongoose_models.js
// ... some code
const app = require('../app.js')
app.on('request', (req)=>{
// here I have the request
})
This approach is, essentially, middleware. So write is as middleware.
const myMiddleware = (req, res, next) => {
// here you have the request
next(); // go to next middleware
}
module.exports = myMiddleware
Attaching something to listen for requests is done with use (for non-method specific functions) and post, get, etc. There is no on method or onRequest method.
// mongoose_models.js
// ... some code
const { req } = require('express')
console.log(req.body) // ? maybe something like that ?
The request object doesn't exist until the client makes a request to the server.
You get a new request object each time a request is made.
The server might be handling multiple requests at the same time.
So no, you can't do anything like that.
Is there a way to reach the request object without having to be inside an express middleware in NodeJS?
No.

Can't get response body in Axios with express

My frontend code:
const user = useSelector(selectUser)
function getWatchLater(name){
axios.get('http://localhost:5000/watchlater', {user:name})
.then((response)=>{
// console.log(response.data)
setWatchLater(response.data)
})
}
The user variable holds the username and the function sends that username to the backend to get the data. Don't worry, the user variable does hold the username, i have checked it thoroughly.
My backend code:
const mysql = require('mysql2')
const express = require('express')
const cors = require('cors');
const app = express()
app.use(cors());
app.use(express.json())
app.get("/watchlater", (request, response)=>{
const user = request.body.user;
//console.log(user);
});
So basically, it will get the username and run the query. The problem is it does not get the username at all from the frontend. I tried console logging the variable user but to no avail. It returns empty.
The second argument in the axios.get() function is expecting a config object. Checkout the axios documentations on instance method and request config.
In short, in the frontend part, pass your payload into the data field of the config object, as shown in the example below.
const config = {
headers: { Authorization: token },
data: { user:name }
}
const response = await axios.get(`${url}`, config)
You need to send parameter using params object of config in case of get request. Your frontend request should change to this,
const user = useSelector(selectUser)
function getWatchLater(name){
axios.get('http://localhost:5000/watchlater', { params: { user: name }
}).then((response)=>{
// console.log(response.data)
setWatchLater(response.data)
})
}
In your express endpoint you should receive it as,
app.get("/watchlater", (request, response)=>{
const user = request.params.user;
//console.log(user);
});

How can I solve the problem with forwarding messages?

I'm created a chat-bot using 'botact' library, but
when I'm trying to verificate my bot on vk-community API working page I receive an error in 'Windows PowerShell' (Here I started the server for bot):
TypeError: Cannot read property 'fwd_messages' of undefined
at Botact.getLastMessage (C:\Users\whoami\Desktop\Bot-test\node_modules\botact\lib\utils\getLastMessage.js:2:11)
at Botact.module.exports (C:\Users\whoami\Desktop\Bot-test\node_modules\botact\lib\methods\listen.js:29:28).
The file 'getLastMessage.js' contains this code:
const getLastMessage = (msg) => {
if (msg.fwd_messages && msg.fwd_messages.length) {
return getLastMessage(msg.fwd_messages[0])
}
return msg
}
module.exports = getLastMessage
So I don't know much about botact but according to the code when you are hitting the / route, you need to pass a body containing an object property.
Now as this is bot framework for vk bots maybe it automatically sends the request body. You can make sure by logging the request body.
server.post('/', async (req,res)=>{
console.dir(req.body);
await bot.listen(req, res);
});
/lib/methods/listen.js:
const { type, secret, object, group_id } = framework === 'koa'
? args[0].request.body
: args[0].body
...
...
...
const { events, middlewares } = actions
const forwarded = this.getLastMessage(object)
Now, when you do bot.listen express passes req as first argument. and { type, secret, object, group_id } these fields get distructured from the req.body.
And then object is getting passed to the getLastMessage function.
So for the request body in minimum you would need
{ "object": {} }
Here is the 200 OK output that I got after added that to the request body from Postman
POC Code:
const express = require("express");
const bodyParser = require("body-parser");
const { Botact } = require("botact");
const server = express();
const bot = new Botact({
token: "token_for_my_group_i_just_hided_it",
confirmation: "code_for_my_group_i_just_hided_it"
});
server.use(bodyParser.json());
server.post("/",bot.listen);
server.listen(8080);

Node/Express - use API JSON response to (server-side) render the app

Preamble: I'm new to web dev so maybe this might be a very basic question for you vets.
I'm using MVC architecture pattern for this basic app. I've models (MongoDB), views (Express Handlebars), and controllers (functions that take in req, res, next and returns promises (.then > JSON is returned, .catch > error is returned). I'll be routing the paths reqs to their corresponding api endpoints in the controllers.
This makes sense (right?) when I'm purely working on API calls where JSON is the res. However, I also want to call these api endpoints > get their res.json > and use that to render my HTML using Handlebars. What is the best way to accomplish this? I can create same controllers and instead of resp being JSON, I can do render ("html view", res.json). But that seems like I'm repeating same code again just to change what to do with the response (return JSON or Render the JSON).
Hope I'm making sense, if not, do let me know. Please advise.
p.s. try to ELI5 things for me. (:
Edit:
//Model Example
const Schema = require('mongoose').Schema;
const testSchema = new Schema({
testText: { type: String, required: true },
});
const Test = mongoose.model('Test', testSchema);
module.exports = Test;
//Controller Example
const model = require('../models');
module.exports = {
getAll: function(req, res, next) {
model.Test.find(req.query)
.then((testItems) => {
!testItems.length
? res.status(404).json({ message: 'No Test Item Found' })
: res.status(200).json(testItems);
})
.catch((err) => next(err));
},
};
//Route Example
const router = require('express').Router(),
controller = require('../controllers');
router.get('/', controller.getAll);
module.exports = router;
I want the endpoints to return JSON and somehow manage whether to render (if the req comes from a browser) or stay with JSON (if called from Postman or an API web URL for example) without repeating the code. I'm trying to not create two endpoitns with 99% of the code being the same, the only difference being .then > res.status(200).json(testItems); vs .then > res.status(200).render('testPage', { testItems}).
For postman you could check the existence of postman-token in req.headers, then you could render accordingly, something like this:
req.headers['postman-token'] ? res.json({ /* json */ }) : render('view', {/ * json */});
If you want to go with checking postman token then you can use similar to method1.
if you want to check with query params in this case you can get json response or html even from browser for future use also and is not dependent on postman then use similar to method2 of the following example.
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
const port = 5000
app.get('/method1', (req, res) => {
const isJSONResp = req.headers['postman-token']
const resp = { status: "hello" }
if (isJSONResp) {
res.json(resp)
} else {
res.render('some.html', resp)
}
})
app.get('/method2', (req, res) => {
const params = req.params
const resp = { status: "hello" }
if (params.resp === 'json') {
res.json(resp)
} else {
res.render('some.html', resp)
}
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

getting empty body in record action - twilio

My use case:
My case is that i'm making a bot for listening podcast in which user will make call to twilio number and bot will ask what type of podcast would you like to listen then record for 10 seconds
when recording finish, it say user to please wait while we are finding podcast
I want that recording in my webhook so i will figure out caller mood and find appropriate podcast mp3 file from my database and play to caller
Issue I'm Facing:
I'm getting empty body in all of my webhooks
My code:
var express = require("express");
var bodyParser = require("body-parser");
var VoiceResponse = require('twilio').twiml.VoiceResponse;
var app = express();
var port = (process.env.PORT || 4000);
app.use(bodyParser.json())
// helper to append a new "Say" verb with alice voice
function say(text, twimlRef) {
twimlRef.say({ voice: 'alice' }, text);
}
// respond with the current TwiML content
function respond(responseRef, twimlRef) {
responseRef.type('text/xml');
responseRef.send(twimlRef.toString());
}
app.post("/voice", function (request, response, next) {
console.log("request: ", request.body); //body is comming as empty object
var phone = request.body.From;
var input = request.body.RecordingUrl;
var twiml = new VoiceResponse();
console.log("phone, input: ", phone, input);
say('What type of podcast would you like to listen. Press any key to finish.', twiml);
twiml.record({
method: 'POST',
action: '/voice/transcribe',
transcribeCallback: '/voice/transcribe',
maxLength: 10
});
respond(response, twiml);
});
app.post("/voice/transcribe", function (request, response, next) {
console.log("request: ", request.body); //body is comming as empty object
var phone = request.body.From;
var input = request.body.RecordingUrl;
var twiml = new VoiceResponse();
var transcript = request.body.TranscriptionText;
console.log("transcribe text: ", transcript);
//here i will do some magic(Ai) to detect user mood and find an
//appropriate mp3 file from my database and send to twilio
var mp3Url = 'https://api.twilio.com/cowbell.mp3'
say('start playing.', twiml);
twiml.play(mp3Url);
respond(response, twiml);
});
app.listen(port, function () {
console.log('app is running on port', port);
});
API Test with postman:
added url as webhook on twilio:
Heroku Logs:
Twilio developer evangelist here.
You are using body-parser which is good. However, you are using the JSON parser. Twilio makes requests in the format of application/www-x-form-urlencoded so you should change:
app.use(bodyParser.json())
to
app.use(bodyParser.urlencoded({ extended: false }))
Then you should see the parsed body as part of the request.body object.
As an extra note, the transcribeCallback is sent asynchronously to the call. So returning TwiML in response to that request won't affect the call at all. You will need to modify the call in flight, by redirecting it to some new TwiML when you get the result of transcription. An example of updating a call with Node.js is below:
const accountSid = 'your_account_sid';
const authToken = 'your_auth_token';
const client = require('twilio')(accountSid, authToken);
client.calls('CAe1644a7eed5088b159577c5802d8be38')
.update({
url: 'http://demo.twilio.com/docs/voice.xml',
method: 'POST',
})
.then((call) => console.log(call.to));

Categories

Resources