New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

express-zod-api

Package Overview
Dependencies
Maintainers
1
Versions
432
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-zod-api - npm Package Compare versions

Comparing version 2.5.2 to 2.6.0

37

CHANGELOG.md

@@ -5,2 +5,39 @@ # Changelog

### v2.6.0
- Zod version is 3.9.8
- It supports the ability to specify the key schema of `z.record()`.
- In case of using enums and literals in the key schema they will be described as required ones in the generated
OpenAPI / Swagger documentation.
```typescript
// example
z.record(
z.enum(['option1', 'option2']), // keys
z.boolean() // values
);
```
- Feature #145: `attachRouting()` now returns the `logger` instance and `notFoundHandler`. You can use it with your
custom express app for handling `404` (not found) errors:
```typescript
const {notFoundHandler} = attachRouting(config, routing);
app.use(notFoundHandler);
app.listen();
```
Or you can use the `logger` instance with any `ResultHandler` for the same purpose:
```typescript
const {logger} = attachRouting(config, routing);
app.use((request, response) => {
defaultResultHandler.handler({
request, response, logger,
error: createHttpError(404, `${request.path} not found`),
input: null,
output: null
});
});
app.listen();
```
### v2.5.2

@@ -7,0 +44,0 @@

4

dist/file-schema.d.ts

@@ -1,2 +0,2 @@

import { ParseContext, ParseReturnType, ZodParsedType, ZodType } from 'zod';
import { ParseContext, ParseReturnType, ZodParsedType, ZodType, ZodTypeDef } from 'zod';
import { ErrMessage } from './helpers';

@@ -11,3 +11,3 @@ declare const zodFileKind = "ZodFile";

};
export interface ZodFileDef {
export interface ZodFileDef extends ZodTypeDef {
checks: ZodFileCheck[];

@@ -14,0 +14,0 @@ typeName: typeof zodFileKind;

@@ -28,7 +28,7 @@ "use strict";

if (parsedType !== zod_1.ZodParsedType.string) {
ctx.addIssue(data, {
this.addIssue(ctx, {
code: zod_1.ZodIssueCode.invalid_type,
expected: zod_1.ZodParsedType.string,
received: parsedType,
});
}, { data });
return zod_1.INVALID;

@@ -41,6 +41,6 @@ }

invalid = true;
ctx.addIssue(data, {
this.addIssue(ctx, {
code: zod_1.ZodIssueCode.custom,
message: check.message,
});
}, { data });
}

@@ -47,0 +47,0 @@ }

@@ -49,4 +49,3 @@ "use strict";

...otherProps,
type: 'object',
additionalProperties: describeSchema(value._def.valueType, isResponse)
...describeRecord(value._def, isResponse)
};

@@ -78,3 +77,3 @@ case value instanceof zod_1.z.ZodObject:

...otherProps,
...describeTransformation(value, isResponse)
...describeEffect(value, isResponse)
};

@@ -137,2 +136,44 @@ case value instanceof zod_1.z.ZodOptional:

};
const describeRecord = (definition, isResponse) => {
if (definition.keyType instanceof zod_1.z.ZodEnum || definition.keyType instanceof zod_1.z.ZodNativeEnum) {
const keys = Object.values(definition.keyType._def.values);
const shape = keys.reduce((carry, key) => ({
...carry,
[key]: definition.valueType
}), {});
return {
type: 'object',
properties: describeObjectProperties(zod_1.z.object(shape), isResponse),
required: keys
};
}
if (definition.keyType instanceof zod_1.z.ZodLiteral) {
return {
type: 'object',
properties: describeObjectProperties(zod_1.z.object({
[definition.keyType._def.value]: definition.valueType
}), isResponse),
required: [definition.keyType._def.value]
};
}
if (definition.keyType instanceof zod_1.z.ZodUnion) {
const areOptionsLiteral = definition.keyType.options
.reduce((carry, option) => carry && option instanceof zod_1.z.ZodLiteral, true);
if (areOptionsLiteral) {
const shape = definition.keyType.options.reduce((carry, option) => ({
...carry,
[option.value]: definition.valueType
}), {});
return {
type: 'object',
properties: describeObjectProperties(zod_1.z.object(shape), isResponse),
required: definition.keyType.options.map((option) => option.value)
};
}
}
return {
type: 'object',
additionalProperties: describeSchema(definition.valueType, isResponse)
};
};
const describeArray = (definition, isResponse) => {

@@ -205,20 +246,9 @@ var _a;

};
const getTransformationMod = (def) => {
if ('effects' in def && def.effects && def.effects.length > 0) {
const effect = def.effects.filter((ef) => ef.type === 'transform').slice(-1)[0];
if (effect && 'transform' in effect) {
return { ...effect, isPreprocess: false };
}
}
if ('preprocess' in def && def.preprocess && def.preprocess.type === 'transform') {
return { ...def.preprocess, isPreprocess: true };
}
};
const describeTransformation = (value, isResponse) => {
const describeEffect = (value, isResponse) => {
const input = describeSchema(value._def.schema, isResponse);
const mod = getTransformationMod(value._def);
if (isResponse && mod && !mod.isPreprocess) {
const effect = value._def.effect;
if (isResponse && effect && effect.type === 'transform') {
let output = 'undefined';
try {
output = typeof mod.transform(['integer', 'number'].includes(`${input.type}`) ? 0 :
output = typeof effect.transform(['integer', 'number'].includes(`${input.type}`) ? 0 :
'string' === input.type ? '' :

@@ -238,3 +268,3 @@ 'boolean' === input.type ? false :

}
if (!isResponse && mod && mod.isPreprocess) {
if (!isResponse && effect && effect.type === 'preprocess') {
const { type: inputType, ...rest } = input;

@@ -241,0 +271,0 @@ return {

@@ -0,3 +1,4 @@

/// <reference types="qs" />
/// <reference types="node" />
import { ErrorRequestHandler, RequestHandler } from 'express';
import express, { ErrorRequestHandler, RequestHandler } from 'express';
import { Server } from 'http';

@@ -9,3 +10,6 @@ import { Logger } from 'winston';

export declare const createNotFoundHandler: (errorHandler: import("./result-handler").ResultHandlerDefinition<any, any>, logger: Logger) => RequestHandler;
export declare function attachRouting(config: AppConfig & CommonConfig, routing: Routing): void;
export declare function attachRouting(config: AppConfig & CommonConfig, routing: Routing): {
notFoundHandler: express.RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
logger: Logger;
};
export declare function createServer(config: ServerConfig & CommonConfig, routing: Routing): Server;

@@ -66,3 +66,6 @@ "use strict";

const logger = (0, helpers_1.isLoggerConfig)(config.logger) ? (0, logger_1.createLogger)(config.logger) : config.logger;
return (0, routing_1.initRouting)({ app: config.app, routing, logger, config });
(0, routing_1.initRouting)({ app: config.app, routing, logger, config });
const errorHandler = config.errorHandler || result_handler_1.defaultResultHandler;
const notFoundHandler = (0, exports.createNotFoundHandler)(errorHandler, logger);
return { notFoundHandler, logger };
}

@@ -69,0 +72,0 @@ exports.attachRouting = attachRouting;

import { UploadedFile } from 'express-fileupload';
import { ParseContext, ParseReturnType, ZodParsedType, ZodType } from 'zod';
import { ParseContext, ParseReturnType, ZodParsedType, ZodType, ZodTypeDef } from 'zod';
declare const zodUploadKind = "ZodUpload";
export interface ZodUploadDef {
export interface ZodUploadDef extends ZodTypeDef {
typeName: typeof zodUploadKind;

@@ -6,0 +6,0 @@ }

@@ -15,6 +15,6 @@ "use strict";

if (parsedType !== zod_1.ZodParsedType.object || !isUploadedFile(data)) {
ctx.addIssue(data, {
this.addIssue(ctx, {
code: zod_1.ZodIssueCode.custom,
message: `Expected file upload, received ${parsedType}`
});
}, { data });
return zod_1.INVALID;

@@ -21,0 +21,0 @@ }

{
"name": "express-zod-api",
"version": "2.5.2",
"version": "2.6.0",
"description": "A Typescript library to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -16,4 +16,6 @@ # Express Zod API

1. [Technologies](#technologies)
2. [Concept](#concept)
1. [Why and what is it for](#why-and-what-is-it-for)
2. [How it works](#how-it-works)
1. [Technologies](#technologies)
2. [Concept](#concept)
3. [Installation](#installation)

@@ -43,13 +45,34 @@ 4. [Basic usage](#basic-usage)

If you're upgrading from v1 please check out the information in [Changelog](CHANGELOG.md).
If you're upgrading from v1 please check out the information in [Changelog](CHANGELOG.md#v200-beta1).
# Technologies
# Why and what is it for
- [Typescript](https://www.typescriptlang.org/) first
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.
Therefore, many basic tasks can be accomplished faster and easier, in particular:
- You can describe web server routes as a hierarchical object.
- You can keep the endpoint's input and output type declarations right next to its handler.
- All input and output data types are validated, so it ensures you won't have an empty string, null or undefined where
you expect a number.
- Variables within an endpoint handler have types according to the declared schema, so your IDE and Typescript will
provide you with necessary hints to focus on bringing your vision to life.
- All of your endpoints can respond in a similar way.
- The expected endpoint input and response types can be exported to the frontend, so you don't get confused about the
field names when you implement the client for your API.
- You can generate your API documentation in a Swagger / OpenAPI compatible format.
# How it works
## Technologies
- [Typescript](https://www.typescriptlang.org/) first.
- Web server — [Express.js](https://expressjs.com/).
- Schema validation — [Zod 3.x](https://github.com/colinhacks/zod).
- Webserver — [Express.js](https://expressjs.com/).
- Logger — [Winston](https://github.com/winstonjs/winston).
- Swagger - [OpenAPI 3.x](https://github.com/metadevpro/openapi3-ts)
- Documenting - [OpenAPI 3.x](https://github.com/metadevpro/openapi3-ts) (formerly known as the Swagger Specification).
- File uploads — [Express-FileUpload](https://github.com/richardgirges/express-fileupload)
(based on [Busboy](https://github.com/mscdex/busboy))
# Concept
## Concept
The API operates object schemas for input and output, including unions and intersections of object schemas

@@ -62,9 +85,9 @@ (`.or()`, `.and()`), but in general the API can [respond with any data type](#non-object-response) and

Middlewares can handle validated inputs and the original `request`, for example, to perform the authentication or
provide the endpoint's handler with some request properties like the actual method. The returns of middlewares are
combined into the `options` parameter available to the next middlewares and the endpoint's handler.
Middlewares can handle inputs and the `request` properties, like headers, for example, to perform the authentication or
provide the endpoint with some properties like the actual request method. The returns of middlewares are combined into
the `options` parameter available to the next connected middlewares and the endpoint's handler.
The handler's parameter `input` combines the validated inputs of all connected middlewares along with the handler's one.
The result that the handler returns goes to the `ResultHandler` which is responsible for transmission of the final
response or possible error.
The `input` parameter of the endpoint's handler consists of the inputs of all connected middlewares along with its own
one. The output of the endpoint's handler goes to the `ResultHandler` which is responsible for transmission of the
final response or possible error.

@@ -105,3 +128,3 @@ All inputs and outputs are validated and there are also advanced powerful features like transformations and refinements.

server: {
listen: 8090,
listen: 8090, // port or socket
},

@@ -119,10 +142,21 @@ cors: true,

In the basic case, you can just import and use the default factory:
```typescript
import {defaultEndpointsFactory} from './endpoints-factory';
// same as: new EndpointsFactory(defaultResultHandler)
const endpointsFactory = defaultEndpointsFactory;
import {defaultEndpointsFactory} from 'express-zod-api';
```
You can also instantly add middlewares to it using `.addMiddleware()` method.
If you want to connect [middlewares](#create-a-middleware) to the default factory right away, you can do it the
following way:
```typescript
import {defaultEndpointsFactory} from 'express-zod-api';
const endpointsFactory = defaultEndpointsFactory.addMiddleware(
yourMiddleware
);
```
By the way, `defaultEndpointsFactory` is the same as `new EndpointsFactory(defaultResultHandler)`.
Therefore, if you need to customize the response, see [ResultHandler](#resulthandler).
## Create your first endpoint

@@ -150,7 +184,10 @@

The endpoint can also handle multiple types of requests, this feature is available by using `methods` property that
accepts an array. You can also add middlewares to the endpoint by using `.addMiddleware()` before `.build()`.
Endpoints can also handle multiple types of requests, by using `methods` property instead of `method` that
accepts an array. You can also add [middlewares](#create-a-middleware) to the endpoint by using `.addMiddleware()`
before `.build()`.
## Set up routing
Connect your endpoint to the `/v1/setUser` route:
```typescript

@@ -165,3 +202,2 @@ import {Routing} from 'express-zod-api';

```
This implementation sets up `setUserEndpoint` to handle requests to the `/v1/setUser` route.

@@ -381,3 +417,3 @@ ## Start your server

handler: async ({input: {avatar}}) => {
// avatar: {name, mv(), mimetype, encoding, data, truncated, size, ...}
// avatar: {name, mv(), mimetype, data, size, ...}
// avatar.truncated is true on failure

@@ -418,8 +454,13 @@ return {...};

attachRouting(config, routing);
const {notFoundHandler, logger} = attachRouting(config, routing);
app.use(notFoundHandler); // optional
app.listen();
logger.info('Glory to science!');
```
**Please note** that in this case you probably need to: parse `request.body`, call `app.listen()` and handle `404`
errors yourself;
errors yourself. In this regard `attachRouting()` provides you with `notFoundHandler` which you can optionally connect
to your custom express app.

@@ -426,0 +467,0 @@ ## Multiple schemas for a single route

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc