Every question bank,
one open spec.

Question Commons reads question files from Canvas, Moodle, Blackboard, Kahoot, Blooket, Quizlet and more into one versioned Question Bank format, so any tool can take a teacher's questions in.

npm install @teacherdotdev/question-commons

biology.gift
::Q1:: Which gas do plants take in? {
  =carbon dioxide
  ~oxygen
  ~nitrogen
  ####Plants use CO2 to make sugar.
}
readQuestionFile() → Question Bank
{
  "format": "question-commons/question-bank",
  "version": "1.0.0",
  "name": "biology",
  "provenance": { "format": "gift", "fileName": "biology.gift" },
  "questions": [{
    "type": "multiple-choice",
    "stem": /* Which gas do plants take in? */,
    "choices": [
      { "content": /* carbon dioxide */, "correct": true },
      { "content": /* oxygen */, "correct": false },
      { "content": /* nitrogen */, "correct": false }
    ],
    "explanation": /* Plants use CO2 to make sugar. */,
    "provenance": { "line": 1, "number": 1 }
  }]
}

Reads the files teachers already have

17 formats, each recognised by its own markers and read by its own parser.

Learning platforms

  • Canvas
  • Moodle XML, GIFT and Aiken
  • Blackboard pools, exports and uploads
  • Brightspace (D2L)
  • QTI 1.2, 2.x and 3.0

Classroom games

  • Kahoot
  • Blooket
  • Class Gauntlet
  • Quizlet and Anki

Test makers and text

  • Respondus text and CSV
  • Blackboard Test Generator
  • text2qti
  • CSV and Excel spreadsheets
  • Word documents

How it reads

Never changes a question

A question comes in exactly as its file meant it, or it's left out with an issue saying where and why.

Keeps every question type

Multiple choice and answer, true/false, matching, fill in the blank, short answer, numeric and ordering.

Rules, not AI

The same file always gives the same result. Nothing is sent anywhere. Runs in the browser, Node and Bun.

Says where it came from

Every bank records its format and file, and every question its line in that file.

The spec

A Question Bank is plain JSON: a name, its questions, and where they came from. It's described by a versioned JSON Schema, and every bank names the version it follows.

question-bank-1.0.0.schema.json →

app.ts
import { readQuestionFile } from '@teacherdotdev/question-commons'

const reading = await readQuestionFile({
  name: file.name,
  bytes: new Uint8Array(await file.arrayBuffer()),
})

reading.format    // 'kahoot'
reading.banks     // QuestionBank[]
reading.pictures  // pictures the file carried
reading.issues    // what was left out, and why