Build Instagram Using TypeScript, Node, Express and Vue - Part 3
Tags: node, vue, javascript, typescript
Published: 2020-06-10 19:48:28

This is tutorial 3 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. 

<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 3*)
1. [Setting up Node and Express API with TypeScript](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-1-57do)
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
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)

After you have gotten your view ready in [part 2](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-2-k48), we must now create API endpoints allow users to post pictures and captions, as well produce a post feed.

If you have not, you can start building by cloning and checking out tutorial-part2 branch:
```
git clone https://github.com/calvintwr/basicgram.git
git checkout tutorial-part2
```

## 1. Setting up runtime checking for your API

The first thing you want to do is to set up runtime checking for your API, because TypeScript does not do that. Also you may also need to produce comprehensible error messages for your requestors. [A library that will serve this specific need is `you-are-not`](https://github.com/calvintwr/you-are-not):

```
npm install you-are-not --save
```

We will change the "routes/user.js" file to ".ts", and set up a `/add` route so that we can add users:

```ts
import express from 'express'
const router = express.Router()
const Not = require('you-are-not')
const not = Not.create()

router.get('/', function (req: express.Request, res: express.Response, next: express.NextFunction) {
    res.send('respond with a resource')
})

router.put('/add', function (req: express.Request, res: express.Response, next: express.NextFunction) {

    not('string', req.body.name, 'User Name', 'Please check your entry') // will error if name is not what you expected.

    // perform tasks here

    res.send('completed task and respond with results')
})
module.exports = router
```

The `Not` library also have many more features to strengthen your API, read more [here](https://github.com/calvintwr/you-are-not). 

You can use [Postman](https://www.postman.com) or just cURL to test it out. 

```
# This will error
curl -X PUT -d wrong=Input localhost:3000/users/add
# Outputs {"message":"Wrong Type (User Name): Expecting type `string` but got `undefined`. Please check your entry"}

# This will work
curl -X PUT -d name=Jane localhost:3000/users/add
```

Now that we have an endpoint working, and the error handling looks pretty alright with [`Not`](https://github.com/calvintwr/you-are-not), it's time to create the database to store stuff.

## 2. Setting up a database - PostgresSQL with SequelizeJS

### On PostgresSQL

We will use PostgresSQL as the database. It is fast, reliable and also have JSON field to store schema-less data. So it fulfils quite a lot as a principle database. In truth, a very large project will use different databases to complement each other. But for the most bit, Postgres has everything you need.

#### This tutorial will not cover how to install postgres, but you can read the following installation guides:

1. [For MacOS](https://www.codementor.io/@engineerapart/getting-started-with-postgresql-on-mac-osx-are8jcopb)
2. [For Linux Ubuntu](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04)
3. [For Windows](https://www.robinwieruch.de/postgres-sql-windows-setup)

Once you have installed postgres, set up a database and keep the credentials with you, as we will need it later.

> Tip: I personally like [DBeaver](https://dbeaver.io) as the GUI db management tool the most. 

### SequelizeJS

[Sequelize](https://sequelize.org) is a mature NodeJS Object Relational Mapper/Mapping (ORM) that uses a **promise-style** syntax, which makes your code very readable: 

```js
DB.Model
    .findAll()
    .then(results => { 
        //do something with results
    }).then(results => {
        //respond to client
    }).catch(err => {
        //the promise chain is broken
        //and error handling is elegant
    })
```

Promise patterns are also very powerful once you get the hang of it. It copes with error bubbling exceptionally well, which is critical in an API context. Promise syntax also makes it easy to use [transactions](https://sequelize.org/master/manual/transactions.html), a data rollback feature which in my experience, is quite a baseline requirement but not always present in even popular apps.

#### We will continue to bleeding-edge our way with  Sequelize beta 6. You will also need to install Postgres' adaptors as well:

```
npm install sequelize@6.0.0-beta.6 --save
npm install pg pg-hstore --save
```

You also want the `sequelize-cli` tool as it will become the default way to handle the models. The cli now generates JS files but we shouldn't need to care.

```
# @next tag brings in the beta 6 cli
npm install sequelize-cli@next -g

# cd into "basicgram/api/src"
sequelize init
```

#### Set up configurations:

```js
/* config/config.json */

// by default node runs in development mode if you
// did not explicitly specify
"development": {
  "username": your username,
  "password": password,
  "database": database name,
  "host": "localhost", // this is usually the cause
  "dialect": "postgres"
}
```

#### Sequelize is pretty silent about successfully connecting to the database, so we will just insert some code in `models/index.js` to notify us when the connection succeeds:

```js
/* models/index.js */

// Test and log connection to the database
sequelize
    .authenticate()
    .then(function (err) {
        console.log('Connection has been established successfully.')
    })
    .catch(function (err) {
        console.log('Unable to connect to the database:', err)
    })
```

## 3. Create our models

Seqeulize is enforcing use of CLI as a default way. Although it is a little clunky in my opinion, it does prevent a lot of errors once you get the hang of it. To create the `User` and `Post` models:

```
sequelize model:create --name User --attributes nam e:string
sequelize model:create --name Post --attributes caption:string, image:string
```

This creates `user.js` and `post.js` inside of "src/models", and also the migration files, which will be persisted to the db when you run:

```
sequelize db:migrate 
```
And you should see:
![Sequelize migrations](https://dev-to-uploads.s3.amazonaws.com/i/b8foijtiu102dw4yqbqm.png)

**Note:** And if you check your database, you will find that Seqeulize created "Users" and "Posts" tables, and took the initiative to plurialise the names. So just keep that in mind.

## 4. Add associations

The way to do this is to create a new migration file. It's again rather clunky at first but you'll be fine once you get the hang of it. 

#### First we will need to add the associations to the files:

```js
/* user.js */
User.associate = function(models) {
    // I always prefer to manually name my foreignKey.
    // But you can have Sequelize automatically take care of naming for you.
    User.hasMany(models.Post, {foreignKey: 'User_userID' })
}

/* post.js */
Post.associate = function(models) {
    Post.belongsTo(models.User, {foreignKey: 'User_userID' })
}
// #belongsTo will create the foreignKey on the Post table.
```

And set up a migration file:

```
sequelize migration:generate --name add-post-user-association
```

#### Go under the migrations folder and find a file with "[timestamp]-add-post-user-association". It is a template. And remember that your table names are pluralised:

```js
module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.addColumn(
      'Posts', // name of Source model - PLURAL!
      'User_userID', // name of the key we're adding 
      {
        type: Sequelize.INTEGER,
        references: {
          model: 'Users', // name of Target model - PLURAL!
          key: 'id', // key in Target model that we're referencing
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
      }
    )
  },

  down: (queryInterface, Sequelize) => {
    return queryInterface.removeColumn(
      'Posts', // name of Source model - PLURAL!
      'User_userID' // key we want to remove
    );
  }
};
```
**Explanation:** What a migration file does is that the change you want to make is defined in `up`, and to undo this change, the reversal is defined in `down`. Which is why in this case the reversal is just to simply remove the `User_userID` column. To understand better, [read this excellent guide by Maria Burlando](https://medium.com/@Ayra_Lux/a-guide-to-orm-sequelize-c276c7b6dd18).

#### We run the migration again:

```
sequelize db:migrate
```

If you get `ERROR: relation "public.Post" does not exist` error, remember it's Posts, not Post.

## 5. See if Sequelize works - Get all users

#### Set up the code to get all users for the '/' route:

```ts
/*.routes/users.ts */

router.get('/', function (req: express.Request, res: express.Response, next: express.NextFunction) {
    DB.User.findAll().then((result: any) => {
        console.log(result)
        res.send(result)
    }).catch((err: Error) => {
        // Calling #next will hand the error back to express,
        // so that the error handler defined in `app.ts` will handle.
        next(err)
    })
})
```

If all goes well, you will be able to see this in your terminal:

```
[nodemon] restarting due to changes...
[nodemon] starting `ts-node ./src/bin/www`
/ => .routes/index.js
/users => .routes/users.ts
Executing (default): SELECT 1+1 AS result
Connection has been established successfully.
```


In your browser, go to `localhost:3000/users`, to verify that a DB query will execute and return results:

```
Executing (default): SELECT "id", "name", "createdAt", "updatedAt" FROM "Users" AS "User";
[]
GET /users 304 31.818 ms - -
Executing (default): SELECT "id", "name", "createdAt", "updatedAt" FROM "Users" AS "User";
```

Where's the result? It's the empty array `[]` because we don't have any users yet, which we will add next.

## 6: Adding users

For simplicity, we will skip user signup and authentication (many tutorials out there), and provide a simple way to add users in the '/add' route:

```js
/*.routes/users.ts */

router.put('/add', function (req: express.Request, res: express.Response, next: express.NextFunction) {

    not('string', req.body.name, 'User Name', 'Please check your entry')
    
    // most of the time, you will need findOrCreate
    // as users may attempt to create a user account
    // with a username or email that already exist
    return DB.User.findOrCreate({
        where: {
            name: req.body.name
        }
    }).then((user: any) => {
        // #findOrCreate returns an array [user, created (true or false)]
        console.log(user)
        // if created, it's 201, otherwise Express defaults to 200 so nothing needs to be done.
        if (user[1]) res.status(201)
        res.send(user[0])
    }).catch((err: Error) => {
        next(err)
    })
})
```

We will use cURL to add a few users:

```
curl -X POST -d name=Jane localhost:3000/users/add
curl -X POST -d name=John localhost:3000/users/add
curl -X POST -d name=Mike localhost:3000/users/add
```

You should see the corresponding server logs that shows that the users have been successfully added. And if you now navigate to `localhost:3000/users`, you will see some results:

![Express serving data from Postgres via Sequelize](https://dev-to-uploads.s3.amazonaws.com/i/73v15us8wyampx724sp9.png)

Now that your database is all set up, you can head over to [Part 4](https://dev.to/calvintwr/build-instagram-using-typescript-node-express-and-vue-part-4-23ll) to learn some basic Vue templating and how it interacts with the Express/Node API.