Cover Page
Backend Page
TypeScript with Express
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.toString())
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 (username, message, id, imageurl, videourl) VALUES (${chatt.username}, ${chatt.message}, ${randomUUID()}, ${imageurl}, ${videourl})`
res.json({})
} catch (error) {
logClientErr(res, HttpStatus.BAD_REQUEST, `${error as PostgresError}`)
}
}
We also add postuser() function to return the username of a posted chatt. We will use this 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.username
} catch (error) {
return null
}
}
Finally, make a copy of your getchatts() in handlers.ts and name the copy getimages(). In getimages(), replace the declaration of chatts with:
const chatts = await chatterDB`SELECT username, message, id, time, imageurl, videourl FROM chatts`.values()
This 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: string
if (username) {
securename = basename(username)
} else {
securename = 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 store uploaded files to disk. 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.username, with any path-looking part strips off, or the client’s IP address. We further limit the upload file size to MEDIA_MXSZ, which we have 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 tsc
![]()
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 run your server:
server$ sudo node main.js
# Hit ^C to end the test
The cover backend spec provides instructions for Testing image and video upload.
References
- Multer Usage <– Handling File Uploads with Multer in a Class-based Node.js and Express Application –>
| Prepared by Sugih Jamin | Last updated August 31st, 2025 |