Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v15 is for Vika #1301

Merged
merged 68 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
ab6bdea
v15: Adjusting workflows.
Nov 10, 2023
545f366
`compressor` and `uploader` config, optional peers (#1255)
RobinTail Nov 10, 2023
198c94a
Supporting both `jest` and `vitest` for `testEndpoint()` method (#1280)
RobinTail Nov 10, 2023
f00a9a3
Moving jsdoc comment and removing todo in EndpointsFactory.
RobinTail Nov 10, 2023
c76f4e4
Merge branch 'master' into prepare-v15
RobinTail Nov 13, 2023
b3b2547
Merge branch 'master' into prepare-v15
RobinTail Nov 13, 2023
504424c
Merge branch 'master' into prepare-v15
RobinTail Nov 15, 2023
8259934
Removing lock file from vitest test.
RobinTail Nov 16, 2023
e57d3e9
Integration and ESM tests: using latest compatible versions each time.
RobinTail Nov 16, 2023
a60f3f0
Security: listing the upcoming version, adjusting the link.
RobinTail Nov 16, 2023
648d11e
Dedication.
RobinTail Nov 16, 2023
2358649
Merge branch 'master' into prepare-v15
RobinTail Nov 18, 2023
dbc4fcf
Changelog: cherry-picking the anounce from abstract-logger branch, wi…
Nov 19, 2023
ff6c1f1
Merge branch 'master' into prepare-v15
RobinTail Nov 19, 2023
bcadc97
Merge branch 'master' into prepare-v15
RobinTail Nov 19, 2023
154f1df
Merge branch 'master' into prepare-v15
RobinTail Nov 19, 2023
26c6bfc
Merge branch 'master' into prepare-v15
RobinTail Nov 19, 2023
0421629
Update SECURITY.md
RobinTail Nov 19, 2023
5eb7b99
Merge branch 'master' into prepare-v15
RobinTail Nov 20, 2023
e0c34c9
Fix: ensure type import only in upload schema.
RobinTail Nov 20, 2023
0a8c567
Ref: Async peers instead of changing config type (#1321)
RobinTail Nov 20, 2023
4b1100c
Feat: Supporting any logger (#1314)
RobinTail Nov 21, 2023
50f51a3
Changelog: adjusting what is breaking and what is feature.
RobinTail Nov 21, 2023
9ee8859
Merge branch 'master' into prepare-v15
RobinTail Nov 22, 2023
abbdcfa
Fix ramda dependency expression, since it's zero.
RobinTail Nov 22, 2023
0fc4351
Merge branch 'master' into prepare-v15
RobinTail Nov 22, 2023
62a0c1a
Renamed the test for testing.
RobinTail Nov 24, 2023
bd27de6
Ref: renaming mockFn to fnMethod.
RobinTail Nov 24, 2023
5b0f7b5
Ref: constraints for loggerProps.
RobinTail Nov 24, 2023
3ac3efa
Detaching compatibility test from the main workspace.
RobinTail Nov 24, 2023
8e33be3
Ref: Testing: finally simpler implementation of type arguments and re…
RobinTail Nov 24, 2023
aba231f
Minor: typo.
RobinTail Nov 24, 2023
667ead2
Merge branch 'master' into prepare-v15
RobinTail Nov 24, 2023
d16fbed
Apply suggestions from code review
RobinTail Nov 24, 2023
fc0832d
15.0.0-beta1
RobinTail Nov 24, 2023
13b253f
Augmentation of mocking function type (#1335)
RobinTail Nov 25, 2023
5a4ced1
Readme: typo
RobinTail Nov 25, 2023
0f89fb8
Apply suggestions from code review
RobinTail Nov 25, 2023
9af53ed
Ref: changing MockFunction interface to a type instead, since neither…
RobinTail Nov 26, 2023
5406658
Minor: jsdoc.
RobinTail Nov 26, 2023
2d810dc
Update CHANGELOG.md
RobinTail Nov 29, 2023
55cbb4a
Changelog: adjusting the keynotes of release.
Nov 29, 2023
003568c
Rev, Ref: restoring createWinstonLogger() export.
Nov 29, 2023
c8f697c
Changelog: adjusting example.
Nov 29, 2023
5a10588
Readme: adusting Technologies section.
Nov 29, 2023
813e2e9
Merge branch 'master' into prepare-v15
RobinTail Nov 29, 2023
0cc5f02
Revert "Rev, Ref: restoring createWinstonLogger() export."
RobinTail Nov 29, 2023
4e2888b
Readme: minor.
RobinTail Nov 29, 2023
36ab3ca
Changelog: minor.
RobinTail Nov 29, 2023
23ae2c7
Merge branch 'master' into prepare-v15
RobinTail Nov 30, 2023
59bbbc8
Changelog: minor fix.
RobinTail Nov 30, 2023
feb4f99
Changelog: improving clarity and consistency of migration guide.
RobinTail Nov 30, 2023
d33d874
Changelog: minor.
RobinTail Nov 30, 2023
a25e801
Readme: minor adjustments for clarity.
RobinTail Nov 30, 2023
93a0d49
Security: organizing package to make reference stable.
RobinTail Nov 30, 2023
1796922
SimplifiedWinstonConfig: making color optional feature.
RobinTail Nov 30, 2023
6c8c9ed
Additional tests for isSimplifiedWinstonConfig().
RobinTail Nov 30, 2023
034d31c
Fix: supporting primitive values for logging metadata (#1343)
RobinTail Dec 1, 2023
142ad04
Ref: common starter for all servers (#1342)
RobinTail Dec 1, 2023
b3fa16b
15.0.0-beta5
RobinTail Dec 1, 2023
0422d30
Merge branch 'master' into prepare-v15
RobinTail Dec 1, 2023
cb568d8
Restoring `createLogger` in v15 (#1347)
RobinTail Dec 1, 2023
1280957
15.0.0-beta6
RobinTail Dec 1, 2023
e8c8b14
CI: removing this branch from triggers
RobinTail Dec 2, 2023
ff6b9d0
Peer fix: allowing vitest >=0.34.6 <2.
RobinTail Dec 2, 2023
90551f4
Merge branch 'master' into prepare-v15
RobinTail Dec 2, 2023
50debed
15.0.0-beta7
RobinTail Dec 2, 2023
a5488ac
Changelog: minor, mentioning again that any compatible logger is supp…
RobinTail Dec 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ name: "CodeQL"

on:
push:
branches: [ master, v11, v12 ]
branches: [ master, v12, v14, prepare-v15 ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master, v11, v12 ]
branches: [ master, v12, v14, prepare-v15 ]
schedule:
- cron: '26 8 * * 1'

Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: Node.js CI

on:
push:
branches: [ master, v11, v12 ]
branches: [ master, v12, v14, prepare-v15 ]
pull_request:
branches: [ master, v11, v12 ]
branches: [ master, v12, v14, prepare-v15 ]

jobs:
build:
Expand Down Expand Up @@ -48,6 +48,8 @@ jobs:
github-token: ${{ secrets.github_token }}
flag-name: run-${{ matrix.node-version }}
parallel: true
- name: Vitest compatibility
run: yarn test:vi
- name: Integration test
run: yarn test:int
- name: Issue 952
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: OpenAPI Validation

on:
push:
branches: [ master, v11, v12 ]
branches: [ master, v12, v14, prepare-v15 ]
pull_request:
branches: [ master, v11, v12 ]
branches: [ master, v12, v14, prepare-v15 ]


jobs:
Expand Down
79 changes: 79 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,84 @@
# Changelog

## Version 15

### 15.0.0

- **Breaking changes**:
- Packages `express-fileupload` and `compression` become optional peer dependencies;
- Methods `createServer()` and `attachRouting()` become async;
- Method `createLogger()` requires an additional argument;
- Read the migration guide below.
- Features:
- Supporting any logger having `debug()`, `warn()`, `info()` and `error()` methods;
- Package `winston` is now optional.
- Supporting any testing framework having a function mocking method for `testEndpoint()`:
- Both `jest` and `vitest` are supported automatically;
- With most modern Node.js you can also use the integrated `node:test` module.
- Introducing module augmentation approach for integrating chosen logger and testing framework.
- How to migrate while maintaining previous functionality and behavior:
- Near your `const config` add a module augmentation statement based on `winston.Logger` type (see example below).
- If you have `upload` option enabled in your config:
- Install `express-fileupload` and `@types/express-fileupload` packages;
- If you have `compression` option enabled in your config:
- Install `compression` and `@types/compression` packages;
- If you're using the entities returned from `createServer()` or `attachRouting()` methods:
- Add `await` before calling those methods.
- If you can not use `await` (on the top level of CommonJS):
- Wrap your code with async IIFE or use `.then()` (see example below).
- If you're using `testEndpoint()` method:
- Add module augmentation statement once anywhere within your tests based on `jest.Mock` type (see example below).
- If you're using `createLogger()` helper:
- Consider using `logger` property supplied to `createConfig()` instead;
- Otherwise, supply also the `winston` argument to the helper (`import winston from "winston"`).

```typescript
import winston from "winston";
import { createConfig, createLogger, createServer } from "express-zod-api";

// Use the logger property of config to use Winston logger
const config = createConfig({
logger: { level: "debug", color: true },
});

// If you need that pretty logger outside the API, use the existing helper instead:
const logger = createLogger({ winston, level: "debug", color: true });

// Set the type of the logger used near your configuration
declare module "express-zod-api" {
interface LoggerOverrides extends winston.Logger {}
}

// if using entities returned from createServer() or attachRouting(): add "await" before it.
// For using await on the top level CJS, wrap it in async IIFE:
// (async () => { await ... })();
const { app, httpServer } = await createServer(config, routing);
```

```typescript
// Adjust your tests: set the MockOverrides type once anywhere
declare module "express-zod-api" {
interface MockOverrides extends jest.Mock {} // or Mock from vitest
}

// Both jest and vitest are supported automatically
import { testEndpoint } from "express-zod-api";
const { responseMock } = await testEndpoint({ endpoint });

// For other testing frameworks:

// 1. specify fnMethod property
import { mock, Mock } from "node:test";
await testEndpoint({
endpoint,
fnMethod: mock.fn.bind(mock), // https://nodejs.org/docs/latest-v20.x/api/test.html#mocking
});
// 2. and set the MockOverrides type once
declare module "express-zod-api" {
interface MockOverrides extends Mock {} // Mock of your testing framework
}
```

## Version 14

### v14.2.4
Expand Down
121 changes: 78 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ You can find the release notes and migration guides in [Changelog](CHANGELOG.md)
# Why and what is it for

I made this library because of the often repetitive tasks of starting a web server APIs with the need to validate input
data. It integrates and provides the capabilities of popular web server, logger, validation and documenting solutions.
data. It integrates and provides the capabilities of popular web server, logging, validation and documenting solutions.
Therefore, many basic tasks can be accomplished faster and easier, in particular:

- You can describe web server routes as a hierarchical object.
Expand All @@ -85,12 +85,16 @@ Therefore, many basic tasks can be accomplished faster and easier, in particular
- [Typescript](https://www.typescriptlang.org/) first.
- Web server — [Express.js](https://expressjs.com/).
- Schema validation — [Zod 3.x](https://github.com/colinhacks/zod).
- Logger — [Winston](https://github.com/winstonjs/winston).
- Supports any logger having `info()`, `debug()`, `error()` and `warn()` methods;
- [Winston](https://github.com/winstonjs/winston) is default.
- Generators:
- Documentation — [OpenAPI 3.x](https://github.com/metadevpro/openapi3-ts) (Swagger Specification).
- Documentation — [OpenAPI 3.x](https://github.com/metadevpro/openapi3-ts) (Swagger Specification);
- Client side types — inspired by [zod-to-ts](https://github.com/sachinraja/zod-to-ts).
- File uploads — [Express-FileUpload](https://github.com/richardgirges/express-fileupload)
(based on [Busboy](https://github.com/mscdex/busboy)).
- Supports any testing framework having a function mocking method;
- [Jest](https://github.com/jestjs/jest) and [Vitest](https://github.com/vitest-dev/vitest)
are both supported automatically.

## Concept

Expand Down Expand Up @@ -139,6 +143,7 @@ Create a minimal configuration. _See all available options

```typescript
import { createConfig } from "express-zod-api";
import type { Logger } from "winston";

const config = createConfig({
server: {
Expand All @@ -147,6 +152,11 @@ const config = createConfig({
cors: true,
logger: { level: "debug", color: true },
});

// Setting the type of the logger used
declare module "express-zod-api" {
interface LoggerOverrides extends Logger {}
}
```

## Create an endpoints factory
Expand Down Expand Up @@ -483,7 +493,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 top level CJS 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,
Expand All @@ -492,44 +507,48 @@ your API at [Let's Encrypt](https://letsencrypt.org/).

## Customizing logger

You can specify your custom Winston logger in config:
You can uninstall `winston` (which is the default and recommended logger) and use another compatible one, having
`info()`, `debug()`, `error()` and `warn()` methods. For example, `pino` logger with `pino-pretty` extension:

```typescript
import winston from "winston";
import pino, { Logger } from "pino";
import { createConfig } from "express-zod-api";

const logger = winston.createLogger({
/* ... */
const logger = pino({
transport: {
target: "pino-pretty",
options: { colorize: true },
},
});
const config = createConfig({ logger /* ..., */ });
const config = createConfig({ logger });

// Setting the type of logger used
declare module "express-zod-api" {
interface LoggerOverrides extends Logger {}
}
```

## Enabling compression

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. You can achieve and customize it by using the
corresponding configuration option when using the `createServer()` method.
it might be a good idea to enable GZIP compression of your API responses.

In order to receive the compressed response the client should include the following header in the request:
`Accept-Encoding: gzip, deflate`. Only responses with compressible content types are subject to compression. There is
also a default threshold of 1KB that can be configured.
Install the following additional packages: `compression` and `@types/compression`, and enable or configure compression:

```typescript
import { createConfig } from "express-zod-api";

const config = createConfig({
server: {
// compression: true, or:
compression: {
// @see https://www.npmjs.com/package/compression#options
threshold: "100b",
},
// ... other options
/** @link https://www.npmjs.com/package/compression#options */
compression: { threshold: "1kb" }, // or true
},
// ... other options
});
```

In order to receive a compressed response the client should include the following header in the request:
`Accept-Encoding: gzip, deflate`. Only responses with compressible content types are subject to compression.

# Advances features

## Customizing input sources
Expand Down Expand Up @@ -715,29 +734,34 @@ const fileStreamingEndpointsFactory = new EndpointsFactory(

## File uploads

You can switch the `Endpoint` to handle requests with the `multipart/form-data` content type instead of JSON by using
`ez.upload()` schema. Together with a corresponding configuration option, this makes it possible to handle file uploads.
Here is a simplified example:
Install the following additional packages: `express-fileupload` and `@types/express-fileupload`, and enable or
configure file uploads:

```typescript
import { z } from "zod";
import { createConfig, ez, defaultEndpointsFactory } from "express-zod-api";
import { createConfig } from "express-zod-api";

const config = createConfig({
server: {
upload: true, // <- required
// ...,
upload: true, // or options
},
});
```

Refer to [documentation](https://www.npmjs.com/package/express-fileupload#available-options) on available options.
Then you can change the `Endpoint` to handle requests having the `multipart/form-data` content type instead of JSON by
using `ez.upload()` schema. Together with a corresponding configuration option, this makes it possible to handle file
uploads. Here is a simplified example:

```typescript
import { z } from "zod";
import { ez, defaultEndpointsFactory } from "express-zod-api";

const fileUploadEndpoint = defaultEndpointsFactory.build({
method: "post",
input: z.object({
avatar: ez.upload(), // <--
}),
output: z.object({
/* ... */
}),
output: z.object({}),
handler: async ({ input: { avatar } }) => {
// avatar: {name, mv(), mimetype, data, size, etc}
// avatar.truncated is true on failure
Expand Down Expand Up @@ -774,20 +798,21 @@ you can connect your routing to the app instead of using `createServer()`.

```typescript
import express from "express";
import { createConfig, attachRouting } from "express-zod-api";
import { createConfig, attachRouting, Routing } from "express-zod-api";

const app = express();
const config = createConfig({ app /* cors, logger, ... */ });
const routing = {
/* ... */
};
const routing: Routing = {};

const { notFoundHandler, logger } = attachRouting(config, routing);
// This async IIFE is only required for the top level CommonJS
(async () => {
const { notFoundHandler, logger } = await attachRouting(config, routing);

app.use(notFoundHandler); // optional
app.listen();
app.use(notFoundHandler); // optional
app.listen();

logger.info("Glory to science!");
logger.info("Glory to science!");
})();
```

**Please note** that in this case you probably need to parse `request.body`, call `app.listen()` and handle `404`
Expand Down Expand Up @@ -989,26 +1014,36 @@ const exampleEndpoint = taggedEndpointsFactory.build({
## How to test endpoints

The way to test endpoints is to mock the request, response, and logger objects, invoke the `execute()` method, and
assert the expectations for calls of certain mocked methods. The library provides a special method that makes mocking
easier, it requires `jest` (and optionally `@types/jest`) to be installed, so the test might look the following way:
assert the expectations for calls of certain mocked methods. The library provides a special method `testEndpoint` that
makes mocking easier. It requires you either to install `jest` (with `@types/jest`) or `vitest`
(detects automatically), or to specify the `fnMethod` property assigned with a function mocking method of your testing
framework, which can also be `node:test` module of most modern Node.js versions.
However, in order to have proper mocking types in your own tests, you also need to specify `MockOverrides` once in your
tests excplicitly, so the tests should look this way:

```typescript
import { testEndpoint } from "express-zod-api";

// place it once anywhere in your tests
declare module "express-zod-api" {
interface MockOverrides extends jest.Mock {} // or Mock from vitest
}

test("should respond successfully", async () => {
const { responseMock, loggerMock } = await testEndpoint({
endpoint: yourEndpoint,
requestProps: {
method: "POST", // default: GET
body: { /* parsed JSON */ },
body: {}, // incoming data as if after parsing (JSON)
},
// fnMethod — for testing frameworks other than jest or vitest
// responseProps, configProps, loggerProps
});
expect(loggerMock.error).toHaveBeenCalledTimes(0);
expect(responseMock.status).toHaveBeenCalledWith(200);
expect(responseMock.json).toHaveBeenCalledWith({
status: "success",
data: { ... },
data: {},
});
});
```
Expand Down
5 changes: 3 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

| Version | Release | Supported |
| ------: | :------ | :----------------: |
| 15.x.x | 12.2023 | :white_check_mark: |
| 14.x.x | 10.2023 | :white_check_mark: |
| 12.x.x | 09.2023 | :white_check_mark: |
| 11.x.x | 06.2023 | :white_check_mark: |
| 11.x.x | 06.2023 | :x: |
| 10.x.x | 03.2023 | :x: |
| 9.x.x | 03.2023 | :x: |
| 8.x.x | 09.2022 | :x: |
Expand All @@ -24,6 +25,6 @@
Found a vulnerability or other security issue?

Please urgently inform me privately by
[email](https://github.com/RobinTail/express-zod-api/blob/master/package.json#L137).
[email](https://github.com/RobinTail/express-zod-api/blob/master/package.json#L14).

I will try to fix it as soon as possible.
Loading