Skip to content

Commit 59e171c

Browse files
committed
feat(validation): add Middy.js middleware for JSON Schema validation
1 parent 89c30fd commit 59e171c

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

packages/validation/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { validate } from './validate.js';
22
export { SchemaValidationError } from './errors.js';
33
export { validator } from './decorator.js';
4+
export { validationMiddleware } from './middleware.js';

packages/validation/src/middleware.ts

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { SchemaValidationError } from './errors.js';
2+
import type { ValidatorOptions } from './types.js';
3+
import { validate } from './validate.js';
4+
5+
export function validationMiddleware(options: ValidatorOptions) {
6+
if (!options.inboundSchema && !options.outboundSchema) {
7+
return {};
8+
}
9+
return {
10+
before: async (handler: { event: unknown }) => {
11+
if (options.inboundSchema) {
12+
try {
13+
handler.event = validate({
14+
payload: handler.event,
15+
schema: options.inboundSchema,
16+
envelope: options.envelope,
17+
formats: options.formats,
18+
externalRefs: options.externalRefs,
19+
ajv: options.ajv,
20+
});
21+
} catch (error) {
22+
throw new SchemaValidationError('Inbound validation failed', error);
23+
}
24+
}
25+
},
26+
after: async (handler: { response: unknown }) => {
27+
if (options.outboundSchema) {
28+
try {
29+
handler.response = validate({
30+
payload: handler.response,
31+
schema: options.outboundSchema,
32+
formats: options.formats,
33+
externalRefs: options.externalRefs,
34+
ajv: options.ajv,
35+
});
36+
} catch (error) {
37+
throw new SchemaValidationError('Outbound validation failed', error);
38+
}
39+
}
40+
},
41+
};
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { SchemaValidationError } from '../../src/errors.js';
3+
import { validationMiddleware } from '../../src/middleware.js';
4+
5+
const inboundSchema = {
6+
type: 'object',
7+
properties: {
8+
inputValue: { type: 'number' },
9+
},
10+
required: ['inputValue'],
11+
additionalProperties: false,
12+
};
13+
14+
const outboundSchema = {
15+
type: 'object',
16+
properties: {
17+
outputValue: { type: 'number' },
18+
},
19+
required: ['outputValue'],
20+
additionalProperties: false,
21+
};
22+
23+
describe('validatorMiddleware', () => {
24+
it('should validate inbound and outbound successfully', async () => {
25+
// Prepare
26+
const middleware = validationMiddleware({ inboundSchema, outboundSchema });
27+
const handler = {
28+
event: { inputValue: 10 },
29+
response: { outputValue: 20 },
30+
};
31+
// Act
32+
if (middleware.before) {
33+
await middleware.before(handler);
34+
}
35+
if (middleware.after) {
36+
await middleware.after(handler);
37+
}
38+
// Assess
39+
expect(handler.event).toEqual({ inputValue: 10 });
40+
expect(handler.response).toEqual({ outputValue: 20 });
41+
});
42+
43+
it('should throw error on inbound validation failure', async () => {
44+
// Prepare
45+
const middleware = validationMiddleware({ inboundSchema });
46+
const handler = {
47+
event: { inputValue: 'invalid' },
48+
response: {},
49+
};
50+
// Act & Assess
51+
await expect(middleware.before?.(handler)).rejects.toThrow(
52+
SchemaValidationError
53+
);
54+
});
55+
56+
it('should throw error on outbound validation failure', async () => {
57+
// Prepare
58+
const middleware = validationMiddleware({ outboundSchema });
59+
const handler = {
60+
event: {},
61+
response: { outputValue: 'invalid' },
62+
};
63+
// Act & Assess
64+
await expect(middleware.after?.(handler)).rejects.toThrow(
65+
SchemaValidationError
66+
);
67+
});
68+
69+
it('should no-op when no schemas are provided', async () => {
70+
// Prepare
71+
const middleware = validationMiddleware({});
72+
const handler = {
73+
event: { someKey: 'value' },
74+
response: { anotherKey: 'value' },
75+
};
76+
// Act
77+
if (middleware.before) {
78+
await middleware.before(handler);
79+
}
80+
if (middleware.after) {
81+
await middleware.after(handler);
82+
}
83+
// Assess
84+
expect(handler.event).toEqual({ someKey: 'value' });
85+
expect(handler.response).toEqual({ anotherKey: 'value' });
86+
});
87+
});

0 commit comments

Comments
 (0)