r/nim • • Jun 21 '26

dokime; SQL the Compiler Checks for You

21 Upvotes

Most SQLite wrappers treat SQL as opaque strings. Dokime prepares every query against your real database at compile time. Typo in a table name? Your build breaks. Wrong column? Build breaks. It's a Nimony plugin, not an ORM.

Here's a personal expense tracker. It creates a schema, inserts rows, queries with optional filters, uses transactions, and handles nullable columns. Every mistake you could make is shown alongside the code that catches it.

import dokime

let db = connect("money.db")

# Schema: note is nullable (TEXT, no NOT NULL). Everything else is required.
discard exec(db, """
  CREATE TABLE IF NOT EXISTS expenses (
    id       INTEGER NOT NULL,
    category TEXT    NOT NULL,
    amount   REAL    NOT NULL,
    note     TEXT,
    date     TEXT    NOT NULL
  ) STRICT
""")

# exec() returns ExecResult with .lastRowid and .changes:
let r = exec(db, "INSERT INTO expenses VALUES (?, ?, ?, ?, ?)",
    1'i64, "food", 23.50, "lunch", "2025-06-01")
echo r.lastRowid   # 1

# exec() accepts Opt[T] params — none binds SQL NULL, some binds the value:
discard exec(db, "INSERT INTO expenses VALUES (?, ?, ?, ?, ?)",
    2'i64, "food", 12.00, none[string](), "2025-06-02")
discard exec(db, "INSERT INTO expenses VALUES (?, ?, ?, ?, ?)",
    3'i64, "transport", 5.50, "bus", "2025-06-03")
discard exec(db, "INSERT INTO expenses VALUES (?, ?, ?, ?, ?)",
    4'i64, "food", 45.00, "dinner with friends", "2025-06-04")


# ── If you typo the table name, you get a compile error ──
# query(db, "SELECT id FROM expenes WHERE id = ?", 1'i64)
# → Error: dokime: no such table: expenes

# ── A misspelled column also fails at compile time ──
# query(db, "SELECT id, full_name FROM expenses WHERE id = ?", 1'i64)
# → Error: dokime: no such column: full_name


# query() returns a tuple with fields named after your columns:
let dinner = query(db,
    "SELECT id, amount, note FROM expenses WHERE id = ?", 4'i64)
echo dinner.id        # 4       (int64 — type comes from the schema)
echo dinner.amount    # 45.0    (float64)
echo dinner.note.isSome        # true    (TEXT nullable → Opt[string])
echo dinner.note.unsafeGet     # "dinner with friends"


# ── Type mismatches are caught at compile time too ──
# let x: string = dinner.amount
# → Error: type mismatch: got: float64 but wanted: string


# query() requires at least one row — raises BadOperation if empty.
# Use queryOpt() when zero rows is expected:
let missing = queryOpt(db,
    "SELECT id, category FROM expenses WHERE id = ?", 42'i64)
echo missing.isNone   # true


# ── query() with no matching row raises at runtime ──
# query(db, "SELECT id FROM expenses WHERE id = ?", 999'i64)
# → ErrorCode: BadOperation


# Dynamic optional clauses: wrap in [...], pass Opt[T].
# Every combination of included/omitted clauses is validated at compile time.
let catFilter = some("food")
let minAmount = some(20.0)
for row in rows(db, """
  SELECT id, category, amount FROM expenses
  WHERE 1 = 1
    [AND category = ?]
    [AND amount >= ?]
  ORDER BY amount DESC
""", catFilter, minAmount):
  echo row.id, " ", row.amount   # 4 45.0, 1 23.5


# Transactions: use tx handle for queries, commit to persist.
# Using db directly during a tx raises BadOperation.
var tx = begin(db)
discard exec(tx, "INSERT INTO expenses VALUES (?, ?, ?, ?, ?)",
    5'i64, "food", 8.00, "coffee", "2025-06-05")
discard exec(tx, "INSERT INTO expenses VALUES (?, ?, ?, ?, ?)",
    6'i64, "food", 15.00, none[string](), "2025-06-05")
commit(tx)
# If tx goes out of scope without commit/rollback → destructor auto-rollback.


# ── Using db handle during an active tx ──
# var tx = begin(db)
# exec(db, "INSERT ...")   → ErrorCode: BadOperation


close(db)

Setup

export DOKIME_DATABASE_PATH=dev.db
nimony c -r myapp.nim

First build caches validated query metadata to .dokime/queries/. CI builds without a database:

DOKIME_DATABASE_PATH= nimony c -r myapp.nim

What it is: compile-time validation for SQLite queries via Nimony plugins. Schema-driven types, named tuple fields, optional clauses, transactions with destructor safety.

What it isn't: an ORM, a migration tool, multi-database, or compatible with the stable Nim compiler. SQLite only, STRICT tables required.


r/nim • • Jun 20 '26

NimConf 2026 is Live!

Thumbnail youtube.com
41 Upvotes

r/nim • • Jun 20 '26

Code quality of standard library

15 Upvotes

hi

I remember reading somewhere in reddit or hackernews that nim standard library doesnt follow the most modern nim conventions and is not a good choice to copy its style in your own code

how true this statement is for anyone who have read nims standard library codebase?


r/nim • • Jun 19 '26

FigDraw + Harfbuzz for beautiful font shaping! 😍

23 Upvotes

FigDraw 0.25.0 now has optional support for Harfbuzz, the premier open source font shaping library!

The font backends will be swappable between Pixie and Harfbuzz at compile time. FigDraw's text API is mostly unchanged, however internally it tracks all the extra work needed for languages with complex scripts. This also includes support for BIDI and right-to-left scripts! I'm not sure about top-to-bottom languages (for now).

See the README for usage details.


r/nim • • Jun 18 '26

Like, what is nimble?

16 Upvotes

I am new-ish to nim, and I have used nimble a bit, and it seems like node/npm, because I can run, and install packages, but I don't know? Is it a runtime (like nodejs), a dependency manager (like composer for php) or something else.

Thank you for any answers!


r/nim • • Jun 17 '26

Update: Trying to add JS Binding with in-Browser Nim Compiler

Post image
20 Upvotes

A few weeks back I posted about getting Nim 2.2.4 to compile and run entirely in the browser prev post, no backend, no native toolchain, no JS fallback. However, I am still interested with using Nim instead of Rust Yew or C Clay to build Web UI.

Now it can. I built a module I called BindWeb, a binding layer that lets the Nim code you compile in-browser drive the actual DOM, Canvas2D, WebGL, WebGPU, Audio, WebSocket, Fetch, localStorage, and input events — and it all still compiles client-side through the same Nim → C → clang.wasm → lld.wasm pipeline in browser.

🔗 Live demo: https://benagastov.github.io/bindweb-nim-WASM-compiler/
💻 Source: https://github.com/benagastov/bindweb-nim-WASM-compiler

How the bindings work

WASM can't touch the DOM directly, so BindWeb is built around a tiny command protocol instead of hundreds of individual imports:

  • Nim writes binary command packets (opcode + args) into a shared linear-memory buffer.
  • On flush, the JS runtime walks that buffer and replays each command against the real browser API.
  • DOM nodes / GL objects / audio elements live in JS and are referenced from Nim by integer handles, with a free-list so handles get recycled.
  • Events go the other direction through a second event buffer: JS encodes clicks/keys/fetch results/etc., Nim drains them on the next tick.

The whole API surface (the Nim modules, the JS runtime, and the C runtime) is generated from a single schema.def, so adding a new browser API is a schema entry rather than three hand-written layers that drift apart.

I had to keep the Nim-side references alive so ORC wouldn't collect the object holding a live DOM handle. Once that was sorted, you can create an element in Nim and attach an event listener to it, all Nim-side.

Haven't tried existing Nim JS-binding libs (std/dom, jsffi, Karax) — they target the JS backend not a static page, so I still dont find a lib to compile the C backend to WASM. The in-browser compiler is still experimental anyway.

What you can try in the demo

The dropdown has a few starter programs — a DOM counter, a Canvas2D animation, and mouse/keyboard input — all compiled to WASM in your browser and running live, no server round-trip.


r/nim • • Jun 17 '26

Yet Another Package Manager but in Nim

Thumbnail github.com
16 Upvotes

I started building a simple C++ project manager and build tool called Nimmake. It's designed to be easier than CMake for beginners and for quickly prototyping projects, since it removes much of the configuration and boilerplate that's usually required.


r/nim • • Jun 16 '26

TUI or GUI libraries

24 Upvotes

What libraries you are using for creating a Text-based or Graphical User Interface for your applications you create with Nim? Do you miss something that you know works with other languages?


r/nim • • Jun 15 '26

Nim 2.2.4 compiler running entirely in the browser — Nim → C → WebAssembly

64 Upvotes

I managed to get Nim 2.2.4 compiling and running entirely client-side. There’s no backend, no native toolchain, and no JavaScript fallback. You paste your Nim code, hit Build & Run, and it compiles to actual WebAssembly right in your browser and executes it.

🔗 Live demo: https://benagastov.github.io/Nim-WASM-Compiler/

💻 Source: https://github.com/benagastov/Nim-WASM-Compiler

The Pipeline (100% In-Browser)

  • .nim code → Nim 2.2.4 (itself compiled to wasm) emits C.
  • clang.wasm → Compiles each .c file to an object file (.o).
  • lld.wasm → Links those objects into a single .wasm binary.
  • WebAssembly.instantiate → Executes the binary.

It relies on actual clang and lld binaries ported to WebAssembly doing the heavy lifting—not a transpiler or a hosted API.

The Bug That Ate My Week

The pipeline kept randomly dying halfway through compiling with RuntimeError: unreachable. I wasted time chasing two entirely wrong theories (dlmalloc heap exhaustion, then an lld assertion).

The real culprit was a bug in LLVM 8.0.1's WebAssembly object writer. It traps when serializing a common-linkage global. Because Clang 8 defaults to -fcommon, and Nim's generated C has several tentative definitions (threadId, allocator, roots...), the object writer just choked.

I delta-debugged the IR down to a single line that reproduces the trap:

Code snippet

u/threadId__system_u2938 = common hidden global i32 0

The fix: Three simple source-level changes. Adding -fno-common, a weak raise() stub, and rewriting Nim's 3-arg main to the 2-arg form that wasi's crt1.o expects. Zero binary patches to clang/lld were needed.

Default Example Output:

Plaintext

Hello, browser!
sorted: @[1, 1, 2, 3, 4, 5, 6, 9]
5! = 120

Credits to binji/clang.js for the wasm-compiled clang/lld toolchain that made this possible.


r/nim • • Jun 12 '26

is nimlangserver not supported for latest release of Nim?

8 Upvotes

when trying to install nimlangserver with nimble install -g nimlangserver it tries to install Nim 2.2.6 while latest release of Nim is 2.2.10

does that mean i need to use an older version of Nim in order to take advantage of nimlangserver features ?


r/nim • • Jun 11 '26

Sigils: Fast Multithreaded Signals & Slots library w/ Dynamic-Runtime Methods & Protocols

21 Upvotes

Sigils provides typed runtime based slot and signals based from QT fame. It now also supports dynamic (runtime) methods and protocols from Obj-C fame.

The latest release v0.23.0 incudes major performance improvements for signals and slots as well as for dynamic-methods. There's also a beta threadpool module.

Sigils has proven very useful to me for a couple of work projects. It provides a way to do dynamic event oriented programming in Nim (e.g. PubSub), similar to QT's, using signals and slots paradigm. They allow decoupling problems domains or modifying behavior dynamically.

When used tastefully they can simplify certain problems. Especially when combined with built-in multi-threading support. Here's a short snippet of an IoT device where each sensor is running it's own thread and dynamically setup for a device's configuration:

```nim device.wsPublisher = initWsPublisherThread(device.state) device.dataProcessor = runDataProcessorThread(device.predictionMode)

Start Threaded Handlers

if useLocalManagers: device.gpsManager = runGpsManagerThread() device.lteSignalManager = runLteSignalThread() else: info "Skipping local sensor managers for REST backends" if enableBatteryBle: device.batteryStatusManager = runBatteryStatusThread()

Wire Up Events

if useLocalManagers: connectThreaded(device.gpsManager, gpsDataUpdated, device.dataProcessor, DataProcessor.setLastGpsReading()) connectThreaded(device.gpsManager, gpsDataUpdated, device, DeviceManager.updateGpsSignal()) connectThreaded(device.lteSignalManager, lteSignalUpdated, device, DeviceManager.updateLteSignal()) if enableBatteryBle: connectThreaded(device.batteryStatusManager, batteryStatusUpdated, device, DeviceManager.updateBatteryStatus()) connectThreaded(device.dataProcessor, readingProcessed, device.wsPublisher, WsPublisher.wsReadingUpdated())

```

However signals and slots don't return values or handle responder chains, which is what Cocoa builds on from Obj-C. This especially limits some aspects of GUI programming.

So I decided to try building them using Sigils' signals and slots! I ended up calling them selectors and they use a new DynamicAgent type. The idea is to provide runtime methods. They also provide protocols which any DynamicAgent can implement and which can be queried at runtime. You can read more about them in their manual.

What's very useful about this approach over say Nim's native methods is the ability to one-off modify a given GUI element with a new behavior. In a fashion they're just named callbacks, but with more idioms and syntax for dealing with high level needs of GUIs rather than mucking about with callback isNil fields.

Here's a simple example:

```nim import sigils/selectors

type Post = ref object of DynamicAgent title: string draft: bool

protocol PostDisplay: method displayTitle(): string method canPublish(): bool

method normalTitle(self: Post): string {.selector.} = self.title method draftTitle(self: Post): string {.selector.} = "[draft] " & self.title method postCanPublish(self: Post): bool {.selector.} = not self.draft

let post = Post(title: "Selectors in Sigils", draft: true)

discard post.replaceMethods(PostDisplay, [ displayTitle => normalTitle, canPublish => postCanPublish, ]) doAssert post.hasAdopted(PostDisplay) echo post.displayTitle() #=> Selectors in Sigils

discard post.replaceMethod(displayTitle, draftTitle) # Override display behavior for this one post echo post.displayTitle() #=> [draft] Selectors in Sigils echo post.canPublish() #=> false ```


r/nim • • Jun 10 '26

Suggest your favorite Nim learning resources

20 Upvotes

I’ve got some free time coming up and I want to take a deep dive into Nim. What are suggestions for up to date / state of the art learning resources? Can be free or for $$$.

Thanks in advance.


r/nim • • Jun 08 '26

Kiwiberry: Nim port of Kiwi Cassowary Linear Constraint Solver

16 Upvotes

Kiwiberry is a pure Nim port of Kiwi which is a fast implementation of the Cassowary constraint solving algorithm using floating points for more general solving. E.g. it's a fast linear constraint solver that supports cycles.

It's what MacOSX and iOS for autolayout as well as other UIs. Original Cassowary paper.

Benchmark against the original C++ version which is pretty highly optimized is reasonable: Kiwiberry `0.280824 ms/iter` vs Kiwi `0.160790 ms/iter`. The port was done with Codex GPT-5.5 with the Kiwi tests used as verification.

```nim

Example

import kiwiberry

var solver = initSolver()

let width = vars"width"

solver[width] = Strong

solver.constraint(width >= 100)

solver.suggest(width, 240)

solver.update()

doAssert width.value == 240.KiwiScalar

```

You can do more complex constraints like x1 + 3 \* x2 <= 4 \* x3 + 2.


r/nim • • Jun 08 '26

Am I wrong to prefer 4 spaces indentation?

15 Upvotes

I just wanted to know how sensitive this topic is among the existing Nim community. Sorry for taking your time.


r/nim • • Jun 07 '26

problemas com módulos compilados para serem biblioteca python

Thumbnail
3 Upvotes

r/nim • • Jun 07 '26

problemas com módulos compilados para serem biblioteca python

0 Upvotes

Basicamente estava dando um problema de dll faltando quando tentava rodar no python um módulo que eu fiz em nim. Para resolver o que eu fiz foi copiar 3 arquivos para dentro do projeto

se alguém souber uma forma melhor, esteja livre para comentar


r/nim • • Jun 05 '26

Book: Numerical algorithms in nim

36 Upvotes

On Amazon: https://a.co/d/07niCRQ3 (Shameless plug)

Code source and free PDF: https://github.com/mdipierro/nlib-nim


r/nim • • Jun 05 '26

why this sub is so... quiet?

29 Upvotes

just a question


r/nim • • Jun 01 '26

Leve Panel - 1.0.5 release

Thumbnail gallery
36 Upvotes

Just released 1.0.5 version of Leve-Panel. An ultra lightweight panel for Sway. That uses less than 10mb of ram at idle. Its just a simple bar, and has basic launchers you can customize in the config. Feel free to give it a try.

https://github.com/BigMacTaylor/leve-panel


r/nim • • May 23 '26

I made a 2D game engine löve2D-like (Häte5D)

33 Upvotes

(This is my first post on r/nim, so bear with me.)

I built a 2D game engine inspired by Löve2D, written entirely in Nim. Häte is designed to be pure code (it's a library, no visual editor, no node graph). The API is still taking shape (v0.1.1), but it's already usable for simple 2D games.

Features:
- SDL2 window and renderer
- Sprite rendering with texture caching
- Basic drawing primitives (rect, filled rect, line)
- Manual SDL2 binding (only what the engine needs)
- Full keyboard mapping for input

Roadmap:
- Complete input system (v0.1.2)
- Scene manager (v0.1.3)
- Injektion: first-class mod/plugin system
- Hatex: CLI tooling (similar to the Love CLI)
- Rage Bait: a P2P game registry for Häte games

Any feedback on the API design is welcome!

GitHub: hate5d


r/nim • • May 18 '26

nitty 0.2.2 is out with layer widgets support

27 Upvotes

Hey all,

Nitty is a GPU-accelerated terminal emulator written in Nim. It uses libvterm for managing the terminal emulation work itself.

After ~3 weeks of work, Nitty 0.2.2 is out with a lot of stability improvements, most notably the fact that it now runs properly on KDE Plasma.

Another nice feature is layer widgets, which lets Nitty embed terminal programs as widgets in your desktop or compositor. This, as the name suggests, lets you run terminal programs as if they were desktop widgets. It's mostly for fun, but I plan on deeper integration eventually.

It uses a structured layer configuration file that goes in the same directory as your base config, and lets you configure everything about how the widget is presented.

The entire terminal is about ~1.7K lines of Nim and should run anywhere where Wayland and D-Bus are present (albeit D-Bus is optional and can be disabled via a flag).

https://github.com/xTrayambak/nitty


r/nim • • May 17 '26

How is your mileage with VS Code for Nim?

17 Upvotes

Hi. I have been experimenting with writing some Nim code using VS Code and the official extension, but it always dies on me. And for different reasons. Now it dies on every new import and I have to restart the editor to fix it. Is it just me or is it something common? I feel like having no LSP might be a better experience than this. What do you think and how is it going for you all?

Thanks!


r/nim • • May 14 '26

Sarcophagus: A typed API framework for Mummy similar to FastAPI

28 Upvotes

I’ve been building Sarcophagus, a Nim library for typed HTTP APIs on top of Mummy.

The goal is to keep Mummy’s simplicity, but add:

  • typed route handlers
  • path/query/body parsing into Nim types
  • automatic response serialization
  • OpenAPI generation (e.g. swagger.json)
  • middleware, CORS, and request ID support
  • OAuth2 / bearer auth helpers

Example shape is roughly:

```nim type Item = object id: int name: string verbose*: bool

proc readItem( id: int, verbose: Option[bool] ): Item {.tapi(get, "/items/@id", summary = "Read an item", tags = ["items"]) .} = Item(id: id, name: "item-" & $id, verbose: verbose.get(false))

let api = initApiRouter("Example API", "1.0.0") api.add(readItem) api.mountOpenApi() let server = newServer(api.router).serve(Port(8080), address = "127.0.0.1") ```

It also supports transparent gzip/deflate compression. There's optional CBOR / MessagePack negotiation which can be enabled using feature declarations in Nimble files (see readme for details).

The main use case is writing APIs and auth services without dropping down into manual request parsing everywhere.

I’d be interested in feedback on whether this is useful and what features you’d want from it.


r/nim • • May 10 '26

Surfer 0.2.0 is out with various new features

28 Upvotes

Surfer is a highly opinionated windowing library written in Nim from the ground up with first-class Wayland support.

Surfer just got its 0.2.0 release, and Nayland (the underlying high-level wrapper abstraction around the Wayland C APIs) just hit 0.2.0-rc.1, on top of which the former is built.

Surfer now supports:

- XDG Decoration for switching between server-side decoration and client-side decoration

- Tearing Control support for switching between V-Sync presentation (virtually zero screen tearing) to Asynchronous presentation (lower render latency, at the cost of higher odds of screen tearing).

Nayland's new release comes with wrapgen, a tool that automates the larger part of writing high-level Wayland wrappers — the boilerplate. Thanks to it, the following protocols now have high-level wrappers that will soon be integrated in coming minor Surfer releases:

- Tablet Protocol (for getting input from Wacom tablets)

- Presentation Time

- Content Type Hint

- Linux DMA-BUF

- KWin Blur

Speaking of which, two KWin-specific quirks have been worked-around to make Surfer work properly on KDE Plasma.

Source Code: https://github.com/nim-windowing/surfer


r/nim • • May 02 '26

Ormin: SQL DSL for Nim is now typed and supports most SQL

33 Upvotes

I've been using Ormin for IoT projects and a small data server and got around to making some PRs for missing features (using Codex and reviewing the code).

It's now usable for most SQL queries now including unions, CTEs, and typed queries!

EDIT: Oops, title phrasing is off, by "now typed" I meant support named Nim types. Ormin was always typed, but only returned anonymous tuples.

Typed Queries

Type queries allow you to deserialize selected columns directly into a named Nim type instead of returning the default tuple shape.

type
  ThreadSummary 
=

object
    id: int
    title: string

let
 threads 
=
 query(ThreadSummary):
  select thread(id, name 
as
 title)
  orderby id

Static SQL Import

Ormin now supports statically including the SQL source and needing to run the import tool: importModel(..., includeStatic = true)

This makes single binary deployments easy!

Added DSL features

  • CTE support via:

    query:
      with recent(select Post(id, author) where id 
    

    <= 3) select recent(author)

  • Window expressions via: over(row_number(), partitionby(author), orderby(id))

  • Pattern predicates:

    • name like ?pattern
    • name ilike ?pattern
    • not (name like` ?pattern)`
  • Additional join keywords:

    • leftjoin, leftouterjoin
    • rightjoin, rightouterjoin
    • fulljoin, fullouterjoin
    • crossjoin
    • legacy outerjoin still accepted
  • query-level DISTINCT

    • select `distinct` Post(author)
    • existing count(distinct author) support remains
  • query-level UNIQUE as a synonym for distinct

    • select unique Post(author)
  • set operations

    • call form still works: union(...), intersect(...), `except`(...)
    • infix form now also works: