如何将Typescript,NodeJS和Express应用程序部署到Heroku [英] How to deploy a Typescript, NodeJS and Express app to Heroku

查看:50
本文介绍了如何将Typescript,NodeJS和Express应用程序部署到Heroku的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想提供一个方法,因为我找不到任何完整的信息.我认为这在Stackoverflow文档中似乎最合适.但是,它已被淘汰*-)-日落文档(*).

I wanted to provide a howto on this as I couldn't find any complete information. I thought that this seemed most appropriate in Stackoverflow documentation. However it has been sunsetted *-) - Sunsetting Documentation (*).

相反,我将其写为StackOverflow问题与解答.

Instead I will write this as a StackOverflow Q&A.

如何将Typescript,NodeJS和Express应用程序部署到Heroku

How to deploy a Typescript, NodeJS and Express app to Heroku

推荐答案

我创建了一个项目( https://gitlab.com/OehmSmith_Examples/herokumovies ),其中包含自述文件,其中描述了需要做的事情,我将在此处进行复制.作为良好的StackOverflow做法,我还将在本文底部提供所有代码的副本.

I created a project (https://gitlab.com/OehmSmith_Examples/herokumovies) that includes a README describing what needs to be done and I will reproduce that here. As a good StackOverflow practice I will also provide a copy of all the code at the bottom of this post.

本教程可从 https://amenallah.com/node-js-typescript-jest-express-starter/作为基本应用.我与该网站或作者没有任何隶属关系.我选择它是因为它简单易用.这也是良好的OO Typescript代码的示例.

This tutorial will work from https://amenallah.com/node-js-typescript-jest-express-starter/ as the base app. I have no affiliation with that site or the author. I chose it as it is simple and works. It is also an example of good OO Typescript code.

https://中的大多数教程或什至官方文档www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html 说要在全球范围内安装打字稿:

Most tutorials or even the official documentation in https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html say to install typescript globally:

 > npm install -g typescript

Heroku没有全局安装的打字稿,因此需要将其保存在本地.示例项目就是这样做的:

Heroku doesn't have a global install of typescript so it needs to be kept locally. The example project does just this:

 > npm i -D nodemon rimraf typescript ts-node ts-jest jest @types/jest @types/node

@ types/node

在您将 @ types/node 固定在较旧版本的情况下,您会看到类似此错误的信息:

@types/node

In the case you have pinned your @types/node at an older version you will see something like this error:

~/AppData/Roaming/nvm/v11.15.0/node_modules/typescript/lib/lib.es2015.iterable.d.ts:41:6 - error TS2300: Duplicate identifier 'IteratorResult'.

41 type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
        ~~~~~~~~~~~~~~

  node_modules/@types/node/index.d.ts:170:11
    170 interface IteratorResult<T> { }
                  ~~~~~~~~~~~~~~
    'IteratorResult' was also declared here.

node_modules/@types/node/index.d.ts:170:11 - error TS2300: Duplicate identifier 'IteratorResult'.

170 interface IteratorResult<T> { }
              ~~~~~~~~~~~~~~

~/AppData/Roaming/nvm/v11.15.0/node_modules/typescript/lib/lib.es2015.iterable.d.ts:41:6
    41 type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
            ~~~~~~~~~~~~~~
    'IteratorResult' was also declared here.


Found 2 errors.

来自 TypeScript:重复的标识符"IteratorResult" .并且根据您需要更新 @ types/node 的版本.在处理较旧的代码时,我遇到了这个问题,并希望对此进行讨论.

From TypeScript: Duplicate identifier 'IteratorResult'. And as per that you need to update your version of @types/node. This was a problem I struck as I was working with older code and wanted to include this discussion of it.

index.ts 更改为以下内容,因为原始代码对端口5000进行了硬编码:

Change the index.ts to be the following instead since the original code hard-coded port 5000:

app.listen(process.env.PORT, () => {
    console.log(`server started on port ${process.env.PORT}`)
});

为此,我将PORT添加到了 npm脚本中,包括添加了 start:dev ,以便您可以像已编译的打字稿中的Heroku一样运行它.

To allow for this I have added the PORT to the npm scripts, including adding a start:dev so you can run it like Heroku does from the compiled typescript.

    "start:dev": "PORT=5000 node dist/index.js",
    "dev": "PORT=5000 nodemon --exec ts-node src/index.ts --watch src",

或者可以在.env文件中设置

Or it can be set in a .env file:

PORT=5000

NPM依赖项

Heroku将不会安装开发依赖(其他任何云提供商也不会安装).因此,您需要将一些依赖项移至主块.例如,一个 NestJS 应用程序将其作为开发依赖项,因此需要将其移动:

NPM dependencies

Heroku will NOT install dev dependencies (neither will any other cloud provider). Therefore you need to move some dependencies to the main block. For example, a NestJS application has these as Dev Dependencies and they need to be moved:

 @nestjs/cli

虚拟数据

我将此构造函数添加到了 MoviesApi.ts :

constructor() {
    // setup some dummy data
    movies.push({
        name: 'Pirates of the caribbean',
        rating: 8.5
    })
    movies.push({
        name: 'Star Wars: A new hope',
        rating: 8.7
    })
}

Heroku

现在部署到Heroku

Heroku

Now deploy to Heroku

  1. 如果您还没有帐户,请设置一个帐户,然后在Heroku上创建一个应用程序
  2. 在您的终端中:

  1. Setup an account if you don't already have one and create an app on Heroku
  2. In your terminal:

heroku login
heroku create moviesheroku // this needs to be unique

  • 您可能需要将返回的git url添加为远程文件(请使用 git remote -v 检查):

    git remote add heroku <git url>
    

  • 使用以下内容查找或搜索buildpack(下一步已经指定了我使用的内容):

  • Lookup or search for buildpacks with (the next step already specifies those I use):

    • Lookup at: https://devcenter.heroku.com/articles/buildpacks#officially-supported-buildpacks
    • Search with:

    heroku buildpacks:搜索打字稿

    添加构建包:

    heroku buildpacks:add zidizei/typescript
    heroku buildpacks:add heroku/nodejs
    

  • 确认构建包:

  • Confirm buildpacks:

    heroku buildpacks
    

  • 提交到本地存储库

  • Commit to your local repository

    git init  // if not already done
    git add --all
    git ci -m "Initial commit.  Test project all setup and should be ready to 'serve' but not yet ready to deploy to heroku"
    

  • 运行方式

  • Run with

    npm dev # OR
    npm run start:dev # It depends on your npm scripts
    

  • 使用邮递员或类似人员进行测试,或从命令行运行该程序:

  • Test with postman or similar or run this from the command-line:

    curl http://localhost:5000/movies
    

  • 使用 npm run build

    更新npm脚本,以便在Heroku上安装( npm install )之后,它将在尝试 npm run start

    Update the npm scripts so that after installation (npm install) on Heroku, it will build it before attempting to npm run start

    "postinstall": "npm run build"  # Depends on your npm scripts
    

  • 提交到本地存储库:

  • Commit to local repository:

    git add --all
    git ci -m "Now it should deploy, build and run on heroku"
    

  • 部署到heroku.它应该构建并启动.

  • Deploy to heroku. It should build and start up.

    git push heroku master
    

  • 测试(假设您 heroku创建 d的应用是 moviesheroku -进行相应调整)

  • Test (assuming the app you heroku created is moviesheroku - adjust accordingly)

    curl https://moviesheroku.herokuapp.com/movies
    

  • 变化

    Procfile

    我还没有指定一个 Procfile 来告诉Heroku关于该应用程序的任何信息.幸运的是,它通过确定这是一个 node + npm 应用程序来创建自己的默认值.但是,您可以显式定义它,如果您有多个应用程序或类似应用程序,则需要执行此操作.您可以添加要包含的Procfile(这是默认设置):

    Variations

    Procfile

    I haven't specified a Procfile telling Heroku anything about the app. Fortunately it creates its own defaults by determining that this is a node + npm app. However you can explicitly define this and will need to perform this action if you have multiple apps or similar. You could add a Procfile to contain (this is the default):

    web: npm start
    

    节点和NPM版本

    Heroku还默认使用这些软件的最新版本之一.您可以在 package.json 文件的顶层显式设置版本,例如:

    Node and NPM version

    Heroku also defaults to using one of the most recent versions of these. You could explicitly set the versions at the top-level in the package.json file like:

     "engines": {
         "node": "10.x",
         "npm": "6.x"
     },
    

    尽管如果您未指定 npm 版本,则Heroku将使用明智的默认节点版本.

    Although if you don't specify a npm version then Heroku will use a sensible default for the version of node.

    我只用了几个小时就完成了.我需要解决的主要问题是打字稿必须是本地的,而不是全局的.还有构建包.尽管每个云提供商都要求使用 process.env.PORT ,但是PORT也是一个问题,所以这对我来说很明显.

    I had this going in only a couple hours. The main issues I needed to work out is that typescript has to be local, not global. And the buildpacks. The PORT is an issue also though every cloud provider requires the use of process.env.PORT so this was obvious to me.

    Azure是一场噩梦,花了几天时间,但这主要是因为我所在的工作场所坚持使用Windows服务器.长话短说,我不会再讨论了.

    Azure was a nightmare and took days, but that was mainly because the workplace I was at insisted on using Windows servers. Long story and I won't go in to it.

    AWS令人费解.经过一天的尝试,我没有得到我曾经工作过的实例.但是我确实需要再试一次.我尝试的应用使用了 https://tsed.io/库.简单的Node/Typescript/Express应用程序应该很容易工作.

    AWS was so convoluted. I didn't get the instance I had working after trying for a day. however I do need to try again. The app I was trying used the https://tsed.io/ library. Simple Node / Typescript / Express apps should work out quite easily.

    (*)-尽管文档发生在2年前,但我认为它并不是我所使用的东西,尽管它的文档消失令人有些惊讶.而且我一直认为Q& A是最容易记录文档的地方.

    (*) - the sunsetting of documentation was a little bit surprising though given it happened over 2 years ago I guess it wasn't something I used. And I always thought that the Q&A was the easiest place for documentation.

    .gitignore

        node_modules
        dist
        coverage
    

    .jest.config.js

        module.exports = {
            preset: 'ts-jest',
            testEnvironment: 'node'
        };
    

    package.json

        {
          "name": "movies",
          "version": "1.0.0",
          "description": "Example from https://amenallah.com/node-js-typescript-jest-express-starter/ but then modify and / or show steps for how to deploy this Typescript NodeJS Express RESTful app to Heroku.",
          "main": "index.js",
          "scripts": {
            "build": "rimraf dist && tsc",
            "postinstall": "npm run build",
            "start": "node dist/index.js",
            "start:dev": "PORT=5000 node dist/index.js",
            "dev": "PORT=5000 nodemon --exec ts-node src/index.ts --watch src",
            "test": "jest --watch",
            "coverage": "jest --coverage"
          },
          "keywords": [],
          "author": "",
          "license": "ISC",
          "devDependencies": {
            "@types/express": "^4.17.2",
            "@types/jest": "^24.0.25",
            "@types/node": "^13.1.2",
            "jest": "^24.9.0",
            "nodemon": "^2.0.2",
            "rimraf": "^3.0.0",
            "ts-jest": "^24.2.0",
            "ts-node": "^8.5.4",
            "typescript": "^3.7.4"
          },
          "dependencies": {
            "body-parser": "^1.19.0",
            "express": "^4.17.1"
          }
        }
    

    tsconfig.json

        {
          "compilerOptions": {
            "target": "es5",
            "module": "commonjs",
            "outDir": "dist",
            "sourceMap": false,
            "allowSyntheticDefaultImports": true,
            "baseUrl": ".",
            "paths": {
              "*": [
                "node_modules/",
              ],
              "typings/*": [
                "src/typings/*"
              ]
            },
          },
          "include": [
            "src/**/*.ts"
          ],
          "exclude": [
            "src/test/**/*.spec.ts"
          ]
        }
    

    src/api/MoviesApi.ts

        import IResource from "typings/IResource";
    
        let movies: object[] = []
    
        export default class MoviesApi implements IResource {
            constructor() {
                // setup some dummy data
                movies.push({
                    name: 'Pirates of the caribbean',
                    rating: 8.5
                })
                movies.push({
                    name: 'Star Wars: A new hope',
                    rating: 8.7
                })
            }
    
            create(data: any): any {
                movies.push(data)
                return data
            }
    
            findMany(): any[] {
                return movies;
            }
        }
    

    src/test/api/Movies.spec.ts

        import IResource from '../typings/IResource'
        import MoviesApi from '../api/MoviesApi'
    
        const moviesApi: IResource = new MoviesApi()
    
        describe('Movies API', () => {
            it('should create a new movie', () => {
                const movieData: object = {
                    name: 'Pirates of the caribbean',
                    rating: 8.5
                };
    
                const movie: object = moviesApi.create(movieData);
    
                expect(movie).toEqual(movieData)
            })
        });
    

    src/typings/IResource/index.d.ts

        export default interface IResource {
            create(data: any): any
            findMany(): any[]
        }
    

    src/index.ts

        import * as express from 'express'
        import * as bodyParser from 'body-parser'
    
        import MoviesApi from './api/MoviesApi'
    
        const app = express();
        const moviesApi = new MoviesApi();
    
        app.use(bodyParser.json());
    
        app.post('/movies', (req: express.Request, res: express.Response) => {
            res.json(moviesApi.create(req.body))
        });
    
        app.get('/movies', (req: express.Request, res: express.Response) => {
            res.json(moviesApi.findMany())
        });
    
        app.listen(process.env.PORT, () => {
            console.log(`server started on port ${process.env.PORT}`)
        });
    

    这篇关于如何将Typescript,NodeJS和Express应用程序部署到Heroku的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆