Request for developer feedback: contacts data-module

tl:dr: There is a new library to manage :card_index: address books & contacts in Solid Pods. Please try it and give feedback. :woman_technologist:

Hi all,

as part of the solid-data-modules initiative I have built a library that is meant to make it easier for developers to handle address books and contacts in Solid Pods.

  • find address books in a Pod
  • create and read address books
  • create, read and update contacts
  • organize contacts into groups

You can do all this through a simple interface, without having to struggle with the details of the (yet to be) contacts client-to-client specification.

The library is interoperable with the address books from SolidOS.

What you can do with the library :bulb:

  • Build your own solid contacts manager app
  • Easily integrate contacts to whatever app your are building, while focussing on the actual domain of your app

Quick start :rocket:

The library currently comes in the “flavor” of rdflib, but you do not need to know anything about rdflib to use it.

npm install @solid-data-modules/contacts-rdflib rdflib
import {Fetcher, graph, UpdateManager} from "rdflib";
import ContactsModuleRdfLib, { ContactsModule } from "@solid-data-module/contacts-rdflib";

// 1️⃣ create rdflib store, fetcher and updater as usual
const store = graph();
const fetcher = new Fetcher(
        // 💡 pass an authenticated fetch
        // to be able to access private resources*
        /* fetch: authenticatedFetch */
const updater = new UpdateManager(store);

// 2️⃣ create the contacts module
const module: ContactsModule = new ContactsModuleRdfLib({store, fetcher, updater});

// 3️⃣ use the module to interact with address books and contacts
const uri = await contacts.createAddressBook({
  container: "https://pod.example/alice/",
  name: "new address book"

const contactUri = await contacts.createNewContact({
  addressBookUri: uri,
  contact: {
      name: "Maurice Moss",
      email: "maurice.moss@reynholm-industries.example",
      phone: "0118-999-881-99-9119-725-3"

const addressBook = await contacts.readAddressBook(uri)

:speech_balloon: Request for Feedback :heart:

Any feedback is very welcome, even if you do not plan to use the library:

  • What features are you missing?
  • What is confusing?
  • What is valuable?
  • What needs to be added for your use cases?



Great, trying it out now! Learning curve for me:


I think this line of work is awesome, and I hope to see a lot more of this in Solid :D. Good job!

I’ve tinkered with the snippet for a bit, and here’s my first impressions:

  • I like the TypeScript typings, they are helpful and quite easy to follow without looking at the docs. There were some syntax errors (I’ll share them below), but I was able to fix them without much trouble.
  • Calling createAddressBook worked properly, but it doesn’t seem like it did anything with the type index. Should I do something to register it? I was expecting it to be done out of the box. Basically, I created an address book and then calling listAddressBooks returned empty arrays, so after reloading I wasn’t able to obtain the uri of the bookmark I had created (it was also unintuitive that it asked for a webId when I hadn’t provided one to create an address book).
  • I noticed it’s always creating documents and containers using a random string. For example I passed /contacts/ to create an address book and it created it under /contacts/QBLL34/. Is there a way to specify the exact container name? And maybe get an error if it already existed or something.
  • Finally, if I were to implement a real app, I’m sure I’d want to add some fields that aren’t covered by the library. Do you have any plans for extending the core fields? For example, if I wanted to add the birthday for a contact, how hard would it be to make it work nicely with the library?

PS: Here’s the syntax errors I found in the snippet:

  • The second import is missing the s from @solid-data-modules/contacts-rdflib.
  • The contacts variable doesn’t exist (I suppose it should be module instead).
  • The createAddressBook method takes containerUri, not container.
  • The createNewContact method takes contact.phoneNumber, not

Thanks a lot for your feedback @michielbdejong

Great that you could resolve it yourself and thanks for the compliment. What do you think would have been needed to not run into the issue at all? I tried to improve the docs in that regard here


I adjusted the peer dependencies, this should be fixed with the next release

Thank you very much!

I am going to fix those good catch. The markdown did not get the latest changes.

This is a tricky one. I am still unsure what user expectations are: Should everything be indexed by default? Should they be asked? How to decide what goes into private and what into public index? I took a look at how SolidOS handles this for address books, and it does not add anything to the type index unless you check one of those “public link to” / “private link to” boxes:

SolidOS type index check boxes for address book

This is why I did not implement it either. There might be reasons to want to create an address book, but not index it. Another reason is that one would need to pass a WebID when creating an address book. That seemd unintuitive to me.

But: I totally get the confusion this creates regarding listAddressBooks. This actually needs the WebID to find the type indexes, so it would be “symetric” to pass a WebID to create as well as to list.

I am thinking about changing the interface and put new address books to the private type index by default. Other options like adding it to the public type index or prevent all indexing could be added optionally. What do you think?

I wanted to keep it simple. You already pass a human readable title for the address book. I thought it might be confusing to differ between container name and address book name. I thought about slugifying the title, but this brings other issues, like title and slug diverging when changing the title or accidentally expose a sensitive address book title via the URI. If this is important to you anyway, please raise a feature request on github. It could still be added as an option.

Contact related fields like birthday should definetly be added to the data-module as a first-class citizen in the long run. For now and for more complex or unrelated properties / statements, you can still use rdflib directly. As a consumer of the library you have to create a store / updater / fetcher anyway. The module makes common address book related things easier, but does not take control away from your rdflib usage.

I think it’s ok that address books are not listed on the type indexes by default, but I guess I’d improve two things:

  • In the docs and code examples, make sure to mention the type index and how things can be registered using the library. Otherwise, people may not event be aware that those exist.
  • If an address book is not registered, I’m not sure how I can retrieve it after reloading. Hard-coding the url doesn’t work, so I guess I should list all the documents in a container and filter the documents containing address books myself? I would also clarify this use case in the docs.

The thing is, that the data module does not have any means to add things to a type index. And I don’t want to add those since type index is not specific to address books. Currently I am thinking it would be best to implicitly add it to the private type index when an optional owner WebID is passed on create.

You will get the uri as the response to create. An app could store It anywhere it likes, e.g. in local storage write it to some document in a pod. But I see how this burdens the developer which is the opposite of what a data module should do. Which brings me back to the approach of choosing a reasonable default and add it to the private type index.


@NoelDeMartin and all who are interested: I published version 0.6.0 of the data module, which now allows to optionally pass a ownerWebId when creating an address book. If so the private type index of that WebID will be updated to include the new address book.

Additionally features to update existing phone numbers and email addresses have been added as well as renaming a contact.


Thanks for the updates @aveltens,

After tinkering with it, there are a couple of issues I’ve found:

  • I tried to use it with an empty POD, but it didn’t work and I think that’s because it expects the type index to exist. What do you think about that, is that an expectation apps should make? In my apps, I’m currently creating a type index if it doesn’t exist. Which brings me to the next point.
  • If the type index exists, but it’s declared in the profile, it doesn’t work. Now, I understand the rationale behind this, because I am declaring solid:privateTypeIndex in the profile which is probably not the best idea. I think I’m doing this because that’s how it worked before in SolidOS (the document itself is private though, so it shouldn’t be that much of a problem). What your library expects, which seems to be better, is that it’s declared in a pim:preferencesFile, which I assume should be private. The thing is that the type-indexes spec is still a draft, so I’m not sure if I should change the way my apps work until that’s stable :/. Also, there is already some data in the wild using that old approach, so maybe the library should support it as well; even if it’s just to read data and not to create new type indexes.
  • I wanted to debug using breakpoints, but I noticed that the source files are not included in the npm package. But the source maps are included, so you probably want to do something about that. I copy&pasted the sources manually and it seems to work, but it’s still not working perfectly. Maybe that has something to do with the minification; I’m not sure how you could improve it (to be honest, I don’t think my libraries are perfectly debuggable either :sweat_smile:). But at least including the TypeScript sources should be a better experience :).

Hi Noel, thanks again for your valuable feedback.

  • you are right, the data module does not create type indexes at the moment. The whole type index thing is quite complex and there have been discussions in the data-modules group on how and where to handle them. Since this is not specific to the contacts module I will give it some more thought while working on other data-modules implementation, and see if a common pattern emerges.
  • correct, the module assumes that the private type index is linked from the preferences file as described in the current version of the type index spec (Type Indexes). But you are probably right that it would be pragmatic to at least read from the other location
  • it never occured to me to include the sources, but I guess it makes sense for debugging