Rewrite the projections tutorial around gaffer - #5710
Conversation
The tutorial walked readers through creating a projection by posting JavaScript to the HTTP API and clicking through the embedded UI, then reading results back over AtomPub. Two of those three no longer work on a default node: AtomPub has been off by default since v20 and is slated for removal, so the "read the result stream" steps fail for anyone following the quick-start install. Rewrite it around gaffer, which authors and deploys projections and runs the same JavaScript engine the server does: - The projection now runs locally against a JSON fixture before any database exists, so the write-run-inspect loop takes seconds and the reader sees state after every event. - Deploying is `gaffer deploy` against an environment in `gaffer.toml`, replacing the hand-written create and update calls, and state is read back over the management API rather than AtomPub. - A closing section changes the projection and shows the change as a diff against what is deployed. - Documents that a logic change does not rebuild existing state, and how to opt into reprocessing. The old page never mentioned it. Keeps the shopping-cart sample data and the Xbox One S counter, so readers who know the old page recognise this one. `outputState()` and result streams move to a note pointing at the projections API reference, since a curl walkthrough of them cannot work without AtomPub. Engine V1 throughout, matching the server default. V2 is linked as the experimental alternative. The two embedded-UI screenshots the page carried are deleted with it - nothing else referenced them.
Deploying eventstore with
|
| Latest commit: |
72a5160
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d262b1c7.eventstore.pages.dev |
| Branch Preview URL: | https://george-ui-1855-gaffer-first.eventstore.pages.dev |
PR Summary by QodoRewrite projections tutorial to use gaffer (no AtomPub dependency)
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR |
There was a problem hiding this comment.
🚨 @George-Payne Failed to create cherry Pick PR due to error:
RequestError [HttpError]: Merge conflict
at /home/runner/work/_actions/kurrent-io/Automations/master/cherry-pick-pr-for-label/node_modules/@octokit/request/dist-node/index.js:66:23
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
status: '409',
headers: {
'access-control-allow-origin': '*',
'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning',
'content-length': '127',
'content-security-policy': "default-src 'none'",
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 13 Aug 2026 08:43:58 GMT',
'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
server: 'github.com',
'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
vary: 'Accept-Encoding, Accept, X-Requested-With',
'x-accepted-github-permissions': 'contents=write',
'x-content-type-options': 'nosniff',
'x-frame-options': 'deny',
'x-github-api-version-selected': '2022-11-28',
'x-github-edge-region': 'iad',
'x-github-media-type': 'github.v3; format=json',
'x-github-request-id': 'F440:30F394:18D8CED:532E117:6A7D83CD',
'x-ratelimit-limit': '5000',
'x-ratelimit-remaining': '4995',
'x-ratelimit-reset': '1786614233',
'x-ratelimit-resource': 'core',
'x-ratelimit-used': '5',
'x-xss-protection': '0'
},
request: {
method: 'POST',
url: 'https://api.github.com/repos/kurrent-io/KurrentDB/merges',
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': 'octokit-core.js/3.3.2 Node.js/24',
authorization: 'bearer [REDACTED]',
'content-type': 'application/json; charset=utf-8'
},
body: '{"base":"cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v24.10-0ab73c8b-7597-4667-a727-35d9c1a8544c","commit_message":"Merge 72a5160b9171af88650a61e83857731d004c4034 into cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v24.10-0ab73c8b-7597-4667-a727-35d9c1a8544c [skip ci]\\n\\n\\nskip-checks: true\\n","head":"72a5160b9171af88650a61e83857731d004c4034"}',
request: { agent: [Agent], hook: [Function: bound bound register] }
},
documentation_url: 'https://docs.github.com/rest/branches/branches#merge-a-branch'
}
🚨👉 Check https://github.com/kurrent-io/KurrentDB/actions/runs/31683350090
There was a problem hiding this comment.
🚨 @George-Payne Failed to create cherry Pick PR due to error:
RequestError [HttpError]: Merge conflict
at /home/runner/work/_actions/kurrent-io/Automations/master/cherry-pick-pr-for-label/node_modules/@octokit/request/dist-node/index.js:66:23
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
status: '409',
headers: {
'access-control-allow-origin': '*',
'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning',
'content-length': '127',
'content-security-policy': "default-src 'none'",
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 13 Aug 2026 08:44:04 GMT',
'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
server: 'github.com',
'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
vary: 'Accept-Encoding, Accept, X-Requested-With',
'x-accepted-github-permissions': 'contents=write',
'x-content-type-options': 'nosniff',
'x-frame-options': 'deny',
'x-github-api-version-selected': '2022-11-28',
'x-github-edge-region': 'iad',
'x-github-media-type': 'github.v3; format=json',
'x-github-request-id': 'F440:30F394:18DB088:53358A6:6A7D83D3',
'x-ratelimit-limit': '5000',
'x-ratelimit-remaining': '4984',
'x-ratelimit-reset': '1786614233',
'x-ratelimit-resource': 'core',
'x-ratelimit-used': '16',
'x-xss-protection': '0'
},
request: {
method: 'POST',
url: 'https://api.github.com/repos/kurrent-io/KurrentDB/merges',
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': 'octokit-core.js/3.3.2 Node.js/24',
authorization: 'bearer [REDACTED]',
'content-type': 'application/json; charset=utf-8'
},
body: '{"base":"cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v25.0-e6912d6c-1a17-4c5d-9b6a-ee27a5744ab9","commit_message":"Merge 72a5160b9171af88650a61e83857731d004c4034 into cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v25.0-e6912d6c-1a17-4c5d-9b6a-ee27a5744ab9 [skip ci]\\n\\n\\nskip-checks: true\\n","head":"72a5160b9171af88650a61e83857731d004c4034"}',
request: { agent: [Agent], hook: [Function: bound bound register] }
},
documentation_url: 'https://docs.github.com/rest/branches/branches#merge-a-branch'
}
🚨👉 Check https://github.com/kurrent-io/KurrentDB/actions/runs/31683350090
There was a problem hiding this comment.
🚨 @George-Payne Failed to create cherry Pick PR due to error:
RequestError [HttpError]: Merge conflict
at /home/runner/work/_actions/kurrent-io/Automations/master/cherry-pick-pr-for-label/node_modules/@octokit/request/dist-node/index.js:66:23
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
status: '409',
headers: {
'access-control-allow-origin': '*',
'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning',
'content-length': '127',
'content-security-policy': "default-src 'none'",
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 13 Aug 2026 08:44:09 GMT',
'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
server: 'github.com',
'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
vary: 'Accept-Encoding, Accept, X-Requested-With',
'x-accepted-github-permissions': 'contents=write',
'x-content-type-options': 'nosniff',
'x-frame-options': 'deny',
'x-github-api-version-selected': '2022-11-28',
'x-github-edge-region': 'iad',
'x-github-media-type': 'github.v3; format=json',
'x-github-request-id': 'F440:30F394:18DD986:533E108:6A7D83D8',
'x-ratelimit-limit': '5000',
'x-ratelimit-remaining': '4971',
'x-ratelimit-reset': '1786614233',
'x-ratelimit-resource': 'core',
'x-ratelimit-used': '29',
'x-xss-protection': '0'
},
request: {
method: 'POST',
url: 'https://api.github.com/repos/kurrent-io/KurrentDB/merges',
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': 'octokit-core.js/3.3.2 Node.js/24',
authorization: 'bearer [REDACTED]',
'content-type': 'application/json; charset=utf-8'
},
body: '{"base":"cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v25.1-e5e411f4-0f89-420d-9336-7289db85cf8a","commit_message":"Merge 72a5160b9171af88650a61e83857731d004c4034 into cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v25.1-e5e411f4-0f89-420d-9336-7289db85cf8a [skip ci]\\n\\n\\nskip-checks: true\\n","head":"72a5160b9171af88650a61e83857731d004c4034"}',
request: { agent: [Agent], hook: [Function: bound bound register] }
},
documentation_url: 'https://docs.github.com/rest/branches/branches#merge-a-branch'
}
🚨👉 Check https://github.com/kurrent-io/KurrentDB/actions/runs/31683350090
There was a problem hiding this comment.
🚨 @George-Payne Failed to create cherry Pick PR due to error:
RequestError [HttpError]: Merge conflict
at /home/runner/work/_actions/kurrent-io/Automations/master/cherry-pick-pr-for-label/node_modules/@octokit/request/dist-node/index.js:66:23
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
status: '409',
headers: {
'access-control-allow-origin': '*',
'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning',
'content-length': '127',
'content-security-policy': "default-src 'none'",
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 13 Aug 2026 08:44:14 GMT',
'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
server: 'github.com',
'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
vary: 'Accept-Encoding, Accept, X-Requested-With',
'x-accepted-github-permissions': 'contents=write',
'x-content-type-options': 'nosniff',
'x-frame-options': 'deny',
'x-github-api-version-selected': '2022-11-28',
'x-github-edge-region': 'iad',
'x-github-media-type': 'github.v3; format=json',
'x-github-request-id': 'F440:30F394:18DF94A:5344C6D:6A7D83DD',
'x-ratelimit-limit': '5000',
'x-ratelimit-remaining': '4960',
'x-ratelimit-reset': '1786614233',
'x-ratelimit-resource': 'core',
'x-ratelimit-used': '40',
'x-xss-protection': '0'
},
request: {
method: 'POST',
url: 'https://api.github.com/repos/kurrent-io/KurrentDB/merges',
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': 'octokit-core.js/3.3.2 Node.js/24',
authorization: 'bearer [REDACTED]',
'content-type': 'application/json; charset=utf-8'
},
body: '{"base":"cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v26.0-a4580286-8ec5-43c0-ba45-0c6b05603983","commit_message":"Merge 72a5160b9171af88650a61e83857731d004c4034 into cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v26.0-a4580286-8ec5-43c0-ba45-0c6b05603983 [skip ci]\\n\\n\\nskip-checks: true\\n","head":"72a5160b9171af88650a61e83857731d004c4034"}',
request: { agent: [Agent], hook: [Function: bound bound register] }
},
documentation_url: 'https://docs.github.com/rest/branches/branches#merge-a-branch'
}
🚨👉 Check https://github.com/kurrent-io/KurrentDB/actions/runs/31683350090
There was a problem hiding this comment.
🚨 @George-Payne Failed to create cherry Pick PR due to error:
RequestError [HttpError]: Merge conflict
at /home/runner/work/_actions/kurrent-io/Automations/master/cherry-pick-pr-for-label/node_modules/@octokit/request/dist-node/index.js:66:23
at process.processTicksAndRejections (node:internal/process/task_queues:104:5) {
status: '409',
headers: {
'access-control-allow-origin': '*',
'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning',
'content-length': '127',
'content-security-policy': "default-src 'none'",
'content-type': 'application/json; charset=utf-8',
date: 'Thu, 13 Aug 2026 08:44:18 GMT',
'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
server: 'github.com',
'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
vary: 'Accept-Encoding, Accept, X-Requested-With',
'x-accepted-github-permissions': 'contents=write',
'x-content-type-options': 'nosniff',
'x-frame-options': 'deny',
'x-github-api-version-selected': '2022-11-28',
'x-github-edge-region': 'iad',
'x-github-media-type': 'github.v3; format=json',
'x-github-request-id': 'F440:30F394:18E1D2E:534C5C2:6A7D83E2',
'x-ratelimit-limit': '5000',
'x-ratelimit-remaining': '4948',
'x-ratelimit-reset': '1786614233',
'x-ratelimit-resource': 'core',
'x-ratelimit-used': '52',
'x-xss-protection': '0'
},
request: {
method: 'POST',
url: 'https://api.github.com/repos/kurrent-io/KurrentDB/merges',
headers: {
accept: 'application/vnd.github.v3+json',
'user-agent': 'octokit-core.js/3.3.2 Node.js/24',
authorization: 'bearer [REDACTED]',
'content-type': 'application/json; charset=utf-8'
},
body: '{"base":"cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v26.1-5e9b1f2d-bf9d-4796-afdf-6616b2f5bd9e","commit_message":"Merge 72a5160b9171af88650a61e83857731d004c4034 into cherry-pick-cherry-pick/5710/george/ui-1855-gaffer-first-projections-tutorial-release/v26.1-5e9b1f2d-bf9d-4796-afdf-6616b2f5bd9e [skip ci]\\n\\n\\nskip-checks: true\\n","head":"72a5160b9171af88650a61e83857731d004c4034"}',
request: { agent: [Agent], hook: [Function: bound bound register] }
},
documentation_url: 'https://docs.github.com/rest/branches/branches#merge-a-branch'
}
🚨👉 Check https://github.com/kurrent-io/KurrentDB/actions/runs/31683350090
* Rewrite the projections tutorial around gaffer Backport of #5710. This branch has no projections engine V2 page, so the paragraph explaining --engine-version 1 names 26.1 as where V2 arrives instead of linking the page, and the next-steps bullet for it is gone. * Mention the projection tooling in the projections docs Backport of #5711. This branch has no intro.md, so the tooling section lands in README.md, which carries that content here. The debugging section replaced is the browser walkthrough, so its screenshots go too.
* Rewrite the projections tutorial around gaffer Backport of #5710. The tutorial content is taken verbatim from master; this branch's engine-v2 page exists, so the V2 references carry over unchanged. * Mention the projection tooling in the projections docs Backport of #5711. The debugging section replaced here is the browser walkthrough this branch still carries, rather than the short Gaffer paragraph master had, so its three screenshots go with it.
Backport of #5710. This branch has no projections engine V2 page, so the paragraph explaining --engine-version 1 names 26.1 as where V2 arrives instead of linking the page, and the next-steps bullet for it is gone.
Backport of #5710. This branch has no projections engine V2 page, so the paragraph explaining --engine-version 1 names 26.1 as where V2 arrives instead of linking the page, and the next-steps bullet for it is gone.
Backport of #5710. This branch has no projections engine V2 page, so the paragraph explaining --engine-version 1 names 26.1 as where V2 arrives instead of linking the page, and the next-steps bullet for it is gone.
* Rewrite the projections tutorial around gaffer Backport of #5710. This branch has no projections engine V2 page, so the paragraph explaining --engine-version 1 names 26.1 as where V2 arrives instead of linking the page, and the next-steps bullet for it is gone. * Mention the projection tooling in the projections docs Backport of #5711. This branch has no intro.md, so the tooling section lands in README.md, which carries that content here. The debugging section replaced is the browser walkthrough, so its screenshots go too. --------- Co-authored-by: George Payne <georgepayne@hotmail.com>
* Rewrite the projections tutorial around gaffer Backport of #5710. This branch has no projections engine V2 page, so the paragraph explaining --engine-version 1 names 26.1 as where V2 arrives instead of linking the page, and the next-steps bullet for it is gone. * Mention the projection tooling in the projections docs Backport of #5711. This branch has no intro.md, so the tooling section lands in README.md, which carries that content here. The debugging section replaced is the browser walkthrough, so its screenshots go too. --------- Co-authored-by: George Payne <georgepayne@hotmail.com>
Rewrites the projections tutorial so it teaches projections with gaffer instead of hand-posting JavaScript to the HTTP API. Two thirds of the old walkthrough no longer worked on a default node: creating a projection over AtomPub and reading its result stream both need
EnableAtomPubOverHttp, off by default since v20 and slated for removal, so a reader following the quick-start install hit a wall partway through.tutorial.md- the projection now runs locally against a JSON fixture before any database exists, so the write-run-inspect loop takes seconds and the reader sees state after every event. Deploying isgaffer deployagainst an environment ingaffer.toml, and state is read back over the management API rather than AtomPub. A closing section changes the projection and shows the change as a diff against what is deployed.engine-v2.md.outputState()and result streams move to a note pointing at the projections API reference, since a curl walkthrough of them cannot work without AtomPub.The steps are also a companion repo, kurrent-io/projections-tutorial, one branch per step