@clipboard-health/json-api-nestjs 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -21
- package/package.json +2 -4
- package/src/lib/query/cursorPaginationQuery.d.ts +2 -2
- package/src/lib/query/cursorPaginationQuery.js +2 -2
- package/src/lib/query/fieldsQuery.d.ts +2 -2
- package/src/lib/query/fieldsQuery.js +2 -2
- package/src/lib/query/filterQuery.d.ts +5 -5
- package/src/lib/query/filterQuery.js +3 -3
- package/src/lib/query/filterQuery.js.map +1 -1
- package/src/lib/query/includeQuery.d.ts +2 -2
- package/src/lib/query/includeQuery.js +2 -2
- package/src/lib/query/sortQuery.d.ts +2 -2
- package/src/lib/query/sortQuery.js +2 -2
- package/src/lib/schemas.d.ts +5 -1
- package/src/lib/schemas.js +7 -1
- package/src/lib/schemas.js.map +1 -1
- package/src/lib/types.d.ts +7 -7
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# @clipboard-health/json-api-nestjs
|
|
1
|
+
# @clipboard-health/json-api-nestjs <!-- omit from toc -->
|
|
2
2
|
|
|
3
3
|
Utilities for adhering to the [JSON:API](https://jsonapi.org/) specification with [NestJS](https://nestjs.com/).
|
|
4
4
|
|
|
5
|
-
## Table of
|
|
5
|
+
## Table of contents <!-- omit from toc -->
|
|
6
6
|
|
|
7
7
|
- [Install](#install)
|
|
8
8
|
- [Usage](#usage)
|
|
@@ -23,12 +23,13 @@ Create Zod schemas for your API's queries:
|
|
|
23
23
|
|
|
24
24
|
<!-- prettier-ignore -->
|
|
25
25
|
```ts
|
|
26
|
-
//
|
|
26
|
+
// ../example-nestjs/examples/query.ts
|
|
27
27
|
|
|
28
28
|
import {
|
|
29
29
|
booleanString,
|
|
30
30
|
cursorPaginationQuery,
|
|
31
31
|
fieldsQuery,
|
|
32
|
+
type FilterMap,
|
|
32
33
|
filterQuery,
|
|
33
34
|
includeQuery,
|
|
34
35
|
sortQuery,
|
|
@@ -39,14 +40,28 @@ import {
|
|
|
39
40
|
type ArticleAttributeFields,
|
|
40
41
|
type UserAttributeFields,
|
|
41
42
|
type UserIncludeFields,
|
|
42
|
-
} from "
|
|
43
|
+
} from "../src/contract";
|
|
43
44
|
|
|
44
45
|
const articleFields = ["title"] as const satisfies readonly ArticleAttributeFields[];
|
|
45
|
-
const userFields = ["age", "
|
|
46
|
-
const
|
|
46
|
+
const userFields = ["age", "dateOfBirth"] as const satisfies readonly UserAttributeFields[];
|
|
47
|
+
const userIncludeFields = [
|
|
47
48
|
"articles",
|
|
48
49
|
"articles.comments",
|
|
49
50
|
] as const satisfies readonly UserIncludeFields[];
|
|
51
|
+
const userFilterMap = {
|
|
52
|
+
age: {
|
|
53
|
+
filters: ["eq", "gt"],
|
|
54
|
+
schema: z.coerce.number().int().positive().max(125),
|
|
55
|
+
},
|
|
56
|
+
isActive: {
|
|
57
|
+
filters: ["eq"],
|
|
58
|
+
schema: booleanString,
|
|
59
|
+
},
|
|
60
|
+
dateOfBirth: {
|
|
61
|
+
filters: ["gte"],
|
|
62
|
+
schema: z.coerce.date().min(new Date("1900-01-01")).max(new Date()),
|
|
63
|
+
},
|
|
64
|
+
} as const satisfies FilterMap<UserAttributeFields>;
|
|
50
65
|
|
|
51
66
|
/**
|
|
52
67
|
* Disclaimer: Just because JSON:API supports robust querying doesn’t mean your service should
|
|
@@ -60,22 +75,9 @@ export const query = z
|
|
|
60
75
|
.object({
|
|
61
76
|
...cursorPaginationQuery(),
|
|
62
77
|
...fieldsQuery({ user: userFields, article: articleFields }),
|
|
63
|
-
...filterQuery(
|
|
64
|
-
age: {
|
|
65
|
-
filters: ["eq", "gt"],
|
|
66
|
-
schema: z.coerce.number().int().positive().max(125),
|
|
67
|
-
},
|
|
68
|
-
isActive: {
|
|
69
|
-
filters: ["eq"],
|
|
70
|
-
schema: booleanString,
|
|
71
|
-
},
|
|
72
|
-
dateOfBirth: {
|
|
73
|
-
filters: ["gte"],
|
|
74
|
-
schema: z.coerce.date().min(new Date("1900-01-01")).max(new Date()),
|
|
75
|
-
},
|
|
76
|
-
}),
|
|
78
|
+
...filterQuery(userFilterMap),
|
|
77
79
|
...sortQuery(userFields),
|
|
78
|
-
...includeQuery(
|
|
80
|
+
...includeQuery(userIncludeFields),
|
|
79
81
|
})
|
|
80
82
|
.strict();
|
|
81
83
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/json-api-nestjs",
|
|
3
3
|
"description": "Utilities for adhering to the JSON:API specification with NestJS.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"tslib": "2.7.0",
|
|
7
|
+
"type-fest": "4.26.1",
|
|
7
8
|
"zod": "3.23.8"
|
|
8
9
|
},
|
|
9
|
-
"devDependencies": {
|
|
10
|
-
"type-fest": "4.26.1"
|
|
11
|
-
},
|
|
12
10
|
"keywords": [],
|
|
13
11
|
"license": "MIT",
|
|
14
12
|
"main": "./src/index.js",
|
|
@@ -8,9 +8,9 @@ export declare const PAGINATION: {
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates a Zod schema for JSON:API cursor pagination.
|
|
10
10
|
*
|
|
11
|
-
* @includeExample ./packages/
|
|
11
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
12
12
|
*
|
|
13
|
-
* @see [Usage example](
|
|
13
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
14
14
|
* @see {@link https://jsonapi.org/format/#fetching-pagination JSON:API pagination}
|
|
15
15
|
* @see {@link https://jsonapi.org/examples/#pagination JSON:API pagination examples}
|
|
16
16
|
*/
|
|
@@ -13,9 +13,9 @@ exports.PAGINATION = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Creates a Zod schema for JSON:API cursor pagination.
|
|
15
15
|
*
|
|
16
|
-
* @includeExample ./packages/
|
|
16
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
17
17
|
*
|
|
18
|
-
* @see [Usage example](
|
|
18
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
19
19
|
* @see {@link https://jsonapi.org/format/#fetching-pagination JSON:API pagination}
|
|
20
20
|
* @see {@link https://jsonapi.org/examples/#pagination JSON:API pagination examples}
|
|
21
21
|
*/
|
|
@@ -13,9 +13,9 @@ export type AttributeFields<DocumentT extends JsonApiDocument> = DocumentT["data
|
|
|
13
13
|
/**
|
|
14
14
|
* Creates a Zod schema for JSON:API sparse fieldsets.
|
|
15
15
|
*
|
|
16
|
-
* @includeExample ./packages/
|
|
16
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
17
17
|
*
|
|
18
|
-
* @see [Usage example](
|
|
18
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
19
19
|
* @see {@link https://jsonapi.org/format/#fetching-sparse-fieldsets JSON:API sparse fieldsets}
|
|
20
20
|
*/
|
|
21
21
|
export declare function fieldsQuery<const MapT extends FieldsMap>(parameters: Readonly<MapT>): {
|
|
@@ -6,9 +6,9 @@ const splitString_1 = require("../internal/splitString");
|
|
|
6
6
|
/**
|
|
7
7
|
* Creates a Zod schema for JSON:API sparse fieldsets.
|
|
8
8
|
*
|
|
9
|
-
* @includeExample ./packages/
|
|
9
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
10
10
|
*
|
|
11
|
-
* @see [Usage example](
|
|
11
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
12
12
|
* @see {@link https://jsonapi.org/format/#fetching-sparse-fieldsets JSON:API sparse fieldsets}
|
|
13
13
|
*/
|
|
14
14
|
function fieldsQuery(parameters) {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { type
|
|
2
|
+
import { type Field } from "../types";
|
|
3
3
|
export type Filter = "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
|
|
4
|
-
export type FilterMap = Record<
|
|
4
|
+
export type FilterMap<FieldT extends Field = Field> = Record<FieldT, {
|
|
5
5
|
filters: readonly [Filter, ...Filter[]];
|
|
6
6
|
schema: z.ZodTypeAny;
|
|
7
7
|
}>;
|
|
8
8
|
export type FilterSchema<MapT extends FilterMap> = {
|
|
9
9
|
[K in keyof MapT]: z.ZodOptional<z.ZodEffects<z.ZodOptional<z.ZodObject<{
|
|
10
|
-
[F in MapT[K]["filters"][number]]: z.ZodOptional<z.ZodEffects<z.ZodOptional<z.ZodArray<MapT[K]["schema"]
|
|
10
|
+
[F in MapT[K]["filters"][number]]: z.ZodOptional<z.ZodEffects<z.ZodOptional<z.ZodReadonly<z.ZodArray<MapT[K]["schema"]>>>>>;
|
|
11
11
|
}>>>>;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
14
|
* Creates a Zod schema for JSON:API filters.
|
|
15
15
|
*
|
|
16
|
-
* @includeExample ./packages/
|
|
16
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
17
17
|
*
|
|
18
|
-
* @see [Usage example](
|
|
18
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
19
19
|
* @see {@link https://jsonapi.org/recommendations/#filtering JSON:API filtering}
|
|
20
20
|
* @see {@link https://discuss.jsonapi.org/t/share-propose-a-filtering-strategy/257 JSON:API filtering strategy}
|
|
21
21
|
*/
|
|
@@ -7,9 +7,9 @@ const splitString_1 = require("../internal/splitString");
|
|
|
7
7
|
/**
|
|
8
8
|
* Creates a Zod schema for JSON:API filters.
|
|
9
9
|
*
|
|
10
|
-
* @includeExample ./packages/
|
|
10
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
11
11
|
*
|
|
12
|
-
* @see [Usage example](
|
|
12
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
13
13
|
* @see {@link https://jsonapi.org/recommendations/#filtering JSON:API filtering}
|
|
14
14
|
* @see {@link https://discuss.jsonapi.org/t/share-propose-a-filtering-strategy/257 JSON:API filtering strategy}
|
|
15
15
|
*/
|
|
@@ -23,7 +23,7 @@ function filterQuery(parameters) {
|
|
|
23
23
|
.object(Object.fromEntries(filters.map((filter) => [
|
|
24
24
|
filter,
|
|
25
25
|
zod_1.z
|
|
26
|
-
.preprocess(splitString_1.splitString, schema.array().min(1).max(10_000).optional())
|
|
26
|
+
.preprocess(splitString_1.splitString, schema.array().min(1).max(10_000).readonly().optional())
|
|
27
27
|
.optional(),
|
|
28
28
|
])))
|
|
29
29
|
.strict()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterQuery.js","sourceRoot":"","sources":["../../../../../../packages/json-api-nestjs/src/lib/query/filterQuery.ts"],"names":[],"mappings":";;AAuCA,
|
|
1
|
+
{"version":3,"file":"filterQuery.js","sourceRoot":"","sources":["../../../../../../packages/json-api-nestjs/src/lib/query/filterQuery.ts"],"names":[],"mappings":";;AAuCA,kCAqCC;AA5ED,6BAAwB;AAExB,iFAA8E;AAC9E,yDAAsD;AA2BtD;;;;;;;;GAQG;AACH,SAAgB,WAAW,CAA+B,UAA0B;IAClF,OAAO;QACL,MAAM,EAAE,OAAC;aACN,MAAM,CACL,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,CAC5B,CAAC,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAiC,EAAE,EAAE,CAAC;YAClE,OAAO;YACP,OAAC;iBACE,UAAU,CACT,iDAAuB,EACvB,OAAC;iBACE,MAAM,CACL,MAAM,CAAC,WAAW,CAChB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC;gBAC9B,MAAM;gBACN,OAAC;qBACE,UAAU,CACT,yBAAW,EACX,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CACxD;qBACA,QAAQ,EAAE;aACd,CAAC,CACH,CACF;iBACA,MAAM,EAAE;iBACR,QAAQ,EAAE,CACd;iBACA,QAAQ,EAAE;SACd,CACF,CAEoB,CACxB;aACA,MAAM,EAAE;aACR,QAAQ,EAAE;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -21,9 +21,9 @@ export type RelationshipPaths<MapT extends Record<string, z.ZodTypeAny>, Documen
|
|
|
21
21
|
/**
|
|
22
22
|
* Creates a Zod schema for JSON:API include parameters.
|
|
23
23
|
*
|
|
24
|
-
* @includeExample ./packages/
|
|
24
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
25
25
|
*
|
|
26
|
-
* @see [Usage example](
|
|
26
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
27
27
|
* @see {@link https://jsonapi.org/format/#fetching-includes JSON:API includes}
|
|
28
28
|
*/
|
|
29
29
|
export declare function includeQuery<const FieldT extends readonly string[]>(fields: FieldT): {
|
|
@@ -6,9 +6,9 @@ const splitString_1 = require("../internal/splitString");
|
|
|
6
6
|
/**
|
|
7
7
|
* Creates a Zod schema for JSON:API include parameters.
|
|
8
8
|
*
|
|
9
|
-
* @includeExample ./packages/
|
|
9
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
10
10
|
*
|
|
11
|
-
* @see [Usage example](
|
|
11
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
12
12
|
* @see {@link https://jsonapi.org/format/#fetching-includes JSON:API includes}
|
|
13
13
|
*/
|
|
14
14
|
function includeQuery(fields) {
|
|
@@ -3,9 +3,9 @@ import { type Field } from "../types";
|
|
|
3
3
|
/**
|
|
4
4
|
* Creates a Zod schema for JSON:API sort parameters.
|
|
5
5
|
*
|
|
6
|
-
* @includeExample ./packages/
|
|
6
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
7
7
|
*
|
|
8
|
-
* @see [Usage example](
|
|
8
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
9
9
|
* @see {@link https://jsonapi.org/format/#fetching-sorting JSON:API sorting}
|
|
10
10
|
*/
|
|
11
11
|
export declare function sortQuery<const FieldT extends readonly [Field, ...Field[]]>(fields: FieldT): {
|
|
@@ -6,9 +6,9 @@ const splitString_1 = require("../internal/splitString");
|
|
|
6
6
|
/**
|
|
7
7
|
* Creates a Zod schema for JSON:API sort parameters.
|
|
8
8
|
*
|
|
9
|
-
* @includeExample ./packages/
|
|
9
|
+
* @includeExample ./packages/example-nestjs/examples/query.ts
|
|
10
10
|
*
|
|
11
|
-
* @see [Usage example](
|
|
11
|
+
* @see [Usage example](../../../../example-nestjs/examples/query.ts)
|
|
12
12
|
* @see {@link https://jsonapi.org/format/#fetching-sorting JSON:API sorting}
|
|
13
13
|
*/
|
|
14
14
|
function sortQuery(fields) {
|
package/src/lib/schemas.d.ts
CHANGED
|
@@ -6,5 +6,9 @@ export declare const nonEmptyString: z.ZodString;
|
|
|
6
6
|
/**
|
|
7
7
|
* A Zod schema for use with boolean query parameters since `z.coerce.boolean()` treats any truthy
|
|
8
8
|
* value as `true`.
|
|
9
|
+
*
|
|
10
|
+
* `.transform((value) => value === "true")` causes inference issues in controllers.
|
|
9
11
|
*/
|
|
10
|
-
export declare const booleanString: z.
|
|
12
|
+
export declare const booleanString: z.ZodEnum<["true", "false"]>;
|
|
13
|
+
export type BooleanString = z.infer<typeof booleanString>;
|
|
14
|
+
export declare function toBoolean(value: BooleanString): boolean;
|
package/src/lib/schemas.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.booleanString = exports.nonEmptyString = void 0;
|
|
4
|
+
exports.toBoolean = toBoolean;
|
|
4
5
|
const zod_1 = require("zod");
|
|
5
6
|
/**
|
|
6
7
|
* A non-empty string Zod schema.
|
|
@@ -9,6 +10,11 @@ exports.nonEmptyString = zod_1.z.string().min(1);
|
|
|
9
10
|
/**
|
|
10
11
|
* A Zod schema for use with boolean query parameters since `z.coerce.boolean()` treats any truthy
|
|
11
12
|
* value as `true`.
|
|
13
|
+
*
|
|
14
|
+
* `.transform((value) => value === "true")` causes inference issues in controllers.
|
|
12
15
|
*/
|
|
13
|
-
exports.booleanString = zod_1.z.enum(["true", "false"])
|
|
16
|
+
exports.booleanString = zod_1.z.enum(["true", "false"]);
|
|
17
|
+
function toBoolean(value) {
|
|
18
|
+
return value === "true";
|
|
19
|
+
}
|
|
14
20
|
//# sourceMappingURL=schemas.js.map
|
package/src/lib/schemas.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../../../packages/json-api-nestjs/src/lib/schemas.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../../../packages/json-api-nestjs/src/lib/schemas.ts"],"names":[],"mappings":";;;AAiBA,8BAEC;AAnBD,6BAAwB;AAExB;;GAEG;AACU,QAAA,cAAc,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEhD;;;;;GAKG;AACU,QAAA,aAAa,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAIvD,SAAgB,SAAS,CAAC,KAAoB;IAC5C,OAAO,KAAK,KAAK,MAAM,CAAC;AAC1B,CAAC"}
|
package/src/lib/types.d.ts
CHANGED
|
@@ -4,26 +4,26 @@ export type Field = string;
|
|
|
4
4
|
export type Meta = Record<string, unknown>;
|
|
5
5
|
export type ApiLinks = Record<string, string | undefined>;
|
|
6
6
|
export interface Relationship {
|
|
7
|
-
data
|
|
8
|
-
id
|
|
9
|
-
type
|
|
7
|
+
data?: Arrayable<{
|
|
8
|
+
id?: string;
|
|
9
|
+
type?: ApiType;
|
|
10
10
|
}>;
|
|
11
11
|
links?: ApiLinks;
|
|
12
12
|
meta?: Meta;
|
|
13
13
|
}
|
|
14
14
|
export interface Data {
|
|
15
|
-
attributes
|
|
16
|
-
id
|
|
15
|
+
attributes?: Record<string, unknown>;
|
|
16
|
+
id?: string;
|
|
17
17
|
links?: ApiLinks;
|
|
18
18
|
meta?: Meta;
|
|
19
19
|
relationships?: Record<string, Relationship>;
|
|
20
|
-
type
|
|
20
|
+
type?: ApiType;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* The JSON:API document shape.
|
|
24
24
|
*/
|
|
25
25
|
export interface JsonApiDocument {
|
|
26
|
-
data
|
|
26
|
+
data?: Arrayable<Data>;
|
|
27
27
|
included?: Data[];
|
|
28
28
|
jsonapi?: {
|
|
29
29
|
ext?: Record<string, unknown>;
|