DEV Community

Cover image for Express and Body-parser
Chuck
Chuck

Posted on

Express and Body-parser

I am learning how much I love back-end architectures. This week I decided to through away the tutorials and code my first Node Express server. After reviewing a lot of code samples I started to notice something around one middleware package. The way that Body-parser is including in a project can be subjective.

Body Parser

What is Body-parser? It is a middleware which parses the request stream. In most cases, not always, we are parsing the stream as json in the req.body request object.

How do we use it? It is so easy, but at the same time, subjective to the programmer. We are going to look at five ways to use body-parser, all four methods are equally valid as acceptable methods to parse data, and all our methods are subjective. in that it becomes a preference to the programmers methodology.

Most widely accepted method

The first method is to import the body-parser package into to your server file and set the middleware like so:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json())
Enter fullscreen mode Exit fullscreen mode

But do we need the Body-parser package?

I ask this question because of a little Express history. The Body-parser package is a middleware that was part of Express but in early 2014 it was removed from the Express package. This is why most project source code you review, include body-parser as a separate package in the project package.json.

However, as early as September 28, 2017, Body-parser was added back into Express as a dependency. Therefore, we do not technically need to add it as a project dependency.

So, the second method I propose is a way to clean up our code a little:

const express = require('express');

const app = express();
app.use(express.json())
Enter fullscreen mode Exit fullscreen mode

We have removed the package and are using Express, which uses Body-parser. It is a little cleaner. If I am coding an Express server in native code this is my preference.

Babel and ES6

What about using ES6 and Babel in your project. I am all for it. It is sometimes hard to retool the brain away from ES6. If your project is setup with Babel (beyond the scope of this article), then how does Body-parser imports look?

Let's look at our first example. It is basically the same except for the import statements:

import express from 'express';
import bodyParser from 'body-parser';

const app = express();
app.use(bodyParser.json())
Enter fullscreen mode Exit fullscreen mode

But we can abstract this example a little more, using named exports. This really cleans up the call to json():

import express from 'express';
import { json } from 'body-parser';

const app = express();
app.use(json())
Enter fullscreen mode Exit fullscreen mode

Remember our second example? We do not technically need the Body-parser import.

import express from 'express';

const app = express();
app.use(express.json())
Enter fullscreen mode Exit fullscreen mode

Or we can really clean it up with named exports. If I am using ES6, this is my preference:

import express, { json } from 'express';

const app = express();
app.use(json())
Enter fullscreen mode Exit fullscreen mode

Remember each methods works the same way. It is just a different way to present your code. Which method do you use?

Footnote

This has been fun. Leave a comment or send me a DM on Twitter.

Shameless Plug: If you work at a great company and you are in the market for a Software Developer with a varied skill set and life experiences, send me a message on Twitter and check out my LinkedIn.

Top comments (2)

Collapse
 
nickytonline profile image
Nick Taylor

Great write up Chuck! One thing you can look into is if you are using node 14 +, Babel is not necessary unless you are using bleeding edge EcmaScript. As of node 14, ES modules are no longer behind an experimental flag.The only issue I discovered is that if you are using jest, they currently do not support native ES modules yet, so you’d still need to use Babel, for at least jest.

Meta: Native support for ES Modules #9430

ESM support will be unflagged in a future release of Node 12 (maybe not before April github.com/nodejs/node/pull/29866#...) and it is already unflagged in Node 13.2, so I think it's time to evaluate how we can add native support in Jest. I'll try to list which features Jest currently provides that are impacted by ESM support, and how we can solve/investigate them.

There is issue #4842, but I think that's more of a discussion issue, while this issue will be geared towards actually implementing support and more suitable to track for those who just want to get the current implementation status. Any comments added to this issue not related to how we can implement support for the below enumerated features will be marked as spam - please direct any workarounds/discussions to separate issues. Also feel free to tell us if anything related to ESM features is missing from the list!

Please note that Jest will use the vm API (nodejs.org/api/vm.html) and as of writing (node v13.6) the ESM parts of this API is still flagged (--experimental-vm-modules). So saying ESM is unflagged is a bit of a misnomer at the moment. But I think we should start experimenting and potentially provide feedback to the Modules WG.

Lastly, I'm writing this issue mostly for people who will implement support, so it'll be somewhat low-level and specific to how Jest works. For people who just want to know whether support has landed or not, I recommend using GH's wonderful "custom notification" and only subscribe to notifications on closing/reopening.


  • [x] Running the module in the correct context

We achieve sandboxes by running a script within a given vm.Context (either provided by JSDOM or node core APIs). We need to do the same for ESM, but we'll need access to the context during construction of the module, not just when executing the module. I've opened up #9428 which adds the necessary APIs to JestEnvironment.

  • [x] Globals

expect, test, beforeEach etc will still be added as globals, nothing should change here. jasmine global will also still be here.

  • [ ] jest "global" property

This is not really a global - it's injected into the module scope. Since the module scope is gone in ESM, we need to move it somewhere. Adding it to import.meta seems natural - there's an option called initializeImportMeta which we can use.

  • [ ] jest.(do|un)mock

Since ESM has different "stages" when evaluating a module, jest.mock will not work for static imports. It can work for dynamic imports though, so I think we just have to be clear in the docs about what it supports and what it doesn't.

jest.mock calls are hoisted, but that doesn't help in ESM. We might consider transforming import 'thing' to import('thing') which should allow hoisting to work, but then it's async. Using top-level await is probably a necessity for such an approach. I also think it's invasive enough to warrant a separate option. Something to discuss - we don't need to support everything jest.mock can for for an initial release.

  • [ ] jest.requireActual

Not sure if how it should behave in ESM. Should we provide a jest.importActual and let requireActual evaluate in CJS always?

  • [x] import.meta

Node has url as its only property (for now, at least). We need to make sure it's populated in Jest as well. We provide identifier instead of filename when constructing the module so I don't think it'll happen automatically, but url is essentially filename passed though pathToFileURL.

There's also an open PR for import.meta.resolve: github.com/nodejs/node/pull/31032

  • [x] import thing from 'thing'

This should actually be fairly straightforward, we just need to implement a linker where we can also transform the source before returning it, meaning we don't need the loader API (which doesn't exist yet). This allows us to return mocks as well (albeit they'll have to come from a __mocks__ directory).

  • [x] import('thing')

Essentially the same as above, but passed as importModuleDynamically when constructing the module. Will also support jest.mock, jest.resetModules etc more cleanly, so likely to be used quite a bit.

This can also be done for vm.Script via the same option.

  • [ ] Handling errors during evaluation

Right now it's a runtime error (e.g. module not found), but that's not necessarily true with ESM. Does it matter for us? We should verify errors still look nice.

  • [x] module.createRequire

We need to deal with this for people wanting to use CJS from ESM. I've opened up #9426 to track this separately as implementing it is not really related to ESM support.

EDIT: Implemented in #9469

  • [ ] module.syncBuiltinESMExports

nodejs.org/api/modules.html#module.... Do we care about it, or is just making it a no-op enough? Not sure what the use case in Jest would be. Messing with the builtins is already breaking the sandbox and I don't think this should matter.

EDIT: #9469 made this into a no-op. I think that's fine?

  • [ ] Detect if a file is supposed to be ESM or CJS mode

Inspecting type field in a module's package.json seems reasonable: nodejs.org/api/esm.html#esm_enabling. Should we also have our own config flag? Also needs to respect file endings.

github.com/nodejs/modules/issues/393

  • [x] moduleNameMapper

Not sure if this impacts anything. I think not since we'll be linking the modules together ourselves. Needs investigation, though.

EDIT: This is all resolution logic, which we control. So no changes here.

  • [x] jest.config.mjs

Through #9291 we support jest.config.cjs - do we need to do anything special for .mjs? Probably use import('path/to/configFile.mjs') which means it'll have to be async. Is this an issue? Might be worth making config resolution async in Jest 25 so it's not a blocker for incremental support of ESM in Jest 25.

EDIT: #9431

  • [ ] Package Exports

Node supports package exports, which sorta maps to Jest's moduleNameMapper, but also provides encapsulation features. Hopefully resolve will implement this, but if they do not we'll need to do something. Might be enough to use the pathFilter option? Unsure.

  • [ ] JSON/WASM module

nodejs.org/api/esm.html#esm_experi.... Do we need to care? Probably, especially for json. It's trivial for us to support import thing from './package.json' since we control the linking phase, but we probably shouldn't do it by default as it'll differ from default node. Should we force people to define a transform for it?

  • [x] Code coverage

Does it matter? I don't think it's affected as we can still transform the source with babel (maybe it'll be confused by import statements, probably not) and V8 coverage definitely shouldn't care. We should verify though.

  • [ ] Async code resolution

This is absolutely no blocker as sync resolution will work just fine. But we can use async resolution now, which is great. I wonder if we should look into just using the resolve module off of npm again, as it already supports async. See #9505.

  • [ ] Async code transformation

Similar to above, not blocking, but would be nice to support it. Might make @jest/transformer more usable in other environments as well. See #9504.

  • [ ] Bad performance when accessing globals

Due to #5163 we have the extraGlobals option as a workaround - that workaround is no longer viable in ESM. I've opened up and issue with node here: github.com/nodejs/node/issues/31658

Looking forward to your next post!

Collapse
 
eclecticcoding profile image
Chuck

Thank for the feedback. I had read about the fore coming ES6 support in Node and it is exciting to see. I generally stick with LTS versions of Node just for reliability, and 14+ will move to LTS in October. I may do some earlier testing though.