In this article, I record all the steps I took and bugs I bombed into while setting up and deploying an express typescript server.
TL:DR
- Initialization
- Install nodemon and ts-node
- Setup express server
- Deploy the project, fix typescript bug
- References
Initialization
To start a new typescript project, make a new directory and run npm init -y
, this will touch a package.json
and pack-lock.json
for our new project.
Then install typescript into the project by running npm intall typescript
.
Now our package.json
should look like this:
1// package.json 2{ 3 "name": "Your_Dir_Name", 4 "version": "1.0.0", 5 "description": "", 6 "main": "index.js", 7 "scripts": { 8 "test": "echo \"Error: no test specified\" && exit 1" 9 }, 10 "keywords": [], 11 "author": "", 12 "license": "ISC", 13 "dependencies": { 14 "typescript": "^5.2.2" 15 } 16}
Then touch a server.ts
, log a “test” message, and run the file using node ( define the script first )
1// server.ts 2 3console.log("test");
1// package.json 2 3"scripts": { 4 "dev": "node server.ts" // tell npm to execute the file using node 5} 6 7// then run "npm run dev" in the terminal 8// it should log the message "test"
Install nodemon and ts-node
Next, we’re going to install nodemon, it allows us to re-run our server every time we save our file.
1// terminal 2 3$ npm i nodemon --save-dev 4// --save-dev tells npm that the package is installed in devDependencies 5// which means it will only be used in development, not on production
1// package.json 2 3"devDependencies": { 4 "nodemon": "^3.0.1" 5}
Add a new command in the script to tell npm we are going to use nodemon to run our file.
1// package.json 2 3"scripts": { 4 "dev:watch": "nodemon server.ts" // define how we execute the file 5}, 6"devDependencies": { 7 "nodemon": "^3.0.1" 8} 9 10// then run "npm run dev:watch", 11// and then...
Oops, we shall encounter some errors here:
1[nodemon] 3.0.1 2[nodemon] to restart at any time, enter `rs` 3[nodemon] watching path(s): *.* 4[nodemon] watching extensions: ts,json 5[nodemon] starting `ts-node server.ts` 6sh: ts-node: command not found 7[nodemon] failed to start process, "ts-node" exec not found 8[nodemon] Error
The above command is basically saying that since the file server.ts
we are running is typescript , it is going to use ts-node
to run the file instead, but ts-node
command is not found.
So, we need to install ts-node
first.
1$ npm i --save-dev ts-node 2 3// and re-run 4$ npm run dev:watch
Now the error should be resolved, and every time we save the file, it should automatically log the "test"
for us.
Setup express server
Install express
Now we can start setting up our express server.
First, run npm i express
to install express, then import express
inside our server.ts
1import express from 'express';
We shall encounter another typescript error:
1TSError: ⨯ Unable to compile TypeScript: 2server.ts:1:21 - error TS7016: 3Could not find a declaration file for module 'express'. 4'/node_modules/express/index.js' implicitly has an 'any' type. 5Try `npm i --save-dev @types/express` if it exists 6or add a new declaration (.d.ts) file containing `declare module 'express';`
This is because we didn’t install the type of express, run npm i --save-dev @types/express
and this error should be resolved.
Setting up the app
we can now setup our app and define the port to be listened to.
First, let’s create a .env
file and define our port in it.
1// .env 2 3PORT=5001
Then setup our app server.
1// server.ts 2 3const app = express(); 4const port = process.env.PORT || 5000; 5 6console.log(process.env.PORT); 7 8app.listen(port, () => { 9 console.log(`app is listening to port: ${port}`); 10});
Configure .env file
The app should run correctly now, but you will probably find out that process.env.PORT
is undefined
.
To solve the problem, install dotenv
and configure it inside our server.ts
1$ npm install dotenv --save
1// server.ts 2 3import dotEnv from 'dotenv'; 4dotEnv.config();
Now we shall see our app is listening to port 5001 as we defined inside .env
file.
Write a simple GET route
1const app = express(); 2const port = process.env.PORT || 5000; 3 4app.get("/", (rep, res) => { 5 res.send("hello world"); 6}); 7 8app.listen(port, () => { 9 console.log(`app is listening to port: ${port}`); 10});
Open the browser, go to http://localhost:5001
, now we should see the hello world
Deploy the project, fix typescript bug
Now that we finish developing our simple app, we can deploy the app to a platform called Render. Render is a platform which provides free service for us to host our app.
But before we deploy the app, we need to push the project to Github first.
Push Our Project to Github
Let’s create a repo first. Go to your Github account, add new repository, and create the repo.
After creating the repo, we now initialize git inside our express project, add the repo to the remote branch of the project.
1# open terminal inside the project 2$ git init 3$ git remote add origin https://github.com/YOUR_ACCOUNT/YOUR_PROJECT_NAME.git
Before pushing all the files to Github, let’s create a .gitignore
file, add .env
and node_modules
into it.
1# .gitignore 2.env 3node_modules
Let me explain why we add these two files to .gitignore
.
.env
usually stores some important information such as private key, which we don’t want people to know, so we usually config all the .env variables where we host our app; as for node_modules
, they are the dependencies of the project, NPM can manage and install those dependencies for us by checking the package.json
and package-lock.json
every time we run npm install
inside our project, so there’s no need to push them to Github.
After the adding, push all the files to the repo.
1$ git add -A 2$ git commit -m "push all files to repo" 3$ git push --set-upstream origin main
Go to the repo and check if the files are pushed to it.
By the way, here’s the repo for this article, go and check it out.
Deploy project to Render
Let’s start out deployment. Go to Render, add new web service, choose “Build and deploy from a Git repository”, then connect the repo we’ve just pushed to Github, you can connect the repo by entering its link if the repo is public.
Next step, enter the deployment config:
- Name: express-ts
- Region: Singapore
- Branch: main
- Runtime: Node
- Build Command: npm install
- Start Command: npm run dev
- Click Advanced, then click Add Environment Variable, here we can add our .env variables:
- Key: PORT
- value: 5001
Then click Create Web Service, it will start installing project dependencies and build the project for us.
BUT, Oops, an error shall appear inside the building logs:
1$ Running 'npm run dev' 2$ > express-ts@1.0.0 dev /opt/render/project/src 3$ > node server.ts 4$ (node:63) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension
This happens because we forgot to compile typescript file into javascript file, no engine can run typescript directly, so remember to always compile .ts
files into .js
files before we actually runs it during the deployment. Let’s add some typescript config to fix this.
Create tsconfig.json
Let’s initialized a tsconfig.json
, it configs the command tsc
which allows us to compile typescript into javascript.
1# terminal 2$ npx tsc -init
After this command, our project should now looks like this:
1. 2├── package-lock.json 3├── package.json 4├── server.ts 5└── tsconfig.json # the command should add tsconfig.json for us
For this simple project, we actually don’t need to do anything inside the tsconfig.json
.
Now we can add tsc
command inside the package.json.
1"scripts": { 2 "tsc": "tsc", 3},
Try to run npm run tsc
, and you should see server.js appears inside our project.
1. 2├── package-lock.json 3├── package.json 4├── server.ts 5├── server.js # over here!! It is compiled from server.ts 6└── tsconfig.json
Compile while building
Now we can go back to Render.
Before we re-deploy again, we should add tsc
into our package.json
script so as to compile during the deployment. Also, we should run server.js
during the deployment instead. Let’s do a little modification in our script:
1// package.json 2 3"scripts": { 4 "build": "npm install && tsc", 5 "start": "node server.js", 6},
Then inside Render configuration, let’s change our Build Command to npm run build
and Start Command to npm run start
.
- Build Command: npm run build
- Start Command: npm run start
In this case, Render will execute npm run build
which runs npm install
then tsc
to compile our .ts
files under the hood, after that, it will run npm run start
which runs node server.js
for us.
1$ app is listening to port: 5001 2$ Your service is live 🎉
Voilà! Our server should now been successfully deployed. Go visit the link Render provides you, you should see the Hello World
string when you open the page. Congrats!