Best way to use generated declarations in JS / reactJS frontend?

My candid declaration has the following service:

service : {
  update_user_secret : (Secret) -> ();
}

which leads to the the generated files in declarations, e.g. backend.did.js:

const Secret = IDL.Record({
    'id' : IDL.Text,
    'username' : IDL.Opt(IDL.Text),
    'password' : IDL.Opt(IDL.Text),
  });

What is the best way to import the generated Secret object into my reactJS frontend code to create a Secret object which can be passed as parameter into the canister call?

1 Like

In apps, the common way is to use dfx generate which takes care of generating types.

I think you need something like dfx v12+ to run the command.

If you create a sample app, the command dfx generate should be pre-backed in the sample package.json as a generate script if I am not wrong.

Does that help?

1 Like

Generating the types is not an issue, but rather how to include them in the proper way into my frontend JS / reactJS app.
There are 4 generated files in the declarations folder, which one should be imported and how?

In the examples I found (like whoami, helloworld, etc.) there were no object parameters to be sent to the canisters, the canister query and update functions don‘t use any input parameters….

Thanks!

1 Like

Don’t know if @kpeacock has a blog post or example to share that answer this question?

do you mean this?
It can generate did.js related to which did file you want
didc-macos bind src/ic_game/ic_game.did -t js > ic_game.did.js

OP meant - I think - how to import the declarations in the React/TS/JS code

e.g.

import type { _SERVICE as SatelliteActor } from '$declarations/satellite/satellite.did';
import { idlFactory as idlFactorSatellite } from '$declarations/satellite/satellite.factory.did';

I’m doing some mumbo jumbo around it and don’t use React, that’s why I pinged Kyle to ask if there is a nice sample or article about it.

1 Like

Side notes @p_d :

1 Like

Hi @peterparker
Thanks a lot for your hints and links! It’s exactly what I meant. I’ll try it out in the next days when I’m back at work again…
Cheers :slight_smile:

1 Like

I recommend using the createActor or default export from the index. Otherwise, you can construct the actor from the IDL yourself. The important thing (in my mind) is attaching the _SERVICE type, so you can get intellisense from your arguments, return types, and all that.

You can either do createActor<_SERVICE>(...) or you can also optionally do createdActor & _SERVICE if you have already created it somewhere.

The service types are autogenerated, so all you need to get a more complex interface is to have a more complicated backend to interact with!

As a slightly dated React example, check out my IC Avatar codebase: GitHub - krpeacock/ic-avatar: Latest iteration of the IC Avatar tutorial application

1 Like

Hmmm, I had a look at the examples but I’m still struggling to find a way for my problem… Following my artefacts:

declarations/backend.did.d.ts:

import type { Principal } from '@dfinity/principal';
import type { ActorMethod } from '@dfinity/agent';

export interface Secret {
  'id' : bigint,
  'url' : [] | [string],
  'username' : [] | [string],
  'date_created' : bigint,
  'password' : [] | [string],
  'name' : string,
  'notes' : [] | [string],
  'category' : SecretCategory,
  'date_modified' : bigint,
}
export interface _SERVICE {
  'add_user_secret' : ActorMethod<[Secret], Result>,
}

declarations/backend.did.js:

export const idlFactory = ({ IDL }) => {
  const Secret = IDL.Record({
    'id' : IDL.Nat,
    'url' : IDL.Opt(IDL.Text),
    'username' : IDL.Opt(IDL.Text),
    'date_created' : IDL.Nat64,
    'password' : IDL.Opt(IDL.Text),
    'name' : IDL.Text,
    'notes' : IDL.Opt(IDL.Text),
    'category' : SecretCategory,
    'date_modified' : IDL.Nat64,
  });
  return IDL.Service({
    'add_user_secret' : IDL.Func([Secret], [Result], []),
  });
};
export const init = ({ IDL }) => { return []; };

declarations/index.d.ts and index.js are untouched and remain as generated.

In our ReactJS typescript code we try to call function add_user_secret()in the following way:

import { HttpAgent } from "@dfinity/agent";
import { AuthClient } from "@dfinity/auth-client";
import { backend, createActor } from "../../../declarations/backend";

export async function add_secret () {
    let xx = ???
    let authClient = await AuthClient.create();
    let identity = authClient.getIdentity();
    let actor = iccrypt_backend;
    const agent = new HttpAgent({ identity });
    actor = createActor(process.env.ICCRYPT_BACKEND_CANISTER_ID, {
        agent,
    });
   await actor.add_user_secret(xx);
}

…where xx obviously should be of type Secret as defined in the declarations.

Any idea how to create the secret parameter to call the IC backend function is appreciated!
Thank you

Figured out that in fact it’s quite easy when importing the types and interfaces directly:

import { Secret, SecretCategory } from '../../../declarations/backend/backend.did';
const category: SecretCategory = { 'Password': null };
let secret: Secret = {
    id: BigInt(1),
    name: "myFirstSecret",
    url: ["https://foobar.com"],
    category: category,
    username: ["username"],
    password: ["password"],
    date_created: BigInt(1),
    date_modified: BigInt(2),
    notes: []
};
...
await actor.add_user_secret(secret);

The problem was with attribute category which is an enum in rust and needs to be importer as well… Now it works like a charm!

2 Likes

I tried the import line used in the latest post here,

that didn’t work for me.