starlight-openapi
starlight-openapi copied to clipboard
Disable Pre-render
Describe the bug
There is some way to disable the pre-render when using this plugin? I am using an authentication middleware on my documentation, and would like to block some requests.
To Reproduce
Using an authentication middleware won't work because of the following snippet:
export function starlightOpenAPIIntegration(schemas: Schema[]): AstroIntegration {
const starlightOpenAPI: AstroIntegration = {
name: 'starlight-openapi',
hooks: {
'astro:config:setup': ({ injectRoute, updateConfig }) => {
injectRoute({
entrypoint: 'starlight-openapi/route',
pattern: `[...openAPISlug]`,
prerender: true,
})
updateConfig({
vite: {
plugins: [vitePluginStarlightOpenAPISchemas(schemas)],
},
})
},
},
}
return starlightOpenAPI
}
Expected behavior
An option to disable the pre-render
How often does this bug happen?
Every time
System Info
No response
Additional Context
No response
Thanks for the feedback :raised_hands:
This is not something currently supported. This brings up some interesting questions as how this would work as I assume we would not want to parse the schemas for every requests. Some parser utilities are also still used during the rendering process (e.g. to resolve references) and we may not want to bundle them in the final build.
Considering how heavy some schemas can be and how it can increase the build time, if the goal is just to protect the schema pages behind an authentication wall, I also wonder if some other approach could be used, e.g. edge middlewares or something else, so that these pages which don't change very often can still be pre-rendered.
There are definitely more research and investigation to be done here, not something I'll have the bandwidth to do in the near future, but I'll be happy to discuss more if someone wants to take a stab at it.
@HiDeoo - I am currently exploring a similar set up to brunoalano, prerender set to false so I can use middleware for authentication. Have you had any more thoughts on it since it was brought up?
I'd be interested in helping to solve this issue as it is a blocker to migrating away from a docusaurus docs site I'm currently working on. We're using the PaloAlto openapi plugin there rendering several versions of an api spec which totals around 1500 pages.
I am relatively new to Astro + Starlight but and have some experience contributing to the docusaurus plugin listed above so hopefully I would be of some help.
One of the biggest issues we're facing is large build times (~12mins on average) for these pages to be rendered to HTML.
Perhaps there is something we could achieve with a Content Collection + page template built for SSR mode? https://docs.astro.build/en/guides/content-collections/#building-for-server-output-ssr
In the route.astro you then just query the collection for the matching operationId or overview and render on the fly
https://github.com/HiDeoo/starlight-openapi/blob/main/packages/starlight-openapi/components/Route.astro#L23
@HiDeoo
I was having a go at this over the weekend. This is rough but I was just seeing if I could put together the flow I had in my head
The loader:
- Parse the schemas
- Add each operation to the store
import path from 'node:path'
import { dereference } from '@readme/openapi-parser'
import type { Loader } from 'astro/loaders'
import { parseSchema } from '../libs/parser'
interface StarlingOpenApiLoaderOptions {
schemas: Record<string, string>
}
export function starlightOpenAPILoader({ schemas }: StarlingOpenApiLoaderOptions): Loader {
return {
name: 'starlight-openapi-loader',
load: async ({ store, logger }) => {
const parsedSchemas = await Promise.all(
Object.entries(schemas).map(async ([versionId, schema]) => {
const parsedSchema = await parseSchema(logger, {
schema,
base: '',
sidebar: {
collapsed: false,
operations: { sort: 'document', badges: true, labels: 'summary' },
tags: { sort: 'document' },
label: 'label',
},
})
return {
version: versionId,
parsedSchema,
}
}),
)
for (const schema of parsedSchemas) {
const deRefedDocument = await dereference(schema.parsedSchema.document)
store.set({
id: schema.version,
data: {
schema,
},
})
if (deRefedDocument.paths) {
for (const [, pathObject] of Object.entries(deRefedDocument.paths)) {
for (const [method, operationObject] of Object.entries(pathObject)) {
store.set({
id: path.join(schema.version, operationObject.operationId.toLowerCase()),
data: { operationObject, method },
})
}
}
}
}
},
}
}
Route.astro, pick out the entry from the store using the slug
---
import { getEntry } from "astro:content";
import Operation from './operation/Operation.astro'
const slug = Astro.params['openAPISlug']
const [,schemaName,type,id] = slug?.split('/')
const schema = await getEntry('openapi', schemaName)
const entry = await getEntry('openapi', `${schemaName}/${id}`)
if(entry == null) {
Astro.response.status = 404;
Astro.response.statusText = 'Not found';
}
---
It certainly needs some refinement, but what do you think?
Thanks for the investigation @omonk, really appreciated :raised_hands:
While I think a loader might be the best long-term solution here, this is something I have experimented with in another plugin, and I don't quite think the API provided by Astro is quite there yet for this use case. It's still quite tricky to share informations between all the moving part, e.g. the config, an integration, a plugin, the loader, and the runtime. There are also some discrepancies between dev and build mode that make it tricky to get right, and some cache invalidation also needs to be handled manually which is not ideal.
Currently, the injected route is in charge of dereferencing the schema, which is loaded from a virtual module. I think we should be able to move that logic to the integration/plugin side, and have the virtual module export the dereferenced schema directly. Once done, we should be able to inject 2 different routes based on the prerender option (one for prerendered mode, one for SSR mode) just like Starlight is doing.
For the prerendered route, the logic should still be pretty much the same as today, I assume just some small adjustments. For the SSR one, we would need some kind of utility like in Starlight to figure out which part of the schema to serve based on the request. But that should be doable.
I also don't quite remember if dereference is the only utility that we use from the OpenAPI parser so that would have to be checked as well and if needed, move other logic to the integration/plugin side.