Skip to content

Commit

Permalink
feat: add support for yup schema library
Browse files Browse the repository at this point in the history
 * modifications to allow either yup or zod to be used
 * renamed sst example to sst2 as sst3 is released.
  • Loading branch information
refactorthis committed Aug 23, 2024
1 parent f38f034 commit 6b02dbe
Show file tree
Hide file tree
Showing 32 changed files with 242 additions and 30 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "sst-api",
"name": "sst-v2",
"version": "0.0.0",
"private": true,
"type": "module",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@sst-api/core",
"name": "@sst-v2/core",
"version": "0.0.0",
"type": "module",
"scripts": {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@sst-api/functions",
"name": "@sst-v2/functions",
"version": "0.0.0",
"type": "module",
"scripts": {
Expand All @@ -13,7 +13,7 @@
"vitest": "^1.5.0"
},
"dependencies": {
"@rt/funcy": "link:../../../../",
"zod": "^3.22.4"
"zod": "^3.22.4",
"yup": "^1.4.0"
}
}
28 changes: 28 additions & 0 deletions examples/sst-v2/packages/functions/src/api-yup/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { object, string, number, date, array } from 'yup'

export const GetTodoPath = object({
id: string().required(),
})

export const ListQuery = object({
skip: number().optional(),
take: number().optional(),
})

export const CreateTodoRequest = object({
id: string(),
title: string(),
description: string(),
due: date(),
})

export const TodoResponse = object({
id: string(),
title: string(),
})

export const ListTodoResponse = object({
items: array(TodoResponse),
skip: number(),
take: number(),
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Todo } from '@sst-api/core/todo'
import { Todo } from '@sst-v2/core/todo'
import { api, res } from '@refactorthis/funcy'
import { CreateTodoRequest, GetTodoPath, ListQuery, ListTodoResponse, TodoResponse } from './models'

Expand Down
46 changes: 46 additions & 0 deletions examples/sst-v2/packages/functions/src/api-zod/todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Todo } from '@sst-v2/core/todo'
import { api, res } from '@refactorthis/funcy'
import { CreateTodoRequest, GetTodoPath, ListQuery, ListTodoResponse, TodoResponse } from './models'

export const create = api({
parser: {
request: CreateTodoRequest,
},
handler: async ({ request }) => {
await Todo.create(request)
return res.created()
},
})

export const get = api({
parser: {
path: GetTodoPath,
response: TodoResponse,
},
handler: async ({ path }) => {
const response = await Todo.get(path.id)
return res.ok(response)
},
})

export const list = api({
parser: {
query: ListQuery,
response: ListTodoResponse,
},
handler: async ({ query }) => {
const { skip = 0, take = 20 } = query
const items = await Todo.list(skip, take)
return res.ok({ items, skip, take })
},
})

export const remove = api({
parser: {
path: GetTodoPath,
},
handler: async ({ path }) => {
await Todo.remove(path.id)
return res.ok()
},
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventHandler } from 'sst/node/event-bus'
import { Todo } from '@sst-api/core/todo'
import { Todo } from '@sst-v2/core/todo'

export const handler = EventHandler(Todo.Events.Created, async (evt) => {
console.log('Todo created', evt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"baseUrl": ".",
"allowSyntheticDefaultImports": true,
"paths": {
"@sst-api/core/*": ["../core/src/*"],
"@sst-v2/core/*": ["../core/src/*"],
"@refactorthis/funcy": ["../../../../package"]
}
}
Expand Down
35 changes: 35 additions & 0 deletions examples/sst-api/pnpm-lock.yaml → examples/sst-v2/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
4 changes: 2 additions & 2 deletions examples/sst-api/readme.md → examples/sst-v2/readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# sst-api
# sst-v1

This is an example project using SST & funcy.
This is an example project using SST v2 & funcy.

See the [tutorial](https://sst.dev/examples/how-to-create-a-rest-api-with-serverless.html) for more information.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { API } from './stacks/MyStack'
export default {
config(_input) {
return {
name: 'sst-api',
name: 'sst-v2',
region: 'us-east-1',
}
},
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"typescript-eslint": "^7.18.0",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0",
"yup": "^1.4.0",
"zod": "^3.23.8"
},
"dependencies": {
Expand All @@ -64,9 +65,6 @@
"@middy/warmup": "^5.4.6",
"lodash.merge": "^4.6.2"
},
"peerDependencies": {
"zod": "^3.22.4"
},
"engines": {
"node": ">=14"
},
Expand Down
7 changes: 5 additions & 2 deletions package/src/core/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
* We use duck typing here, so that we don't need to pull in the package deps.
*/

// zod
export type ZodSchemaLike<TInput> = {
_input: TInput
parseAsync(...args: any[]): any
}

export type Schema<T> = ZodSchemaLike<T> // add other parsers here, eg. | YupSchemaLike<T>
export type YupSchemaLike<TInput> = {
validate(value: TInput, options?: any): Promise<any>
}

export type Schema<T> = ZodSchemaLike<T> | YupSchemaLike<T>

/**
* Api parsing options
Expand Down
5 changes: 5 additions & 0 deletions package/src/integrations/api/middleware/validator.mware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,10 @@ const validate = async (schema: any, value: any, path: string[]) => {
return await schema.parseAsync(value, { path })
}

// yup
if (typeof schema.validate === 'function') {
return await schema.validate(value)
}

throw new Error(`Unable to find validator function for schema ${schema}`)
}
47 changes: 47 additions & 0 deletions package/test/integrations/api/validation-yup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { it, expect } from 'vitest'
import { createApi, res } from 'package/src/integrations/api'
import * as events from '../../mocks/api-proxy-events'
import { ctx } from '../../mocks/lambda-context'
import { object, number, string } from 'yup'
import { APIGatewayProxyEventV2 } from 'aws-lambda'

const api = createApi()

const fn = api({
parser: {
request: object({
id: number(),
name: string().required(),
}),
},
handler: () => res.ok(),
})

it("should fail if doesn't pass validation", async () => {
const event: APIGatewayProxyEventV2 = {
...events.payloadV2,
body: JSON.stringify({
id: 1,
}),
}

const response = await fn(event, ctx())
expect(response.statusCode).toBe(400)
expect(JSON.parse(response.body!)).toMatchObject({
message: 'Invalid Request',
details: ['name is a required field'],
})
})

it('should succeed if passes validation', async () => {
const event: APIGatewayProxyEventV2 = {
...events.payloadV2,
body: JSON.stringify({
id: 1,
name: 'Test',
}),
}

const response = await fn(event, ctx())
expect(response.statusCode).toBe(200)
})
2 changes: 1 addition & 1 deletion package/test/integrations/api/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ctx } from '../../mocks/lambda-context'
import z from 'zod'
import { APIGatewayProxyEventV2 } from 'aws-lambda'

// TODO fix typescript warnings here
// TODO fix typescript warnings here (need to fix ApiResult to handle error schema)

const api = createApi()

Expand Down
Loading

0 comments on commit 6b02dbe

Please sign in to comment.