Express layers
All checks were successful
Lint / lint (push) Successful in 15s

This commit is contained in:
Julien Valverdé
2024-09-05 00:25:18 +02:00
parent 07578a7ac7
commit 4f16a08b7f
5 changed files with 90 additions and 0 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -83,9 +83,11 @@
"@effect/schema": "^0.72.0", "@effect/schema": "^0.72.0",
"@prisma/studio-server": "^0.502.0", "@prisma/studio-server": "^0.502.0",
"@tanstack/form-core": "^0.30.0", "@tanstack/form-core": "^0.30.0",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",
"bun-types": "^1.1.26", "bun-types": "^1.1.26",
"effect": "^3.7.0", "effect": "^3.7.0",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"mobx": "^6.13.1", "mobx": "^6.13.1",
"npm-check-updates": "^17.1.1", "npm-check-updates": "^17.1.1",

View File

@@ -0,0 +1,19 @@
import { Config, Context, Effect, Layer } from "effect"
import type { Express } from "express"
export class ExpressApp extends Context.Tag("ExpressApp")<ExpressApp, Express>() {}
const importExpress = Effect.tryPromise({
try: () => import("express"),
catch: cause => new Error("Could not import 'express'. Make sure it is installed.", { cause }),
})
export const ExpressAppLive = (config: {
readonly trustProxy?: Config.Config<boolean>
}) => Layer.effect(ExpressApp, Effect.gen(function*() {
const app = (yield* importExpress).default()
app.set("trust proxy", yield* config.trustProxy || Config.succeed(false))
return app
}))

View File

@@ -0,0 +1,67 @@
import { Config, Context, Effect, Layer, Match } from "effect"
import type { Server } from "node:http"
import type { AddressInfo } from "node:net"
import { ExpressApp } from "./ExpressApp"
export class ExpressNodeHTTPServer extends Context.Tag("ExpressNodeHTTPServer")<ExpressNodeHTTPServer, Server>() {}
const importNodeHTTP = Effect.tryPromise({
try: () => import("node:http"),
catch: cause => new Error("Could not import 'node:http'. Make sure you are using a runtime that implements Node APIs.", { cause }),
})
const serverListeningMessage = Match.type<AddressInfo | string | null>().pipe(
Match.when(Match.null, () => "HTTP server listening"),
Match.when(Match.string, v => `HTTP server listening on ${ v }`),
Match.orElse(v => `HTTP server listening on ${ v.address }:${ v.port }`),
)
export const ExpressNodeHTTPServerLive = (config: {
readonly backlog?: Config.Config<number | undefined>
readonly exclusive?: Config.Config<boolean | undefined>
readonly host?: Config.Config<string | undefined>
readonly ipv6Only?: Config.Config<boolean | undefined>
readonly path?: Config.Config<string | undefined>
readonly port?: Config.Config<number | undefined>
readonly readableAll?: Config.Config<boolean | undefined>
readonly signal?: AbortSignal
readonly writableAll?: Config.Config<boolean | undefined>
}) => Layer.effect(ExpressNodeHTTPServer, Effect.acquireRelease(
Effect.gen(function*() {
const app = yield* ExpressApp
const http = yield* importNodeHTTP
const options = {
backlog: yield* config.backlog || Config.succeed(undefined),
exclusive: yield* config.exclusive || Config.succeed(undefined),
host: yield* config.host || Config.succeed(undefined),
ipv6Only: yield* config.ipv6Only || Config.succeed(undefined),
path: yield* config.path || Config.succeed(undefined),
port: yield* config.port || Config.succeed(undefined),
readableAll: yield* config.readableAll || Config.succeed(undefined),
signal: config.signal,
writableAll: yield* config.writableAll || Config.succeed(undefined),
} as const
return yield* Effect.async<Server>(resume => {
const server = http.createServer(app).listen(options,
() => resume(
Effect.succeed(server).pipe(
Effect.tap(Effect.logInfo(
serverListeningMessage(server.address())
))
)
)
)
})
}),
server => Effect.gen(function*() {
yield* Effect.logInfo("HTTP server is stopping. Waiting for existing connections to end...")
yield* Effect.async(resume => {
server.close(() => resume(Effect.logInfo("HTTP server closed")))
})
}),
))

View File

@@ -0,0 +1,2 @@
export * from "./ExpressApp"
export * from "./ExpressNodeHTTPServer"