Parsing Body from request to Class in express - javascript

I got doubt, im coming from .net with c# and i want to parse my body request as .net does for my automatically, how can i set a class or an interface as a request body in express, i have found many options but all of them just destruct the body into the properties that they need, i need a way or a method that allows me to get only the properties that i specified in my class.
In .Net it will be something like this.
[HttpGet("someName")
Public IActionResult GetSomething(Myclass instance){
// the instance with only the properties that i specified in my class and not the hole body with useless props that i don’t request
instance.someProperty
Return Ok()
}

ASP.net is actually smart enough to understand that when a class is declared as an argument, it must map from the request POST body to the class.
Nodejs and express do not always come with batteries included.
You need to add a middleware that can read the raw request and get the json object you want. If you are only receiving JSON then you need the JSON middleware. If you expect to have URL encoded posts (for file uploading or html s) then you also need to add the urlencoded middleware
const app: Application = express();
(...)
app.use(express.json());
app.use(express.urlencoded());
At this point, you can declare your route, and express will corectly fill the req.body object with your post data.
interface MyPostBody {
foo: string;
bar: string;
}
app.post("/api/someName", (req, res) => {
const instance = req.body as MyPostBody;
console.log(instance.foo);
console.log(instance.bar);
const result = doSomething(instance);
res.send(result);
});
Please be aware that we are just casting the type here, so if your client sends an object that does not conform to the MyPostBody interface, things will break. You probably need to add some validation to ensure the data conforms to you api contract. You can use some validation library like yup for that. To keep it simple I will do something very basic here.
app.post("/api/someName", (req, res) => {
if(req.body.foo === null || req.body.foo === undefined) {
res.status(400).send("foo is required");
return;
}
if(req.body.bar === null || req.body.bar === undefined) {
res.status(400).send("bar is required");
return;
}
const instance = req.body as MyPostBody;
console.log(instance.foo);
console.log(instance.bar);
const result = doSomething(instance);
res.send(result);
});

Related

Parse sendBeacon data in Express

I'm trying to start logging my own WebVitals. The simple use-case in their example docs looks like this:
function sendToAnalytics(metric) {
// Replace with whatever serialization method you prefer.
// Note: JSON.stringify will likely include more data than you need.
const body = JSON.stringify(metric);
// Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
(navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
fetch('/analytics', {body, method: 'POST', keepalive: true});
}
That all seems simple enough. Here's my actual implementation:
function sendToLog(metric) {
// Replace with whatever serialization method you prefer.
// Note: JSON.stringify will likely include more data than you need.
const body = JSON.stringify(metric);
console.log(`Sending body ${body}`);
// Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
// (navigator.sendBeacon && navigator.sendBeacon('https://localho st:9292/api/log', body)) ||
fetch('https://localhost:9292/api/log', {body: body, method: 'POST', headers: { 'Content-Type': 'application/json' }, keepalive: true});
}
I had to modify the fetch to include the "body" property name and add headers to get it to work, but that is now working. However, if I uncomment the navigator.sendBeacon line, I just get {} for the body.
I'm using NodeJS and Express on the backend. My starting point was this:
const app = express();
app.use(
express.json({
// We need the raw body to verify webhook signatures.
// Let's compute it only when hitting the Stripe webhook endpoint.
verify: function (req, res, buf) {
if (req.originalUrl.startsWith(API_PREFIX + '/webhook')) {
req.rawBody = buf.toString();
}
},
})
);
app.use(cors());
// Expose an endpoint for client logging.
app.post(API_PREFIX + '/log', async (req, res) => {
const message = req.body;
console.log('Got message');
console.dir(message, { depth: null });
logger.info(message);
res.sendStatus(200);
});
There's this similar question, where the accepted answer suggests that adding body-parser and app.use(bodyParser.raw()) should do the trick, and then in the comments there's discussion of using bodyParser.json()) instead.
I've tried both of those:
import bodyParser from 'body-parser';
...
//app.use(bodyParser.raw());
//app.use(bodyParser.json());
app.use(
express.json({
// We need the raw body to verify webhook signatures.
// Let's compute it only when hitting the Stripe webhook endpoint.
verify: function (req, res, buf) {
if (req.originalUrl.startsWith(API_PREFIX + '/webhook')) {
req.rawBody = buf.toString();
}
},
})
);
app.use(cors());
(i.e., uncommenting either of the two lines at the beginning), and in all 3 cases I still get an empty body when sendBeacon is used.
The Express documentation says,
[express.json] is a built-in middleware function in Express. It
parses incoming requests with JSON payloads and is based on
body-parser....A new body object containing the parsed data is
populated on the request object after the middleware (i.e. req.body),
or an empty object ({}) if there was no body to parse, the
Content-Type was not matched, or an error occurred.
So, a) I guess I shouldn't need the body-parser, since express.json is just doing that anyhow; and b) I'm hitting one of those three conditions (there's no body to parse, the Content-Type was not matched, or an error occurred). Assuming that's the case, how do I diagnose which one it is, and then fix it (in such a way that the fetch fallback continues to work). Or, if there's some other problem, back to the original question, how do I get this to work? :)
navigator.sendBeacon sends Content-Type: text/plain whereas express.json() only parses the body if Content-Type: application/json.
Use express.text() in combination with JSON.parse(req.body) instead, or additionally:
app.post(API_PREFIX + '/log',
express.json(), express.text(), function(req, res) {
if (typeof req.body === "string") req.body = JSON.parse(req.body);
...
});

Feature toggling using Expressjs middleware - frontend and backend

My current project is using Node for both frontend and backend and ExpressJS as the middleware.
I have a requirement where I need a feature toggling implementation to introduce some new features in my application. I am using a url parameter, e.g. &featureToggle=true to determine if the code of execution would be the new one or the existing.
Now I have parts in frontend and backend both which need to be changed based on the feature toggle. In the backend I can get the query object separately and extract the url param, similarly also in the frontend module.
Is there a way in which I can use Express to intercept the query param, set a variable value to either true or false based on the feature toggle, and which could be used across both the frontend and backend modules?
with express you can use req.query which gathers the query string sent in the request. You could pass it like this:
localhost:9000/path?&featureToggle=true
the ? is important it tells express that you are creating a query.
if you then place it into a variable:
const query = req.query
you would get the following output:
{ featureToggle: 'true' }
so as you can see it is returning an object.
you can check it like so:
if(req.query.featureToggle === 'true'){
runSomeCode();
};
or in your case if you want to run some kind of middleware:
router.get('/', (req, res, next) => {
if(req.query.featureToggle === 'true'){
return next(toggle)
}
};

Nestjs What is the right order for #Body(), #Params(), #Req(), #Res()

I want to access the res object to send httpOnly cookies and need to validate body with DTO.
but every time i try to do it something go wrong what is the right order for these params?
There are no order.
Also, they are parameter decorator factories, not parameters.
There is no strict order that needs to be followed. Each controller method may use decorators for retrieving different things (see controller docs: https://docs.nestjs.com/controllers)
Example
Let's imagine you are building an endpoint for handling some kind of search using a POST request and a payload. Nest returns some results and sets cookies with the latest performed search timestamp.
That sounds like your requirements, right?
Add cookie parser to nest application
Make sure you followed cookies documentation and installed all dependencies together and configured cookie parser middleware: https://docs.nestjs.com/techniques/cookies
Search payload data transfer object (DTO)
import { IsInt, IsNotEmpty } from 'class-validator';
export class SearchBodyDto {
#IsNotEmpty()
searchPhrase: string;
#IsInt()
page = 1;
}
Controller method
import { Request, Response } from 'express';
import { Body, Controller, Post, Req, Res } from '#nestjs/common';
#Controller()
export class AppController {
#Post('/search')
async search(
#Body() body: SearchBodyDto,
#Req() req: Request,
// passthrough:true here leaves response handling to framework.
// Otherwise you would need to send response manually, like: `res.json({data})`
#Res({ passthrough: true }) res: Response,
) {
const currentTimestamp = new Date().toISOString();
// Save to cookies current timestamp of search.
res.cookie('lastSearch', currentTimestamp, { httpOnly: true });
return {
// Return last search timestamp and fallback to current timestamp.
lastSearch: req.cookies.lastSearch ?? currentTimestamp,
// Validated search phrase from DTO.
searchPhrase: body.searchPhrase,
// Page by default 1.
page: body.page,
// Some example search results.
searchResults: ['water', 'wind', 'earth'],
};
}
}
Result
Now when you do a request to the endpoint, you will see the latest search time in a response: postman example, and also that value will be set to 'lastSearch' cookie.
The payload still will be validated using decorators on DTO.

execute validation middleware after validation checks

I want to create an Express REST API and want to validate the request params and the request body. If everything is fine I want to call the controller logic.
My validation middleware is
const { validationResult } = require('express-validator/check');
module.exports = (req, res, next) => {
const validationErrors = req.validationResult();
if (!validationErrors.isEmpty()) {
// send a 400
}
next();
}
and I use it within my routes before calling the controller. This is a snippet of my topics.js route file
const validation = require('../middleware/validation.js');
const { check } = require('express-validator/check');
router.get('/', topicsController.getAllTopics);
router.get('/:topicId', [
check('topicId').isUUID()
], validation, topicsController.getTopicById);
router.post('/', authenticationCheck, authorizationCheck, [
check('parentTopicId').isUUID() || check('parentTopicId').isNull(), // check if it's a UUID. If not, check if it's null
!check('name').isEmpty(), // is it not empty?
], validation, topicsController.createTopic);
router.put('/:topicId', authenticationCheck, authorizationCheck, [
check('topicId').isUUID(),
check('parentTopicId').isUUID() || check('parentTopicId').isNull(),
!check('name').isEmpty(),
], validation, topicsController.updateTopic);
router.delete('/:topicId', authenticationCheck, authorizationCheck, [
check('topicId').isUUID()
], validation, topicsController.deleteTopic);
I tried to get into it with the docs
https://express-validator.github.io/docs/#basic-guide
but when launching the API I get this error
Error: Route.post() requires a callback function but got a [object
Boolean]
So it seems I am not able to pass in an array first, then the validation middleware and then the controller.
Is there a way to fix the route file? I don't want to handle the validation logic within my controller file. I think this should be done before.
You need to use oneOf to check for conditional params.
In your routes you use check('parentTopicId').isUUID() || check('parentTopicId').isNull() but that returns a boolean and express needs a middleware. oneOf was build especially for this.
Also you should replace !check with check('name').not().isEmpty().

Express Routes & Controllers

I understand that out of the box Express isn't an MVC framework, however I'm trying to set it up like one.
I've used other similar frameworks in PHP like Laravel where in a route in you can use a controller like
Route::get('user/profile', 'UserController#showProfile');
Which will run all the code in the showProfile method in the UserController class,
so my question is, how would I achieve the same thing or something similar using Express?
I'm using Node 5 and writing the code in ECMAScript 6.
Currently I have a class I want to use as the controller and a method I want to return the data, I'm able to log the data to the console when a user navigates to the route but haven't figured out how to send it back as the response.
If you dive into the documentation, you'll find that the "controller methods" you refer to need to conform to a specific signature. Namely, they receive (at least) the request and response representations.
If you have already created a router, this will be a rough equivalent to the PHP you posted:
router.get('user/profile', userController.showProfile)
Your showProfile "method" needs to have this signature:
const userController = {
showProfile(req, res) { /*...*/}
}
I put "method" in quotes because express won't call it as a method unless you explicitly bind it to the controller object. We're passing it as an unbound function here. If you wanted to use it as a method (to have access to the controller as this), pass userController.showProfile.bind(userController) to router.get†.
But for now let's stick to those req and res parameters of the showProfile handler (that's the proper name). req represents the HTTP request, you can get headers, request payload and other stuff from it. res is the HTTP response that will be sent. So you can use it to set an HTTP status code, send body data and so on.
For illustrative purposes, let's assume you can get your user profile synchronously by calling userController.getProfile(id). And let's assume that, by some mechanism, a property userId is set on the request that is the currently authenticated user's ID.
const userController = {
showProfile(req, res) {
// We call some code to get what we want to send back
const profile = userController.getProfile(req.userId)
// We send it in the response as JSON
res.send(profile)
}
}
res.json will JSON.stringify the profile and send it as response payload.
How do you get req.userId set, you ask? Well, this is just an example, but you can achieve similar results by using middleware. A middleware is simply a handler that does something and then lets other handlers continue processing the request. But again, there's plenty to read from the docs.
† It's usually not necessary though, since controllers tend to be singletons. You can simply access its properties by doing userController.otherProperty. Of course, you don't even need to define a handler as a method of a controller object, it can be a function that stands on its own.
I did something like this
usercontroller.js
class UserController() {
constructor() {
this.users = ['user1', 'user2'];
}
getUsers(req, res) {
res.status(200).json(this.users);
}
}
//router.js
const port = 3000;
const app = express();
const _invoke = function(controller) {
return function(req, res) {
const [controllerClass, method] = controller.split('#')
const className = require(controllerClass)
const obj = new className
obj[method](req, res)
}
}
app.get('/get-users',
_invoke('./controllers/UserController#getUsers'));
app.listen(port);

Categories

Resources