Allow SvelteKit developers to provide schemas or contracts for returned data
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 20.8k
- Forks
- 2.3k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 156
Description
Updated 2024 November 9 to include other proposals from below comments.
Describe the problem
My team is provided a set of internally-managed API client libraries. We use +page.server.js to make API requests within load and return data to the page.
It's easy to make the mistake of returning the full API response to the page without filtering out potentially sensitive fields.
Describe the proposed solution
1. Type overrides
Some way to override generated types through a global declarations file. I would imagine that would be a tough challenge since something like PageServerLoad is generated based on its inferred return type on a per-route basis; so I'm unsure of a conventional declarations file could work versus a JS function received by svelte.config.js.
Hypothetically, this override would allow me to centrally configure unacceptable return types such as the following:
import api from '@internal/api-profiles'
/** @type {import('./$types').PageServerLoad} */
export async function load() {
// inferred type: `Promise<ClientAPIResponse>`
const res = await api.listProfiles({ body: { page: 1, per_page: 10 } })
const data = await res.json()
// ❌ type error, cannot return `Promise<ClientAPIResponse> | ClientAPIResponse`
return { data }
};
And that way, it would give pause to the developer to ensure they have sanitized their response:
import api from '@internal/api-profiles'
/** @type {import('./$types').PageServerLoad} */
export async function load() {
const res = await api.listProfiles({ body: { page: 1, per_page: 10 } })
const data = await res.json()
const profiles = data.map(el => ({
name: el.name,
id: el.id
}))
// ✅ no leaked emails
return { profiles }
};
2. New schema reserved export
In +page.server.js, allow developers to export a validate function that is called after load and before data gets server-rendered or returned to the client.
- This would be a breaking change since it introduces a new reserved name as an export.
- A developer can use this to apply any schema validation library they want
- A new opt-in config will throw an error at build time in case any given route lacks a
validateexport. Such assvelte.config.js@kit.validateAllRoutes:true, which helps developers remember to add avalidateexport to any server file with aloador endpoint export - this could potentially have use with Form Actions
+page.server.js
import { z } from 'zod'
// param is type `RequestEvent & input`
// Where `input` is the returned data from `load`
export function validate({ input }) {
const schema = z.object({
fruit: z.string(),
age: z.number()
}) // strip unrecognized key `socialInsurance`
const result = schema.safeParse(input)
// maybe a `handleValidation` hook can be used to make this less repetitive
if (!result.success) {
// log error
} else {
// client page's `data` prop should infer its type from this result
return result
}
}
export async function load() {
const fetchedObject = {
fruit: 'apple',
age: 2,
socialInsurance: 'apple-54321'
}
return fetchedObject
}
+server.js
This example needs more thought.
- Should users manipulate and return a new
Responseobject? - There needs to be a way to validate potential
400/409/etcresponses
// param is type `RequestEvent & Response`
// Where `Response` is the returned data from a given endpoint
export function validate({ request, response }) {
switch (request.method) {
case 'PUT':
const schema = z.object({
fruit: z.string(),
age: z.number()
}) // strip unrecognized key `socialInsurance`
const result = schema.safeParse(input)
// maybe a `handleValidation` hook can be used to make this less repetitive
if (!result.success) {
// log error
} else {
// client page's `data` prop should infer its type from this result
return result
}
break;
}
}
export async function POST() {
const data = {
fruit: 'apple',
age: 20,
socialInsurance: 'abc123'
}
return new Response(JSON.stringify(data), { status: 201 })
}
3. Schema hooks
Relates to: https://github.com/sveltejs/kit/issues/12623
Inspired by: https://github.com/ahmadnassri/node-oas-fastify
And can potentially be its own ticket or enhancement.
Maybe developers could write a Vite plugin to parse their own schema methodology, such as OpenAPI Schema. The plugin needs a way to parse built SvelteKit routes in order to add custom middleware to them.
Given this file structure:
.
└── src/
├── routes/
│ └── api/
│ └── fruits/
│ └── [id]/
│ └── +server.js
└── spec/
└── paths/
└── fruits/
└── update.yml
The schema update.yml contains data that maps to a path in the file-based router. In this case, update.yml has keys that point to a SvelteKit endpoint as well as JSON schemas for validation based on response:
path: /api/fruits/[id]
method: PUT
responses:
201: ...
400: ...
409: ...
Alternatives considered
- Solve this myself with a wrapper function or by changing the behaviour of our own API client libraries to accept a required 'fields' param. Which will be my workaround
- Wrap all
loadmethod data in a validator before callingreturn - Write a server hook that filters sensitive responses
- Write a plugin that can receives schemas that dictate
loadresponses; similar to https://www.npmjs.com/package/oas-fastify - Use GraphQL
Importance
nice to have
Additional Information
- Discord thread that inspired this feature request.
- Relates to https://github.com/sveltejs/kit/issues/12623
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by comparing the proposed exports in +page.server.js and +server.js with the svelte.config.js option, then review related issue #12623 and the linked discussion. Done requires a single agreed validation or schema-hook design, defined behavior for load data and endpoint responses, and corresponding implementation tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- backend-api-design, security, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100