# New IndexedDB package (based on elm-concurrent-task)

**URL:** <https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559>\
**Category:** Request Feedback\
**Created:** [February 17, 2026, 1:29pm UTC](https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559 "2026-02-17T13:29:06Z")\
**Posts on this page:** 4\
**Page:** 2

<div class="post-metadata">

**Author:** ![elmdev](https://avatars.discourse-cdn.com/v4/letter/e/919ad9/32.png) [@elmdev](https://discourse.elm-lang.org/u/elmdev)\
**Post date:** [February 21, 2026, 7:45am UTC](https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559/21 "2026-02-21T07:45:54Z")

</div>

Could this approach also be used to make a better websocket integration?

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [February 21, 2026, 9:45am UTC](https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559/22 "2026-02-21T09:45:20Z")

</div>

I don’t think so. The point of websocket is to have server push messages and have a subscription on the elm side. If you don’t need that asymmetric style of communication, you can just use http (with concurrent task or without).

Even trying to shoehorn concurrent-task for the parts of websocket that look like a request/response is not the best I think. For example if you want to `open` with a task, it might succeed now, but you get an error from a third port subscribing to events later.

An appropriate approach could reuse the same kind of setup though. A pair of ports, a dual package Elm/JS with an easy install and a standard way of handling it. Maybe something like this:

```elm
port websocketOut : Encode.Value -> Cmd msg
port websocketIn : (Decode.Value -> msg) -> Sub msg

-- Commands (sent via websocketOut)
type OutMsg
    = Open { id : String, url : String }
    | Send { id : String, data : String }
    | Close { id : String, code : Int, reason : String }

-- Events (received via websocketIn)
type InMsg
    = Opened { id : String }
    | Message { id : String, data : String }
    | Closed { id : String, code : Int, reason : String }
    | Error { id : String, message : String }
    | Reconnecting { id : String, attempt : Int, delayMs : Int }
    | Reconnected { id : String }

```

Then the JS side of the package could provide a function like:

```javascript
function createWebSocketManager(ports) {
  const sockets = new Map(); // Initialize a socket manager
  ports.websocketOut.subscribe((cmd) => {
    // Handle commands of type open/send/close
    // Listen to socket events and forward them
  });
}

```

This way can just import the JS package and call that function with a `{ websocketIn, websocketOut }` object containing the two ports of your app for the websocket.

This is roughly what billstclair/elm-websocket-client or [kageurufu/elm-websockets](https://elm.dmy.fr/packages/kageurufu/elm-websockets/latest/) are doing already. I could give a try at an alternative package if I get some need for it. Will see.

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [February 24, 2026, 11:32pm UTC](https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559/23 "2026-02-24T23:32:31Z")

</div>

I’ve added these three other features, because I suspect I’ll have a need for them very soon.

1. Key ranges
2. Secondary indexes
3. Posix (time) keys

These features let you filter data at the IndexedDB level instead of fetching everything into Elm.

Updated package API docs: [Elm Doc Preview](https://elm-doc-preview.netlify.app/IndexedDb?repo=mpizenberg%2Felm-indexeddb&version=elm-doc-preview)

## Key ranges

Query subsets of a store by primary key without loading all records.

```elm
Idb.getAllInRange db store (Idb.between (Idb.IntKey 1) (Idb.IntKey 100)) decoder
Idb.getAllKeysInRange db store (Idb.from (Idb.IntKey 50))
Idb.countInRange db store (Idb.upTo (Idb.IntKey 99))
Idb.deleteInRange db store (Idb.below (Idb.IntKey 10))

```

Six constructors cover all `IDBKeyRange` variants: `only`, `from` (\>=), `above` (\>), `upTo` (\<=), `below` (\<), `between` (\>= and \<=).

## Secondary indexes

Define indexes on value fields for lookups beyond the primary key.

```elm
byTimestamp : Idb.Index
byTimestamp = Idb.defineIndex "by_timestamp" "timestamp"

eventsStore : Idb.Store Idb.GeneratedKey
eventsStore =
    Idb.defineStore "events"
        |> Idb.withAutoIncrement
        |> Idb.withIndex byTimestamp

```

Query through an index with a key range. Here we use `IntKey` instead of `PosixKey` because we most likely encode the timestamp as an Int inside the `Value` with something like `Encode.int (Time.posixToMillis time)`.

```elm
Idb.getByIndex db eventsStore byTimestamp (Idb.between (Idb.IntKey startMs) (Idb.IntKey endMs)) decoder
Idb.getKeysByIndex db eventsStore byTimestamp range
Idb.countByIndex db eventsStore byTimestamp range

```

Index queries return primary keys, so results work directly with `get`, `delete`, etc.

Indexes are managed automatically during schema upgrades: new indexes are created, removed indexes are deleted. Use `uniqueIndex` to enforce uniqueness and `multiEntryIndex` for array-valued fields.

## PosixKey

A new `Key` variant for timestamps, stored as native `Date` objects in IndexedDB.

```elm
Idb.PosixKey (Time.millisToPosix 1700000000000)

```

Round-trips losslessly: Elm `PosixKey` → JS `Date` → IndexedDB → JS `instanceof Date` → Elm `PosixKey`. Use `keyToPosix` to extract the `Time.Posix` value.

This is for primary keys. For timestamp fields inside values (used via secondary indexes), store them as integers and query with `IntKey`.

## Migration

Bump your schema version. Store and index changes are applied automatically by `open`. No code changes needed for existing operations – all new functions are additive.

## The updated example TODO app

Now showcases these new features, by filtering an event store.  
Link to the app: [Elm IndexedDB Example](https://mpizenberg.github.io/elm-indexeddb/)

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/2X/f/f55d413603a6228def27f54a3d168d147cb98c24.png)

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [March 6, 2026, 11:33pm UTC](https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559/24 "2026-03-06T23:33:31Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.

[Previous page](https://discourse.elm-lang.org/t/new-indexeddb-package-based-on-elm-concurrent-task/10559.md?page=1)
