From 3907af96aec5e000358f1c68e9948a5c7ba2bc90 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Mon, 20 Nov 2023 23:04:29 +0100 Subject: [PATCH 01/10] Async createServer(): Restore compression and upload config as bool or options. --- coverage.svg | 2 +- example/config.ts | 6 ++--- src/config-type.ts | 23 ++++++++++++++++-- src/errors.ts | 8 +++++++ src/server.ts | 38 +++++++++++++++++++++++++----- tests/express-mock.ts | 14 ++++++++++- tests/system/system.spec.ts | 37 +++++++++++++++-------------- tests/unit/server.spec.ts | 47 ++++++++++++++++++++++--------------- 8 files changed, 124 insertions(+), 51 deletions(-) diff --git a/coverage.svg b/coverage.svg index 5bb55be2e..840c3364b 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1 @@ -Coverage: 100%Coverage100% \ No newline at end of file +Coverage: 99.71%Coverage99.71% \ No newline at end of file diff --git a/example/config.ts b/example/config.ts index 274561f94..d4b3e6c18 100644 --- a/example/config.ts +++ b/example/config.ts @@ -1,13 +1,11 @@ import express from "express"; -import compression from "compression"; -import fileUpload from "express-fileupload"; import { createConfig } from "../src"; export const config = createConfig({ server: { listen: 8090, - uploader: fileUpload({ abortOnLimit: false, parseNested: true }), - compressor: compression(), // affects sendAvatarEndpoint + upload: true, + compression: true, // affects sendAvatarEndpoint rawParser: express.raw(), // required for rawAcceptingEndpoint }, cors: true, diff --git a/src/config-type.ts b/src/config-type.ts index 05d8d1ddb..0af7f5626 100644 --- a/src/config-type.ts +++ b/src/config-type.ts @@ -1,4 +1,6 @@ +import type compression from "compression"; import { Express, Request, RequestHandler } from "express"; +import type fileUpload from "express-fileupload"; import { ServerOptions } from "node:https"; import { Logger } from "winston"; import { AbstractEndpoint } from "./endpoint"; @@ -65,6 +67,23 @@ export interface CommonConfig { tags?: TagsConfig; } +type UploadOptions = Pick< + fileUpload.Options, + | "createParentPath" + | "uriDecodeFileNames" + | "safeFileNames" + | "preserveExtension" + | "useTempFiles" + | "tempFileDir" + | "debug" + | "uploadTimeout" +>; + +type CompressionOptions = Pick< + compression.CompressionOptions, + "threshold" | "level" | "strategy" | "chunkSize" | "memLevel" +>; + export interface ServerConfig extends CommonConfig { /** @desc Server configuration. */ @@ -83,14 +102,14 @@ export interface ServerConfig * @example import fileUpload from "express-fileupload" * @example uploader: fileUpload({ abortOnLimit: false, parseNested: true }) * */ - uploader?: RequestHandler; + upload?: boolean | UploadOptions; /** * @desc Enable and configure compression * @default undefined * @example import compression from "compression" * @example compressor: compression() */ - compressor?: RequestHandler; + compression?: boolean | CompressionOptions; /** * @desc Enables parsing certain request payloads into raw Buffers (application/octet-stream by default) * @desc When enabled, use ez.raw() as input schema to get input.raw in Endpoint's handler diff --git a/src/errors.ts b/src/errors.ts index 9c50972c1..a2e5e4d52 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -66,3 +66,11 @@ export class ResultHandlerError extends Error { this.originalError = originalError || undefined; } } + +export class MissingPeerError extends Error { + constructor(module: string) { + super( + `Missing peer dependency: '${module}'. Please install it to use the feature activated in config.`, + ); + } +} diff --git a/src/server.ts b/src/server.ts index 2dc4dc182..7576aafa5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,7 +3,7 @@ import http from "node:http"; import https from "node:https"; import { Logger } from "winston"; import { AppConfig, CommonConfig, ServerConfig } from "./config-type"; -import { ResultHandlerError } from "./errors"; +import { MissingPeerError, ResultHandlerError } from "./errors"; import { makeErrorFromAnything } from "./common-helpers"; import { createLogger } from "./logger"; import { @@ -74,14 +74,40 @@ export const attachRouting = (config: AppConfig, routing: Routing) => { return { notFoundHandler, logger }; }; -export const createServer = (config: ServerConfig, routing: Routing) => { +export const createServer = async (config: ServerConfig, routing: Routing) => { const app = express().disable("x-powered-by"); - if (config.server.compressor) { - app.use(config.server.compressor); + if (config.server.compression) { + let compression; + try { + compression = (await import("compression")).default; + } catch { + throw new MissingPeerError("compression"); + } + app.use( + compression( + typeof config.server.compression === "object" + ? config.server.compression + : undefined, + ), + ); } app.use(config.server.jsonParser || express.json()); - if (config.server.uploader) { - app.use(config.server.uploader); + if (config.server.upload) { + let fileUpload; + try { + fileUpload = (await import("express-fileupload")).default; + } catch { + throw new MissingPeerError("express-fileupload"); + } + app.use( + fileUpload({ + ...(typeof config.server.upload === "object" + ? config.server.upload + : {}), + abortOnLimit: false, + parseNested: true, + }), + ); } if (config.server.rawParser) { app.use(config.server.rawParser); diff --git a/tests/express-mock.ts b/tests/express-mock.ts index c7b795dce..caf3e98bf 100644 --- a/tests/express-mock.ts +++ b/tests/express-mock.ts @@ -1,6 +1,10 @@ // @see https://github.com/swc-project/jest/issues/14#issuecomment-970189585 const expressJsonMock = jest.fn(); +const compressionMock = jest.fn(); +const fileUploadMock = jest.fn(); +jest.mock("compression", () => compressionMock); +jest.mock("express-fileupload", () => fileUploadMock); const staticHandler = jest.fn(); const staticMock = jest.fn(() => staticHandler); @@ -22,4 +26,12 @@ appCreatorMock.static = staticMock; const expressMock = jest.mock("express", () => appCreatorMock); -export { expressMock, appMock, expressJsonMock, staticMock, staticHandler }; +export { + compressionMock, + fileUploadMock, + expressMock, + appMock, + expressJsonMock, + staticMock, + staticHandler, +}; diff --git a/tests/system/system.spec.ts b/tests/system/system.spec.ts index fdc073b8a..5bde2960d 100644 --- a/tests/system/system.spec.ts +++ b/tests/system/system.spec.ts @@ -1,4 +1,3 @@ -import compression from "compression"; import cors from "cors"; import type { Server } from "node:http"; import { z } from "zod"; @@ -16,7 +15,7 @@ describe("App", () => { const port = givePort(); let server: Server; - beforeAll(() => { + beforeAll(async () => { const routing = { v1: { corsed: new EndpointsFactory(defaultResultHandler) @@ -136,23 +135,25 @@ describe("App", () => { }, }; jest.spyOn(global.console, "log").mockImplementation(jest.fn()); - server = createServer( - { - server: { - listen: port, - compressor: compression({ threshold: 1 }), - }, - cors: false, - startupLogo: true, - logger: { - level: "silent", - color: false, - }, - inputSources: { - post: ["query", "body", "files"], + server = ( + await createServer( + { + server: { + listen: port, + compression: { threshold: 1 }, + }, + cors: false, + startupLogo: true, + logger: { + level: "silent", + color: false, + }, + inputSources: { + post: ["query", "body", "files"], + }, }, - }, - routing, + routing, + ) ).httpServer; }); diff --git a/tests/unit/server.spec.ts b/tests/unit/server.spec.ts index 7ca077c79..00e98a2a1 100644 --- a/tests/unit/server.spec.ts +++ b/tests/unit/server.spec.ts @@ -1,7 +1,13 @@ import { omit } from "ramda"; import { makeRequestMock } from "../../src/testing"; import { givePort } from "../helpers"; -import { appMock, expressJsonMock, expressMock } from "../express-mock"; +import { + appMock, + compressionMock, + expressJsonMock, + expressMock, + fileUploadMock, +} from "../express-mock"; import { createHttpsServerSpy, httpListenSpy, @@ -33,7 +39,7 @@ describe("Server", () => { }); describe("createServer()", () => { - test("Should create server with minimal config", () => { + test("Should create server with minimal config", async () => { const port = givePort(); const configMock: ServerConfig = { server: { @@ -60,7 +66,7 @@ describe("Server", () => { }), }, }; - createServer(configMock, routingMock); + await createServer(configMock, routingMock); expect(appMock).toBeTruthy(); expect(appMock.disable).toHaveBeenCalledWith("x-powered-by"); expect(appMock.use).toHaveBeenCalledTimes(3); @@ -75,7 +81,7 @@ describe("Server", () => { expect(httpListenSpy).toHaveBeenCalledWith(port, expect.any(Function)); }); - test("Should create server with custom JSON parser, logger and error handler", () => { + test("Should create server with custom JSON parser, logger and error handler", async () => { const customLogger = winston.createLogger({ silent: true }); const infoMethod = jest.spyOn(customLogger, "info"); const port = givePort(); @@ -105,7 +111,7 @@ describe("Server", () => { }), }, }; - const { logger, app } = createServer( + const { logger, app } = await createServer( configMock as unknown as ServerConfig, routingMock, ); @@ -130,7 +136,7 @@ describe("Server", () => { ); }); - test("should create a HTTPS server on request", () => { + test("should create a HTTPS server on request", async () => { const configMock = { server: { listen: givePort() }, https: { @@ -155,7 +161,7 @@ describe("Server", () => { }, }; - const { httpsServer } = createServer(configMock, routingMock); + const { httpsServer } = await createServer(configMock, routingMock); expect(httpsServer).toBeTruthy(); expect(createHttpsServerSpy).toHaveBeenCalledWith( configMock.https.options, @@ -168,12 +174,11 @@ describe("Server", () => { ); }); - test("should enable compression on request", () => { - const compressionMock = jest.fn(); + test("should enable compression on request", async () => { const configMock = { server: { listen: givePort(), - compressor: compressionMock, + compression: true, }, cors: true, startupLogo: false, @@ -189,17 +194,17 @@ describe("Server", () => { }), }, }; - createServer(configMock, routingMock); + await createServer(configMock, routingMock); expect(appMock.use).toHaveBeenCalledTimes(4); - expect(appMock.use).toHaveBeenCalledWith(compressionMock); + expect(compressionMock).toHaveBeenCalledTimes(1); + expect(compressionMock).toHaveBeenCalledWith(undefined); }); - test("should enable uploads on request", () => { - const fileUploadMock = jest.fn(); + test("should enable uploads on request", async () => { const configMock = { server: { listen: givePort(), - uploader: fileUploadMock, + upload: true, }, cors: true, startupLogo: false, @@ -215,12 +220,16 @@ describe("Server", () => { }), }, }; - createServer(configMock, routingMock); + await createServer(configMock, routingMock); expect(appMock.use).toHaveBeenCalledTimes(4); - expect(appMock.use).toHaveBeenCalledWith(fileUploadMock); + expect(fileUploadMock).toHaveBeenCalledTimes(1); + expect(fileUploadMock).toHaveBeenCalledWith({ + abortOnLimit: false, + parseNested: true, + }); }); - test("should enable raw on request", () => { + test("should enable raw on request", async () => { const rawParserMock = jest.fn(); const configMock = { server: { @@ -241,7 +250,7 @@ describe("Server", () => { }), }, }; - createServer(configMock, routingMock); + await createServer(configMock, routingMock); expect(appMock.use).toHaveBeenCalledTimes(5); const rawPropMw = appMock.use.mock.calls[2][0]; // custom middleware for raw expect(typeof rawPropMw).toBe("function"); From 863ba335ca7e88f92258158e7c882a4c32259a36 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Mon, 20 Nov 2023 23:17:14 +0100 Subject: [PATCH 02/10] Add MissingPeerError test. --- coverage.svg | 2 +- src/errors.ts | 1 + src/index.ts | 1 + tests/unit/__snapshots__/index.spec.ts.snap | 3 +++ tests/unit/errors.spec.ts | 17 +++++++++++++++++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/coverage.svg b/coverage.svg index 840c3364b..4eff669d7 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1 @@ -Coverage: 99.71%Coverage99.71% \ No newline at end of file +Coverage: 99.85%Coverage99.85% \ No newline at end of file diff --git a/src/errors.ts b/src/errors.ts index a2e5e4d52..88429cdcd 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -68,6 +68,7 @@ export class ResultHandlerError extends Error { } export class MissingPeerError extends Error { + public override name = "MissingPeerError"; constructor(module: string) { super( `Missing peer dependency: '${module}'. Please install it to use the feature activated in config.`, diff --git a/src/index.ts b/src/index.ts index a58a73844..5ce7adf40 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,7 @@ export { RoutingError, OutputValidationError, InputValidationError, + MissingPeerError, } from "./errors"; export { withMeta } from "./metadata"; export { testEndpoint } from "./testing"; diff --git a/tests/unit/__snapshots__/index.spec.ts.snap b/tests/unit/__snapshots__/index.spec.ts.snap index 32bee2c63..817216ecc 100644 --- a/tests/unit/__snapshots__/index.spec.ts.snap +++ b/tests/unit/__snapshots__/index.spec.ts.snap @@ -14,6 +14,8 @@ exports[`Index Entrypoint exports InputValidationError should have certain value exports[`Index Entrypoint exports Integration should have certain value 1`] = `[Function]`; +exports[`Index Entrypoint exports MissingPeerError should have certain value 1`] = `[Function]`; + exports[`Index Entrypoint exports OutputValidationError should have certain value 1`] = `[Function]`; exports[`Index Entrypoint exports RoutingError should have certain value 1`] = `[Function]`; @@ -101,6 +103,7 @@ exports[`Index Entrypoint exports should have certain entities exposed 1`] = ` "Integration", "LoggerConfig", "Method", + "MissingPeerError", "OutputValidationError", "Routing", "RoutingError", diff --git a/tests/unit/errors.spec.ts b/tests/unit/errors.spec.ts index f7c2dde31..bc70aae86 100644 --- a/tests/unit/errors.spec.ts +++ b/tests/unit/errors.spec.ts @@ -3,6 +3,7 @@ import { DocumentationError, RoutingError } from "../../src"; import { IOSchemaError, InputValidationError, + MissingPeerError, OutputValidationError, ResultHandlerError, } from "../../src/errors"; @@ -107,4 +108,20 @@ describe("Errors", () => { expect(error2.originalError).toBeUndefined(); }); }); + + describe("MissingPeerError", () => { + test("should be an instance of Error", () => { + expect(new MissingPeerError("compression")).toBeInstanceOf(Error); + }); + + test("should have the name matching its class", () => { + expect(new MissingPeerError("compression").name).toBe("MissingPeerError"); + }); + + test("should have a human readable message", () => { + expect(new MissingPeerError("compression").message).toBe( + "Missing peer dependency: 'compression'. Please install it to use the feature activated in config.", + ); + }); + }); }); From f2bcb9c48b493029d3b3c27282f0673a7e0ef8ce Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Mon, 20 Nov 2023 23:27:05 +0100 Subject: [PATCH 03/10] Ref: DNRY: extracting loadPeer(). --- coverage.svg | 2 +- src/common-helpers.ts | 17 ++++++++++++++++- src/server.ts | 24 ++++++++---------------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/coverage.svg b/coverage.svg index 4eff669d7..abf14142a 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1 @@ -Coverage: 99.85%Coverage99.85% \ No newline at end of file +Coverage: 99.92%Coverage99.92% \ No newline at end of file diff --git a/src/common-helpers.ts b/src/common-helpers.ts index 3f6e967bd..2f98b57dd 100644 --- a/src/common-helpers.ts +++ b/src/common-helpers.ts @@ -4,7 +4,11 @@ import { createHash } from "node:crypto"; import { Logger } from "winston"; import { z } from "zod"; import { CommonConfig, InputSource, InputSources } from "./config-type"; -import { InputValidationError, OutputValidationError } from "./errors"; +import { + InputValidationError, + MissingPeerError, + OutputValidationError, +} from "./errors"; import { ZodFile } from "./file-schema"; import { IOSchema } from "./io-schema"; import { getMeta } from "./metadata"; @@ -319,3 +323,14 @@ export type ErrMessage = Exclude< // the copy of the private Zod errorUtil.errToObj export const errToObj = (message: ErrMessage | undefined) => typeof message === "string" ? { message } : message || {}; + +export const loadPeer = async ( + moduleName: string, + moduleExport: string = "default", +): Promise => { + try { + return (await import(moduleName))[moduleExport]; + } catch { + throw new MissingPeerError(moduleName); + } +}; diff --git a/src/server.ts b/src/server.ts index 7576aafa5..872b4f0a7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,10 +1,12 @@ import express, { ErrorRequestHandler, RequestHandler } from "express"; +import type compression from "compression"; +import type fileUpload from "express-fileupload"; import http from "node:http"; import https from "node:https"; import { Logger } from "winston"; import { AppConfig, CommonConfig, ServerConfig } from "./config-type"; -import { MissingPeerError, ResultHandlerError } from "./errors"; -import { makeErrorFromAnything } from "./common-helpers"; +import { ResultHandlerError } from "./errors"; +import { loadPeer, makeErrorFromAnything } from "./common-helpers"; import { createLogger } from "./logger"; import { AnyResultHandlerDefinition, @@ -77,14 +79,9 @@ export const attachRouting = (config: AppConfig, routing: Routing) => { export const createServer = async (config: ServerConfig, routing: Routing) => { const app = express().disable("x-powered-by"); if (config.server.compression) { - let compression; - try { - compression = (await import("compression")).default; - } catch { - throw new MissingPeerError("compression"); - } + const compressor = await loadPeer("compression"); app.use( - compression( + compressor( typeof config.server.compression === "object" ? config.server.compression : undefined, @@ -93,14 +90,9 @@ export const createServer = async (config: ServerConfig, routing: Routing) => { } app.use(config.server.jsonParser || express.json()); if (config.server.upload) { - let fileUpload; - try { - fileUpload = (await import("express-fileupload")).default; - } catch { - throw new MissingPeerError("express-fileupload"); - } + const uploader = await loadPeer("express-fileupload"); app.use( - fileUpload({ + uploader({ ...(typeof config.server.upload === "object" ? config.server.upload : {}), From a399aed36ee761fcefbf26fba56575a8eecc529c Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Mon, 20 Nov 2023 23:36:39 +0100 Subject: [PATCH 04/10] Testing loadPeer(). --- coverage.svg | 2 +- tests/unit/common-helpers.spec.ts | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/coverage.svg b/coverage.svg index abf14142a..5bb55be2e 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1 @@ -Coverage: 99.92%Coverage99.92% \ No newline at end of file +Coverage: 100%Coverage100% \ No newline at end of file diff --git a/tests/unit/common-helpers.spec.ts b/tests/unit/common-helpers.spec.ts index 40d96056c..aa13c74ab 100644 --- a/tests/unit/common-helpers.spec.ts +++ b/tests/unit/common-helpers.spec.ts @@ -14,9 +14,15 @@ import { hasTopLevelTransformingEffect, isCustomHeader, isValidDate, + loadPeer, makeErrorFromAnything, } from "../../src/common-helpers"; -import { InputValidationError, ez, withMeta } from "../../src"; +import { + InputValidationError, + MissingPeerError, + ez, + withMeta, +} from "../../src"; import { Request } from "express"; import { z } from "zod"; import { ZodUpload } from "../../src/upload-schema"; @@ -493,7 +499,7 @@ describe("Common Helpers", () => { }); }); - describe("hasCoercion", () => { + describe("hasCoercion()", () => { test.each([ { schema: z.string(), coercion: false }, { schema: z.coerce.string(), coercion: true }, @@ -506,4 +512,15 @@ describe("Common Helpers", () => { }, ); }); + + describe("loadPeer()", () => { + test("should load the module", async () => { + expect(await loadPeer("compression")).toBeTruthy(); + }); + test("should throw when module not found", async () => { + await expect(async () => loadPeer("missing")).rejects.toThrow( + new MissingPeerError("missing"), + ); + }); + }); }); From 8146f49eff0d8e60fad72bcb3054ec8045902bf2 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Mon, 20 Nov 2023 23:54:24 +0100 Subject: [PATCH 05/10] Restore jsdoc for corresponding config options. --- src/config-type.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/config-type.ts b/src/config-type.ts index 0af7f5626..306b9c3e7 100644 --- a/src/config-type.ts +++ b/src/config-type.ts @@ -97,17 +97,15 @@ export interface ServerConfig * */ jsonParser?: RequestHandler; /** - * @desc Enable and configure file uploads - * @default undefined - * @example import fileUpload from "express-fileupload" - * @example uploader: fileUpload({ abortOnLimit: false, parseNested: true }) + * @desc Enable or configure uploads handling. + * @default false + * @requires express-fileupload * */ upload?: boolean | UploadOptions; /** - * @desc Enable and configure compression - * @default undefined - * @example import compression from "compression" - * @example compressor: compression() + * @desc Enable or configure response compression. + * @default false + * @requires compression */ compression?: boolean | CompressionOptions; /** From 35d879ea33fe7d6816fc8c8701c8610be34469f3 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 21 Nov 2023 00:14:38 +0100 Subject: [PATCH 06/10] Changelog: reducing the migration significantly. --- CHANGELOG.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 608ef0a8f..aa406a033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,31 +6,31 @@ - **Breaking changes**: - `express-fileupload` and `compression` become optional peer dependencies; - - Methods `createConfig` and `testEndpoint()` are changed (read the migration guide below). + - Method `createServer()` becomes async; + - Method `testEndpoint()` requires an additional argument; + - Read the migration guide below. - Features: - Supporting both `jest` and `vitest` frameworks for `testEndpoint()`. - How to migrate while maintaining previous functionality and behavior: - If you have `upload` option enabled in your config: - Install `express-fileupload` and `@types/express-fileupload` packages; - - Replace `upload` property with `uploader: fileUpload({ abortOnLimit: false, parseNested: true })` in config. - If you have `compression` option enabled in your config: - Install `compression` and `@types/compression` packages; - - Replace `compression` property with `compressor: compression()` in config. + - If you're using the entities returned from `createServer()` method: + - Add `await` before calling it: `const {...} = await createServer(...)`. + - If you can not use `await` (on the top level of CommonJS): + - Wrap your code with async IIFE: `(async () => { ... })()`, which will allow you to use `await`; + - Or use `.then()` syntax of `Promise`. - If you're using `testEndpoint()` method: - Specify either `mockFn: jest.fn` or `mockFn: vi.fn` within its object argument. ```typescript -import compression from "compression"; -import fileUpload from "express-fileupload"; -import { createConfig, testEndpoint } from "express-zod-api"; +import { createServer, testEndpoint } from "express-zod-api"; -const config = createConfig({ - server: { - // The following two options are required to operate normally: - uploader: fileUpload({ abortOnLimit: false, parseNested: true }), - compressor: compression(), - }, -}); +// async-await is only needed when you're using the returns of createServer() +(async () => { + const { app, httpServer } = await createServer(config, routing); +})(); const { responseMock } = testEndpoint({ endpoint, From 492715b78254a2aa9ae061fc7849d4d713cec294 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 21 Nov 2023 00:22:44 +0100 Subject: [PATCH 07/10] Readme: adjusting the async usage of the method. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 69095f1e2..a10ba51a6 100644 --- a/README.md +++ b/README.md @@ -483,7 +483,12 @@ const config = createConfig({ // ... cors, logger, etc }); -const { app, httpServer, httpsServer, logger } = createServer(config, routing); +// 'await' is only needed if you're going to use the returned entities. +// For CJS in that case you can wrap you code with (async () => { ... })() +const { app, httpServer, httpsServer, logger } = await createServer( + config, + routing, +); ``` Ensure having `@types/node` package installed. At least you need to specify the port (usually it is 443) or UNIX socket, From 0d14a248f168badcda2206b40c05f62762bf36d5 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 21 Nov 2023 00:26:51 +0100 Subject: [PATCH 08/10] Readme: restoring compression. --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a10ba51a6..ebe678a28 100644 --- a/README.md +++ b/README.md @@ -514,17 +514,15 @@ const config = createConfig({ logger /* ..., */ }); According to [Express.js best practices guide](http://expressjs.com/en/advanced/best-practice-performance.html) it might be a good idea to enable GZIP compression of your API responses. -Install the following additional packages: `compression` and `@types/compression`, and complete your configuration with -a compressor: +Install the following additional packages: `compression` and `@types/compression`, and enable or configure compression: ```typescript -import compression from "compression"; import { createConfig } from "express-zod-api"; const config = createConfig({ server: { /** @link https://www.npmjs.com/package/compression#options */ - compressor: compression({ threshold: "1kb" }), + compression: { threshold: "1kb" }, // or true }, }); ``` From 1cb9312f1148feb172f40283c39e0a39ab58ffed Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 21 Nov 2023 00:29:36 +0100 Subject: [PATCH 09/10] Readme: restoring file uploads. --- README.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ebe678a28..874aba5b8 100644 --- a/README.md +++ b/README.md @@ -715,20 +715,15 @@ const fileStreamingEndpointsFactory = new EndpointsFactory( ## File uploads -Install the following additional packages: `express-fileupload` and `@types/express-fileupload`, and complete your -configuration with a file uploader: +Install the following additional packages: `express-fileupload` and `@types/express-fileupload`, and enable or +configure file uploads: ```typescript -import fileUpload from "express-fileupload"; import { createConfig } from "express-zod-api"; const config = createConfig({ server: { - uploader: fileUpload({ - // These options are required to operate normally: - abortOnLimit: false, - parseNested: true, - }), + upload: true, // or options }, }); ``` From e119c32a205930fe6b1848e1b68e821c84eefc5b Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 21 Nov 2023 00:34:51 +0100 Subject: [PATCH 10/10] Changelog: minor. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa406a033..b2e26c10d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,8 +27,9 @@ ```typescript import { createServer, testEndpoint } from "express-zod-api"; -// async-await is only needed when you're using the returns of createServer() +// This async IIFE wrapper is only needed when using await on the top level CJS (async () => { + // await is only needed when you're using the returns of createServer() const { app, httpServer } = await createServer(config, routing); })();