This project uses PocketBase, and a lesser known UI project called PocketPages.
Pocketbase has a way to extend it with the JSVM which runs inside GoJA. This lets us register endpoints, event hooks, cron jobs all in JS running inside Pocketbase managed GoJA (JSVM).
PocketPages builds on this by having a folder/file based routing system, and allows us to run JS in the JSVM in server-side html blocks in the pocketpages pages.
For example, PB lets you create JSVM scripts in pb_hooks, just name it whatever.pb.js. PP lets us create a page like index.ejs, gives us a <script server> block, and that code will run in the JSVM when the page is rendered. We have full access to the PB API, and a set of PP helpers.
IE for PB you can do $app.findRecordsByFilter where $app is the main interface to interact with your database.
Another example: for PB you can do request.event.findUploadedFiles() which is on the core requestEvent as seen here https://pocketbase.io/jsvm/interfaces/core.RequestEvent.html#findUploadedFiles this is a good example because it shows a combo of PP and PB working together, for example event is exposed by PP as a helper.
See detailed Pocketbase docs here if needed https://pocketbase.io/docs/llms-full.txt
-
<fieldset class="fieldset"> <legend class="fieldset-legend">What is your name?</legend> <input type="text" class="input" placeholder="Type here" /> <p class="label">Optional</p> </fieldset>
- If the project has a
_private/pocketbase-client.jsmake sure you always build clients and send requests using this tool. This fixes a major rate limit issue where server-side http calls come from localhost. You should see example usage of this helper in the projects README most likely, or in existing pages that have forms like login/register. - For server side logging, like in
<script server>tags (or server-side .js files like middleware and pocketpages api files), you need to doinfo("blah")you cant doconsole.logserver-side. - Dont use
time.Now().UTC()always usetypes.NowDateTime()found in"github.com/pocketbase/pocketbase/tools/types"make sure to check for other time related functions in that package as well. This ensures we are using the same time source as the rest of PB and avoids issues with server timezone changes, DST, etc. - For EJS dont await pocketbase calls. The library was modified to be sync for server-side.
- When suggesting names of relations in PocketPages, Do not suffix the column name with "id" unless you know you need to. If you do
author.id = 'abc'in a PB rule or filter, it will trigger a join to the table. If you doauthor = 'abc'it simply compared the key directly. - For filters in PocketBase API calls, use the
client.filter()helper to construct filters with parameters. This ensures proper escaping and avoids syntax errors. For example:client.collection('posts').getList(1, 10, { filter: client.filter('author.id = {:authorId} && published = true', { authorId: someAuthorId }) })
- When including a partial, you dont need to prefix with the
_privatefolder. IEconst config = resolve('config')Uses root/_private/config.js. - When using PB JSD server side (like in EJS
<script server> ...or<% %>blocks, dont use.getwhen accessing members of the returned PB data from the client. Ie this is wrongform = client.collection('user_forms').getOne(params.id); form.get("form_name")that is because the data is serialized to JSON over the wire. Now if you're using$appin PocketPages or you are in golang, you need to unmarshal stuff and will have to use.get()or.getBool/String/Whatever()make sure to peek at the docs if you need this. - Include NPM packages from node_modules with require(), include
_privateJS helpers withresolve(). - Very imports: avoid using
$appin PocketPages server blocks if you can. We want to make sure we are triggering PocketBase API filters and hooks properly. Not only this but we do not want to couple PocketPages to Pocketbase internals if we can. This would allow for a much simpler UI port to a SPA or another framework if desired.<script server> // Get an authenticated client for the current request const client = pb({ request }) // Use any PocketBase SDK method with the authenticated context const records = client.collection('posts').getFullList({ sort: '-created', expand: 'author', }) </script>
- Dont use js hooks, always use GoLang hooks. The JSVM is not designed for high performance or long-running processes, so its best to use it for request handling and page rendering, and use Go hooks for things like cron jobs, event hooks, etc.
- Do NOT use
client.files.getURL()to build file/thumbnail URLs. The PB JS SDK'sgetURLrelies onURLSearchParams(not fully supported in GoJA) and returns URLs with the server-side localhost origin. Instead, construct file URLs manually:For a helper function pattern:<img src="/api/files/<%= record.collectionId %>/<%= record.id %>/<%= record.file %>?thumb=150x150" />
function thumbUrl(record, thumb) { return '/api/files/' + record.collectionId + '/' + record.id + '/' + record.file + '?thumb=' + thumb }
- Cron expressions are evaluated in UTC by PocketBase's scheduler (robfig/cron).
- 5:05 UTC = 00:05 CDT (UTC-5) / 23:05 CST (UTC-6 previous day).
- Adjust this value if the server timezone changes.
-
PocketPages has a global
url()function. This is powered by https://www.npmjs.com/package/url-parse under the hood, theparse()function of that lib is called, which returns the common Node.js interface. These properties are available to you (via url-parse docs):function of that lib is called, which returns the common Node.js interface. These properties are available to you: protocol: The protocol scheme of the URL (e.g. http:). slashes: A boolean which indicates whether the protocol is followed by two forward slashes (//). auth: Authentication information portion (e.g. username:password). username: Username of basic authentication. password: Password of basic authentication. host: Host name with port number. The hostname might be invalid. hostname: Host name without port number. This might be an invalid hostname. port: Optional port number. pathname: URL path. query: Parsed object containing query string, unless parsing is set to false. hash: The "fragment" portion of the URL including the pound-sign (#). href: The full URL. origin: The origin of the URL.- One major thing to note is that PocketPages attempots to
JSON.parsewevery single property value inquery. So passing JSON in query strings becomes easier to process downstream.
- One major thing to note is that PocketPages attempots to
See PocketPages LLM docs here: https://github.com/benallfree/pocketpages/blob/main/llms.txt
I think there is a new SKILLS.md format that all the AI agents are using. Can you check into it further?
https://www.youtube.com/watch?v=5G8xUYXdWFs&t=3s