docs(core): Document creating a RequestContext for a specific user - #5008
Conversation
`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
|
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds documentation for implementing Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
docs/docs/guides/developer-guide/plugins/index.mdxdocs/docs/guides/developer-guide/stand-alone-scripts/index.mdxdocs/docs/reference/typescript-api/request/request-context-service.mdxpackages/core/src/service/helpers/request-context/request-context.service.ts
| 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. | ||
|
|
There was a problem hiding this comment.
📐 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/**' || trueRepository: 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 || trueRepository: 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)}")
PYRepository: 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"
doneRepository: 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"
doneRepository: 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.
| 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
Summary
Documents how to give a manually-created
RequestContexta user (and therefore permissions), which was previously undocumented. Resolves #3132.Root cause
RequestContextService.create()accepts auseroption, 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
create().RequestContextService.create()doc block — note the consequence of omittinguser, with the same snippet; reference page regenerated.ProcessContextguard, linking on to the RequestContext section for startup work that calls services. (The existingProcessContextexample 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 usedrawConnection.getRepository(User)instead: the two-arggetRepository(ctx, …)runs results through the entity access-control proxy, which with an empty context could filter out the very superadmin being fetched.rawConnectionbypasses 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 likeRoleServicebut silently fails checks that read session permissions directly (e.g. the entity access-control checks behindconnection.getRepository(ctx, Entity)).Test plan
docstest:mdx— 830 files, 4748 code blocks, 0 errorsdocstypecheck(tsc --noEmit) — passes@vendure/corebuild:core-common— passes; the only source change is a JSDoc comment (verified: the diff outside comment lines is empty), reference page regenerated from itrawConnectionandConfigService.authOptionsare public;ProcessContextis injectable into a plugin class (global provider, re-exported byPluginCommonModule) andisWorkeris a real getter; therelationsare exactly whatgetChannelPermissionsreads.No runtime code changed, so there is nothing to e2e.
Fixes #3132
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.