Using the React bindings, is there a way to query the ledger with parameters other than the contract key?

I’m looking for a way to use the React bindings to query the ledger with the equivalent of this command:

SELECT * FROM Profiles 
WHERE State = "TN"

We’re currently pulling the entire state of the ledger and filtering results locally, but hoping for a more scalable way to handle it.

Let me know if you need more details, thanks in advance!

1 Like

Hi @liam_e!

You can use query function, see the documentation: https://docs.daml.com/app-dev/bindings-ts/daml-ledger/index.html#query

query makes a call to JSON API /v1/query endpoint: https://docs.daml.com/json-api/index.html#contract-search

Here is the query language specification: https://docs.daml.com/json-api/search-query-language.html

It does not support SQL syntax, but it allows to query active contracts.

1 Like

If you’re using React, you can also use the useQuery hook or the corresponding streaming version useStreamQuery. They are implemented on top of the functions in @daml/ledger @Leonid_Shlyapnikov linked to.

2 Likes

The template looks like this:

template Profile with admin: Party state: Text city: Text eid: Text

We’re currently using useStreamQuery in the front end like this:

const allProfiles = useStreamQuery(Profile.Profile).contracts;

We want to be able to query it based on city and state. Right now we’ve only been able to retrieve all contracts. I see how to do it using the JSON API, but not the React bindings.

A little confused about the documentation and we’re getting errors when we try to pass in query parameters, could you give an example of how we would use this function?

Thanks!

1 Like

useStreamQuery accepts a second argument, take a look at the type signature at https://docs.daml.com/app-dev/bindings-ts/daml-react/modules/defaultledgercontext.html#usequery. This is the query that will be sent to the JSON API. So you can use something like useStreamQuery(Profile.Profile, () => ({state: "yourstate", city: "yourcity"}), []) to filter down to contracts with state set to "yourstate" and city set to "yourcity".

2 Likes

When I try the following,
const result = useQuery(Profile.Profile, () => {state: "yourstate", city: "yourcity"})

I get this error:
No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.

If I take out the second parameter and only write
const result = useQuery(Profile.Profile)
the error goes away.

If I add a third parameter:
const result = useQuery(Profile.Profile, () => {state: "yourstate", city: "yourcity"}, [])

I get this type error:

Argument of type '() => void' is not assignable to parameter of type '() => { admin?: string | undefined; name?: string | undefined; address?: string | undefined; city?: string | undefined; state?: string | undefined; phone?: string | undefined; eid?: string | undefined; ... 4 more ...; timestamp?: string | undefined; }'.

Type 'void' is not assignable to type '{ admin?: string | undefined; name?: string | undefined; address?: string | undefined; city?: string | undefined; state?: string | undefined; phone?: string | undefined; eid?: string | undefined; ... 4 more ...; timestamp?: string | undefined; }'.ts

Any thoughts? Thanks again.

Sorry my bad, I’ve fixed up the above example. There are two points:

  1. Wrap the query in parentheses. Otherwise it gets interpreted as a block instead of an object: () => ({state: "yourstate", city: "yourcity"}).
  2. Add the list of dependencies for the query at the end so react can retrigger as needed. You already did this.

Awesome, got it working now. Thank you!