-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Breaking: use Request
and Response
objects in endpoints and hooks
#3384
Conversation
🦋 Changeset detectedLatest commit: 7a5a03a The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✔️ Deploy Preview for kit-demo canceled. 🔨 Explore the source changes: 7a5a03a 🔍 Inspect the deploy log: https://app.netlify.com/sites/kit-demo/deploys/61e86ce8ece61400079c3eed |
A realisation: if the signature of Netlify provides a So of the officially supported adapters, the only one that can actually make use of the The conclusion is that we should remove |
Another realisation: since request bodies can only be consumed once, and since there are valid reasons to need to control how it's consumed (#831, #70 (comment)), we can't realistically parse the body before calling handlers. And the endpoint-level config idea (#70 (comment)) is a non-starter unfortunately, since we would need to parse the body before identifying which endpoint(s) matched the request. So I've changed my mind a bit about the API — I think we need to expose the underlying -export async function post({ url, method, headers, rawBody, body, params, locals }) {
+export async function post({ request, url, params, locals }) {
+ const body = await request.json();
// ...
} (Since And this would in fact enable file uploads, albeit in a suboptimal way: export async function post({ request }) {
const data = await request.formData();
await send_to_s3(data.get('video'));
// ...
} For handling large files this isn't ideal, since it buffers all the data in memory. Essentially what we'd eventually need is a way to handle uploads in streaming fashion — Remix has parseMultiPartFormData which is close to what I'm imagining, except that it appears to be Node-only. Something like this: import { multipart } from '$app/somewhere';
export async function post({ request }) {
for await (const { name, value } of multipart(request)) {
if (name === 'video') {
await send_to_s3(value.name, value.type, value.stream());
}
}
// ...
} Happily, that doesn't need to be part of this PR. |
A PR in SvelteKit introduces a number of breaking changes that lay the foundation for streaming request/response bodies enable multipart form handling (including file uploads) better align SvelteKit with modern platforms that deal with `Request` and `Response` objects natively Hooks (`handle`, `handleError` and `getSession`) and endpoints previously received a proprietary `Request` object: ```ts interface Request<Locals = Record<string, any>, Body = unknown> { url: URL; method: string; headers: RequestHeaders; rawBody: RawBody; params: Record<string, string>; body: ParameterizedBody<Body>; locals: Locals; } ``` Instead, they now receive a `RequestEvent`: ```ts export interface RequestEvent<Locals = Record<string, any>> { request: Request; // https://developer.mozilla.org/en-US/docs/Web/API/Request url: URL; // https://developer.mozilla.org/en-US/docs/Web/API/URL params: Record<string, string>; locals: Locals; } ``` `method` and `headers` are no longer necessary as they exist on the `request` object. (`url` is still provided, since the `URL` object contains conveniences like `url.searchParams.get('foo')`, whereas `request.url` is a string.) To access the request body use the text/json/arrayBuffer/formData methods, e.g. `body = await request.json()`. See sveltejs/kit#3384 for details
So, is it possible to get the rawBody data now? I'm using Stripe's API and need the raw request body from an endpoint's post function. Help! |
I was lead here by a svelte error when trying to update packages of https://github.com/timsonner/sveltekit-system-call |
I'm also struggling to figure out how to get the |
I'm not sure about mux.com but I was able to get the rawBody for Stripe's checkout webhook with this:
|
Thanks @KTruong008, this is how I fixed my code (you may find using export async function post({ request }) {
const rawBody = await request.buffer();
const body = JSON.parse(rawBody.toString());
const signature = request.headers.get('mux-signature') || '';
const eventData = body.data;
let isValidSignature = false;
try {
isValidSignature = Webhooks.verifyHeader(
rawBody,
signature,
MUX_WEBHOOK_SECRET
); |
@benwoodward — You can also get the raw body from |
I need help finding right replacement. Original import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ request, resolve }) => {
if (request.url.searchParams.has('_method')) {
request.method = request.url.searchParams.get('_method').toUpperCase();
}
const response = await resolve(request);
return response;
}; => So I changed import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
if (event.url.searchParams.has('_method')) {
event.request.method = event.url.searchParams.get('_method').toUpperCase();
}
const response = await resolve(event);
return response;
}; But it is not working giving this error
Would you please tell me how to fix the problem? |
@hyunduk0206 I recommend asking this kind of question on StackOverflow or the svelte discord. However, I would just console.log out the values to see what's contained within the parameters. |
@benwoodward Thank you for your reply. I will not ask this kind of question here, except for this question. I have tried to print the values, but I couldn't due to error. But I can write down the part of code that is related to the value. import type { Request } from '@sveltejs/kit';
import PrismaClient from '$lib/prisma';
const prisma = new PrismaClient();
export const api = async (request: Request, data?: Record<string, unknown>) => {
let status = 500;
let body = {};
switch (request.method.toUpperCase()) {
case 'GET':
status = 200;
body = await prisma.todo.findMany();
break;
case 'POST':
status = 201;
body = await prisma.todo.create({
data: {
created_at: data.created_at as Date,
done: data.done as boolean,
text: data.text as string
}
});
break;
case 'DELETE':
status = 200;
body = await prisma.todo.delete({
where: {
uid: request.params.uid
}
});
break;
case 'PATCH':
status = 200;
body = await prisma.todo.update({
where: {
uid: request.params.uid
},
data: {
done: data.done,
text: data.text
}
});
break;
default:
break;
}
if (request.method.toUpperCase() !== 'GET' && request.headers.accept !== 'application/json') {
return {
status: 303,
headers: {
location: '/'
}
};
}
return {
status,
body
};
}; |
I solved my problem by following the offical doc (https://kit.svelte.dev/docs/routing#endpoints-http-method-overrides). |
There were a few breaking changes to work through (unsprisingly, we jumped about 100 versions here!), namely the new objects being passed to the hook functions (sveltejs/kit#3384).
Adding the info of how to read request body data to kit.svelte.com would be quite useful, it took me over a day to find this thread. |
https://kit.svelte.dev/docs/web-standards#fetch-apis-request ? Could be in a clearer place alongside |
Yes, my app only has I found the answer from this thread, Svelte gave the link as a console error, but all the attempts with |
I’m currently getting “Invalid Request Body” when deployed and uploading a file using a Named Action through a form. Any ideas why? |
Hey does this solution still work for you? I'm having issues with the arraybuffer. It seems that you can get the body via |
This PR introduces a number of breaking changes that
Request
andResponse
objects nativelyHow to update your app
Hooks (
handle
,handleError
andgetSession
) and endpoints previously received a proprietaryRequest
object:Instead, they now receive a
RequestEvent
:method
andheaders
are no longer necessary as they exist on therequest
object. (url
is still provided, since theURL
object contains conveniences likeurl.searchParams.get('foo')
, whereasrequest.url
is a string.)Updating hooks
The
resolve
function passed tohandle
now returns aPromise<Response>
;handle
must do likewise.A rewritten
handle
hook might look like this:(This example illustrates the new APIs but also demonstrates a way in which rewriting HTML is now less efficient; we may need to add a
transformPage
option or something to satisfy that use case in a more streamlined way.)Updating endpoints
Similarly, handlers receive a
RequestEvent
. MostGET
handlers will be unchanged, but any handler that needs to read the request body will need to update:Handlers do not need to return a
Response
object, but they can. Specifically, theheaders
property can be any form that can be passed to theHeaders
constructor......which means that since a
Response
object has a compliantstatus
,headers
andbody
, you can do this sort of thing:Updating svelte.config.js
If you're using the
headers
,host
orprotocol
config options, you should remove them. Most adapters will automatically set the correct URL without the help of these options; in other cases likeadapter-node
the options have been moved into the adapter options.How to update your custom adapter
The interface for adapters has changed as well — the
app.render
function now takes aRequest
and returns aPromise<Response>
.On platforms that use these objects natively (like Cloudflare Workers and Deno) this means you can delete some code. On Lambda-like and Node-like platforms you must create the
Request
and handle theResponse
yourself — consult theadapter-node
andadapter-netlify
examples:kit/packages/adapter-node/src/handler.js
Lines 37 to 49 in 84f6136
kit/packages/adapter-netlify/src/handler.js
Lines 17 to 23 in 84f6136
(Note that your adapter may need to expose options to configure the base URL (or protocol/host header) like
adapter-node
does, following the removal of theheaders
/host
/protocol
config options.)What's next
After these API changes are in place, we can implement support for streaming request/response bodies, which will enable handling of large files and so on (at least on platforms that support streaming, i.e. not Lambda).
Original PR comment
Breaking change — changes the
app.render
signature, which adapters will need to accommodateapp.render
should accept aRequest
as inputapp.render
should return aResponse
Headers
object (enables one-linefetch
proxying)Request
andResponse
and node req/resReadableStream
object is supported everywhere —node-fetch
uses node streams under the hood instead of web streams)This doesn't yet tackle file uploads (#70), that can happen in a follow-up I think. It also doesn't mean that Svelte components somehow support streaming, this is purely about simplifying the API and enabling streamed endpoint request/response bodies
Follow-ups:
ReadableStream
to enable streamed responses in a cross-platform wayPlease don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm test
and lint the project withpnpm lint
andpnpm check
Changesets
pnpx changeset
and following the prompts. All changesets should bepatch
until SvelteKit 1.0