[HN Gopher] Goja: A Golang JavaScript Runtime
       ___________________________________________________________________
        
       Goja: A Golang JavaScript Runtime
        
       Author : mihaitodor
       Score  : 97 points
       Date   : 2024-09-04 13:45 UTC (3 days ago)
        
 (HTM) web link (jtarchie.com)
 (TXT) w3m dump (jtarchie.com)
        
       | throwaway13337 wrote:
       | This is the runtime pocketbase uses to allow javascript interop.
       | 
       | Pocketbase is the framework that makes me want to switch to
       | golang. It just makes a lot of sense.
       | 
       | One thing that concerned me, though. How do I debug goja? It
       | doesn't seem like I can set breakpoints as usual.
       | 
       | https://pocketbase.io/
        
         | thangngoc89 wrote:
         | Actually I decided to learn golang for extending using Golang
         | for better DX (type hints and compiler errors)
        
           | throwaway13337 wrote:
           | It's certainly a good idea to know golang while using
           | something like pocketbase.
           | 
           | However, one of the strengths of pocketbase, I think, is that
           | the creator chose to add bindings for a very well known
           | scripting language (js) to allow projects to be rapidly
           | developed. It feels like django rethought.
           | 
           | Using go for the base of your project and then gluing things
           | together via scripting seems like a good idea. It's quite a
           | big feature of the framework.
        
             | skeeter2020 wrote:
             | performance seems pretty solid too, removing one of my
             | prime motivators for switching to Go for extension work.
             | PocketBase just seems to have made a lot of great decisions
             | based on the conceptual integrity of the project goals.
        
               | jtarchie wrote:
               | I've used Pocketbase for some other projects. A quick
               | small web app, but it was so easy.
        
       | nine_k wrote:
       | In short: Goja is an ES5 interpreter written in pure Go and
       | seamlessly integrating with it. You pass a struct, the JS side
       | receives an object, you update it, and the Go side seamlessly
       | gets an updated struct. No cgo overhead.
        
         | chrsig wrote:
         | It's definitely cool, and and accomplishment. I think it's a
         | bit strange to compare it to "no cgo overhead" though
         | 
         | I say that because it's an interpreter rather than a jit, and
         | doesn't have nearly as many person hours put into it as v8.
         | 
         | I can see the argument that if you just need to evaluate a
         | quick expression or maybe some user supplied script or
         | something, it could be very handy, but I have a hard time
         | believing that the cgo overhead is a factor when using cgo to
         | invoke v8 compared to the parse/jit overhead that v8 will do.
         | 
         | This is all armchair speculation of course. I use go regularly,
         | but have no taste for javascript, so I have no dogs in that
         | race. Definitely cool that there's an interpreter focused on
         | correctness though.
        
         | mappu wrote:
         | Calling a C function with Cgo has a certain overhead if you
         | compare it to calling a Go function. But cgo overheads are
         | completely negligible as soon as the function does any kind of
         | real work. (A bigger reason to avoid cgo would be if you want
         | easier cross-compilation.)
        
           | randomdata wrote:
           | cgo execution overhead is a nothingburger, but the programmer
           | overhead to convert between Go types and C->Javascript types,
           | and vice versa, as required by the use of a third-party
           | engine (v8, etc.) is still significant as compared to what
           | Goja offers. That is what the parent is talking about.
        
       | siamese_puff wrote:
       | Great article. I was looking for something like this last week.
        
         | jtarchie wrote:
         | I'm glad it was helpful. It was a list of things I had known
         | before I used it. So it was meant to be a quick getting started
         | like doc.
        
       | nchmy wrote:
       | The examples in the article seem awfully contrived. Can someone
       | please explain what a real-world use case for something like this
       | would be? Why not just do it all in Go?
       | 
       | Or is the point of it to be able to use existing JS scripts
       | within a Go application?
        
         | BobVawter wrote:
         | I've been using goja in a logical replication tool to make it
         | field-programmable by the end users. No two deployments look
         | alike, so a great deal of flexibility is needed for ETL uses.
         | There's a tendency for configuration languages to become
         | Turing-complete, so we started with a Turing-complete language
         | for configuration.
         | 
         | JS, or in Replicator's case, TypeScript (shout-out to esbuild),
         | is sufficiently well-known that any dev group will have some
         | experience with it. On the whole, I've been very impressed with
         | how straightforward it's been to have user-scripts integrated
         | into the processing pipeline.
         | 
         | https://github.com/cockroachdb/replicator/wiki/User-Scripts
        
           | nchmy wrote:
           | Ah, that makes a lot of sense - far easier to expose a JS api
           | for user-extension than Golang (which is probably not even
           | all that possible, except for via wasm, which would still be
           | challenging)
           | 
           | And, I suppose my initial hunch is probably correct - for the
           | dev/application to be able to use existing JS libraries (be
           | it part of your broader application/system or external)
           | within a Go app.
           | 
           | Check out my other comment [1] in this post for a newer,
           | better version/fork of Goja - from the grafana/k6 team.
           | 
           | [1] https://news.ycombinator.com/item?id=41470601
        
         | jtarchie wrote:
         | Not doing it all, Go allows my API endpoint to accept sandboxed
         | javascript code to enable users to run more complex queries
         | against my data. It is also faster because they don't have to
         | make individual API calls for each data set. Therefore, more
         | data is required to be sent over the wire to them.
        
           | nchmy wrote:
           | Thanks. Ive been exploring all of this for the past many
           | hours and definitely see the appeal and various use cases for
           | it now.
           | 
           | Thanks for opening me up to it all - it will definitely solve
           | a problem that I'm about to start working on!
        
         | nchmy wrote:
         | I just found this excellent comment in an relevant Reddit
         | post[1], which clarifies things quite nicely for me.
         | 
         | ---
         | 
         | Let's set up the scenario. You have a program that a user can
         | manage via a config file. That tune knobs and dials, and
         | everything works. They then ask you one day, "I'd like to add
         | some custom logic for they have."
         | 
         | Do you add another option to the configuration file to support
         | their one use case? Or provide a mechanism for them actually to
         | write some code?
         | 
         | If it's the latter, do you want them to have to recompile your
         | program to be able to run that custom code? Or do you have a
         | runtime in your application for that custom code?
         | 
         | The latter is what a browser does. You can write JavaScript for
         | a website without recompiling the browser just for the website.
         | The user doesn't have to worry about your event loops, SDKs,
         | APIs, wiring code in rust/C, etc. This feature allows others to
         | add custom logic to your application's runtime.
         | 
         | This is the purpose of embedding JavaScript into an
         | application. It's not the whole program, but it allows someone
         | to provide code in your program event loop to do something
         | custom. Not every program needs this. Sometimes, a config file
         | of YAML or command line arguments works, too.
         | 
         | [1]
         | https://www.reddit.com/r/golang/comments/1d78d3j/what_script...
        
         | artpar wrote:
         | I am using otto[0] (similar to goja) in the headless cms I
         | maintain [1] in "actions" api [2]. So one can customize actions
         | according to needs by writing JS. The JS is executed inside a
         | transaction. Sort of a mini-lambda kind of api
         | 
         | [0] https://github.com/robertkrimen/otto [1]
         | https://github.com/daptin/daptin [2]
         | https://daptin.github.io/docs/actions/actions/
        
         | joostdecock wrote:
         | For a real-worl use case, look towards the 'script' processor
         | in Elastic' Beats family of data shippers [1] (example is from
         | Metricbeat but it exists in other Beats too)
         | 
         | They can be configured to mutate the data with some basic DSL
         | syntax, but if you have more advanced needs, you can break out
         | to JavaScript and transform the JSON any way you want.
         | 
         | This is very useful because now any transformation becomes
         | possible. And because this is the user of your product who
         | writes that JavaScript more as configuration than code. And
         | since there's no build step, it is just part if the
         | configuration loaded at startup.
         | 
         | [1]
         | https://www.elastic.co/guide/en/beats/metricbeat/current/pro...
        
         | solatic wrote:
         | On top of the previous comments describing how to use JS as an
         | embedded scripting language, I've seen in production (series B
         | startup) an architecture where:
         | 
         | a) non-technical users describe, in plain English, what they
         | would like from the data; b) an LLM with lots and lots of
         | prompt-engineering massaging translates the English into JS, c)
         | the JS gets executed in a sandbox, d) the results get returned
         | to the user
        
         | bastawhiz wrote:
         | I didn't use Goja (this was maybe a decade ago) but I used
         | Otto, which is the same concept.
         | 
         | I used it for game scripting. Lots of stuff is just easier to
         | write in a high level language. I was able to use inheritance
         | to compose behaviors for entities, and I could restart entities
         | with new code without having to restart the server.
        
       | dialogbox wrote:
       | The name looks a obvious choice but... Ouch. It's a very funny
       | name for Korean. It means eunuch. Lol
        
         | michaelsbradley wrote:
         | Then there's Ejacs
         | 
         | https://github.com/emacsattic/ejacs
         | 
         | http://steve-yegge.blogspot.com/2008/11/ejacs-javascript-int...
        
         | jtarchie wrote:
         | Someone on Reddit mentioned in Norwegian, it means "nothing."
        
         | t3rra wrote:
         | I actually came to comments to say that.. lol.
        
       | alixanderwang wrote:
       | I work on the D2 project ([0]) and we switched from other
       | Javascript runners (v8go) to Goja. Using a JS runtime with a
       | dependency on cgo means your Go program loses the (huge) benefit
       | of cross-compiling to different architectures, since at build
       | time it gets linked to the current system's libc.
       | 
       | If you're interested in some production code with Goja, this is
       | our code for calling RoughJS ([1]) from Go in order to produce
       | the hand-drawn diagram look: [2]
       | 
       | [0] https://github.com/terrastruct/d2
       | 
       | [1] https://roughjs.com/
       | 
       | [2]
       | https://github.com/terrastruct/d2/blob/master/d2renderers/d2...
        
       | donatj wrote:
       | I've been a pretty happy Otto[1] user for a number of years now.
       | 
       | The article makes no comparisons seemingly? I don't think there's
       | even a never. I find that pretty odd.
       | 
       | I'm curious if there'd be any reason to switch.
       | 
       | https://github.com/robertkrimen/otto
        
         | jtarchie wrote:
         | I just wanted to share the details of my experience with Goja.
         | It was not relevant to compare Otto.
        
       | nchmy wrote:
       | I just dug through a lot of the issues and PRs in Goja, and
       | eventually found that the grafana/k6 team recently forked Goja as
       | Sobek [1], because the Goja dev has not been able to dedicate
       | sufficient time to their PRs - namely ES Modules support [2],
       | which was one of the only modern (ES6+) JS features outstanding
       | [3]
       | 
       | So, Sobek seems to be the way forward...
       | 
       | [1] https://github.com/grafana/sobek/
       | 
       | [2] https://github.com/dop251/goja/pull/430
       | 
       | [3] https://github.com/dop251/goja/milestone/1
        
         | jtarchie wrote:
         | I did see that. I'm going to let the fork simmer for awhile. I
         | didn't require module support for my runtime. Since its meant
         | to be very basic ETL runtime.
        
           | nchmy wrote:
           | Fair enough! Though they're quite clearly a much more
           | serious/focused/resourced team than the goja dev, and they've
           | fully integrated it into the K6 codebase now. I expect it'll
           | have a bright future.
           | 
           | Since you're doing ETL, did you consider something like
           | Conduit, which is written in Golang and also allows for JS
           | processing in the pipeline? Or is that overkill for what
           | youre doing?
           | 
           | https://conduit.io/docs/processors/builtin/custom.javascript
        
       | bitwize wrote:
       | You know, back when I got started as a Schemer, I remember people
       | complaining about how many implementations of Scheme there were
       | and how difficult it was to port between them. Today in
       | JavaScript land we have Node, Deno, Bun, Goja, Sobek, Nashorn,
       | whatever the browsers have, QML, GNOME, all of them subtly
       | different. But instead of complaining, which people seem inclined
       | to do when they encounter nested parens, people bro down and
       | wrote code necessary to make them all interoperate if necessary
       | -- despite JS having even less of a core language standard than
       | Scheme does.
       | 
       | I guess it's the Lisp Curse or something, I dunno.
        
       ___________________________________________________________________
       (page generated 2024-09-07 23:01 UTC)