Cover Page

Backend Page

TypeScript with Express

Install dependencies

Change to your chatterd directory and install the PostgreSQL package:

server$ cd ~/reactive/chatterd
server$ pnpm install postgres 

chatterd app

Edit main.ts:

server$ vi main.ts

and add the following import lines:

import postgres from 'postgres'
import type { Sql } from 'postgres'

We set up a pool of open connections to our PostgreSQL chatterdb database. Maintaining a pool of open connections avoids the cost of opening and closing a connection on every database operation. The password used in the call to postgress() must match the one you used when setting up Postgres earlier. The connection pool needs to be accessible to all the URL handlers, so we store it in a global variable chatterDB:

export let chatterDB: Sql

Right inside the try{} block, add:

  chatterDB = postgres('postgres://chatter:chattchatt@localhost/chatterdb')

Find the initialization of app and add two routes right after the route for /llmprompt. These serve Chatter’s two APIs: HTTP GET request, with URL endpoint getchatts/, and HTTP POST request, with endpoint postchatt/, invoking the get() or post() method respectively and each given its handler function:

      .get('/getchatts/', handlers.getchatts)
      .post('/postchatt/', handlers.postchatt)

The functions getchatts() and postchatt() will be implemented in handlers.ts.

We’re done with main.ts. Save and exit the file.

handlers.ts

Edit handlers.ts:

server$ vi handlers.ts

First add the following imports to the top of the file:

import { randomUUID } from 'crypto'
import type { PostgresError } from 'postgres'

import { chatterDB } from './main.js'

We define a Chatt interface to help postchatt() deserialize JSON received from clients. Add these line right below the imports:

interface Chatt {
    username: string
    message: string
}

Now add a number of logging functions that, in case of error, prepare a corresponding JSON response to be returned to the client:

const logServerErr = (res: Response, errmsg: String): Response => {
    return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(errmsg)
}

const logClientErr = (res: Response, errcode: number, errmsg: String): Response => {
    return res.status(errcode).json(errmsg)
}

The handler getchatts() uses an open connection from the connection pool to query the database for stored chatts. By default, postgres.js retrieves each database row as a JSON object, whereas we want each row represented as an array of values only, without column name as tags. We achieve this by calling .values() on each retrieved row. Once all the rows from the database are retrieved, if no exception has been raised, we insert the resulting array of chatts into the response JSON object. Add getchatts() to your handlers.ts after the definition of the llmprompt() function.

export async function getchatts(req: Request, res: Response) {
    try {
        const chatts = await chatterDB`SELECT username, message, id, time FROM chatts`.values()
        res.json(chatts)
    } catch (error) {
        logServerErr(res, `${error as PostgresError}`)
    }
}

Similarly, postchatt() receives a posted chatt in the expected JSON format, deserializes into the Chatt interface, and inserts it into the database, using a connection from the pool. The UUID and time stamp of each chatt are generated at insertion time. Add postchatt() to the end of your handlers.ts:

export async function postchatt(req: Request, res: Response) {
    let chatt: Chatt
    try {
        chatt = req.body
    } catch (error) {
        logClientErr(res, HttpStatus.UNPROCESSABLE_ENTITY, error.toString())
        return
    }
    try {
        await chatterDB`INSERT INTO chatts (username, message, id) VALUES (${chatt.username}, ${chatt.message}, ${randomUUID()})`
        res.json({})
    } catch (error) {
        logClientErr(res, HttpStatus.BAD_REQUEST, `${error as PostgresError}`)
    }
}

We’re done with handlers.ts. Save and exit the file.

Build and Test run

:point_right:TypeScript is a compiled language, like C/C++ and unlike JavaScript and Python, which are an interpreted languages. This means you must run npx tsc each and every time you made changes to your code, for the changes to show up when you run node.

To build your server, transpile TypeScript into JavaScript:

server$ npx tsc

To run your server:

server$ sudo node main.js
# Hit ^C to end the test

You can test your implementation following the instructions in the Testing Chatter APIs section.

References


Prepared by Sugih Jamin Last updated July 27th, 2025