sveltekit-superapi
v0.0.5
Published
Type-safe fetch and endpoint for SvelteKit
Maintainers
Readme
Let SvelteKit enjoy end-to-end type safety like Elysia + Eden
Inspired by Elysia+EdenFetch
Still under development...
!!! API will definitely undergo destructive updates
display
Define interfaces through zod and automatically serialize them into objects
// superEndpoint
import { superEndpoint } from "sveltekit-superapi/server"
import { json, type RequestHandler } from "@sveltejs/kit"
import { mockData } from "./mock"
import z from "zod"
const apiIdPostModel = {
cookies: z.object({ token: z.string() }),
body: z.object({ name: z.string(), age: z.coerce.number().min(1).nullable() }),
params: z.object({ id: z.coerce.number() }),
query: z.object({ pageIndex: z.coerce.number().default(0) }),
}
export const POST: RequestHandler = superEndpoint(
apiIdPostModel,
({ body, query, cookies, params }) => {
let rows = mockData
if (body?.name) rows = rows.filter((row) => row.name.startsWith(body.name))
if (body?.age) rows = rows.filter((rows) => rows.age === body.age)
return json({ rows, cookies, query, fetchId: params.id })
},
)
export type POST = typeof POSTFetch type inference superFetch(url,?options) URL and method are automatically completed based on +server.ts
// superFetch in +page.svelte
import { superFetch } from "sveltekit-superapi/client"
const { data } = $props()
// svelte-ignore state_referenced_locally
let rows = $state(data.rows)
// svelte-ignore state_referenced_locally
let fetchId = $state(data.fetchId)
let filter = $state({ name: "", age: null })
const onclick = async () => {
// superFetch
const data = await superFetch("/api/[id]", {
method: "POST",
body: filter,
params: { id: Date.now() },
})
// const data = await fetch(`/api/${Date.now()}`, {
// method: "POST",
// body: JSON.stringify($state.snapshot(filter)),
// headers: { "content-type": "application/json" },
// }).then((res) => res.json())
rows = data.rows
fetchId = data.fetchId
}// +page.server.ts
import { superFetch } from "sveltekit-superapi/client"
export const load = async ({ fetch }) => {
const users = await superFetch("/api/users", {
fetch, // ← SvelteKit 的 event.fetch,内存调用
query: { page: 1, tags: ["a", "b"] },
})
return { users }
}
export const actions = {
create: async ({ fetch, request }) => {
const fd = await request.formData()
const user = await superFetch("/api/users", {
fetch,
method: "POST",
body: { name: fd.get("name"), age: Number(fd.get("age")) },
// Body supports FormData
})
return { user }
},
}Two methods for implementing type inference
1.Using the Vite plugin
import { superEndpointPlugin } from "sveltekit-superapi/vite"
import adapter from "@sveltejs/adapter-auto"
import { sveltekit } from "@sveltejs/kit/vite"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [
sveltekit({
compilerOptions: {
// Force runes mode for the project, except for libraries. Can be removed in svelte 6.
runes: ({ filename }) =>
filename.split(/[/\\]/).includes("node_modules") ? undefined : true,
},
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
}),
superEndpointPlugin({ debug: true }),
],
})2.Type of input
// +server.ts
export const POST: RequestHandler = superEndpoint(
apiIdPostModel,
({ body, query, cookies, params }) => {
let rows = mockData
if (body?.name) rows = rows.filter((row) => row.name.startsWith(body.name))
if (body?.age) rows = rows.filter((rows) => rows.age === body.age)
return json({ rows, cookies, query, fetchId: params.id })
},
)
export type POST = typeof POST
// +page.server.ts
import { superFetch } from 'sveltekit-superapi/client'
import type { POST } from './+server.ts'
export const load = async ({ fetch }) => {
const users = await superFetch<POST>('/api/users', {
fetch, // ← SvelteKit 的 event.fetch,内存调用
query: { page: 1, tags: ['a', 'b'] },
})
return { users }
}
