- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2.1k
          feat: add getRequestEvent to $app/server
          #13582
        
          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
Conversation
| 🦋 Changeset detectedLatest commit: ddc2381 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
 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 | 
| We should probably mention this in the docs somewhere (i.e. the prose and not just the reference docs) | 
| const hooks = await import('node:async_hooks'); | ||
| als = new hooks.AsyncLocalStorage(); | ||
| } catch { | ||
| // can't use AsyncLocalStorage, but can still call getRequestEvent synchronously. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do users see on StackBlitz? Will it be the Can only read the current request event when the event is being processed message? That might be confusing if so and perhaps it's better to throw a different error here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
upgraded the message to include a mention of ALS, if it's absent
| 
 any thoughts on where it would go? can't find a natural space for it | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for "where make this appear" - I would add it within a section of "loading data".
        
          
                packages/kit/test/apps/basics/src/routes/get-request-event/endpoint/+server.js
              
                Outdated
          
            Show resolved
            Hide resolved
        
      …point/+server.js Co-authored-by: Simon H <[email protected]>
| I'm not up on what the different runtimes provide, but is  | 
| Can you call  If I have have functions inside of functions in a load function, I have to prop drill (on the server only) the  This would really simplify that. J | 
| 
 It's supported in Bun, Deno (which I assume covers Netlify functions) and Cloudflare Workers (might need to add  
 That's what this is, yep! What's important is when the function is called (i.e. during  | 
| Added some docs, though I can't link to the preview because it won't deploy — the types will be wrong until this is released | 
| Is there a plan to make this feature available to shared load functions aswell? On the server the logic could be copied while on the client the event should be a singleton anyway, right? | 
| Imagine you had two routes,  // src/routes/foo/+page.js
import { getNavigationEvent } from '$app/navigation';
export async function load() {
  await sleep(1000);
  console.log(getNavigationEvent().url.pathname);
}The user clicks a link to  In other words this would result in subtle bugs — often harmless, but not always. So for this to behave correctly one of two things would need to happen: 
 Given that, I don't think we should offer a universal version of this function at this time. | 
| Yep, that makes sense. Would love to see AsyncContext everywhere. Thanks for clarifying! | 
| I understand if this would require a huge rewrite, but I figured I would ask. Since Svelte itself is a compiler, why couldn't Svelte(Kit) itself compile the event to be either a global or automatic prop drilled using the parser: From: export const load: PageLoad = async () => {
    await new Promise(r => setTimeout(r, 1000));
    await childFunction(); // may or may not be an async func
};
const childFunction  = async () => {
    console.log(getNavigationEvent().url.pathname);
}Compiled to: export const load: PageLoad = async (event) => {
    const childFunction  = async () => {
        console.log(event.url.pathname);
    }
    await new Promise(r => setTimeout(r, 1000));
    await childFunction(); // may or may not be an async func
};Or compiled to: // or maybe something like this which is perhaps more logical
// automatic prop drilling
export const load: PageLoad = async (event) => {
    await new Promise(r => setTimeout(r, 1000));
    await childFunction(event);
};
const childFunction = async (event) => {
    console.log(event.url.pathname);
}There would be no need for an actual context, as the event would be available anywhere: 
 I imagine this is extremely complex and changes things, but it is theoretically possible. I am first to admit I have a minimum understanding of the compiler. J | 
| @jdgamble555 : I think this is only possible if the two function are on the same file... But above all, I don't really see the point since we can simply use a parameter : export const load: PageLoad = async (event) => {
    await new Promise(r => setTimeout(r, 1000));
    await childFunction(event); // may or may not be an async func
};
const childFunction  = async (event) => {
    console.log(event.url.pathname);
} | 
| The same reason we have context in the first place, to avoid passing it as a parameter to nested components (prop drilling). In a real app, I am not going to have a child component, I'm going to have nested child components. export const load: PageLoad = async () => {
    const posts = await getPosts();
    return {
      posts
   };
};
const getPosts  = async () => {
    const user = await getUser();
    return await getPostsByUserId(user.id);
}
const getUser = async () => {
    // you may have locals setup in hooks.(server).ts i.e.
    const { locals: { db } } = getRequestEvent();
    return db.user;
};Is this not the reason for Context in the first place? Obviously this is incredibly simplified, but once your app gets complex enough, you will have nested functions for the single responsibility principle and to keep things clean. That being said, I do see your point. There would be no way for the parent to know that the child function uses an event if it is in a different file. Is it possible to build a svelte compiled browser version of  Thanks, J | 
| It will make code behave strangely. Even something as simple as this would break: function stuff(...args) {
  const event = getRequestEvent();
  console.log(args);
}It's never worth it. Transformations should be as simple and predictable as possible. | 
| Hi, I think  No sure if it should be done here, or on a separate PR. | 
| That's an interesting idea. I think it ought to be a separate discussion, because there's some tricky nuance around what (for example)  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
| This is going to save me so much complexity thank you so much for this you absolute legend! | 
This adds a
getRequestEventfunction to$app/server, which allows you to get... the currentRequestEvent:The value of this becomes slightly clearer when you have logic shared between multiple
loadfunctions (and/or hooks and actions). For example, you might have an API client that needs access toevent.fetchto make credentialled requests from the server. Today, you might do something like this:With
getRequestEvent, you can make the call simpler:// src/lib/server/api.ts import { API } from '$env/static/private'; +import { getRequestEvent } from '$app/server'; -export function get(path, { fetch }) { +export function get(path) { + const { fetch } = getRequestEvent(); const response = await fetch(`${API}/${path}`); return await response.json(); }// src/routes/blog/[slug]/+page.server.ts import { get } from '$lib/server/api'; -export async function load({ fetch, params }) { - const post = await get(`posts/${params.slug}`, { fetch }); +export async function load({ params }) { + const post = await get(`posts/${params.slug}`); return { post }; }It's also useful if you need to do auth stuff. For example we can make a utility function that redirects the user to
/loginif they're not logged in already:Then, any time you're creating a
loadfunction that requires the user to be logged in, you can do this...Today, most server runtimes support
AsyncLocalStorage. In these runtimes, you can callgetRequestEventanywhere, including after anawait:Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm testand lint the project withpnpm lintandpnpm checkChangesets
pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.Edits