Candid file(s) structure - can I have multiple services or import other .did files

I am trying to handcraft a .did file for a custom canister with multiple services from multiple C functions.

  • Is it possible to import .did files into another .did file?
  • Or alternatively, can you have multiple services in a single candid file ?

For example, using the examples I found for counter.c & reverse.c, who have a counter.did and a reverse.did.

I would love to be able to just import these into a main my_services.did for my canister, like:

# file: my_services.did
<...somehow import file counter.did...>
<...somehow import file reverse.did...>
# file: counter.did
service counter: {
  get : () -> (int) query;
  set : (int) -> ();
  inc : () -> ();
  dec : () -> ();
}
# file: reverse.did
service reverse: {
  "go": (text) -> (text) query;
}

If importing is not possible, another good alternative would be to have multiple services, like:

# file: my_services.did
service counter: {
  get : () -> (int) query;
  set : (int) -> ();
  inc : () -> ();
  dec : () -> ();
}
service reverse: {
  "go": (text) -> (text) query;
}

I could not figure out either of those approaches.
It works to just create a single service, but I would have to update the C code & rename the functions to distinguish between the counter & reverse service:

# file: my_services.did
service : {
  "go": (text) -> (text) query;
  get : () -> (int) query;
  set : (int) -> ();
  inc : () -> ();
  dec : () -> ();
}
2 Likes

I did find a way to group things within a single service:

# file: my_services.did
service: {
  "counter.get" : () -> (int) query;
  "counter.set" : (int) -> ();
  "counter.inc" : () -> ();
  "counter.dec" : () -> ();
  "reverse.go": (text) -> (text) query;
}

And then in the C code, use this:

void go() WASM_EXPORT("canister_query reverse.go");
void go() {
 ...
}

And after deployment, you call this from the command line:

$ dfx canister call myCanister reverse.go reward
("drawer")

Candid is really flexible. Even spaces are allowed. For example, Instead of reverse.go, it works as well to use reverse: go.

I will go with this approach for now.

3 Likes

You can use import "a.did"; to import the type declaration of did file, but the main service will not be imported. Each did file can only contain a single main service. So you can import all your did files, and manually write the combined main service interface. The method name can be unicode as well, but some host language doesn’t support unicode function names.

1 Like

Is the above still valid two years later @chenyan ?

You can now use import service "a.did" to merge the main service definition as well.

1 Like

That’s good! I’ll try thanks for the feedback.

Mmmm

❯ dfx canister call dev_backend hello
error: parser error
  ┌─ /Users/daviddalbusco/projects/lab/experimentless/dev/src/dev_backend/dev_backend.did:1:8
  │
1 │ import service "lib.did"
  │        ^^^^^^^ Unexpected token
  │
  = Expects "text"
import service "lib.did"

service : { greet : (text) -> (text) query }

and lib.did

service : { hello : () -> (text) query }

Am I doing something wrong?

Ok I gues something like that:

lib.did

type A = service{ hello : () -> (text) query };
service : A

and

import "A.did";

type B = service { greet : (text) -> (text) query };

service B : A ;

Both should work in didc. dfx is not caught up with the lastest candid yet. Waiting for some dependency bump on the main ic repo.

1 Like

You are right both work like a glove!

❯ didc bind -t ts src/dev_backend/dev_backend.did
import type { Principal } from '@dfinity/principal';
import type { ActorMethod } from '@dfinity/agent';
import type { IDL } from '@dfinity/candid';

export interface _SERVICE {
  'greet' : ActorMethod<[string], string>,
  'hello' : ActorMethod<[], string>,
}
export declare const idlFactory: IDL.InterfaceFactory;
~/projects/lab/experimentless/dev main +1 !2 ?1                                                                         
❯ 

I don’t need dfx so it’s totally fine for me.

The first is a bit more compact and easier to grasp for me, so I might used that one (I’m researching things currently).

Thanks for the quick feedback.