如何将Typescript,NodeJS和Express应用程序部署到Heroku [英] How to deploy a Typescript, NodeJS and Express app to 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
- 如果您还没有帐户,请设置一个帐户,然后在Heroku上创建一个应用程序
-
在您的终端中:
- Setup an account if you don't already have one and create an app on Heroku
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 create
d 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屋!