Scratch space for learning atproto app development
1import { Request, Response } from 'express'
2import { createHttpTerminator } from 'http-terminator'
3import { once } from 'node:events'
4import type {
5 IncomingMessage,
6 RequestListener,
7 ServerResponse,
8} from 'node:http'
9import { createServer } from 'node:http'
10
11export type NextFunction = (err?: unknown) => void
12
13export type Middleware<
14 Req extends IncomingMessage = IncomingMessage,
15 Res extends ServerResponse = ServerResponse,
16> = (req: Req, res: Res, next: NextFunction) => void
17
18export type Handler<
19 Req extends IncomingMessage = IncomingMessage,
20 Res extends ServerResponse = ServerResponse,
21> = (req: Req, res: Res) => unknown | Promise<unknown>
22/**
23 * Wraps a request handler middleware to ensure that `next` is called if it
24 * throws or returns a promise that rejects.
25 */
26export function handler<
27 Req extends IncomingMessage = Request,
28 Res extends ServerResponse = Response,
29>(fn: Handler<Req, Res>): Middleware<Req, Res> {
30 return async (req, res, next) => {
31 try {
32 await fn(req, res)
33 } catch (err) {
34 next(err)
35 }
36 }
37}
38
39/**
40 * Create an HTTP server with the provided request listener, ensuring that it
41 * can bind the listening port, and returns a termination function that allows
42 * graceful termination of HTTP connections.
43 */
44export async function startServer(
45 requestListener: RequestListener,
46 {
47 port,
48 gracefulTerminationTimeout,
49 }: { port?: number; gracefulTerminationTimeout?: number } = {},
50) {
51 const server = createServer(requestListener)
52 const { terminate } = createHttpTerminator({
53 gracefulTerminationTimeout,
54 server,
55 })
56 server.listen(port)
57 await once(server, 'listening')
58 return { server, terminate }
59}