Cut-Short Requests
Cut-short is an Astro integration that lets you stop processing a request instantly and send back a custom response, simplifying control flow in your Astro applications. By introducing the endRequest
function, it eliminates the need for cumbersome workarounds like bubbling up response objects, throwing and catching sentinel errors, implementing custom middleware logic or replicating error response logic across all your pages.
Keep the the custom response for specific conditions close to the conditions and have it shared across all your application. It’s especially useful for scenarios like user authentication and access control, where you might need to redirect users to sign-in page from anywhere that requires authentication or to turn any page they don’t have access to into a 404 to avoid information leak (like GitHub does).
Installing the integration
npx astro add @inox-tools/cut-short
pnpm astro add @inox-tools/cut-short
yarn astro add @inox-tools/cut-short
How to use
From any code that is reachable from a page rendering context can use the endRequest
function to stop the.
A page-rendering context is when you are inside of:
- A middleware;
- The frontmatter of a page component (not components in the page, see streaming);
- An API endpoint;
- A function called from another page-rendering context.
import { endRequest } from '@it-astro/cut-short';
export function getUser() { if (noUser) endRequest(new Response('No user', { status: 401 }));
return {...};}
---import { getUser } from '../lib/auth';
// Calling a function from a rendering context allows it to stop the request.const user = getUser();---
<Dashboard />
import { endRequest } from '@it-astro/cut-short';import { getUser } from '../lib/auth';
export const GET = () => { if (someCondition) endRequest(new Response('Matched inline blocking condition'));
// Calling a function from a rendering context allows it to stop the request. const user = getUser();
return new Response(user.id);};
endRequest()
Type: (withResponse: Response | (() => Response | Promise<Response>)) => void
Stop the current request and send back a custom response.
The argument can be a Response
object to be used directly or a function that returns a Response
object or a promise that resolves to a Response
.
Options
disableStreaming
Type: boolean
Default: false
Disables HTML streaming, lifting its limitations.
Streaming
Astro serves all server-rendered pages using HTML streaming, which brings improved performance for users by allowing the page to start rendering earlier while parts of the page still being generated on the server. You can lean even more on streaming by passing promises around to components and into the final templates instead of awaiting them on the frontmatter, as shown on Astro’s streaming guide.
Although this is benefial for most sites, and is a sane default for Astro, HTML streaming has some caveats. Once Astro executes the frontmatter of the page component, the HTML response is streamed to the client as it is rendered. This means that when the frontmatter of components deep in the page is executed the response has already been partially sent. In the example below, when MyComponent
is executed, the response has already been constructed and is being streamed.
---import MyComponent from '../components/MyComponent.astro';---
<html> <head> <title>Example</title> </head> <body> <MyComponent /> </body></html>
This prevents components from changing the status code of the response and from completely switching the response under some condition. For that reason, calling endRequest
from a component in the page is not allowed, just like returning a response:
---import { endRequest } from '@it-astro/cut-short';
// Neither of these is allowedendRequest(new Response('Page not found', { status: 404 }));return new Response('Page not found', { status: 404 });---
This is not only limited to returning a response. Other features for on-demand rendering are also restricted to the frontmatter of the page and unavailable for any other component, such as:
- Setting response headers using
Astro.response.headers
- Setting the page status using
Astro.response.status
- Setting cookies using
Astro.cookies
- Redirecting to a different page using
return Astro.redirect()
- Rewriting the page as a different page using
return Astro.rewrite()
- Showing the 500 error page when an error is thrown from a component
Angelika Cathor wrote a great summary of those limitations and caveats, along with a few others external to Astro like SEO and instrumentation1. I am not well-versed in SEO, so I don’t know what are the impacts it can have.
License
Cut-Short Requests is available under the MIT license.
Footnotes
-
The Sentry limitation ws a bug on the integration and not a streaming limitation. Integrations can detect errors thrown from components even with streaming enabled, they just can’t change the page status based on the error. This bug was fixed on https://github.com/getsentry/sentry-javascript/pull/15995 ↩