Skip to content

feat: support proxying useSanityQuery requests#1286

Merged
danielroe merged 5 commits intomainfrom
feat/proxy-requests
Feb 9, 2026
Merged

feat: support proxying useSanityQuery requests#1286
danielroe merged 5 commits intomainfrom
feat/proxy-requests

Conversation

@rdunk
Copy link
Copy Markdown
Collaborator

@rdunk rdunk commented Aug 14, 2025

🔗 Linked issue

N/A

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Introduces support for proxying requests made using useSanityQuery, this will allow apps that need to query private datasets to do so without having to use server components, and they can largely follow the same patterns as those used by apps that query public datasets.

It will be up to the user to implement their own server handler for proxying requests. However this PR also ships a useful helper (validateSanityQuery, see example in playground app) which makes this quite simple and means one handler can be used for all (most?) queries, rather than needing to create server handlers for each unique GROQ query users want to execute. The module now statically analyses all GROQ queries in the app at build time, so the helper can validate incoming queries against a whitelist to prevent use of this endpoint for other queries.

@netlify
Copy link
Copy Markdown

netlify bot commented Aug 14, 2025

Deploy Preview for nuxt-sanity-module failed. Why did it fail? →

Name Link
🔨 Latest commit 9e3ec86
🔍 Latest deploy log https://app.netlify.com/projects/nuxt-sanity-module/deploys/698a14be6d0cc5000825f9a2

@rdunk rdunk force-pushed the feat/proxy-requests branch 5 times, most recently from 0ba1f8f to 4dc067c Compare August 18, 2025 12:46
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Aug 18, 2025

Open in StackBlitz

npm i https://pkg.pr.new/@nuxtjs/sanity@1286

commit: 9e3ec86

@rdunk rdunk force-pushed the feat/proxy-requests branch from 4dc067c to 7d9ee0b Compare August 18, 2025 13:18
@rdunk rdunk marked this pull request as ready for review August 18, 2025 16:32
@rdunk rdunk requested a review from danielroe as a code owner August 18, 2025 16:32
@rdunk rdunk force-pushed the feat/proxy-requests branch 2 times, most recently from a92101c to bb09f6b Compare August 23, 2025 10:00
if (writeTimer) clearTimeout(writeTimer)
writeTimer = setTimeout(async () => {
try {
await writeFile(queriesFilePath, JSON.stringify(queryArr), 'utf8')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to use addServerTemplate so this becomes a virtual file and we don't need to read from disk

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh cool, I wasn't sure how to make this work with addServerTemplate so I'm all ears here!

Currently we only read from the disk at runtime in dev so we can have an up-to-date list, as we find queries incrementally using a Vite plugin during transform, which obviously processes files on demand. In prod, the Rollup plugin exposes a virtual module (#sanity-groq-queries) that reads that file at build time and inlines the contents, so there shouldn't be any runtime reading from disk, AFAIK.

I originally tried this using addServerTemplate, but realized that updateTemplates won't refresh virtual Nitro templates, so was struggling to make things work in dev. Is there another way to do that?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @danielroe! Can I bump you on this one? 😅

@rdunk rdunk force-pushed the feat/proxy-requests branch 2 times, most recently from 314ef79 to f832e7a Compare September 1, 2025 12:13
@rdunk rdunk force-pushed the feat/proxy-requests branch from f832e7a to 1cf6123 Compare September 4, 2025 11:46
@rdunk rdunk force-pushed the feat/proxy-requests branch from 1cf6123 to 85c34a2 Compare September 16, 2025 11:22
@rdunk rdunk force-pushed the feat/proxy-requests branch 3 times, most recently from c22f1d8 to b794995 Compare January 23, 2026 10:15
@rdunk rdunk requested a review from justshiv January 23, 2026 10:24
addServerTemplate({
filename: '#sanity-groq-queries-info',
getContents: () =>
`export const queriesFilePath = ${JSON.stringify(queriesFilePath)}`,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't we just do something like export const queries = ${JSON.stringify(queryArr}?

that way no need to write to file system.

Copy link
Copy Markdown
Collaborator Author

@rdunk rdunk Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that the getContents method of addServerTemplate is called once during build, even in dev mode. So something like this will always return 0:

let counter = 0
addServerTemplate({
  filename: '#test',
  getContents: () => `export const value = ${counter++}`,
})

So as far as I can tell this approach won't work in dev mode because it won't return an up-to-date list of queries as they are added/modified. I tried this approach originally using updateTemplates but couldn't get that to refresh virtual Nitro templates.

This filesystem approach is only used for dev, in production the Rollup plugin exposes the #sanity-groq-queries virtual module that reads and inlines the file contents at build time, so no runtime writing/reading.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should call getContents again. it doesn't track dependencies, but when nitro rebuilds the server, the latest values should be present.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think I understand now. getContents does indeed get called on Nitro rebuild as you say, but the GROQ queries we need to whitelist are scanned from .vue files using the Vite plugin. AFAICT modifying those .vue files only triggers Vite rebuilds, not Nitro rebuilds.

From testing, updateTemplates seems to call builder:generateApp here, and generateApp looks at app.templates here, which again as far as I can tell doesn't do anything Nitro related (i.e. no reference to nuxt.options.nitro.virtual), but maybe I've missed something?

If there was an updateServerTemplates utility that would do it, but without a mechanism for triggering a Nitro rebuild, the only way I've found to share the GROQ queries discovered by Vite with Nitro in dev is with the filesystem approach.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danielroe I spent a bit more time re-validating my assumptions and tried three other approaches:

  1. updateTemplates: Doesn't work because it only regenerates client templates, not server templates (see above!).
  2. rollup:reload hook with addServerTemplate: Triggers Nitro rebuild and successfully calls getContents. Works technically, but has a race condition where:
    • Vite detects query change → updates in-memory array → triggers rollup:reload.
    • Page HMR reload happens immediately.
    • Nitro rebuild takes ~400ms to complete.
    • During that window, query validation fails because the virtual module with whitelisted queries is not yet updated.
    • Data fetching fails, no content rendered. Reloading/refocusing the page does re-execute the query, but the temporary error/empty page is not very pleasant DX.
  3. globalThis: Attempted to share queries on the globalThis object, but Nitro runs in an isolated worker thread so has a separate globalThis context. I couldn't find any APIs to do main thread/worker communication.

The only way I can see to share queries without that race condition is the filesystem approach.

Note: I've also removed the existing globalThis fallback since the above validated that it doesn't actually work.

@rdunk rdunk force-pushed the feat/proxy-requests branch 2 times, most recently from af90f04 to 41cf1bd Compare January 26, 2026 14:51
Comment on lines +25 to +36
if (fromFileSystem) {
try {
const raw = await readFile(queriesFilePath, 'utf8')
return JSON.parse(raw)
}
catch (err) {
console.debug('Failed to read GROQ queries file, falling back to globalThis:', err)
}

const g = globalThis
return hasGroqQueries(g) && Array.isArray(g.__nuxt_sanity_groqQueries) ? g.__nuxt_sanity_groqQueries : []
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't look like this will be tree-shaken from the build

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I've refactored this in 6133537.

@rdunk rdunk force-pushed the feat/proxy-requests branch 3 times, most recently from 5f9eeec to 79beffd Compare February 5, 2026 11:34
@rdunk rdunk force-pushed the feat/proxy-requests branch from 79beffd to 7e5acfb Compare February 5, 2026 16:48
Copy link
Copy Markdown
Collaborator

@danielroe danielroe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for bearing with me 🚀

@danielroe danielroe merged commit 52d505e into main Feb 9, 2026
5 of 9 checks passed
@danielroe danielroe deleted the feat/proxy-requests branch February 9, 2026 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants