TypeError: res.send is not a function when trying to load site

Viewed 3890

I recently converted my build process over to using typescript and webpack. Now after resolving most the TS errors, I'm trying to run my site finally.

When I load the site, I'm getting TypeError: res.send is not a function from my expressjs static server. This code hasn't changed, it's always been the same even when I used gulp and worked fine but for some reason now after using webpack and TS, it's giving me this error now.

The server does indeed start on port 8080 and listens for requests. I start it with "start": "node --trace-warnings dist/server/server.js",

I don't know if it's because I typed the res param with any or what it is..

Full Error:

TypeError: res.send is not a function
    at dist/server/api.js:58:9
    at Layer.handle [as handle_request] (/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/node_modules/express/lib/router/index.js:317:13)

dist (output from TS and webpack)

  • TS is outputting my server folder code
  • webpack is outputting the rest via copy and bundling

enter image description here

webpack.config.js

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

const isProduction = process.env.NODE_ENV === 'production';

const html = () => {
  return new HtmlWebPackPlugin({
    template: path.resolve(__dirname, 'src/client', 'index.html'),
    filename: 'index.html',
    hash: true,
  });
};

const copyAllOtherDistFiles = () => {
  return new CopyPlugin({
    patterns: [
      { from: 'src/client/assets', to: 'lib/assets' },
      { from: 'package.json', to: './' },
      { from: 'ext/ink-3.1.10/js/ink-all.min.js', to: 'lib/js' },
      { from: 'ext/ink-3.1.10/js/autoload.min.js', to: 'lib/js' },
      { from: 'ext/js/jquery-2.2.3.min.js', to: 'lib/js' },
      { from: 'ext/ink-3.1.10/css/ink.min.css', to: 'lib/css/ink.min.css' },
      { from: 'feed.xml', to: './' },
      {
        from: 'src/shared',
        to: './shared',
        globOptions: {
          ignore: ['**/*suppressed.json'],
        },
      },
    ],
  });
};

module.exports = {
  entry: './src/client/index.tsx',
  output: {
    filename: 'scripts/app.[hash].bundle.js',
    publicPath: '/',
    path: path.resolve(__dirname, 'dist'),
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
  },
  devtool: isProduction ? '' : 'inline-source-map',
  devServer: {
    writeToDisk: true,
    port: 8080,
  },
  optimization: {
    minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  module: {
    rules: [
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.(tsx|ts)?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
          },
        ],
      },
      {
        test: /\.less$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'],
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: process.env.NODE_ENV === 'development',
            },
          },
          'css-loader',
        ],
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        loader: 'file-loader',
        options: {
          outputPath: 'lib/assets/fonts',
        },
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: ['url-loader'],
      },
    ],
  },
  plugins: isProduction
    ? [
        new CleanWebpackPlugin(),
        new MiniCssExtractPlugin({
          filename: isProduction ? 'lib/css/main.[hash].css' : 'main.css',
        }),
        html(),
        copyAllOtherDistFiles(),
      ]
    : [new CleanWebpackPlugin(), html(), copyAllOtherDistFiles()],
};

./tsconfig.json

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */
    "target": "ES2015",                     /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "es2020",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": ["es6", "dom"],                      /* Specify library files to be included in the compilation. */
        "moduleResolution": "node",
        "allowJs": true,                     /* Allow javascript files to be compiled. */
        "checkJs": true,                     /* Report errors in .js files. */
    "jsx": "react",
    "noImplicitAny": true,
        "sourceMap": false,                   /* Generates corresponding '.map' file. */
    "rootDir": "./",                     /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
        "removeComments": true,              /* Do not emit comments to output. */
    "strict": true,                      /* Enable all strict type-checking options. */
        "noUnusedLocals": true,                /* Report errors on unused locals. */
        "noUnusedParameters": true,            /* Report errors on unused parameters. */
//    "rootDirs": ["."],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    "typeRoots": [
      "node_modules/@types"
    ],                      /* List of folders to include type definitions from. */
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
        "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
//      "resolveJsonModule": true,
        "skipLibCheck": true,                     /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true
    },
    "include": [
        "src"
    ],
    "exclude": [
        "/node_modules",
        "/src/server",
        "/src/client/js/ink-config.js",
        "**/test"
  ]
}

./client/server/tsconfig.js (where server.ts and api.ts live)

{
    "extends": "../../tsconfig",
    "compilerOptions": {
        "outDir": "../../dist/server",                        
        "rootDir": "."                        
    },
    "include": ["./*.ts"]
}

./src/server/api.ts (api.js is outputted to dist/server)

const compression = require('compression'),
  express = require('express'),
  historyApi = require('connect-history-api-fallback'),
  oneYear = 31536000;

module.exports = express()
  .use(compression())
  .on('error', function (err: string) {
    console.log(err);
  })
  .use(historyApi())
  .use(
    express.static('dist', {
      maxage: oneYear,
    })
  )
  .use((res: any) => {
    res.send('Sorry, Page Not Found');
  });
1 Answers

As @num8er writes in the comment, you need to correct the signature of the handler you passed to express.use(). Your code fails because the first parameter in that function is the request object, not the response object. The request object doesn't have a send() method, so you need to add at least two parameters if you're trying to send a response:

import express from 'express'

const app = express();

app.use((req, res) => {
    res.send()
})

The reason Typescript didn't catch this error is because you're using the any type. To actually get type checking, make sure you don't specify the type of either of those parameters as any. Instead, either:

  • Allow Typescript to correctly infer the type by removing the any; or
  • Specify the type explicitly:
import express from 'express'

const app = express();

app.use((req: express.Request, res: express.Response) => { 

})

Finally, make sure @types/express is installed.

You can check it out here:

https://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=11&pc=3#code/JYWwDg9gTgLgBAUwB5iggzuuAzKERwDkyqG6hAUBQMYQB268AhmGHALyIpqYAUAlAG4qLMADoArugS9eaAI4AaOD34cAfHADecKgF9+I1pOmyFALi6lMYgEoJ5EjDGU9LJHujsZIDBGvZNHX1+IA

Related