In my previous post, I mentioned I had some difficulties with deploying my backend. In this post, I'll be talking about what those difficulties were, and how you can deploy your Apollo Server with TypeScript using path aliases without going through all the hassle I experienced. I hear you asking why did I choose Vercel? I'm a simple man; I see good UI, I deploy... You might be also wondering what's up with that cover image? Don't worry, I don't know how my mind works either. Let's start with explaining what path aliases are and explaining the problem, then we'll continue with the setup.
Path Alias
A path alias is a representation of a certain path that you don't want to hardcode everytime you import something. So, instead of this:
import { normalizeString } from "../../../../../../../../tools";
You can do this:
import { normalizeString } from "tools";
Aliases are very handy for keeping your project sane. The problem with my setup though; you have to specify your aliases for both TypeScript and webpack.
The Problem
At first, I tried both Vercel and Heroku. Both were unable to run TypeScript directly. Since I like its UI, I decided on Vercel for going forward. When I tried to deploy the project again by compiling it to JavaScript first, the output file didn't work. The reason for that is I used path aliases in the project, but TypeScript doesn't convert them into real paths when compiling. For that, I used webpack with ts-loader to compile the project into JavaScript. I also configured my path aliases in webpack config too. Now the build file was working on localhost. Once again, I tried to deploy it to Vercel, but again, it didn't work. Turns out, you shouldn't be containing your app.listen() function inside another function. And I was doing that, because I was using TypeORM at that time. And TypeORM requires you to wrap your app.listen() function inside its initialization function so that you can establish your database connection before your API starts running. So I switched to Mongoose and it was a better choice to be honest since I was using a NoSQL database anyway. And I tried to deploy the project, again. Well.. It didn't work, again. I figured that I had to specify my API route in vercel.json, so I tried again. This time, it worked! Everything was flawless after that. Now I deploy the project with npm run deploy without any problems. However, enough stories. Now we'll talk about how you can do that too.
1. Configure TypeScript
Here is how my tsconfig.json looks like:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "dist",
"removeComments": true,
"strict": true,
"strictPropertyInitialization": false,
"esModuleInterop": true,
"resolveJsonModule": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": "./",
"paths": {
"config": ["config"],
"interfaces": ["interfaces"],
"services": ["services"],
"entities": ["entities"],
"resolvers": ["resolvers"]
}
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
}
As you can see I have 5 path aliases named config, interfaces, services, entities and resolvers. They all located at the root of the project, so the baseUrl is "./". Don't forget to specify that.
2. Install and Configure Webpack
First let's install webpack and other dependencies we need:
npm i --save-dev webpack
npm i --save-dev webpack-cli
npm i --save-dev webpack-node-externals
npm i --save-dev ts-loader
Now we need to create a config file named webpack.config.js. Create that in your root folder. You can copypasta and edit mine:
const nodeExternals = require("webpack-node-externals");
const path = require("path");
module.exports = {
entry: "./src/app.ts",
target: "node",
externals: [nodeExternals()],
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
}
]
},
resolve: {
alias: {
config: path.resolve(__dirname, "config"),
interfaces: path.resolve(__dirname, "interfaces"),
services: path.resolve(__dirname, "services"),
entities: path.resolve(__dirname, "entities"),
resolvers: path.resolve(__dirname, "resolvers")
},
modules: ["src"],
extensions: [".ts", ".js"]
},
output: {
filename: "app.js",
path: path.resolve(__dirname, "dist")
}
};
There are some important fields here. entry is of course the starting point of your app. In alias, you have to specify all the path aliases you also configured in your tsconfig.json. In output, filename is the file name of the output file webpack builds for us. And the path is the location where you want webpack to put it. In my case, it's the "dist" folder.
3. Compile Your Project with Webpack
Open the command line in your root folder and run:
npx webpack
If you configured your webpack.config.js same as mine, your output file should be located at the dist folder. This is what we will be deploying to Vercel.
4. Install Vercel CLI and Login
To install:
npm i -g vercel
And to login:
vercel login
It'll send you an email, don't forget to check your junks folder.
If you use Windows and you are getting an security error in the command line, launch command line again as administrator and type:
Set-ExecutionPolicy RemoteSigned
Press A and enter. Then run the login command again.
5. Configure Your Vercel Deployment
Create a vercel.json file at the root folder of your project. And again, just copypasta mine and edit if you need to:
{
"version": 2,
"builds": [{ "src": "dist/app.js", "use": "@now/node" }],
"routes": [{ "src": "/", "dest": "dist/app.js" }]
}
This tells Vercel to run your API on the root directory with node runtime. Here is the important part; the path you specified in vercel.json must match with the path you specified in Apollo's applyMiddleware() function. This is what I'm talking about:
server.applyMiddleware({
app,
path: "/"
});
This is a simplified version of my usage of applyMiddleware() function. If I wanted to run my API in the "/api" directory, the vercel.json would look like this:
{
"version": 2,
"builds": [{ "src": "dist/app.js", "use": "@now/node" }],
"routes": [{ "src": "/api", "dest": "dist/app.js" }]
}
And my applyMiddleware() function would look like this:
server.applyMiddleware({
app,
path: "/api"
});
With that, we're done with the setup.
6. Deploy Your App to Vercel
This is the hardest part. I'm kidding, just run this on command line:
vercel --prod
In your first deployment, it'll ask you some properties to create your project on Vercel. After your deployment is done, it'll show you the link and it'll automatically copy that link to your clipboard. You can also add these lines in the scripts field of your package.json file for easing future deployments:
"build": "npx webpack",
"deploy": "npm run build && vercel --prod"
Conclusion
I wanted to post this because the first few steps are the same for every platform. However, I think Vercel is more intended to be used with serverless functions. And as far as I know it doesn't support web sockets in serverside, so be aware of that. Considering those, you might want to rethink your architecture according to your needs. Although in my case, my project -which I talked about in this post- was a small scaled personal one. You might want to go with Heroku, AWS or Netlify, but in my opinion this is also a good option for hobbyists.
I hope this was useful, you can also follow me on Twitter for future content:
Top comments (4)
You've inspired me to migrate my cra project
Text to GIF animation — React Pet Project Devlog
Kostia Palchyk ・ Jun 17 ・ 6 min read
to the next.js/vercel platform! So far so good: the deployment experience is very smooth! Thanks :)
I'm yet to try adding authentication and db connections ("api routes" promise to solve that), will see how that goes.
Another promising platform I'd like to try some time is: nx.dev/react .
It is really flawless with GitHub Continuous Integration. I'll look into Nx too, thank you!
👍 Looking forward your next dev journey update!
Btw, I have a project on Heroku, but I'm not sure I can reproduce the deployment process again: truly, the UX could be smoother.
Though my worst experience was with Google Cloud Run. I failed with it miserably :)
Next one will be wild on frontend...
I don't like Heroku's Git based CLI either. I use it when I need to use web sockets.