Build Instagram Using TypeScript, Node, Express and Vue - Part 1
Tags: node, vue, javascript, typescript
Published: 2020-06-10 19:44:13

This is tutorial 1 of a 5-part tutorial, but each tutorial can be read in isolation to learn various aspects Node+Express+TypeScript+Vue API/Vue web app set up. 

At the end of this 5-part tutorial, you will learn to build an app like this:

![Web app tutorial using Node, Express and Vue in TypeScript](https://dev-to-uploads.s3.amazonaws.com/i/tmiggdej0mms0lqextjf.gif)

<sub>Looking to learn mobile/desktop apps? The skills and concepts here are fundamental and re-usable for mobile apps (NativeScript) or desktop apps (Electron). I may cover them as a follow-on.</sub>

#### Navigation to other parts (you are at *part 1*)
1. Setting up Node and Express API with TypeScript
2. [Setting up VueJs with TypeScript](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-2-k48)
3. [Setting up Postgres with Sequelize ORM](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-3-4d1n)
4. [Basic Vue templating and interaction with API](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-4-23ll)
5. [Advanced Vue templating and image uploading to Express](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-5-gga)

## Introduction
All good apps should start from a rock-solid base, which is what this tutorial about, illustrated through building a very simple photo sharing app, instead of a Todo (which really doesn't show much). Through these tutorials, you will learn TypeScript, Node, Express and VueJS, using versions as *bleeding edge as* it can get at the time of this post (some pre-releases where practicable). 

*Sadly, [Deno](https://deno.land/) was considered but is still too early and to use. However, when the time comes, you are likely able to switch to Deno and reuse much of your API codebase if you generally follow best practices in this tutorial. You will be able to re-use all your view coding as it is not coupled to the API.

To be completely honest, Instagram can't be built in a single tutorial, so admittedly the title of this post is an exaggeration. We will call this project "Basicgram".

## Get your repo

You can start building by cloning and checking out tutorial-part1 branch:
```
git clone https://github.com/calvintwr/basicgram.git
git checkout tutorial-part1
```

## Folder Structure

Folders will be split into "api", which will run a Node+Express set up, and "view", which will run a Vue+Webpack set up.

![Express/VueJS Folder Structure](https://dev-to-uploads.s3.amazonaws.com/i/8gy6szhw7iwrc8cpyuse.png)

## Get started - Installing Express (API engine)
```
npx express-generator --view=hbs
```
I opted for Handlebars (hbs) as the view engine because it looks like HTML so you won't need to learn new templating syntax. But you hardly will use since we will only be using Express for API service -- but it's there for you when you need it.

We will use the latest Express 5.0 (pre-release) and update all module versions, so edit the `package.json` file:
```js
{
  "name": "api",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.5",
    "debug": "~4.1.1",
    "express": "~5.0.0-alpha.8",
    "hbs": "~4.1.1",
    "http-errors": "~1.7.3",
    "morgan": "~1.10.0"
  }
}
```

#### Fire it up and see if it all works

```
npm install
npm start
```
Go to `localhost:3000` and express should greet you.
![Express Welcome Page](https://dev-to-uploads.s3.amazonaws.com/i/5fkrbvnwf9m7qxg3p0qq.png)

### Express Routings

One of the first Express things you want to get is [`express-routemagic`](https://github.com/calvintwr/express-routemagic), which automatically require all our routes instead of declaring them file by file (you will see huge Express apps and their tower of routing codes which doesn't make sense). So just get [routemagic](https://github.com/calvintwr/express-routemagic), problem solved.

```
npm install express-routemagic --save
```

We will replace the routing requires:
```js
var indexRouter = require('./routes/index')
var usersRouter = require('./routes/users')
app.use('/', indexRouter)
app.use('/users', usersRouter)
```

With:

```js
const Magic = require('express-routemagic')
Magic.use(app, { invokerPath: __dirname }) // need `invokerPath` because we shifting Express into a `src` folder.
```

That's it, you will never need to worry about routings. Let's move on.

## Converting to TypeScript
TypeScript provides quite a lot of useful features to build better code. You can google its benefits. It has its downsides too, especially being more tedious and having to deal with non-typescript packages (there are many useful and time-proven ones, but didn't see the need to port themselves over to TypeScript syntax). Throughout this tutorial, figuring how to convert some JS syntax to TypeScript were either painful or close to impossible. But well, we soldier on.

Since now we need to compile our TS to JS for Node runtime, we will need a few steps to get there.

### 1. Pack your Express into a "src" folder like this:

![Express TypeScript File Structure](https://dev-to-uploads.s3.amazonaws.com/i/yqod292luti81aev0c6u.png)

And also notice that "app.js" is renamed to "app.ts". We will start with this and leave the rest alone for now. Baby steps.

>Tip: I think it is OK to have mixed codebase. Not everything needs to be in TypeScript, plus you will be missing out **a lot** of really good JS modules. But I'm sure this statement invokes agitation amongst TypeScriptors. Oh well, make your case below.

### 2. Install TypeScript package and set up configurations
Install TypeScript (note: all npm commands are to run in the `basicgram/api` folder. `api` and `view` are technically two different apps. If you run npm in `basicgram`, you will mixed their node_modules and other configurations up.)

#### Setting up the TypeScript compiler

```
npm install typescript --save-dev
```

#### Set up the `tsc` command in `package.json`:
```js
"script": {
    "start": "node ./bin/www", // this came default with express, but we will change it later.
    "tsc": "tsc"
}
```

#### And initialise tsc which will generate a configuration file:

```
npx tsc --init
```
`tsconfig.json` will now appear in `basicgram/api`. This controls the compiler's behaviour. There's generally 2 default behaviours we want to change:

1. TSC by default outputs ES5, which is really unnecessary for Node, being a server-side runtime (if what is stopping you from upgrading Node are your old apps, see [Node Version Manager](https://github.com/nvm-sh/nvm)).

2. It will search compile all `.ts` files inside of `basicgram/api` and produce `.js` alongside it, which really isn't what we want. 

#### So we make the following changes:
```js
{
    "compilerOptions": {
        "target": "ES6", // you can go for higher or lower ECMA versions depending on the node version you intend to target.
        "outDir": "./dist" // to output the compiled files.
    }, "include": [
        "src" // this tells tsc where to read the source files to compile.
    ]
 }
```

Now let's try out our command:

```
npm run tsc
```

You will see errors like:

```
src/app.ts:21:19 - error TS7006: Parameter 'req' implicitly has an 'any' type.

21 app.use(function (req, res, next) {
```

That's mean TypeScript works, and it's telling you `app.ts` -- which is still in Javascript -- has type safety violations, rightfully so. And so we start the conversion.

### 3. Code conversion and type declarations

First, we need to install type declaration for all modules. Just go with me first, I'll explain what this is all about later. They are named "@types/[modulename]". Whether they are available depends on if the package owner has made it. A lot of them didn't really bothered. In any case, we are only going to do it for `node` and `express` as an example, while skipping over type-checking for other modules using `// @ts-ignore`.

```
npm install @types/node
npm install @types/express
```

And convert the `app.ts` into this:

*(Note: Use of `@ts-ignore` is not recommended, and only for the purposes of this demo.)*

```ts
// @ts-ignore
import createError = require('http-errors') // change all `var` to import
import express = require('express')
import { join } from 'path' // this is a Node native module. only using #join from `path`
// @ts-ignore
import cookieParser = require('cookie-parser')
// @ts-ignore
import logger = require ('morgan')
// @ts-ignore
import Magic = require('express-routemagic')
const app: express.Application = express() // the correct type declaration style.
// view engine setup
app.set('views', join(__dirname, 'views'))
app.set('view engine', 'hbs')
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(join(__dirname, 'public')))
Magic.use(app, { invokerPath: __dirname }) // // need to use `invokerPath` because we are not in api's root dir.
// catch 404 and forward to error handler
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { // type declaration, and changed to use arrow function
    next(createError(404))
})
// error handler
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
    // set locals, only providing error in development
    res.locals.message = err.message
    res.locals.error = req.app.get('env') === 'development' ? err : {}
    // render the error page
    res.status(err.status || 500)
    res.render('error')
})
module.exports = app
``` 
> What is `app.use`? It is Express's way of using "middleware" (functions that will run and be given the HTTP request to "read" and do something to it). And it is sequential, and one way is to imagine a HTTP request "falling" through your middlewares: In this case it will first  pass through `logger('dev')`, which logs the request on your terminal, then `express.json()` which parses the request into json... and so on so forth sequentially.

### TypeScript Basics Explanation

The `@types/express` module you have installed are TypeScript declarations for Express objects. Declarations are like a dictionary -- it explains what something is or isn't.

If you refer lower down in `app.ts`, the block of `// error handler` code shows how this "dictionary" is applied to function arguments:

```ts
(err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => { ... }
```

What it means is that, the `req` argument are "of the same type and -- for a lack of better word -- form" with Express' `Request` object/prototype (I refuse to use the word "class", because [Javascript is irrefutably classless](https://stackoverflow.com/questions/10430279/extending-an-object-in-javascript/28281845#28281845)). 

So within the function, if you try to use `Request` as a type that it isn't, or if you try to invoke a method that `Request` does not have, TypeScript will bitch about it.

```TS
(req: express.Request) => {

   req.aMethodThatDoesNotExist() // red curlies underlines, and will not compile.

   if (req === 'someString') {} // TypeScript will tell you this is always false.

})
```

All that, is in essence a very basic explanation of how TypeScript type-checks your code.

And now if you run `npm run tsc` again, you should get no errors.

### 4. Copy all files to "./dist"

TSC will only compile `.ts` files, rightfully so. But you need to copy the rest of the files over, including those `.js` files that you either don't intend to convert, or will convert later (that's the beauty, you don't need to always OCD everything to TypeScript -- not all code is worth your time). `tsc` doesn't seem to provide a good way ([see issue here](https://github.com/Microsoft/TypeScript/issues/30835#issuecomment-553733016)), so we will use the `cpy-cli` and `del-cli` modules:

```bash
npm install cpy-cli del-cli --save-dev
```

Set up the npm scripts in `package.json`.
1. A `prebuild` script that uses `del` shell command (from `del-cli` module) to delete the old "./dist" folder:

```js
"prebuild": "del './dist'"
```

2. A `postbuild` script that uses `cpy` shell command (from `cpy-cli` module) to copy remaining files over:

```js
"postbuild": "cpy --cwd=src '**/*' '!**/*.ts' './../dist' --parents"

// --cwd=src means the Current Working Directory is set to "./src"
// '**/*' means all files and folder in the cwd.
// '!**/*.ts' means excluding all typescript files.
// './../dist' means "basicgram/api/dist", and is relative to "src" folder
// --parents will retain the folder structure in "src"
```

And your scripts in `package.json` will be:

```js
{
    "scripts": {
        "start": "node ./dist/bin/www",
        "build": "npm run tsc",
        "prebuild": "del './dist'",
        "postbuild": "cpy '**/*' '!**/*.ts' './../dist' --cwd=src --parents",
        "tsc": "tsc"
    }
}
```

Now, just to check everything is working, go to "src/routes/index.js" and change `title` from `Express` to `Express in TypeScript`:

```js
res.render('index', { title: 'Express with TypeScript' })
```

Build and run it:

```
npm build
npm start
```

![Running express compiled from TypeScript](https://dev-to-uploads.s3.amazonaws.com/i/8t8wxy5kf0is3fbv9vgy.png)

## 5. Setting up auto re-compiling

For development, it's inefficient to keep running `npm build` and `npm start`. So we are going to use `nodemon` to auto restart the server on file changes, and `ts-node` to execute the TypeScript files as though they are Javascript (note: this is intended for development environment and does not output to `./dist`):

```
npm install nodemon ts-node --save-dev
```

Add the following to `package.json`:
```json
"scripts": {
    "dev": "nodemon --ext js,ts,json --watch src --exec 'ts-node' ./src/bin/www"
}
```

Explanation: 

`--exec`: We use `--exec` flag because `nodemon` will not use `ts-node`, instead will use `node` if the entry file is not ".ts". In this case `www` is not. 
`--ext`: When `--exec` is used, we also need to use `--ext` to manually specify the files to watch for changes. 
`--watch`: This defines which folder nodemon will watch for changes to do a restart.
(Credits to [this video](https://www.youtube.com/watch?v=zRo2tvQpus8))

#### Run your dev server:

```
npm run dev
```

Your API is all fired up! Make some changes to see how nodemon auto re-compiles. See [Part 2](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-2-k48) to set up your view engine with VueJS in TypeScript.

### Endnotes:
1. [Getting Started Using TypeScript with Node.js and Express](ttps://medium.com/@pankaj.itdeveloper/getting-started-using-typescript-with-node-js-and-express-6aff573667d5)
2. [Typescript compiler file copy issue](https://github.com/Microsoft/TypeScript/issues/30835#issuecomment-553733016)
3. [Video: TypeScript Setup With Node & Express](https://www.youtube.com/watch?v=zRo2tvQpus8)