Servoy Tutorials

Hey everyone,

This is my running list of Servoy tutorials on dotzlaw.com. I add new ones as I publish them and I keep the older ones linked here because most of the fundamentals still apply a decade later. Bookmark this post. I will keep updating it.

If you are not using the new AI Runtime Plugin (`plugins.ai`) that shipped with Servoy 2025.12, you need to read the latest articles below to see what you are missing. If you are new to Servoy, start at the bottom of the post with the Foundations section and work your way up.

I aim to publish a new tutorial every week. Check back on Sundays.


Latest Additions

**The Servoy AI Runtime Plugin Series (April 2026).** A four-part deep dive into `plugins.ai`, the Langchain4j-backed runtime that shipped with Servoy 2025.12. Read them in order.

**[Part 1: Getting Started with the Servoy AI Runtime Plugin]( Getting Started with the Servoy AI Runtime Plugin - Dotzlaw Team )**. Chat completions with OpenAI and Gemini, streaming responses, conversation memory, multimodal input (PDFs, images), and your first vector embedding with a working semantic product search that turns “spicy hot condiments” into a ranked list of actual products.

**[Part 2: Tool Calling with the AI Runtime Plugin: Agentic Servoy]( Tool Calling with the AI Runtime Plugin: Agentic Servoy - Dotzlaw Team )**. Register your existing Servoy methods as tools the LLM can call. The agent decides what to call and in what order, then executes real business logic through your QBSelect, foundset, and `databaseManager` code. Not a demo.

**[Part 3: Embedding Your Servoy Data for Semantic Search]( Embedding Your Servoy Data for Semantic Search - Dotzlaw Team )**. Move semantic search off the in-memory store and into production with PgVector. FoundSet `embedAll()` that preserves primary keys as metadata, custom metadata columns, PDF document chunking for RAG, and batch strategies for keeping embeddings in sync.

**[Part 4: Hybrid Queries with QBVectorColumn: Semantic Meets SQL]( Hybrid Queries with QBVectorColumn: Semantic Meets SQL - Dotzlaw Team )**. The payoff article. Combine semantic similarity with traditional WHERE clauses in a single Query Builder query. Products similar to “lightweight laptop for travel”, under $2,000, in stock, active, all in one database round-trip.


The Back Catalogue (Still Relevant Today)

A lot of the older stuff is just as useful today as the day I wrote it. My favourite is **The Mighty Array**. Who knew all that stuff was hiding in the humble JavaScript array?


Foundations

**[The Mighty Array]( The Mighty Array - Dotzlaw Team )**. Advanced JavaScript array functions in Servoy. The one I still get the most feedback on.

**[Coding Efficiency]( Coding Efficiency - Dotzlaw Team )**. Frameworks, conventions, and the mindset shift that makes you twice as productive.

**[Maintainable Code]( Maintainable Code - Dotzlaw Team )**. How to write code that your future self will thank you for.

**[Encapsulation]( Encapsulation - Dotzlaw Team )**. Using Servoy’s encapsulation features on forms, relations, and value lists.

**[Using Git Flow and SourceTree]( Using Git Flow and SourceTree - Dotzlaw Team )**. The Git Flow model I have used on every Servoy project for the last decade.


Object-Oriented Programming in Servoy

**[Object-Oriented Programming]( Object-Oriented Programming - Dotzlaw Team )**. The entry point for OOP in Servoy.

**[Inheritance Patterns]( Inheritance Patterns - Dotzlaw Team )**. A tour of the different ways to do inheritance.

**[Prototypal Inheritance]( Prototypal Inheritance - Dotzlaw Team )**. Prototypes in Servoy, done right.

**[Multiple Inheritance]( Multiple Inheritance - Dotzlaw Team )**. How to share behaviour across unrelated objects.

**[Parasitic Inheritance]( Parasitic Inheritance - Dotzlaw Team )**. A pattern with a strange name and a legitimate use case.

**[Decorator Design Pattern]( Decorator Design Pattern - OOP - Dotzlaw Team )**. Extending behaviour without touching the original object.

**[Factory Design Pattern]( Factory Design Pattern - OOP - Dotzlaw Team )**. Centralizing object creation.


Performance and Caching

**[Optimizing Code Performance]( Optimizing Code Performance - Dotzlaw Team )**. How to find and fix the slow parts.

**[Function Memoization]( Function Memoization - Dotzlaw Team )**. Caching return values to make repeat calls free.

**[Using an Object as a Cache]( Using an Object as a Cache - Dotzlaw Team )**. The pattern that still saves me database round-trips every week.

**[Optimized Table Shuffle]( Optimized Table Shuffle - Dotzlaw Team )**. Shuffling rows in a table view, the efficient way.


UI and Forms

**[Take Back the UI with OOP]( Take Back the UI with OOP - Dotzlaw Team )**. Toggling groups of UI elements elegantly.

**[Using an Object to Control Elements on Servoy Forms]( Using an Object to Control Elements on Servoy Forms - Dotzlaw Team )**. Treat form elements as members of a bigger object.

**[Button Magic]( Button Magic - Dotzlaw Team )**. Custom SVG CSS buttons that do not look like the 1990s.

**[Using CSS UI Components with Callbacks]( Using CSS UI Components with Callbacks - Dotzlaw Team )**. CSS components in Servoy with real event handling.

**[The Demise of the TreeView]( The Demise of the TreeView - Dotzlaw Team )**. Building a better tree with CSS and callbacks.

**[Table Tree View]( Table Tree View - Dotzlaw Team )**. A table with expandable and collapsible nodes.

**[Add Forms to Tab Panels using a Map]( Add Forms to Tab Panels using a Map - Dotzlaw Team )**. Controlling exactly where a form lands in a tab panel.

**[The New DesignTimeProperties]( The New DesignTimeProperties - Dotzlaw Team )**. The feature that changed how I build reusable form components.


Architecture and Testing

**[Event-Driven Architecture]( Event-Driven Architecture - Dotzlaw Team )**. Loosely coupled modules that do not know about each other.

**[Automated Testing]( Automated Testing - Dotzlaw Team )**. If you are still testing by clicking around, this one is for you.


If any of these articles are useful, pass them on. If you run into anything while building with Servoy, drop a reply here. I read every one.

Cheers,

Gary Dotzlaw

4 Likes

Quick update — adding a new tutorial to the list.

The modern ECMAScript parser is now the only parser in 2026.03, and the language has quietly grown up around us. This tutorial walks through what is actually available — let, const, arrow functions, template literals, destructuring, optional chaining, default parameters — when each one earns its place in Servoy code, and when the old patterns are still the right answer.

A few highlights:

  • The 2025.12 breaking change you should know about (block scoping for const and let finally works the way the spec says it should).

  • The arrow function trap at scope and form level. Named function declarations still win, and there is a real platform reason for it.

  • Template literals for multi-line SQL, with the one rule you cannot forget about parameterization and SQL injection.

  • A complete before-and-after rewrite of a real scope function, showing exactly where the modern features clean up the code and where the conventions stay the same.

The article targets Servoy 2025.09 or later for most features, with a couple of editor improvements that need 2026.03. If you maintain an LTS branch (2024.03 LTS or 2025.03 LTS), stick with classic ES5 — the article calls out where that line is.

This is the prelude to the next tutorial in the pipeline on Promises and async data APIs, which builds directly on the language features covered here. Cheers!

Quick update — next tutorial is up, and it is the Promises one I teased last week.

The async APIs that landed in Servoy 2025.06 for the database layer expanded in 2025.12 to cover every async plugin, and 2026.03 added Promise<T> JSDoc generics so the script editor finally types your .then() callbacks correctly. This tutorial walks through what to actually do with them — when async earns its place over a synchronous call, how to compose parallel queries, and how to handle errors without creating a new category of bugs.

A few highlights:

  • The dashboard speedup you cannot get any other way. Four independent queries at 200 milliseconds each is 800 milliseconds sequentially. Wrap them in Promise.all() and the function returns in 200 milliseconds. Same database work, four times less waiting. The article shows the exact pattern with destructured results.
  • The two-level error handling pattern. An outer try/catch for synchronous setup failures, and a terminating .catch() on the chain for async rejections. Skip either one and you get a different category of silently-swallowed bug. The tutorial walks through what each layer is actually catching, and why one of them on its own is not enough.
  • @return {Promise<JSDataSet>} is the line that makes async readable. Without the generic, every .then() callback comes through as Object and you lose autocomplete on the resolved value. With it, the editor knows what the chain is producing. Needs 2026.03 for full editor support, but the code itself runs fine on 2025.06.
  • The sync-is-still-correct cases. Transactions, single sequential queries, simple form CRUD on save. Async is not a universal upgrade — it is a tool for a specific shape of problem, and the article is direct about where the synchronous call is still the right answer.

The article targets Servoy 2025.06 or later for the data async APIs, 2025.12 for the full async plugin coverage, and 2026.03 for the Promise generic editor support. If you maintain an LTS branch (2024.03 LTS or 2025.03 LTS), the synchronous patterns from earlier tutorials still apply unchanged — the article calls out where the version line falls.

This builds directly on the modern JavaScript tutorial — the destructuring, arrow-function callbacks, and template literals from that one all show up here in .then() chains and Promise.all() result handling. And it sets up what is coming next: the typed Query Builder, and the AI Runtime Plugin, which returns a Promise from every chat completion and embedding call.

1 Like

Quick update. Tutorial #19 is up, and it is the typed Query Builder one I teased at the end of the Promises tutorial.

Servoy 2025.09 split the old monolithic `QBColumn` into five typed subclasses: `QBTextColumn`, `QBIntegerColumn`, `QBNumberColumn`, `QBDatetimeColumn`, and `QBMediaColumn`. The editor now knows what type each column actually is, code completion only offers the methods that make sense, and a whole category of “looked fine, blew up at runtime” QBSelect bugs gets caught at edit time. This tutorial walks through what changed, why it matters, and where to add the JSDoc generic so the typed columns work properly across function boundaries.

A few highlights:

- The bug the old API could not catch. `qbCust.columns.cust_id.upper.like(…)` on an integer PK used to compile silently and fail at runtime with a database error. In 2025.09+ the editor underlines `.upper` with a warning before you even save the file. The article shows the exact slip-of-the-finger pattern that triggers it.

- Five typed subclasses, plus one. `QBTextColumn`, `QBIntegerColumn`, `QBNumberColumn`, `QBDatetimeColumn`, `QBMediaColumn` shipped in 2025.09. `QBVectorColumn` followed in 2025.12 with the AI plugin work and is documented separately. The article spells out which methods live on which subclass and which return types you actually get back.

- The JSDoc generic that makes it all work. `/**@type {QBSelectdb:/myserver/crm_task}*/` is the line that brings the typed columns to life, especially after `foundset.getQuery()` (which returns `QBSelect` until you annotate it) and on `QBJoindb:/myserver/whatever` join variables.

- Migration is free. Every typed subclass extends `QBColumn`, so existing code keeps working without changes. The upgrade payoff is the editor warnings that surface on lines that used to be silent. Walk through your QBSelect-heavy modules once after the upgrade and you will find bugs you did not know were there.

The article targets Servoy 2025.09 or later for the typed `QBColumn` subclasses, and 2025.12 for `QBVectorColumn`. If you maintain an LTS branch on 2024.03 LTS or 2025.03 LTS, every QBSelect pattern you already use still works unchanged. You just do not get the editor help, so the runtime-only failure mode is still in play. The article calls out where the version line falls.

This builds directly on the Promises and async tutorial. Async returns and typed columns combine nicely once you start passing typed `QBSelect` references around through `.then()` chains. And it sets up what is coming next: the typed Solution Model, where the same generic-annotation pattern brings type-safe form, valuelist, and component references into the editor as well.

1 Like

Quick update. Tutorial #20 is up, and it is the typed Solution Model one I teased at the end of the typed Query Builder tutorial.

Servoy 2025.12 introduced typed namespaces that mirror your solution model: `JSForm.NAMES.xxx` for form names, `JSForm.INSTANCES.xxx` for the actual JSForm objects, `JSValueList.NAMES.xxx` for value lists, and `JSWebComponent...` for typed component property paths. 2026.03 added F3 navigation and find-references to `JSForm.NAMES.xxx` so renaming a form is finally a refactor instead of a grep. This tutorial walks through the migration shape and the editor warnings that surface on every typo you used to find at runtime.

A few highlights:

- The grep-and-pray problem, finally solved. Renaming a form used to mean searching every `.js` file for `‘frm_customer_detail’` and hoping nobody concatenated the name from two variables. With `JSForm.NAMES.frm_customer_detail`, the IDE refactor updates every reference, and 2026.03 find-references shows you exactly where they live before you commit. The article shows the exact before-and-after.

- Three typed surfaces from one release. `JSForm.NAMES`, `JSValueList.NAMES`, and `JSWebComponent...` all landed in 2025.12. Each one autocompletes from your actual solution and produces a compiler warning on typos. The article walks through each surface with a navigation-scope example that uses all three.

- Typed permissions go back to 2025.06. `JSPermission.` is the typed enum form. Compiler warnings on invalid references, no more silently-passing security checks because somebody typoed the permission name. Documented constants are sparse on the reference page, but the autocomplete in your IDE pulls from your actual security configuration.

- Migration is free. Existing string-literal calls keep working. The typed namespaces are additive. The editor warnings start appearing on the first new function you write after the upgrade, and you clean up the rest as you touch each file. No migration sprint, no codemod.

The article targets Servoy 2025.06 or later for `JSPermission`, 2025.12 for the bulk of the typed solution-model surface, and 2026.03 for the F3 + find-references enhancement on `JSForm.NAMES.xxx`. If you maintain an LTS branch on 2024.03 LTS or 2025.03 LTS, every string-literal call you already use still works unchanged. You just do not get the editor help, so a typo in a form name is still a runtime-only failure. The article is direct about where the version line falls.

This builds directly on the typed Query Builder tutorial. Once you start passing typed `QBSelect` references around and reaching for `JSForm.NAMES.xxx` and `JSValueList.NAMES.xxx` in the same call site, the magic-string surface area drops to almost nothing. And it sets up what is coming next: data-driven UI with `visibleDataprovider` and `enabledDataprovider`, where 2025.06 swapped imperative show/hide scripting for declarative data binding on every component that has `visible` and `enabled`.

Quick update. Tutorial #21 is up, and it is the data-driven UI one I teased at the end of the typed Solution Model tutorial.

Servoy 2025.06 added `visibleDataprovider` and `enabledDataprovider` to every component that already has `visible` and `enabled`. Instead of toggling component state from `onShow`, `onRecordSelection`, and a pile of data-change handlers, you bind a calculation (or a global, or a column) to the dataprovider slot and the platform keeps the component in sync for you. This tutorial walks through the migration shape, the combination rule you have to keep in your head, and where the declarative binding still loses to plain imperative script.

A few highlights:

- The stale-UI bug, finally designed out. The classic failure was a button that was correct on form open and correct after a record selection, but went stale when a user edited the status inline because nobody wired the refresh helper into the data-change event. Bind a calculation to `visibleDataprovider` instead and it reevaluates on its own when its inputs change. The article shows the exact before-and-after, three handlers down to one calculation.

- The combination rule. Final state is the static property AND the dataprovider value. If you bind a `visibleDataprovider` and the component refuses to appear, check that the plain `visible` property has not been left at false in the editor. One sentence in the release notes, one debugging session saved.

- Role-based visibility in one calculation. `security.hasPermission(JSPermission.Approvers)` works inside a calculation because the security API is global, so a single calc can gate a button on both a permission and the record state. The typed permission enum from #20 means a misspelled permission name warns at design time instead of silently failing inside a visibility check.

- The honest `enabledDataprovider` caveat. One early adopter reported `enabledDataprovider` not taking effect on a button while `visibleDataprovider` worked. Lead with `visibleDataprovider`, and verify `enabledDataprovider` on your component and point release before you rely on it. The article is direct about it rather than pretending parity.

The article targets Servoy 2025.06 or later, which is where both properties shipped. If you maintain an LTS branch on 2024.03 LTS or 2025.03 LTS, the imperative show/hide patterns from earlier tutorials still apply unchanged, since the dataprovider slots are not present pre-2025.06. The article calls out where the version line falls.

This builds directly on the typed Solution Model tutorial, since the role-based example leans on `JSPermission`. And it sets up what is coming next: the Events Manager, the other half of the 2025.06 declarative shift, which moves UI event wiring out of `onShow` and `onHide` the same way these dataproviders move component state out of the event handlers.

Quick update. The Events Manager tutorial is up, and it is the one I have been building toward with the declarative-form series.

https://www.dotzlaw.com/insights/servoy-tutorial-22-events-manager/

The Events Manager is the Observer pattern for Servoy: custom events flow from a module to any number of consumer forms, with the module having no knowledge of who is listening. The API itself (addEventListener / fireEventListeners / removeEventListener) has been in place for a while, but the 2025.06 release added a declarative consumer-side layer called UI Events that I think is worth a careful look. The tutorial covers both, because the “before” story is necessary context for why the “after” is an improvement.

A few highlights:

- The deregister-function pattern. `addEventListener` returns a deregister function. Store it in a form variable; call it in `onHide`. Forget the `onHide` step and the handler fires twice on the next visit, three times on the one after that. The platform does not warn you.

- The gate-pattern with `fireEventListeners`. The method returns the logical AND of all listener return values by default. Fire a pre-save event across embedded sections, collect their validations in one Boolean, and block the save if any section objects. The tutorial walks through a full order-entry example with the `databaseManager.saveData()` return-value check included.

- What changes with UI Events. Mark a custom event as a UI Event in the Event Types Editor and it appears in the Properties view for consumer forms, right alongside button `onClick`. Consumers bind a handler with no code; Servoy auto-registers on form creation and auto-deregisters on form destruction.

- Firing stays in code, always. This is the critical accuracy point. UI Events change how consumers attach handlers. The module still calls `eventsManager.fireEventListeners()`. The declarative layer is purely on the consumer side.

- The five `fireEventListeners` overloads. The tutorial enumerates all of them, including `EVENTS_AGGREGATION_TYPE.RETURN_VALUE_ARRAY` for when you need each listener’s individual return value rather than the AND-combined Boolean.

The article targets Servoy 2025.06 or later for UI Events. The programmatic `eventsManager` API covered in the first half of the tutorial applies to any Servoy version where the Events Manager plugin is available. If you are on the 2024.03 LTS or 2025.03 LTS branches, the scripted addEventListener / removeEventListener pattern from the tutorial works unchanged. The UI Events declarative feature requires 2025.06 or later.

This wraps up the six-part Modern Servoy Language and Platform Features sub-series. It builds directly on the Data-Driven UI tutorial (visibleDataprovider / enabledDataprovider), and together with form-scoped LESS these three 2025.06 additions substantially reduce form event handler boilerplate. More topics queued up; I will post here when the next one is ready.

Quick update. Next tutorial is up, and it is the typed Map and Set one I mentioned was coming in the Tier 5 language series.

If you have been writing Servoy code long enough to have a personal flavour of the plain-object caching pattern (the `oCache = {}` style from 2013), this one is for you. Servoy 2025.09 landed typed `Map<K, V>` and `Set` support in the IDE, and this tutorial walks through what that actually changes in practice: a lookup table that the script editor understands at the value level, not just the key level.

A few highlights:

- The five problems with plain-object caches that typed Map solves: key coercion, prototype collision risk, no `.size`, a fragile `.has()` workaround, and zero IDE type inference on the retrieved value. A typed `Map<Number, String>` product-name lookup demonstrates all five fixes in working code.

- The `Map.forEach` callback order is `(value, key, map)`, value first. This catches almost everyone the first time. The tutorial flags it prominently because getting it backwards produces silent logic errors that are not type-checked.

- Typed `Set` for deduplication: collecting unique category codes from a foundset with no manual `indexOf` checks. The `.add()`, `.has()`, `.delete()`, and `.size` API is shown with annotated examples.

- The 2025.12 improvements (SVY-20595, SVY-20596): forEach callbacks are now auto-typed from the Map or Set’s generic annotation, and iteration methods like `.entries()` return properly typed iterators. If you are on 2025.12, you get this without any extra JSDoc on the callback.

The tutorial targets Servoy 2025.09 or later, which is when the IDE typing for generics landed (SVY-18614). If you are on 2025.06, Map and Set work at runtime because Rhino 1.8.0 ships with ES6 support, but you get no IDE type inference on retrieved values. The code in the article runs on 2025.06; the typed annotation is the 2025.09 feature. If you maintain a 2024.03 LTS or 2025.03 LTS branch, the plain-object oCache approach from the earlier tutorial still applies unchanged.

This builds on the Modern JavaScript tutorial from early in the Tier 5 series, which covers the Rhino 1.8.0 baseline. It also pairs naturally with the Typed Query Builder and Typed Solution Model tutorials, since you will often populate a Map from a dataset built by a typed QBSelect query. That combination, typed query feeding a typed Map, is a pattern worth getting comfortable with.

If there are particular Servoy topics you would like to see covered next, reply here or send me a message. I am happy to point the series wherever it is most useful.