TypeScript with Express
Cover Page
Back-end Page
To support uploading of large media files, first change to your chatterd folder and
install the multer package as dependency:
server$ cd ~/reactive/chatterd
server$ pnpm install multer
server$ pnpm install -D @types/multer
This should add to your package.json, in the dependencies block:
"multer": "^2.0.2",
and in the devDependencies block:
"@types/multer": "^2.0.0",
handlers.ts
Edit handlers.ts:
server$ vi handlers.ts
Add the postimages() function:
export async function postimages(req: Request, res: Response) {
let chatt: Chatt
try {
chatt = req.body
} catch (error) {
logClientErr(res, HttpStatus.UNPROCESSABLE_ENTITY, `${error}`)
return
}
let imageurl: string | null
let videourl: string | null
try {
const files = req.files as { [fieldname: string]: Express.Multer.File[] }
const imagefilename = files['image']?.[0]?.filename
const videofilename = files.video?.[0]?.filename
imageurl = imagefilename ? `https://${req.headers[":authority"]}/media/${imagefilename}` : null
videourl = videofilename ? `https://${req.headers[":authority"]}/media/${videofilename}` : null
} catch (error) {
logClientErr(res, HttpStatus.UNPROCESSABLE_ENTITY, `${error}`)
return
}
try {
await chatterDB`INSERT INTO chatts (name, message, id, imageurl, videourl) VALUES (${chatt.name}, ${chatt.message}, ${randomUUID()}, ${imageurl}, ${videourl})`
res.json({})
} catch (error) {
logClientErr(res, HttpStatus.BAD_REQUEST, `${error as PostgresError}`)
}
}
We also add a postuser() function to return the name of a posted chatt. We will use this function
in the next section to construct filenames for uploaded files:
export function postuser(req: Request): string | null {
let chatt: Chatt
try {
chatt = req.body
return chatt.name
} catch (error) {
return null
}
}
Make a copy of your getchatts() function inside your handlers.ts and name the copy
getimages(). In getimages(), replace the SELECT statement with: SELECT name, message, id,
time, imageurl, videourl FROM chatts ORDER BY time ASC. This statement will retrieve all data,
including our new image and video URLs from the PostgreSQL database.
We’re done with handlers.ts. Save and exit the file.
main.ts
Edit the file main.ts:
server$ vi main.ts
To serve image/video static files, add the following lines of import at the top of main.ts:
import multer from 'multer'
import { basename } from 'path'
We also specify our designated media directory to store image/video files, and maximum allowed media file size. Add the following lines to your main.ts, before the try block, right after the type declaration of chatterDB:
const MEDIA_ROOT = '/home/ubuntu/reactive/chatterd/media'
const MEDIA_MXSZ = 10485760 // 10 MB, default is infinity
Next, we set up the multer middleware. Inside your try block, before the instantiation of app, add:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, MEDIA_ROOT)
},
filename: (req, file, cb) => {
const username = handlers.postuser(req)
let securename = username ? basename(username) : req.ip
const time = new Date(Date.now()).toISOString()
const filename = `${securename}-${time}.${basename(file.mimetype)}`
cb(null, filename)
}
})
const upload = multer({storage: storage, limits: {fileSize: MEDIA_MXSZ}})
We have set up multer to stream uploaded files directly to disk, bypassing memory. The
destination directory for uploaded files is in MEDIA_ROOT, which we defined earlier. Each
uploaded file will be given a file name of the form securename-uploadTime.mimeType, where
securename is chatt.name, with any path-looking parts stripped off, or the client’s IP address.
We further limit the upload file size to MEDIA_MXSZ, previously defined to be 10 MB.
Find the initialization of app and add the following new routes for the new APIs /getimages and
/postimages. Also add the .use() line to tell Express to redirect URL path /media to serve
media files from our designated media directory.
.get('/getimages/', handlers.getimages)
.post('/postimages/', upload.fields([
{name: 'image', maxCount: 1},
{name: 'video', maxCount: 1}
]), handlers.postimages)
.use('/media/', express.static(MEDIA_ROOT))
We’re done with main.ts. Save and exit the file.
Build and test run
To build your server, transpile TypeScript into JavaScript:
server$ npx tsgo
![]()
TypeScript is a compiled language, like C/C++ and unlike JavaScript and Python, which are an interpreted languages. This means you must run npx tsgo each and every time you made changes to your code, for the changes to show up when you run node.
To run your server:
server$ sudo node main.js
# Hit ^C to end the test
The cover back-end spec provides instructions for Testing image and video upload.
References
| Prepared by Sugih Jamin | Last updated February 22nd, 2026 |