如何将 Typescript、NodeJS 和 Express 应用程序部署到 Heroku
Posted
技术标签:
【中文标题】如何将 Typescript、NodeJS 和 Express 应用程序部署到 Heroku【英文标题】:How to deploy a Typescript, NodeJS and Express app to Heroku 【发布时间】:2020-04-22 12:46:57 【问题描述】:由于找不到任何完整的信息,因此我想提供有关此操作的方法。我认为这在 *** 文档中似乎最合适。但是它已被取消 *-) - Sunsetting Documentation (*)。
相反,我将把它写成 *** 问答。
如何将 Typescript、NodeJS 和 Express 应用部署到 Heroku
【问题讨论】:
【参考方案1】:我创建了一个项目 (https://gitlab.com/OehmSmith_Examples/herokumovies),其中包含一个描述需要做什么的自述文件,我将在此处重现。作为一个良好的 *** 实践,我还将在本文底部提供所有代码的副本。
教程 - 将 Typescript NodeJS Express 应用程序部署到 Heroku
本教程将以 https://amenallah.com/node-js-typescript-jest-express-starter/ 作为基础应用程序。我与该网站或作者没有任何关系。我选择它是因为它简单且有效。这也是一个很好的OO Typescript代码的例子。
安装打字稿
https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html 中的大多数教程甚至官方文档都说要全局安装 typescript:
> npm install -g typescript
Heroku 没有全局安装 typescript,因此需要将其保存在本地。示例项目就是这样做的:
> npm i -D nodemon rimraf typescript ts-node ts-jest jest @types/jest @types/node
@types/节点
如果您将 @types/node
固定在旧版本,您将看到类似以下错误:
~/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: Duplicate identifier 'IteratorResult'。根据您需要更新@types/node
的版本。这是我在处理旧代码时遇到的一个问题,并希望包括对它的讨论。
更新云服务端口
将index.ts
更改为以下,因为原始代码硬编码端口5000:
app.listen(process.env.PORT, () =>
console.log(`server started on port $process.env.PORT`)
);
为此,我已将 PORT 添加到 npm scripts
,包括添加 start:dev
,以便您可以像 Heroku 从已编译的打字稿中一样运行它。
"start:dev": "PORT=5000 node dist/index.js",
"dev": "PORT=5000 nodemon --exec ts-node src/index.ts --watch src",
或者可以在 .env 文件中设置:
PORT=5000
NPM 依赖项
Heroku 不会安装开发依赖项(任何其他云提供商也不会)。因此,您需要将一些依赖项移动到主块。例如,NestJS
应用程序将这些作为开发依赖项,它们需要被移动:
@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 上创建一个应用
在您的终端中:
heroku login
heroku create moviesheroku // this needs to be unique
您可能需要将返回的 git url 添加为远程(检查 git remote -v
):
git remote add heroku <git url>
查找或搜索构建包(下一步已经指定了我使用的那些):
查找地址:https://devcenter.heroku.com/articles/buildpacks#officially-supported-buildpacks搜索:
heroku buildpacks:search typescript
添加构建包:
heroku buildpacks:add zidizei/typescript
heroku buildpacks:add heroku/nodejs
确认构建包:
heroku buildpacks
提交到您的本地存储库
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"
运行
npm dev # OR
npm run start:dev # It depends on your npm scripts
使用 postman 或类似工具进行测试或从命令行运行:
curl http://localhost:5000/movies
用npm run build
测试它是否可以转译
更新 npm 脚本,以便在 Heroku 上安装 (npm install
) 后,它会在尝试 npm run start
之前构建它
"postinstall": "npm run build" # Depends on your npm scripts
提交到本地存储库:
git add --all
git ci -m "Now it should deploy, build and run on heroku"
部署到heroku。它应该构建并启动。
git push heroku master
测试(假设您 heroku create
d 的应用是 moviesheroku
- 相应调整)
curl https://moviesheroku.herokuapp.com/movies
变化
Procfile
我没有指定Procfile
告诉 Heroku 任何有关该应用程序的信息。幸运的是,它通过确定这是一个node + npm
应用程序来创建自己的默认值。但是,您可以明确定义它,如果您有多个应用程序或类似应用程序,则需要执行此操作。您可以添加一个 Procfile 来包含(这是默认设置):
web: npm start
节点和 NPM 版本
Heroku 也默认使用其中一个最新版本。您可以在 package.json
文件的顶层显式设置版本,例如:
"engines":
"node": "10.x",
"npm": "6.x"
,
虽然如果你不指定 npm
版本,那么 Heroku 将使用合理的默认节点版本。
结论性想法
我只用了几个小时就搞定了。我需要解决的主要问题是打字稿必须是本地的,而不是全局的。还有构建包。 PORT 也是一个问题,尽管每个云提供商都需要使用 process.env.PORT
,所以这对我来说很明显。
Azure 是一场噩梦,需要几天时间,但这主要是因为我所在的工作场所坚持使用 Windows 服务器。长篇大论,我就不赘述了。
AWS 太复杂了。尝试了一天后,我没有得到我工作的实例。但是我确实需要再试一次。我尝试的应用程序使用了https://tsed.io/ 库。简单的 Node / Typescript / Express 应用程序应该很容易运行。
(*) - 文档的终止有点令人惊讶,尽管考虑到它发生在 2 年前,我想这不是我使用的东西。而且我一直认为问答是最容易记录文档的地方。
代码
.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的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 nodejs 和 typescript 自动重启节点应用程序?
如何在 NodeJS、Mongoose 和 TypeScript 中使用日期和时间?
如何使用 NodeJS 13 和 Typescript 3.8 导入 esm 模块?