Skip to content

Instantly share code, notes, and snippets.

@GuilhermeRossato
Created May 23, 2023 09:16
Show Gist options
  • Save GuilhermeRossato/2b9418844a514d43ed3bb1d6593381be to your computer and use it in GitHub Desktop.
Save GuilhermeRossato/2b9418844a514d43ed3bb1d6593381be to your computer and use it in GitHub Desktop.
Simple nodejs project with typescript in the frontend and backend with good stack traces and as little dependencies as necessary

Minimal Typescript Web App Project

This is a minimal project with a frontend and a backend in typescript that has automatic rebuilding of typescript files and preserves the call stack when an error is thrown.

To start your app just run npm run start and it will serve the project at port 8080.

The compilation of the backend and the frontend happens automatically when a file changes with the use of tsc in watch mode.

The backend server process is restarted automatically with the use of nodemon.

The frontend must be refreshed manually (by pressing F5) on the browser for it to reload the changed javascript file.

import http from 'node:http';
import fs from 'node:fs';
async function onServerRequest(req: http.IncomingMessage, res: http.ServerResponse) {
// Serve the html page
if (req.url === '/') {
return res.end(await fs.promises.readFile('./index.html'));
}
// Serve the compiled frontend files
if (['/frontend.js', '/frontend.js.map'].includes(req.url as string)) {
const extension = req.url?.substring(req.url?.lastIndexOf('.')) || '.txt';
const mimeTypes = { '.js': 'application/javascript', '.json': 'application/json', '.html': 'text/html', '.css': 'text/css' };
res.writeHead(200, { 'content-type': mimeTypes[extension] || 'text/plain' });
return res.end(await fs.promises.readFile(`./dist/${req.url}`));
}
// Serve the frontend source file (for easier debugging)
if (['/frontend.ts'].includes(req.url as string)) {
res.writeHead(200, { 'content-type': 'application/typescript' });
return res.end(await fs.promises.readFile('./' + req.url as string));
}
// Serve empty favicon to stop warning on console
if (req.url === '/favicon.ico') {
return res.writeHead(200).end();
}
// Fallback for all other routes
res.writeHead(404).end(`Nothing to see at url ${req.url}`);
}
const server = http.createServer(
(req, res) => onServerRequest(req, res).catch(err => res.writeHead(500).end(err.stack))
);
server.on('error', console.error);
server.listen(8080, () => {
console.log('Server listening at http://localhost:8080/');
});
document.body.appendChild(document.createTextNode(', everything is alright!'));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Project</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimal-ui">
<meta name="description" content="Project">
<meta name="msapplication-TileColor" content="#673ab8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Project">
<meta name="application-name" content="Project">
<meta name="theme-color" content="#673ab8">
<script src="/frontend.js" async type="module"></script>
</head>
<body style="background-color: #222; color: #ddd;">
Hello world
</body>
</html>
{
"name": "vis",
"version": "1.0.0",
"type": "module",
"description": "",
"scripts": {
"start": "concurrently npm:frontend npm:backend-compilation npm:backend",
"frontend": "tsc --preserveWatchOutput --watch frontend.ts --target es2020 --sourceMap --pretty --outDir dist",
"backend-compilation": "tsc --preserveWatchOutput --watch backend.ts --target es2020 --sourceMap --pretty --outDir dist",
"backend": "nodemon --exec \"node --enable-source-maps ./dist/backend.js\""
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"concurrently": "^8.0.1",
"nodemon": "^2.0.22",
"typescript": "^5.0.4"
},
"devDependencies": {
"@types/node": "^20.2.3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment