Building Speakeasy
EasyTemplate OSS Release: Templating Superpowers for Go
Tristan Cartledge
February 28, 2023
At Speakeasy, we work in a variety of languages, but most of our backend is written in Go, specifically for its no nonsense outlook on code quality and long term maintainability. Without Go’s vibrant OSS community, we wouldn’t have been able to build the product we have today, which is why we’re very excited to have the opportunity to contribute back to the community.
Check it out (opens in a new tab)
What is EasyTemplate?
easytemplate (opens in a new tab) is Go’s text/template (opens in a new tab) with super powers. It is a templating engine that allows you to use Go’s text/template (opens in a new tab) syntax, but with the ability to use JavaScript or Typescript snippets to manipulate data, control templating and run more complex logic while templating.
easytemplate (opens in a new tab) powers Speakeasy’s SDK Generation product and is used by thousands of developers to generate SDKs for their APIs.
The module includes a number of features on top of the standard text/template (opens in a new tab) package, including:
- Support for JavaScript snippets in templates (opens in a new tab).
- ES5 Support provided by goja (opens in a new tab).
- Built-in support for underscore.js (opens in a new tab)
- Import JavaScripts scripts from other files and inline JavaScript snippets
- Use JavaScript or Typescript
- Modify the templating context from within JavaScript.
- Controlling the flow of templating within the engine (opens in a new tab).
- Inject Go functions into the JavaScript context (opens in a new tab), in addition to Go functions into the templates (opens in a new tab).
- Inject JS functions into the template context. (opens in a new tab)
Why’d we build it?
Speakeasy needed a way of templating complex hierarchy’s of templates that all relied on each other and the content they contained (like for when you generate SDKs from API Specs). By building a templating engine that allows more complex logic to be run at templating time via JS and allowing templates to template other templates, we unlock the ability to tailor templates to our needs based on the target output.
This allows us to decouple templating from our core binary, allowing new templates to be provided at runtime (think plugins) without the core go code/binary needing to know what templates there are, what data they need, enabling the templating to call itself on a dynamic set of files.
We chose JS/TS as the language for the embedded scripting because of its ubiquity, and ease of learning. It also has a thriving ecosystem of data and string manipulation modules which provide additional super powers to your templates.
Basic Example
main.go
package mainimport ("fmt""log""os""github.com/speakeasy-api/easytemplate")func main() {// Create a new easytemplate engine.engine := easytemplate.New()// Start the engine from a javascript entrypoint.err := engine.RunScript("main.js", data)if err != nil {log.Fatal(err)}}
main.js
// From our main entrypoint, we can render a template file, the last argument is the data to pass to the template.templateFile("tmpl.stmpl", "out.txt", { name: "John" });
tmpl.stmpl
In the below template we are using the name
variable from the data we passed to the template from main.js.
We then also have an embedded JavaScript block that both renders output (the sjs block is replaced in the final output by any rendered text or just removed if nothing is rendered) and sets up additional data available to the template that it then uses to render another template inline.
Hello {{ .Local.name }}!```sjsconsole.log("Hello from JavaScript!"); // Logs message to stdout useful for debugging.render("This text is rendered from JavaScript!");context.LocalComputed.SomeComputedText = "This text is computed from JavaScript!";sjs```{{ templateString "tmpl2.stmpl" .LocalComputed }}
tmpl2.stmpl
And then we are showing some computed text from JavaScript:{{ .SomeComputedText }}
The rendered file out.txt
Hello John!This text is rendered from JavaScript!And then we are showing some computed text from JavaScript:This text is computed from JavaScript!
How should you use it?
easytemplate
allows you to control templating directly from scripts or other templates which among other things, allows you to:
- Break templates into smaller, more manageable templates and reuse them, while also including them within one another without the need for your Go code to know about them or control the flow of templating them.
- Provide templates and scripts at runtime allowing pluggable templating for your application.
- Separate your templates and scripts from your Go code, allowing you to easily update them without having to recompile your application and keeping concerns separate.
We can’t wait to see what the Go community uses EasyTemplate for!