Typescript/JavaScript serverless cloud functions for PocketBase

Overview

TS/JS Cloud Functions for PocketBase

Write all your PocketBase server-side logic in TS/JS

PBScript allows you to write PocketBase server-side functions in Typescript or Javascript without recompiling.

With PBScript, you can:

  • Write your server-side logic in Typescript or JS
  • Access models, collections, transactions, hooks, and all server-side features of PocketBase
  • Communicate with PocketBase using a streamlined JavaScript-style API
  • Deploy and alter cloud functions without rebuilding or even restarting PocketBase
  • Roll back to previous versions
  • Use the pbscript CLI tool for intelligent dev mode, live deployment, and rollback

Table of Contents

Introduction

PBScript extends PocketBase with an ES-5.1 (ECMA 262) scripting engine powered by goja.

Code executes in a secure sandboxed environment without a Node.js API or Web APIs. Instead, the runtime environment is PocketBase-specific, with full access to PocketBase's native Go extension API and includes streamlined functions and helper utilities written in JavaScript (@pbscript/core).

Use pbscript command line tool to build and publish cloud functions to any PBScript-enabled PocketBase instance.

Getting Started

1. Create a PocketBase instance

To run JS functions, you need to run a PocketBase instance which has been enhanced with PBScript.

You can do it any way you like, just as long as you end up with an admin login for the next section.

Option 1 (free): run fully managed on pockethost.io

The absolute easiest way to provision a new PocketBase instance enhanced with PBScript is to use pockethost.io. You'll be up and running with a PocketBase URL in under 30 seconds. This is as close to a Firebase/Supabase BaaS experience as you can get.

Option 2 (free): run self-managed on fly.io

If you'd rather manage your resources yourself, you can follow the instructions in this thread to get up and running on fly.io.

This option takes about 30 minutes to set up.

Option 3 (free): run a custom build locally

If you just want to run locally or have a special use case, you can create your own build.

Create pocketbase.go:

package main

import (
	"log"

	pocketbase "github.com/benallfree/pbscript/modules/pbscript"
)


func main() {
    app := pocketbase.New()

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

On the command line, run:

go get github.com/benallfree/pbscript/modules/pbscript
go run pocketbase.go serve

> Server started at: http://127.0.0.1:8090
  - REST API: http://127.0.0.1:8090/api/
  - Admin UI: http://127.0.0.1:8090/_/

2. Create a new JS/TS project

You can create any type of TS/JS project you want, but here's the package.json we recommend. We also have a sample PBScript project you can check out.

The important part is that your script gets bundled as ES5:

{
  "name": "sample",
  "version": "0.0.1",
  "dependencies": {
    "@pbscript/core": "^0.0.1"
  },
  "scripts": {
    "build": "parcel build --no-cache",
    "deploy:local": "pbscript deploy --host 'http://127.0.0.1:8090'",
    "dev": "chokidar 'src/**' './node_modules/**' -c 'yarn build && yarn deploy:local' --initial"
  },
  "targets": {
    "iife": {
      "source": "./src/index.ts",
      "context": "browser",
      "outputFormat": "global",
      "sourceMap": {
        "inline": true
      }
    }
  },
  "devDependencies": {
    "chokidar-cli": "^3.0.0",
    "parcel": "^2.7.0"
  }
}

3. Use the `pbscript` CLI tool to log in

Enter your project directory and log in to your PocketBase instance using the admin account you created in Step #1:

npx pbscript login <username> <password> --host <pocketbase URL>

This will create a file named .pbcache. Add it to .gitignore.

Developing your script

In your command shell, run:

npx pbscript dev

PBScript is designed for a fast development cycle. If you used our package.json, this command will watch for changes (in ./dist) and re-deploy your script on every change.

PBScript updates do not require a PocketBase restart. Old versions of your script are kept in an archive table for easy rollbacks.

Building and deploying your script

pbscript knows how to deploy to any PBScript-enabled PocketBase instance.

Connect to your live site

First, connect to your live site. pbscript will remember your credentials for future commands.

npx pbscript login <username> <password> --host https://yourproject.pockethost.io
Saved to .pbcache

Build your ./dist/index.js bundle

You can build your script bundle however you want, just make sure you end up with ONE bundle file in ./dist/index.js. If you use source maps, inline them. pbscript only deploys this one file.

Deploy your latest changes

You can deploy changes at any time without restarting PocketBase. All realtime connections and users will remain connected.

pbscript deploy --host <your pocketbase URL>

Or, add it to package.json:

"scripts": {
  "deploy": "pbscript deploy --host https://yourproject.pockethost.io"
}

API

PBScript runs in a secure sandboxed environment inside PocketBase. A simplified subset of PocketBase's hooks are available. Complete Typescript definitions are included.

You might be accustomed to using the NodeJS API or the browser Web API, but those APIs are not core features of ECMAScript. They are not safe or allowed in the PocketBase execution environment.

Instead, your script runs in the PocketBaseApi execution environment. PocketBaseApi set of API calls to interact with PocketBase. With it, you can do CRUD, transactions, hook into PocketBase events, new API endpoints, and generally extend PocketBase.

PBScript imports a __go variable containing low-level access to the PocketBase native API and other helpers implemented in Go. @pbscript/core uses __go internally, but if there is something missing, you may be able to accomplish it yourself by accessing __go directly.

__go.ping()

Test function that should return Hello from Go!

Example:

console.log(__go.ping())

__go.addRoute()

Add an API route.

Example:

__go.addRoute({
  method: HttpMethods.Get,
  path: `/api/hello`
  handler: (context)=>{
    context.send(HttpStatus.Ok, `Hello back!`)
  }
})

__go.app

Low-level primitive providing direct access to the PocketBase app instance. Normally you will not access this directly. The @pbscript/core library is built on top of this.

Advanced

Upgrading an Existing Custom PocketBase

The easiest way to get PBScript is to use our custom PocketBase module github.com/benallfree/pbscript/modules/pocketbase:

package main

import (
    "log"

    "github.com/benallfree/pbscript/packages/pocketbase" // Notice this is a custom version of the PocketBase module
)

func main() {
    app := pocketbase.New()

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}

If you are already using a custom PocketBase build, just swap out github.com/pocketbase/pocketbase with github.com/benallfree/pbscript/packages/pocketbase and everything will work as expected.

Or, if you prefer, you can do exactly what our custom module does:

package main

import (
	"github.com/pocketbase/pocketbase"

	"github.com/benallfree/pbscript/packages/pbscript"
)

func main() {
    app := pocketbase.New()
    pbscript.StartPBScript(app) // Magic line to enable PBScript

    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}
You might also like...

Deploy Serverless Functions at the Edge. Current status: Dev

Deploy Serverless Functions at the Edge. Current status: Dev

Deploy Serverless Functions at the Edge lagon.app ✉️ Get email updates Open Source • TypeScript • Web APIs Cron triggers • Instant deployments Interac

Dec 30, 2022

Deploy Serverless Functions at the Edge. Current status: Dev

Deploy Serverless Functions at the Edge. Current status: Dev

Deploy Serverless Functions at the Edge 💻 Join the Discord ✉️ Get email updates Open Source • TypeScript • Web APIs Cron triggers • Instant deploymen

Jul 26, 2022

Test cloud functions, firestore triggers, fcm locally without need to upgrade for blaze plan 🔥

Idea Test and try cloud functions with FCM locally and for free without upgrade to firebase blaze plan 🔥 What you will learn 🧑‍💻 Setup NodeJs for c

Dec 15, 2022

MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are still part of a flow in the AWS cloud remote.

MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are still part of a flow in the AWS cloud remote.

MerLoc MerLoc is a live AWS Lambda function development and debugging tool. MerLoc allows you to run AWS Lambda functions on your local while they are

Dec 21, 2022

Collection of Rowy's templates for cloud functions cod snippets - including for derivative, action columns and extensions.

Collection of Rowy's templates for cloud functions cod snippets - including for derivative, action columns and extensions.

Rowy Templates Collection of Rowy's backend templates and code snippets for cloud functions - including for derivative, action columns and extensions.

Nov 16, 2022

Functions Recipes is a library of examples to help you getting started with Salesforce Functions and get used to their main features.

Functions Recipes is a library of examples to help you getting started with Salesforce Functions and get used to their main features.

Functions Recipes Introduction Salesforce Functions lets you use the Salesforce Platform for building event-driven, elastically scalable apps and expe

Dec 29, 2022

Collection of utility functions for common JavaScript/TypeScript task

@oysterlee/utils Collection of utility functions for common JavaScript/TypeScript task Features Tree-shakable No dependencies Installation pnpm i @oys

Apr 4, 2022

Example Serverless DynamoDB integration tests using Jest, TypeScript and the AWS CDK

Example Serverless DynamoDB integration tests using Jest, TypeScript and the AWS CDK

serverless dynamodb integration tests 🚀 Example Serverless DynamoDB integration tests using Jest, TypeScript and the AWS CDK Introduction How to inte

Nov 4, 2022

Serverless Framework + typescript + mongoDB atlas, sample

sls_ts6 Version: 0.9.1 Author : Kouji Nakashima / kuc-arc-f.com date : 2022/01/21 update : Summary Serverless Framework + typescript + mongoDB atlas,

Jan 22, 2022
Comments
  • support for SSR?

    support for SSR?

    Thanks for starting pbscript. It's a great companion to pocketbase.

    My default strategy with pocketbase is to use SvelteKit frontend, and make it as static (JAMStack) as possible. But sometimes dynamic code at runtime is still needed. For example:

    1. When you want to have a runtime API endpoint to generate runtime results.
    2. Or when you need runtime SSR of a page (imagine a site with local business listings of 50,000 cities; not practical to prerender them all).

    pbscript (I think) would be useful in scenario #1, but perhaps not in scenario #2? Technically, SSR is still just script execution, but maybe I don't know enough about pbscript to see how it would work.

    Eventually, it would be nice to have a SvelteKit adapter that generates JS code that simply runs in pbscript.

    PS: I put this in as an issue, since "discussions" feature is not turned on yet.

    opened by jkdoshi 2
  • Add WASM/Wazero support

    Add WASM/Wazero support

    @gedw99 suggested swapping out goja for wazero. The benefits cited were:

    • faster startup and execution
    • ability to write serverless functions in any language

    There are many WASM compilers various languages:

    However, Javascript itself - as well as any other dynamic language - cannot compile directly to WASM. Instead, it must be run by a WASM-based Javascript interpreter:

    • Duktape - an embeddable Javascript engine, with a focus on portability and compact footprint that's capable of being run in the browser via WebAssembly.
    • Javy - a JavaScript to WebAssembly toolchain, capable of generating WASI-compatible modules from JS by embedding the QuickJS engine.
    • SpiderMonkey - experimental Rust bindings and generic builtins for SpiderMonkey for building WASI-compatible modules from JavaScript.

    Or, we could continue using goja as the JS interpreter of choice. This seems like it might be the best route, unless it can be shown that a WASM-based JS VM is more performant than goja.

    In any case, the biggest hurdle to WASM support is that we would need to create explicit bindings, effectively introducing a "serverless API".

    Currently using goja, I can pass object references and pointers back and forth between Go and JS. goja makes that easy and pretty seamless. But with WASM, the interface feels more like an old style C function interface or a REST and webhook API because all you can really do is pass numeric and string values back and forth.

    Example: It would not be possible, for example, to pass an Echo route context object into WASM and simply register an anonymous function handler that calls response.Send(200, 'Hello world'). Instead, the WASM environment would need to register a hook callback with OnBeforeServe in the host environment and, when that hook is called, register ANOTHER host callback for a route, which in turn calls ANOTHER hook callback when the route fires:

    // TS serverless function example
    
    // A lookup table of callbacks into the WASM environment
    let callbackId = 0
    const callbackLookup = {}
    const addCallback = (name:string, context:any, cb: any) =>{
       callbackId++
       callbacks[callbackId] = { name, context, cb }
       return callbackId
    }
    
    // Register a given callback with the host environment
    const registerInteropCallback = (callbackId: number) =>{
       const callback = callbackLookup[callbackId]
       const interopPayload = JSON.stringify(callback)
    
       // This calls into the host environment with a known payload format
       // The payload format includes an event name and context data that
       // varies based on the type of event
       // It is responsible for hooking into the specified event in the host
       // and behaving according to the context data passed
       _interop_addCallback(interopPayload) 
    }
    
    // This is exported from WASM. The PocketBase Go host environment knows that every WASM serverless plugin
    // exports this function and expects a specific type of JSON payload to accompany each callback depending
    // on what type of callback was registered
    export const _interop_executeCallback = (callbackId: number, jsonPayload: string)=> {
       // Execute the WASM callback and return true for success, false for failure
       const res = callbackLookup[callbackId](JSON.parse(jsonPayload))
       return res
    }
    
    // Here is the user-level code
    addCallback('OnBeforeServe', (e:OnBeforeServeEventData)=>{
       const myCustomMiddleware = createCallback(`EchoMiddleware`, middlewareInteropContext =>{
          return false
       })
       addCallback(`OnRoute`, {
          method: 'GET',
          path: '/api/foo',
          middlewares: [ myCustomMiddleware ]
       }, routeInteropContext => {
          console.log(`Do something here to create a host-level Echo response. The rabbit hole just goes deeper`)
       })
    })
    
    

    I'll leave this issue open for discussion. My opinion is that supporting WASM is a cool idea without a clear use case. Is anyone begging to write PocketBase hooks in Rust?

    Tagging @ganigeorgiev for thoughts too.

    opened by benallfree 8
  • @pbscript/core is not in the npm registry

    @pbscript/core is not in the npm registry

    I get this error - @pbscript/core is not in the npm registry - when I try to install it with any of the following:

    • npm install (with ./package.json)
    • npm install -S @pbscript/core
    • pnpm install -S @pbscript/core

    npx pbscript ... also fails.

    opened by jkdoshi 1
Owner
Ben Allfree
Dank meme hunter
Ben Allfree
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Learn Web 2.0 and Web 3.0 Development using Next.js, Typescript, AWS CDK, AWS Serverless, Ethereum and AWS Aurora Serverless

Learn Web 2.0 Cloud and Web 3.0 Development in Baby Steps In this course repo we will learn Web 2.0 cloud development using the latest state of the ar

Panacloud Multi-Cloud Internet-Scale Modern Global Apps 89 Jan 3, 2023
A Serverless GraphQL Sample project using Apollo and Serverless Framework with TypeScript and Webpack.

Serverless GraphQL Boilerplate This is a base project with a structure that includes Serverless Framework, Apollo, TypeScript and Webpack. It can be d

Ravi Souza 5 Aug 23, 2022
Run REST APIs in Node.js applications frameworks (Express, Koa, Hapi and Fastify) on top of any Serverless Cloud.

?? Serverless Adapter Install | Usage | Support | Architecture | Credits Run REST APIs and other web applications using your existing Node.js applicat

Vinicius Lourenço 45 Jan 1, 2023
awsrun 189 Jan 3, 2023
An AWS Cloud Native application using CDK that defines a Serverless Event Driven application for interacting with Twitter and utilising Machine Learning / AI as a Service.

AWS Serverless Event Driven Twitter Bot An AWS Cloud Native application using CDK (Written in TypeScript) that defines a Serverless Event Driven appli

null 4 Dec 18, 2022
A serverless AWS expense tracker API. AWS Lambda functions, API gateway, and Dynamodb are among the ingredients.

AWS-Serverless-API A serverless AWS expense tracker API. AWS Lambda functions API gateway Dynamodb Endpoints Create a new expense: Method: POST Body f

Ondiek Elijah Ochieng 1 Jul 16, 2022
Cardinal generator encompasses serverless functions and smart contracts for rendering generative NFTs

Cardinal Generator An open protocol for generative NFTs. Background Cardinal generator encompasses serverless functions and smart contracts for render

Cardinal 31 Dec 6, 2022