sveltejs / sveltejs/kit

Remote functions: provide spec-driven response contracts and mandatory guards

Open
#14,612 3 comments 14 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
20.8k
Forks
2.3k
Avg merge
1d 16h
Merged PRs (30d)
156

Description

Describe the problem

Teams that rely on assurances when shipping secure applications look to integration and contract testing, and ideally defining schemas that translate to routes (in the case of RESTful APIs) and request and response parsers. At the time of this writing, SvelteKit's Remote Functions do well to require a Standard Schema or an 'unchecked' declaration for methods that accept input parameters.

To guard these functions with logic that must run before these functions' execution (particularly authentication), the current recommendation is to either inline logic with a reusable function call or with repeated code, or to write a higher order function. These are optional approaches that require teams to explicitly train or document as part of onboarding since there are no protections from neglect or errors at build time nor run time as new features are authored in a project.

Since function request parameters are currently able to be validated with a Standard Schema, the remaining missing pieces for full end-to-end contracts are:

  • The ability to provide a schema declaration for response/output types that automatically parse away unexpected object keys.
  • The ability to explicitly define function guards.
  • Adding an opt-in configuration to require these new features.
Describe the proposed solution

I can imagine writing response schemas and guard methods to be an opt-in feature via a flag such as kit.remoteFunctions.protections = 'strict' | 'request-only' | 'request-and-response', and could take one of a few forms:

  1. As chained methods
import { itemSlug, itemBrief } from './schemas'
import { checkAuth } from '$lib/hooks.server'
import { query } from '$app/server'

export const getItem = query(itemSlug, async (slug) => {
  // ...
})
  .guard([checkAuth]) // error thrown if no `guard` defined
  .validateResponse(itemBrief) // error thrown if no schema provided for response

In all cases, 'unchecked' can be passed to forego checks, but are required to be passed in strict mode.

- .guard([checkAuth])
+ .guard('unchecked')
  1. As additional parameters
import { itemSlug, itemBrief } from './schemas'
import { checkAuth } from '$lib/hooks.server'
import { query } from '$app/server'

export const getItem = query(itemSlug, async (slug) => {
  // ...
}, {
  guard: [checkAuth], // throw error if not provided
  validateResponse(itemBrief), // throw error if not provided
  // other potential hooks or options
})
  1. As new functions or altered parameters
import { itemSlug, itemBrief } from './schemas'
import { checkAuth } from '$lib/hooks.server'
import { advancedQuery } from '$app/server'

export const getItem = advancedQuery({
  preHandler: [checkAuth],
  handler: async function (slug) { /* ... */ },
  requestSchema: itemSlug,
  responseSchema: itemBrief
})

Things I'm uncertain about:

  • How these suggestions would impact single-flight mutations or batching.
  • Whether there's a need for comprehensive hooks (Fastify Hooks for reference), or if an array of functions is all that's needed before a Remote Function's handler gets called.

Other examples:

The outcome of this ticket could aid with desktop applications, as described in https://github.com/sveltejs/kit/issues/14336.

To be clear, I'd like a way to oblige developers to write response schemas and list pre-run hooks for every Remote Function definition. This takes inspiration from RESTful API tools, but is not intended for writing RESTful APIs.

Alternatives considered
  • Rely on a potential solution for the server handle hook to conditionally guard certain remote functions based on their path; part of https://github.com/sveltejs/kit/issues/14528.
  • Write actual contract tests as a practice.
Importance

would make my life easier

Additional Information

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names Remote Functions through the $app/server query entry point but no implementation files or tests. First map the existing request-schema validation path and how batching or single-flight mutations work; done means an agreed opt-in API covering response schemas, guards, and explicit unchecked cases, with tests for each mode.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
backend-api-design, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.