Dealing with env variables for mainnet deployment

I’m doing this locally. This is some of the frontend code:

authenticated_user_actor = createActor(process.env.BACKEND_CANISTER_ID, {
  agentOptions: {
    identity,
  },
});

then canister_ids.json has only local ids for each canister.

Will that setup just work automatically when deploying to mainnet, basically writing another canister_ids.json automatically, or do I need to make manual changes?

A bit more context:

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

await authClient.login({
  onSuccess: async () => {
    handleAuthenticated(authClient);
  },

  identityProvider:
      process.env.DFX_NETWORK === "ic"
        ? "https://identity.ic0.app/#authorize"
        : http://localhost:${process.env.REPLICA_PORT}?canisterId=${process.env.INTERNET_IDENTITY_CANISTER_ID}#authorize`,
  windowOpenerFeatures: 
    left=${window.screen.width / 2 - 525}, +
    top=${window.screen.height / 2 - 705}, +
    toolbar=0,location=0,menubar=0,width=525,height=705,
});
}
}) 

async function handleAuthenticated(authClient) {
set_ui_to_loading();
document.getElementById('loginBtn').value = " Log Out ";
console.log("authClient is: ", authClient);
const identity = await authClient.getIdentity();
user_principal = identity.getPrincipal();
authenticated_user_actor = createActor(process.env.BACKEND_CANISTER_ID, {
  agentOptions: {
    identity,
  },
}); `

Or otherwise what’s a good way to deploy to mainnet without getting the canister ids tangled / disconnected from each other?

Generally speaking, as long as you simply use dfx deploy dfx manages the env vars automatically for you and you don’t need to deal with it yourself.

You are right that builds are network-specific. Because of that any dfx deploy rebuilds your canisters for the network you’re targeting. If you’re doing things manually (dfx build and dfx canister install), then you should specify the network for both commands.

We have done some investigations into general builds and then injecting the right canister IDs. We concluded that it is possible to do, but also that it’s too much work to switch everyone and everything to the other model.

What about the backend? Is there a way to dynamically switch canister id that is not:
import "canister:canistername"?

You can import a type/actor class and then inject the canister id through e.g. the install arguments. But dfx doesn’t support that out of the box, so you’d have to write a script that does the installation and feeds the right IDs as install/upgrade argument(s) into your canister.

Example actor definition:

// deployable with
// dfx canister install <my canister> --argument '(service "<injected id>")'
shared (msg) actor class MyCanister(
  injected_canister : InjectedCanisterService
) {
  //actual canister content
}

Or with an ad-hoc definition of the injected canister interface:

// deployable with
// dfx canister install <my canister> --argument '(service "<injected id>")'
shared (msg) actor class MyCanister(
  injected_canister : actor { add : [(Text, Nat)] -> async [(Text, Nat)]}
) {
  //actual canister content
}

Yeah I was hoping for something “cleaner”, the current approach with remote attribute in the dfx.json would be the best if it worked for all use cases.

Strangely, I tried hardcoding the canister id, and at the same time left a console.log running on javascript for the (now unused) env variable, and it just logs the right env variable despite it never having contact with my hardcoded string.

I comented this out

// authenticated_user_actor = createActor(process.env.BACKEND_CANISTER_ID, {

replaced with

authenticated_user_actor = createActor("hard-coded-canister-id", {

and yet

console.log("process.env.BACKEND_CANISTER_ID is " + process.env.BACKEND_CANISTER_ID )

prints the correct id.