supergreg
supergreg2y ago

`convex/http.js` must have a default export of a Router.

Trying to follow instructions on https://docs.convex.dev/file-storage/upload-files#uploading-files-via-an-http-action Error:
⠋ Preparing Convex functions...Error: Unable to push deployment config to https://[redacted].convex.cloud
✖ Preparing Convex functions...
400 Bad Request: InvalidModules: Loading the pushed modules encountered the following
error:
`convex/http.js` must have a default export of a Router.
⠋ Preparing Convex functions...Error: Unable to push deployment config to https://[redacted].convex.cloud
✖ Preparing Convex functions...
400 Bad Request: InvalidModules: Loading the pushed modules encountered the following
error:
`convex/http.js` must have a default export of a Router.
My code is:
import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";

const http = httpRouter();

http.route({
path: "/sendImage",
method: "POST",
handler: httpAction(async ({ storage, runMutation }, request) => {
// Step 1: Store the file
const blob = await request.blob();
const storageId = await storage.store(blob);

// Step 2: Save the storage ID to the database via a mutation
const author = new URL(request.url).searchParams.get("author");
await runMutation("sendImage", { prompt, storageId });

// Step 3: Return a response with the correct CORS headers
return new Response(null, {
status: 200,
// CORS headers
headers: new Headers({
// e.g. https://mywebsite.com
"Access-Control-Allow-Origin": process.env.CLIENT_ORIGIN,
Vary: "origin",
}),
});
}),
});
import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";

const http = httpRouter();

http.route({
path: "/sendImage",
method: "POST",
handler: httpAction(async ({ storage, runMutation }, request) => {
// Step 1: Store the file
const blob = await request.blob();
const storageId = await storage.store(blob);

// Step 2: Save the storage ID to the database via a mutation
const author = new URL(request.url).searchParams.get("author");
await runMutation("sendImage", { prompt, storageId });

// Step 3: Return a response with the correct CORS headers
return new Response(null, {
status: 200,
// CORS headers
headers: new Headers({
// e.g. https://mywebsite.com
"Access-Control-Allow-Origin": process.env.CLIENT_ORIGIN,
Vary: "origin",
}),
});
}),
});
Uploading and Storing Files | Convex Developer Hub
Files can be uploaded by your users and stored in Convex.
17 Replies
sshader
sshader2y ago
Ah we're missing a line in the docs. I think export default http is what you need? (If that works, I'll update the docs accordingly, thanks for raising!)
supergreg
supergregOP2y ago
Yes, that worked! Thank you 🙂 It's also confusing to have the same file listed twice with different contents
supergreg
supergregOP2y ago
No description
supergreg
supergregOP2y ago
also running into issues with CORS:
localhost/:1 Access to fetch at 'https://decisive-nightingale-272.convex.cloud/sendImage?prompt=cyberpunk+man+in+trenchcoat+holding+a+flag+that+says+%22I+love+AI%22' from origin 'http://localhost:3001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
localhost/:1 Access to fetch at 'https://decisive-nightingale-272.convex.cloud/sendImage?prompt=cyberpunk+man+in+trenchcoat+holding+a+flag+that+says+%22I+love+AI%22' from origin 'http://localhost:3001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I followed the example so:
headers: new Headers({
// e.g. https://mywebsite.com
"Access-Control-Allow-Origin": process.env.CLIENT_ORIGIN,
Vary: "origin",
}),
headers: new Headers({
// e.g. https://mywebsite.com
"Access-Control-Allow-Origin": process.env.CLIENT_ORIGIN,
Vary: "origin",
}),
and my .env.local has CLIENT_ORIGIN=http://localhost:3001/
sshader
sshader2y ago
Re: docs -- yeah thanks for the feedback! In our demos they're actually two snippets in the same file (https://github.com/get-convex/convex-demos/blob/main/file-storage/convex/sendMessage.js) but definitely looks confusing
GitHub
convex-demos/sendMessage.js at main · get-convex/convex-demos
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
sshader
sshader2y ago
And I think you might want your URL as https://decisive-nightingale-272.convex.site/sendImage (convex.site as opposed to convex.cloud)?
supergreg
supergregOP2y ago
I don't have .site as a value, that's just the error text My value is localhost
sshader
sshader2y ago
I'm guessing you have something like this in your web code somewhere?
// e.g. https://happy-animal-123.convex.site/sendImage?author=User+123 const sendImageUrl = new URL(${convexSiteUrl}/sendImage); sendImageUrl.searchParams.set("author", name); await fetch(sendImageUrl, { method: "POST", headers: { "Content-Type": selectedImage.type }, body: selectedImage, });
supergreg
supergregOP2y ago
Yeah: const sendImageUrl = new URL(${process.env.NEXT_PUBLIC_CONVEX_URL}/sendImage); Is that wrong? And thank you for the help @sshader, really appreciate it. I've been banging my head on this for a while 😦
sshader
sshader2y ago
Yeah you might want to check your .env and .env.local files and define something like NEXT_PUBLIC_CONVEX_SITE_URL that ends with .site instead of .cloud
supergreg
supergregOP2y ago
Ok now I have NEXT_PUBLIC_CONVEX_URL="https://avid-badger-157.convex.cloud" NEXT_PUBLIC_CONVEX_SITE="https://avid-badger-157.convex.site" And changed
async function uploadImageToConvex(imageUrl, prompt) {
const response = await fetch(imageUrl);
const imageBlob = await response.blob();

const sendImageUrl = new URL(`${process.env.NEXT_PUBLIC_CONVEX_SITE}/sendImage`);
sendImageUrl.searchParams.set("prompt", prompt);

await fetch(sendImageUrl, {
method: "POST",
headers: { "Content-Type": imageBlob.type },
body: imageBlob,
});
}
async function uploadImageToConvex(imageUrl, prompt) {
const response = await fetch(imageUrl);
const imageBlob = await response.blob();

const sendImageUrl = new URL(`${process.env.NEXT_PUBLIC_CONVEX_SITE}/sendImage`);
sendImageUrl.searchParams.set("prompt", prompt);

await fetch(sendImageUrl, {
method: "POST",
headers: { "Content-Type": imageBlob.type },
body: imageBlob,
});
}
to use .site instead of cloud Testing now 🙂
supergreg
supergregOP2y ago
Still failing 😦
No description
sshader
sshader2y ago
Hmm. Do you have an httpAction for sendImage and OPTIONS (like in https://docs.convex.dev/functions/http-actions#cors) as well as POST? Otherwise I'd maybe try hitting that endpoint outside of a browser (e.g. via curl) to see if it's an issue with the endpoint or CORS. Sorry this is hard to set up :/
HTTP Actions | Convex Developer Hub
HTTP actions allow you to build an HTTP API right in Convex!
supergreg
supergregOP2y ago
Running into a larger problem, which is that I can't get it to work on Vercel. It's deployed to https://print-generator.vercel.app but keeps showing error
[CONVEX Q(getImages.js:default)] Could not find public function for 'getImages'. Did you forget to run `npx convex dev` or `npx convex deploy`?
[CONVEX Q(getImages.js:default)] Could not find public function for 'getImages'. Did you forget to run `npx convex dev` or `npx convex deploy`?
For build command I've tried nextjs build && npx convex deploy and npm run build && npx convex deploy but both show the same error, even after pushing a change ->Github->Vercel
ballingt
ballingt2y ago
Where do you see that error? When I try this I just see the 404s from the HTTP requests
supergreg
supergregOP2y ago
Oh hang on, it might have just taken 10 minutes to get Vercel's act together, let me test something Yes! It is working, I guess changing build commands takes a while to go into effect Still stuck on the sendImage problem, but today is presentation day so that will wait 🙂
ian
ian2y ago
You might want to use hono for your http routing. Check out Sarah's post on it here: https://stack.convex.dev/hono-with-convex
Advanced HTTP Endpoints: Convex ❤️ Hono
Adding advanced HTTP Endpoint functionality by extending Convex with Hono.

Did you find this page helpful?