Why We Built QFlow: Deno, QTI 3.0, and the Case for a New Authoring System
QTI 3.0 is mature, open-source runners exist, and institutions need authoring tools that keep assessment content portable. QFlow is the authoring tool we wanted for that work.
For years, assessment work mostly happened inside the LMS or testing platform. Questions were written there, stored there, previewed there, and eventually trapped there. Standards existed, but too often they showed up as export buttons or RFP checkboxes, not as the thing the product was built around.
This works until an institution needs content to move.
Then the hard cases show up: different LMSes, different delivery systems, imported banks from older tools, accessibility requirements that cannot be shrugged off, and AI-assisted authoring that can generate plenty of text but still leaves the hard part unsolved. The output still has to be structured, valid, reviewable, accessible, and portable.
QFlow comes from that problem. The product starts from QTI itself rather than treating XML as an after-the-fact export. You can see the project at QFlowLearn.com.
Why QTI 3.0 feels different now
QTI 3.0 is maintained by 1EdTech now, and building with it feels more practical than its old reputation suggests.
One reason is simple: the important materials are public. The current QTI overview, implementation guide, and procurement language are all available in the open. Institutions have something concrete to request, review, and verify.
QTI 3 also handles accessibility more deliberately. It does more than serialize question types. It has structures for support content and delivery behavior, instead of leaving accessibility as cleanup work downstream.
There is also something to build against now. AMP Up has already built a working open-source QTI 3 player and test runner, with source available in the AMP Up QTI 3 Player/TestRunner repository and a public demo at qti.amp-up.io. It is easier to build the authoring side when the delivery side is already real.
Why this is the right time for a new authoring system
QTI got better. The old authoring tools also became harder to defend.
A lot of older authoring environments were built for a different web: server-heavy workflows, platform-bound storage, weak import support, fragile portability, and a general expectation that content will stay where it was born. That is not how institutions work anymore.
Institutions inherit item banks from older systems. They merge platforms. They need content to survive migration. They want better accessibility. And now they are starting to use AI-assisted authoring, which makes the standards question more important, not less. It is easier than ever to generate questions. It is still hard to generate assessment content that can survive outside one vendor’s system.
So speed is not enough. The content also has to last.
In QFlow today, that means the system can import existing QTI 3.0, QTI 2.1/2.2, and QTI 1.2/1.2.1 content; keep question revisions in Postgres; validate output against vendored QTI 3 XSDs locally; and export standards-based assessment content through the same workflows authors use day to day. The point is to meet institutions where their content actually is instead of pretending everything starts from a clean slate.

A QFlow demo assessment editing flow: structured question content, preview, accessibility, and export-minded authoring in the same workspace.
Why we chose Deno
We chose Deno because QTI work already has enough complexity.
We did not want one runtime for the frontend, another for the backend, and a permanent translation problem between the authoring UI and the import, validation, and export services behind it.
Deno appealed because it let us keep the stack coherent.
- TypeScript is first-class from the start instead of being layered on later.
- The tooling is built in: formatting, linting, task running, testing, and runtime config all live in one place.
- The permission model fits a system that handles authored content, uploads, and integrations carefully.
- The development loop is fast without a lot of framework glue and build-system ceremony.
Fresh made sense for the same reason. QFlow can stay mostly server-rendered, then hydrate only the places that really need client-side behavior. Rich editing and interaction builders should be interactive. There is no reason to turn the whole application shell into a single-page app just to support them.
The stack looks like this:
- Deno 2.5+, Fresh 2.x, and Vite for a TypeScript-first application shell
- Preact islands and signals for focused client-side interactivity
- ProseKit for the rich-text editing layer
- Postgres plus Drizzle as the main authoring store
- Cloudflare R2-backed upload handling for portable asset references
- Local
xmllintvalidation against vendored QTI 3 XSDs
With this stack, the authoring UI, import logic, validation, storage, and export routes all use the same language and runtime. When import or validation breaks, users get broken content, not a tidy developer-only bug.
What we are actually building
QFlow has to handle real authoring work. The current system already handles single-select, multi-select, ordering, matching, and text-entry items; keeps versioned question revisions; rewrites imported assets into portable upload references; and builds QTI 3 assessment output through the same code paths the product uses day to day.
Deno and Fresh help here too. The route handlers under routes/ really are the backend. The import flow, export flow, and authoring UI live close together. There are fewer layers between the data model and how the product actually behaves.
We care more about usable authoring than a standards slide deck.
Closing thought
QTI 3.0 is mature. Open-source delivery is happening. Institutions need better authoring tools than the ones they inherited.
A useful standard shows up everywhere authors touch content: import, authoring, validation, preview, export, and the daily work of using the tool. That shaped QFlow, and it is why Deno felt like the right foundation.
References
- QFlowLearn: https://qflowlearn.com/
- 1EdTech QTI overview: https://www.1edtech.org/standards/qti
- QTI v3 Best Practices and Implementation Guide: https://www.imsglobal.org/spec/qti/v3p0/impl
- Suggested QTI requirements for procurement and RFPs: https://www.1edtech.org/standards/qti/rfp-procurement-agreements
- AMP Up QTI 3 Player/TestRunner: https://github.com/amp-up-io/qti3-item-player
- AMP Up live demo: https://qti.amp-up.io
- Deno: https://deno.com/
- Fresh: https://fresh.deno.dev/