Skip to content

docs(core): Document creating a RequestContext for a specific user - #5008

Merged
grolmus merged 2 commits into
vendurehq:masterfrom
grolmus:mgrolmus/oss-211-document-requestcontext-creation-more-explicitly
Jul 20, 2026
Merged

docs(core): Document creating a RequestContext for a specific user#5008
grolmus merged 2 commits into
vendurehq:masterfrom
grolmus:mgrolmus/oss-211-document-requestcontext-creation-more-explicitly

Conversation

@grolmus

@grolmus grolmus commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Documents how to give a manually-created RequestContext a user (and therefore permissions), which was previously undocumented. Resolves #3132.

Root cause

RequestContextService.create() accepts a user option, but it was not mentioned anywhere in the docs or in the method's own doc block. Without it the created context is anonymous, and services that check permissions — e.g. RoleService — reject it. The reporter only discovered the option by reading the source.

Change

  • Stand-alone scripts guide — new "Acting as a specific user" section: load the superadmin and pass it to create().
  • RequestContextService.create() doc block — note the consequence of omitting user, with the same snippet; reference page regenerated.
  • Plugin lifecycle guide — show the lifecycle hooks implemented on the plugin itself with a ProcessContext guard, linking on to the RequestContext section for startup work that calls services. (The existing ProcessContext example lives on the worker/job-queue page at service level; the reporter specifically asked for a plugin-level example, so this complements it rather than duplicating it.)

A note on the user lookup

The reporter's original snippet used getRepository(RequestContext.empty(), User). I used rawConnection.getRepository(User) instead: the two-arg getRepository(ctx, …) runs results through the entity access-control proxy, which with an empty context could filter out the very superadmin being fetched. rawConnection bypasses that, which is correct for a bootstrap lookup.

The lookup loads relations: { roles: { channels: true } } — without the roles (and their channels) the context would carry the user's id but an empty permission set, which passes id-reloading checks like RoleService but silently fails checks that read session permissions directly (e.g. the entity access-control checks behind connection.getRepository(ctx, Entity)).

Test plan

  • docs test:mdx — 830 files, 4748 code blocks, 0 errors
  • docs typecheck (tsc --noEmit) — passes
  • @vendure/core build:core-common — passes; the only source change is a JSDoc comment (verified: the diff outside comment lines is empty), reference page regenerated from it
  • eslint on the touched core file — clean
  • Every code sample's API surface verified against the source: rawConnection and ConfigService.authOptions are public; ProcessContext is injectable into a plugin class (global provider, re-exported by PluginCommonModule) and isWorker is a real getter; the relations are exactly what getChannelPermissions reads.

No runtime code changed, so there is nothing to e2e.

Fixes #3132


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

grolmus added 2 commits July 20, 2026 15:22
`RequestContextService.create()` returns an anonymous context when no `user` is
given, which services that check permissions then reject. The `user` option was
not mentioned anywhere, so the only way to find it was reading the source.

- Stand-alone scripts guide: new "Acting as a specific user" section showing how
  to load the superadmin and pass it to `create()`, using the same
  `rawConnection.getRepository(User)` lookup that `AdministratorService` uses.
- `RequestContextService.create()` doc block: note the consequence of omitting
  `user`, with the same snippet. Reference page regenerated.
- Plugin lifecycle guide: show the hooks implemented on the plugin itself with a
  `ProcessContext` guard, and link on to the RequestContext section for startup
  work that needs to call services.

Fixes vendurehq#3132
The superadmin lookup in the previous commit loaded the User without its
`roles` relation. `RequestContextService.create()` only derives
`channelPermissions` when `user.roles` is present, so the resulting context
carried the user's id but an empty permission set.

That happens to pass the RoleService case (it reloads the user by id), but any
check that reads session permissions directly — e.g. the entity access-control
checks behind `connection.getRepository(ctx, Entity)` — would silently deny
access, which is the opposite of what the docs promise.

Load `relations: { roles: { channels: true } }` so `channelPermissions` is
populated, matching a real login-derived session. Added a note explaining why
the relation is required.

Relates to vendurehq#3132
@vendure-developer-hub

vendure-developer-hub Bot commented Jul 20, 2026

Copy link
Copy Markdown

Docs previewPR merged

@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vendure-storybook Ready Ready Preview, Comment Jul 20, 2026 2:26pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds documentation for implementing OnApplicationBootstrap on a Vendure plugin, guarding server-only startup logic with ProcessContext.isWorker, and creating request contexts for service calls. Expands standalone-script and API reference guidance with examples that load a user’s roles and channels before creating an authorized admin RequestContext.

Suggested reviewers: dlhck

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately describes the main change: documenting RequestContext creation for a specific user.
Description check ✅ Passed The PR description covers the summary, root cause, implementation details, and linked issue, with mostly complete template coverage.
Linked Issues check ✅ Passed The docs updates match issue #3132 by covering the user option, RequestContextService.create notes, and a plugin-level ProcessContext example.
Out of Scope Changes check ✅ Passed The changes stay within the documented issue scope and only add related documentation/reference updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/docs/guides/developer-guide/plugins/index.mdx`:
- Around line 137-141: Update the prose around the RequestContext guidance to
say “outside the request-response cycle,” and change the “Creating a
RequestContext” link target to
/developer-guide/stand-alone-scripts/#creating-a-requestcontext while preserving
the surrounding explanation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 60df5b8a-9f01-4abc-98ed-64b4f50af046

📥 Commits

Reviewing files that changed from the base of the PR and between 284722f and 87bc314.

📒 Files selected for processing (4)
  • docs/docs/guides/developer-guide/plugins/index.mdx
  • docs/docs/guides/developer-guide/stand-alone-scripts/index.mdx
  • docs/docs/reference/typescript-api/request/request-context-service.mdx
  • packages/core/src/service/helpers/request-context/request-context.service.ts

Comment on lines +137 to +141
If that startup work needs to call Vendure's services, it will need a `RequestContext`, which
outside of the request-response cycle you create yourself — see
[Creating a RequestContext](/guides/developer-guide/stand-alone-scripts/#creating-a-requestcontext),
including how to give it a user so that permission checks pass.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for existing links to the stand-alone-scripts guide to verify the correct URL path.

rg -ho '\[.*?\]\(/[^)]+stand-alone-scripts[^)]*\)' docs/docs/

Repository: vendurehq/vendure

Length of output: 8046


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Relevant links ==\n'
rg -n --no-heading --glob 'docs/docs/**/*.mdx' --glob 'docs/docs/**/*.md' 'stand-alone-scripts|Creating a RequestContext|/guides/developer-guide/|/developer-guide/' docs/docs || true

printf '\n== Target file excerpt ==\n'
sed -n '120,150p' docs/docs/guides/developer-guide/plugins/index.mdx

printf '\n== Docs config / routing hints ==\n'
rg -n --no-heading 'slug|routeBasePath|guides/developer-guide|developer-guide' docs/docs docs -g '!**/node_modules/**' || true

Repository: vendurehq/vendure

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Targeted file list ==\n'
git ls-files 'docs/docs/**' | sed -n '1,200p'

printf '\n== Direct references to the target page ==\n'
rg -n --no-heading 'stand-alone-scripts/index\.mdx|Creating a RequestContext|API Layer' docs/docs || true

Repository: vendurehq/vendure

Length of output: 14150


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

root = Path("docs/docs")
for p in root.rglob("*.mdx"):
    text = p.read_text(encoding="utf-8")
    for m in re.finditer(r'\[[^\]]+\]\((/[^)]+stand-alone-scripts[^)]*)\)', text):
        print(f"{p}:{m.start(1)}:{m.group(1)}")
PY

Repository: vendurehq/vendure

Length of output: 589


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file and the referenced guide file paths.
printf '\n== Target excerpt ==\n'
sed -n '130,145p' docs/docs/guides/developer-guide/plugins/index.mdx

printf '\n== stand-alone-scripts file location(s) ==\n'
fd -a 'index.mdx' docs/docs | rg 'stand-alone-scripts|developer-guide'

printf '\n== Surrounding frontmatter / slugs ==\n'
for f in $(fd -a 'index.mdx' docs/docs | rg 'stand-alone-scripts|developer-guide'); do
  echo "--- $f ---"
  sed -n '1,40p' "$f"
done

Repository: vendurehq/vendure

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "FILES:"
git ls-files 'docs/docs/guides/developer-guide/**/index.mdx' | sed -n '1,120p'

Repository: vendurehq/vendure

Length of output: 2341


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Docusaurus / docs config files ==\n'
fd -a 'docusaurus.config.*|sidebars.*|docs.*config*' . | sed -n '1,80p'

printf '\n== Route base path references ==\n'
rg -n --no-heading 'routeBasePath|slug:|path:|docs:|guides' docusaurus.config.* docs -g '!**/node_modules/**' | sed -n '1,200p' || true

printf '\n== Slug/frontmatter for target and related guides ==\n'
for f in \
  docs/docs/guides/developer-guide/plugins/index.mdx \
  docs/docs/guides/developer-guide/stand-alone-scripts/index.mdx \
  docs/docs/guides/developer-guide/ai-assisted-development/index.mdx
do
  echo "--- $f ---"
  sed -n '1,20p' "$f"
done

Repository: vendurehq/vendure

Length of output: 22971


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== developer-guide manifest entries ==\n'
rg -n --no-heading -A4 -B2 "developer-guide|stand-alone-scripts|plugins" docs/src/manifest.ts | sed -n '1,220p'

printf '\n== plugin page links ==\n'
rg -n --no-heading '/guides/developer-guide/|/developer-guide/' docs/docs/guides/developer-guide/plugins/index.mdx docs/docs/guides/developer-guide/stand-alone-scripts/index.mdx | sed -n '1,120p'

Repository: vendurehq/vendure

Length of output: 11670


Fix the internal docs link

The RequestContext link should use /developer-guide/stand-alone-scripts/#creating-a-requestcontext to match the Developer Guide routes. Also drop the extra “of” in outside of the.

📝 Proposed fixes
  If that startup work needs to call Vendure's services, it will need a `RequestContext`, which
- outside of the request-response cycle you create yourself — see
- [Creating a RequestContext](/guides/developer-guide/stand-alone-scripts/#creating-a-requestcontext),
+ outside the request-response cycle you create yourself — see
+ [Creating a RequestContext](/developer-guide/stand-alone-scripts/#creating-a-requestcontext),
  including how to give it a user so that permission checks pass.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
If that startup work needs to call Vendure's services, it will need a `RequestContext`, which
outside of the request-response cycle you create yourself — see
[Creating a RequestContext](/guides/developer-guide/stand-alone-scripts/#creating-a-requestcontext),
including how to give it a user so that permission checks pass.
If that startup work needs to call Vendure's services, it will need a `RequestContext`, which
outside the request-response cycle you create yourself — see
[Creating a RequestContext](/developer-guide/stand-alone-scripts/#creating-a-requestcontext),
including how to give it a user so that permission checks pass.
🧰 Tools
🪛 LanguageTool

[style] ~137-~137: This phrase is redundant. Consider using “outside”.
Context: ... it will need a RequestContext, which outside of the request-response cycle you create y...

(OUTSIDE_OF)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/guides/developer-guide/plugins/index.mdx` around lines 137 - 141,
Update the prose around the RequestContext guidance to say “outside the
request-response cycle,” and change the “Creating a RequestContext” link target
to /developer-guide/stand-alone-scripts/#creating-a-requestcontext while
preserving the surrounding explanation.

Source: Linters/SAST tools

@grolmus
grolmus merged commit ecac272 into vendurehq:master Jul 20, 2026
27 of 28 checks passed
@vendure-ci-automation-bot vendure-ci-automation-bot Bot locked and limited conversation to collaborators Jul 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document RequestContext creation more explicitly

2 participants