supabase functions serve: Import maps fail to resolve inside of 'functions' directory
Issue
It seems like import maps are failing in multiple ways:
- When the mapped directory is INSIDE the function folder, the import map does not properly resolve in the docker instance
- When the import is outside of the
supabasefolder, it seems like a private module is created? But this causes theDuplicate mount pointissue - Behavior of import maps seems to be different if
--import-mapsflag vs using the defaults
Note: I haven't had a chance to test any of this with deploy, but I assume these issues will carry over in one way or another...
Reproduction
(this was previously posted to #1093, but decided it deserves a new, open issue)
This is failing in my setup:
Project Structure
root
- common
- react-app
- supabase
- functions
- _core
- supabaseClient.ts
- chat
- index.ts
- import_map.json
- package.json
supabase/functions/chat/index.ts
import { corsHeaders } from "core/supabaseClient.ts";
import { fnSharedWithReact } from "common/template";
In "supabase_edge_runtime" Docker container
home/deno
home/deno
- fallback_import_map.json
- main
- functions
- _core
- supabaseClient.ts
- chat
- index.ts
- import_map.json
- modules
- 4b0ed4fd36b0064d333d0ebb53b49ba5d84e9b130af0ab0c15f9924364d718c6
fallback_import_map.json
{
"imports": {
"@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2",
"common/": "/home/deno/modules/4b0ed4fd36b0064d333d0ebb53b49ba5d84e9b130af0ab0c15f9924364d718c6/",
"core/": "./_core/"
},
"scopes": {}
}
The deno LSP (vscode) is happy with this configuration, but when I try to invoke the functions/chat function, I get this error:
Failed to load module: "file:///home/deno/_core/supabaseClient.ts" - No such file or directory (os error 2)
It seems like thefallback_import_map.json that gets setup in the "supabase_edge_runtime" container is referencing the wrong path:
"core/": "./_core/" should be "core/": "./functions/_core/"
Also, when I try to run it with the --import-map flag, I get the Duplicate mount point error as above:
supabase functions serve --env-file .env --no-verify-jwt --import-map supabase/functions/import_map.json
-> Error response from daemon: Duplicate mount point: /home/deno/modules/4b0ed4fd36b0064d333d0ebb53b49ba5d84e9b130af0ab0c15f9924364d718c6
Update
If I update my import map to:
"core/": "./functions/_core/",
Then it works (as suspected), but this breaks the imports in my local deno LSP... Also, it goes against what was said in #1093:
paths in import_map are relative to supabase/functions directory
So... I don't really understand what's going on.
My workaround was to move my import map OUTSIDE of functions (./supabase/import-map.json), and now vscode and supabase serve are at least consistent. However, when I do this, it doesn't even attempt to generate the module for common anymore.
Hi! Currently I abandoned using import maps (aka path aliases) within functions folder and moved all my client-server re-usable code outside supabase folder, and it works perfectly both in local and deployed versions.
However, since using relative paths is evil, I'm also looking for a solution for this issue to be able to use backend-only aliases for functions.
I suppose it could be resolved by keeping the same folders structure as in the project folder, I mean avoid moving import_map.json from supabase/functions to supabase level and mount /supabase/functions (instead of just functions) without any extra stuff like migrations etc., so in such case relative mappings in import_map.json shouldn't break I think. (or maybe re-map them by adding ./functions prefix?)
In my case this issue occurs only on Windows, on MacOS is working fine
@idudinov Hi, could you provide an example of how you structured the file? I tried what you said but still getting errors. Thank you!
@Matx00 no problem, here's my setup.
// supabase/functions/import_map.json
{
"imports": {
// here listed all packages that are used in "common"
"yup": "https://esm.sh/[email protected]",
// the "common" folder is in the root dir
"common/": "../../src/common/"
}
}
Now I can reference my models from common in Deno files:
import { UserProfile } from 'common/types/models/index.ts';
However I still use relative paths for my shared code within functions:
import '../_shared/utils/bigint.ts';
Hopefully this helps, but don't hesitate to ask any questions if any.
@laktek based on the comments here and additional research in my own project, I think the scope of this issue can be defined using the following reproduction steps.
Reproduction Steps
- Create a new supabase project
- create a new file
supabase/functions/_shared/foo.tswith the following:
export const foo = 'bar'
- create a file
supabase/functions/import_map.jsonwith the following:
"shared/": "./_shared/"
- create a new supabase function with the following:
import { serve } from "https://deno.land/[email protected]/http/server.ts"
import { foo } from 'shared/foo.ts';
console.log("Hello from Functions!")
serve(async (req) => {
console.log(foo);
const { name } = await req.json()
const data = {
message: `Hello ${name}!`,
}
return new Response(
JSON.stringify(data),
{ headers: { "Content-Type": "application/json" } },
)
})
- run the
function:servelocally and curl the created function. You should see an error similar to this one:
Failed to load module: "file:///home/deno/_shared/foo.ts" - No such file or directory (os error 2)
An error has occured
InvalidWorkerCreation: worker boot error
at async Function.create (ext:sb_user_workers/user_workers.js:124:15)
at async Server.<anonymous> (file:///home/deno/main/index.ts:110:20)
at async Server.#respond (https://deno.land/[email protected]/http/server.ts:220:18) {
name: "InvalidWorkerCreation"
}
Expected Result
The module should be resolving to file:///home/deno/functions/_shared/foo.ts
Workaround
Move supabase/functions/_shared to supabase/_shared and update import_map.json.
Other Notes
The fix might be to update the bindImportMap function to detect imports inside of the functions directory (./) and properly rewrite these paths to ./functions.
@sweatybridge have you had a chance to look at this? It seems to boil down to the cli mounting the import map in the container at /home/deno when it should be in /home/deno/functions so that relative paths remain correct since the default placement of import_map.json is supabase/functions in a project.
I was struggling with trying to get import_map.json to work, only to discover that since v1.30, Deno wants you to put import aliases in the imports object of deno.json.
Since version 1.30, the deno.json configuration file acts as an import map for resolving bare specifiers.
See https://docs.deno.com/runtime/manual/getting_started/configuration_file#imports-and-scopes
Update: This makes my IDE happy but I hit a runtime error - "Relative import path "..." not prefixed with / or ./ or ../"...
Update 2: The error only occurs for executable code, it works fine for importing models
I finally managed to look into this more. I had to use absolute path when mounting import map inside the container.
Feel free to verify this fix with npx supabase@beta functions serve
The issue persists in beta (v1.167.4). I found that the import_map.json only works correctly when the file is placed inside the supabase directory and not within the functions subdirectory. Here’s the content of my import_map.json:
{
"imports": {
"chai/": "npm:/[email protected]/",
"drizzle-orm": "npm:[email protected]",
"drizzle-orm/": "npm:/[email protected]/",
"postgres": "npm:[email protected]",
"date-fns": "npm:[email protected]",
"date-fns-tz": "npm:[email protected]",
"langchain": "npm:[email protected]",
"supabase-js": "npm:@supabase/[email protected]",
"delay": "https://deno.land/x/[email protected]/mod.ts",
"std/": "https://deno.land/[email protected]/",
"oak/": "https://deno.land/x/[email protected]/",
"validasaur/": "https://deno.land/x/[email protected]/",
"@utils/": "./functions/_utils/",
"@repositories/": "./functions/_repositories/",
"@shared/": "./functions/_shared/",
"@tests/": "./functions/tests/"
}
}
Here is the command I use as a workaround the issue:
npx supabase functions serve --import-map ./supabase/import_map.json --no-verify-jwt