Intro
To create a docker image, I wanted to compile the Express application from TypeScript to JavaScript.
But I got an error because the application couldn't find TypeORM files after compiling.
This time, I will try bundling the application files with Webpack.
- [TypeScript][PostgreSQL][TSyringe][Express]Try TypeORM 2
- [TypeScript][Express] Try React
- [TypeScript][Express] Try React 2
Environments
- Node.js ver.16.6.1
- Express ver.4.17.1
- TypeScript ver.4.3.5
- Webpack ver.5.50.0
- webpack-cli ver.4.7.2
- ts-loader ver.9.2.5
- cors ver.2.8.5
- pg ver.8.6.0
- reflect-metadata ver.0.1.13
- TSyringe ver.4.5.0
- TypeoORM ver.0.2.34
Bundle an Express application
First, I create an Express application to try bundling.
index.ts
import express from 'express';
import cors from 'cors';
const port = 3098;
const app = express();
const allowlist = ['http://localhost:3000', 'http://localhost:3099']
const corsOptionsDelegate: cors.CorsOptionsDelegate<any> = (req, callback) => {
const corsOptions = (allowlist.indexOf(req.header('Origin')) !== -1)? { origin: true }: { origin: false };
callback(null, corsOptions);
};
app.use(express.json());
app.use(cors(corsOptionsDelegate));
app.get('/', async (req, res) => {
res.json({ message: 'hello' });
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
webpack.config.js
var path = require('path');
module.exports = {
mode: 'development',
entry: {
'index': './index.ts',
},
target: 'node',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, './js'),
}
};
Although I can get bundled file and execute it by "node js/index.js", I get a warning like below.
WARNING in ./node_modules/express/lib/view.js 81:13-25
Critical dependency: the request of a dependency is an expression
@ ./node_modules/express/lib/application.js 22:11-28
@ ./node_modules/express/lib/express.js 18:12-36
@ ./node_modules/express/index.js 11:0-41
@ ./index.ts 42:32-50
1 warning has detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.
To avoid the warning, I add "webpack-node-externals".
webpack.config.js
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'development',
entry: {
'index': './index.ts',
},
target: 'node',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
externals: [nodeExternals()],
output: {
filename: '[name].js',
path: path.resolve(__dirname, './js'),
}
};
Some samples on some web sites write like below instead of using "nodeExternals()".
...
externals: ['express'],
...
Although I can bundle the application, but when I running it, I will get an error.
C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:62
module.exports = express;
^
ReferenceError: express is not defined
at Object.express (C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:62:18)
at __webpack_require__ (C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:86:42)
at eval (webpack://webpack-sample/./index.ts?:42:33)
at Object../index.ts (C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:40:1)
at __webpack_require__ (C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:86:42)
at C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:97:37
at Object.<anonymous> (C:\Users\example\OneDrive\Documents\workspace\node-webpack-sample\js\index.js:99:12)
...
- How to set up an Express.js API using Webpack and TypeScript. - The Andela Way - Medium
- Creating a Node Express-Webpack App with Dev and Prod Builds - Ben Grunfeld - Medium
Using TypeORM
Because I wanted to use TypeORM like the project what I had created last time, I added TypeORM into the sample.
Although I could add it and execute "npx webpack", I got an error on runtime.
C:\Users\example\OneDrive\Documents\workspace\bookstore_sample\entities\author.ts:1
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1031:15)
at Module._compile (node:internal/modules/cjs/loader:1065:27)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:94:18)
...
I think it's because entity classes are loaded dynamically.
So if I change the code to just connecting the Database and avoiding accessing tables with entity classes, the error won't be occurred.
Now I use "ormconfig.json" for configuration.
ormconfig.json
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "example",
"database": "book_store",
"synchronize": false,
"logging": false,
"entities": ["./entities/*.{ts,js}"],
"migrations": ["./migrations/*.{ts,js}"],
"cli": {
"entitiesDir": "entities",
"migrationsDir": "migrations"
}
}
From the result, I moved the config data into "createConnection".
dataContext.ts (Before)
import "reflect-metadata";
import { singleton } from "tsyringe";
import { Connection, createConnection } from "typeorm";
@singleton()
export class DataContext {
private connection: Connection|null = null;
public async getConnection(): Promise<Connection> {
if(this.connection != null) {
return this.connection;
}
this.connection = await createConnection();
return this.connection;
}
}
dataContext.ts (After)
import "reflect-metadata";
import { singleton } from "tsyringe";
import { Connection, createConnection } from "typeorm";
import { Author } from "../entities/author";
import { Book } from "../entities/book";
import { Genre } from "../entities/genre";
@singleton()
export class DataContext {
private connection: Connection|null = null;
public async getConnection(): Promise<Connection> {
if(this.connection != null) {
return this.connection;
}
this.connection = await createConnection({
type: "postgres",
host: "localhost",
port: 5432,
username: "postgres",
password: "example",
database: "book_store",
synchronize: false,
logging: false,
entities: [Author, Book, Genre]
});
return this.connection;
}
}
For production, maybe I have to change configuration data like "host".
Top comments (1)
regarding your issue: "ReferenceError: express is not defined"
perhaps you want to try this approach: archive.jlongster.com/Backend-Apps...
general whinging: why can't webpack make it more obvious how to get this done the right way?