Say I have a API between frontend and canister (e.g. motoko):
type Request = {
#getInfo;
#getMessages;
}
type Response {
#info = Text;
#messages = [Text];
}
actor {
public query func makeRequest(request: ?Request) : async ?Response {
switch(request) {
case (null) {
//got unsupported variant
null;
};
case (?requestUnwrapped) {
//respond with corresponding response variant...
?#info "hello from info";
}
}
}
I would like to evolve this API over time - add new request-response variant pairs.
E.g. add “#getLastMessages” case to Request
or add “#status” to Response
Those changes can be made in different ways:
1. new UI can talk to old canister API
UI candid (passing Request #getLastMessages
)
type Request = {
#getInfo;
#getMessages;
#getLastMessages; //new!
}
type Response {
#info = Text;
#messages = [Text];
#status = Text; //new!
}
Canister
type Request = {
#getInfo;
#getMessages;
}
type Response {
#info = Text;
#messages = [Text];
}
All is good - canister gets null
as a fallback for unsupported opt type.
2. old UI can talk to new canister API
UI candid
type Request = {
#getInfo;
#getMessages;
}
type Response {
#info = Text;
#messages = [Text];
}
Canister (responding with Response #status
)
type Request = {
#getInfo;
#getMessages;
#getLastMessages; //new!
}
type Response {
#info = Text;
#messages = [Text];
#status = Text; //new!
}
Provided approach give an error in second case “old UI- new canister”: adding new variant case in canister and returning it to UI (javascript in the browser) gives an error: Error: Cannot find field hash _xxxxxx_
.
If there is a way to get a fallback to null
for non matching types on both sides - in canister and in javascript?
As I understand - I get NULL case in motoko because of “Candid - opt is a special”.
@nomeata @claudio Please share your thoughts on building API that evolves and handles unsupported variant cases in parameter and return type on both sides.