Skip to content

Instantly share code, notes, and snippets.

@rsaryev
Created April 27, 2023 20:00
Show Gist options
  • Save rsaryev/fe134b2f1ad17291237c32417e170c2d to your computer and use it in GitHub Desktop.
Save rsaryev/fe134b2f1ad17291237c32417e170c2d to your computer and use it in GitHub Desktop.
This example is a simple implementation of a wrapper the Node.js http server.
const http = require('http')
const {parse} = require('url')
/**
* This example is a simple implementation of a wrapper the Node.js http server.
*/
class Router {
#routes
constructor() {
this.#routes = new Map()
}
get(path, handler) {
const key = this.#key(path, 'GET')
this.#routes.set(key, handler)
return this
}
post(path, handler) {
const key = this.#key(path, 'POST')
this.#routes.set(key, handler)
return this
}
find(path, method) {
const key = this.#key(path, method)
return this.#routes.get(key)
}
#key(path, method) {
return `${path}:${method}`
}
}
class Middleware {
#handler;
#next;
constructor(handler) {
this.#next = null;
this.#handler = handler;
}
setNext(handler) {
this.#next = handler;
return this;
}
async handle(context) {
await this.#handler();
if (this.#next) {
return this.#next.handle(context);
}
return null;
}
}
class Server {
#router;
#headMiddleware;
#middleware;
constructor() {
this.#router = new Router()
this.#headMiddleware = null
this.#middleware = null
}
get router() {
return this.#router
}
use(handler) {
if (this.#middleware === null) {
this.#headMiddleware = new Middleware(handler)
this.#middleware = this.#headMiddleware
return this
}
const middleware = new Middleware(handler)
this.#middleware.setNext(middleware)
this.#middleware = middleware
return this
}
createContext(req, res) {
return {
req,
res,
}
}
async handleRequest(req, res) {
const ctx = this.createContext(req, res)
try {
const path = parse(req.url).pathname
const router = this.#router.find(path, req.method)
if (!router) {
ctx.status = 404
ctx.body = 'Not Found'
} else {
await this.#headMiddleware.handle(ctx)
await router(ctx)
}
res.statusCode = ctx.status || 200
if (typeof ctx.body === "object") {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(ctx.body))
} else {
res.setHeader('Content-Type', 'text/plain')
res.end(ctx.body)
}
} catch (err) {
console.error(err)
res.statusCode = 500
res.end('Internal Server Error')
}
}
async listen(port) {
return new Promise((resolve, reject) => {
const server = http.createServer(this.handleRequest.bind(this))
server.on('error', (err) => {
reject(err)
})
server.listen(port, () => {
console.log(`Server listening on ${port}`)
resolve()
})
});
}
}
const app = new Server()
app.router
.get('/hello', (ctx) => {
ctx.body = {message: 'GET: Hello World'}
})
.post('/hello', (ctx) => {
ctx.body = {message: 'POST: Hello World'}
})
app
.use(async (ctx) => {
console.log('Middleware 1')
})
.use(async (ctx) => {
console.log('Middleware 2')
})
.use(async (ctx) => {
console.log('Middleware 3')
})
app.listen(3000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment