Introduction#
GitHub this note shows
- Node.js and Typescript
- Node.js http server
- Prisma and PostgreSQL
Setup Project#
Let create a new TypeScript project
npm initnpm install --save-dev typescript tslint @types/node
Then create a tsconfig.json
{"compilerOptions": {"lib": ["ES2015"],"module": "CommonJS","outDir": "dist","sourceMap": true,"strict": true,"target": "ES2015"},"include": ["src"]}
Next install Node.js http server
npm i http-server
Then install ESJ template engine
npm i ejs
Project structure
|--node_modules|--prisma|--schema.presima|--src|--index.ts|--test.ts|--static|--book.html|--upload.html|--.env|--package.json|--tsconfig.json
HTTP Server#
Let create a simple http server to handler
- Static html file
- Response json
- Response with template ESJ
const handleStatic = (request: IncomingMessage, response: ServerResponse) => {response.setHeader("Content-Type", "text/html");response.writeHead(200);fs.readFile(path.join(__dirname, "./../static/book.html"), (error, data) => {response.end(data);});};const listener = (req: IncomingMessage, res: ServerResponse) => {if (req.url == "/book") {handleStatic(req, res);}};const server = createServer(listener);server.listen(3000, () => {console.log("listening...");});
Full detail example as below
http server example
import { IncomingMessage, ServerResponse, createServer } from 'http'import * as fs from 'fs'import * as path from 'path'import * as formidable from 'formidable'import * as ejs from 'ejs'import { PrismaClient } from '@prisma/client'const prisma = new PrismaClient();(BigInt.prototype as any).toJSON = function () {return this.toString()}const handleStatic = (request: IncomingMessage, response: ServerResponse) => {response.setHeader('Content-Type', 'text/html')response.writeHead(200)fs.readFile(path.join(__dirname, './../static/book.html'), (error, data) => {response.end(data)})}const handleUpload = async (req: IncomingMessage, res: ServerResponse) => {if (req.url == '/fileupload') {console.log('file uploading ...')const form = new formidable.IncomingForm({})let fieldslet filestry {;[fields, files] = await form.parse(req)console.log(files)files.myFile?.forEach(file => {console.log(file.originalFilename)fs.copyFile(file.filepath,path.join(__dirname, './../static/hehe.jpg'),error => {console.log(error)})// fs.rename(// file.filepath,// path.join(__dirname, "./../static/hehe.jpg"),// (error) => {// console.log(error);// }// );})} catch (error) {}res.writeHead(200, { 'Content-Type': 'application/json' })res.end(JSON.stringify({ fields, files }, null, 2))} else {fs.readFile(path.join(__dirname, './../static/upload.html'),(error, data) => {console.log('read upload form ...', data)res.writeHead(200, { 'Content-Type': 'text/html' })res.write(data)res.end()})}}const handleDownloadBooks = async (req: IncomingMessage,res: ServerResponse) => {try {const books = await prisma.book.findMany()console.log(books)// res.writeHead(200, { "Content-Type": "application/json" });// res.end(JSON.stringify({ books }, null, 2));// const content = ejs.render("<%= username %>", { username: "minh hai" });const content = ejs.render(`<% for (var i = 0; i < books.length; i++) { %><div class='card'><h4 class='title'><%= books[i].author %></h4><h4 class='title'><%= books[i].title%></h4><img src='https://d2cvlmmg8c0xrp.cloudfront.net/web-css/data_engineering_with_aws.jpg' class='image'/><p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Officia officiis voluptates ab eum totam atque deleniti accusantium nulla illo provident et nesciunt, nisi laudantium iusto animi rem repudiandae, asperiores consequuntur Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloremque ipsam deserunt quaerat corrupti nihil error amet libero. Dignissimos, dolorem laudantium optio id, blanditiis eveniet repellendus pariatur neque facilis reprehenderit excepturi! Lorem ipsum dolor sit amet consectetur, adipisicing elit. Non repellendus, praesentium quasi quidem itaque numquam qui ex ducimus harum, perferendis officia deserunt libero magni assumenda mollitia aut ratione ipsam illo! Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime, corporis suscipit, natus odio nobis vel totam atque vitae porro animi in, cupiditate mollitia pariatur minus quos! Maiores assumenda explicabo expedita?</p></div><% } %>`,{ books: books })res.writeHead(200, { 'Content-Type': 'text/html' })res.write(`<html><head><style>:root {box-sizing: border-box;}*,::before,::after {box-sizing: inherit;}.body {background-color: antiquewhite;}.container {max-width: 800px;margin-left: auto;margin-right: auto;}.grid {display: grid;row-gap: 10px;column-gap: 10px;grid-template-columns: repeat(1, minmax(0, 1fr));}.card {margin-left: 4px;margin-right: 4px;padding: 0.5em;background-color: white;width: 100%;}@media (min-width: 800px) {.grid {grid-template-columns: repeat(2, minmax(0, 1fr));}}.image {float: left;height: auto;width: 128px;margin-right: 6px;}.title {font: bold;margin-bottom: 8px;}</style></head><body class='body'><div class='container'><div class='grid'>${content}</div></div></body></html>`)res.end()} catch (error) {console.log(error)res.writeHead(200, { 'Content-Type': 'application/json' })res.end('ERROR')}}const listener = (req: IncomingMessage, res: ServerResponse) => {if (req.url == '/book') {handleStatic(req, res)}if (req.url == '/' || req.url == '/fileupload') {handleUpload(req, res)}if (req.url == '/prisma') {handleDownloadBooks(req, res)}}const server = createServer(listener)server.listen(3000, () => {console.log('listening...')})
Prisma and PostgreSQL#
- Setup prisma
- Get books
- Response using ESJ template
Let setup prisma for our typescript project
npm install prisma --save-dev
Then invoke Prisma CLI by prefixing it with npx
npx prisma
Let init Prisma project which will create a director called prisma with schema.prisma and an .env
npx prisma init
Let update the .env with database connection, for example, connect to a postgresql running on my local machine
DATABASE_URL="postgresql://demo:Mike@865525@localhost:5432/dvdrental?schema=public"
Assume that there are already some tables in the dvdrental database, now query it using prisma. Let pull database
npx prisma db pull
Then the schema.prisma is updated as the following
generator client {provider = "prisma-client-js"}datasource db {provider = "postgresql"url = env("DATABASE_URL")}model actor {actor_id Int @id @default(autoincrement())first_name String @db.VarChar(45)last_name String @db.VarChar(45)last_update DateTime @default(now()) @db.Timestamp(6)@@index([last_name], map: "idx_actor_last_name")}/// We could not retrieve columns for the underlying table. Either it has none or you are missing rights to see them. Please check your privileges.// model address {// }model book {id BigInt @id @default(autoincrement())title String?author String?amazon String?image String?description String?}
Now we need to install Prisma client
npm install @prisma/client
And generate client code
npx prisma generate
Finally we can write code to get books from the book table
import { PrismaClient } from '@prisma/client'const prisma = new PrismaClient();(BigInt.prototype as any).toJSON = function () {return this.toString()}const getBooks = async () => {try {const books = await prisma.book.findMany()console.log(books)console.log(JSON.stringify({ books }), null, 2)} catch (error) {console.log(error)}}getBooks()
Here is the full handler for getbooks from database and response to users.
handleDownloadBooks
const handleDownloadBooks = async (req: IncomingMessage,res: ServerResponse) => {try {const books = await prisma.book.findMany()console.log(books)// res.writeHead(200, { "Content-Type": "application/json" });// res.end(JSON.stringify({ books }, null, 2));// const content = ejs.render("<%= username %>", { username: "minh hai" });const content = ejs.render(`<% for (var i = 0; i < books.length; i++) { %><div class='card'><h4 class='title'><%= books[i].author %></h4><h4 class='title'><%= books[i].title%></h4><img src='https://d2cvlmmg8c0xrp.cloudfront.net/web-css/data_engineering_with_aws.jpg' class='image'/><p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Officia officiis voluptates ab eum totam atque deleniti accusantium nulla illo provident et nesciunt, nisi laudantium iusto animi rem repudiandae, asperiores consequuntur Lorem ipsum dolor sit amet, consectetur adipisicing elit. Doloremque ipsam deserunt quaerat corrupti nihil error amet libero. Dignissimos, dolorem laudantium optio id, blanditiis eveniet repellendus pariatur neque facilis reprehenderit excepturi! Lorem ipsum dolor sit amet consectetur, adipisicing elit. Non repellendus, praesentium quasi quidem itaque numquam qui ex ducimus harum, perferendis officia deserunt libero magni assumenda mollitia aut ratione ipsam illo! Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime, corporis suscipit, natus odio nobis vel totam atque vitae porro animi in, cupiditate mollitia pariatur minus quos! Maiores assumenda explicabo expedita?</p></div><% } %>`,{ books: books })res.writeHead(200, { 'Content-Type': 'text/html' })res.write(`<html><head><style>:root {box-sizing: border-box;}*,::before,::after {box-sizing: inherit;}.body {background-color: antiquewhite;}.container {max-width: 800px;margin-left: auto;margin-right: auto;}.grid {display: grid;row-gap: 10px;column-gap: 10px;grid-template-columns: repeat(1, minmax(0, 1fr));}.card {margin-left: 4px;margin-right: 4px;padding: 0.5em;background-color: white;width: 100%;}@media (min-width: 800px) {.grid {grid-template-columns: repeat(2, minmax(0, 1fr));}}.image {float: left;height: auto;width: 128px;margin-right: 6px;}.title {font: bold;margin-bottom: 8px;}</style></head><body class='body'><div class='container'><div class='grid'>${content}</div></div></body></html>`)res.end()} catch (error) {console.log(error)res.writeHead(200, { 'Content-Type': 'application/json' })res.end('ERROR')}}