Rayy
Rayy
CCConvex Community
Created by Rayy on 6/3/2024 in #support-community
Getting adapter error with Next Auth and Convex in production.
My application works fine in development, and I am able to authenticate without any errors, but after deploying to vercel I get the following error
[auth][cause]: Error: [Request ID: 93a27ea7dd312e50] Server Error
at m.query (/var/task/.next/server/chunks/945.js:50:4256)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async getUserByAccount (/var/task/.next/server/app/api/auth/[...nextauth]/route.js:32:1695)
at async n.<computed> (/var/task/.next/server/chunks/945.js:50:78723)
at async n3 (/var/task/.next/server/chunks/945.js:426:34371)
at async rn (/var/task/.next/server/chunks/945.js:426:45478)
at async ra (/var/task/.next/server/chunks/945.js:426:50347)
at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:34666
at async eS.execute (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:25813)
at async eS.handle (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:35920)

[auth][error] AdapterError: Read more at https://errors.authjs.dev#adaptererror
[auth][cause]: Error: [Request ID: 93a27ea7dd312e50] Server Error
at m.query (/var/task/.next/server/chunks/945.js:50:4256)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async getUserByAccount (/var/task/.next/server/app/api/auth/[...nextauth]/route.js:32:1695)
at async n.<computed> (/var/task/.next/server/chunks/945.js:50:78723)
at async n3 (/var/task/.next/server/chunks/945.js:426:34371)
at async rn (/var/task/.next/server/chunks/945.js:426:45478)
at async ra (/var/task/.next/server/chunks/945.js:426:50347)
at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:34666
at async eS.execute (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:25813)
at async eS.handle (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:35920)

[auth][error] AdapterError: Read more at https://errors.authjs.dev#adaptererror
I have followed this (https://docs.convex.dev/production/hosting/vercel) to deploy my project and have set up the necessary environment variables, and also set up the callbacks for the specific providers, but I am yet getting this error. Can someone please help me out with this?
4 replies
CCConvex Community
Created by Rayy on 5/21/2024 in #support-community
Unable to authenticate Convex with next auth when provider is Credentials.
I have followed this post to setup the adapter https://stack.convex.dev/nextauth-adapter, and authenticate Convex with next auth, and it works with providers with github and google, but it does not with credentials. I am able to authenticate in the client side still with credentials, but unable to authenticate the same with Convex. It is not returning any sessions with credentials provider. Here is my auth.ts
export const {
handlers: { GET, POST },
auth,
signIn,
signOut,
} = NextAuth({
pages: {
signIn: "/auth/login",
error: "/auth/error",
},
events: {
async linkAccount({ user }) {
await updateUserEmailVerification(user.id as Id<"users">);
},
},
callbacks: {
async signIn({ user, account }) {
if (account?.provider !== "credentials") return true;

if (!user.email) return false;

const existingUser = await getUserByEmail(user.email);

if (!existingUser?.emailVerified) return false;

return true;
},
async session({ session }) {
console.log(session);
const privateKey = await importPKCS8(
process.env.CONVEX_AUTH_PRIVATE_KEY!,
"RS256"
);
const convexToken = await new SignJWT({
sub: session.userId,
})
.setProtectedHeader({ alg: "RS256" })
.setIssuedAt()
.setIssuer(CONVEX_SITE_URL)
.setAudience("convex")
.setExpirationTime("1h")
.sign(privateKey);
return { ...session, convexToken };
},
},
adapter: ConvexAdapter,
...authConfig,
});

declare module "next-auth" {
interface Session {
convexToken: string;
}
}
export const {
handlers: { GET, POST },
auth,
signIn,
signOut,
} = NextAuth({
pages: {
signIn: "/auth/login",
error: "/auth/error",
},
events: {
async linkAccount({ user }) {
await updateUserEmailVerification(user.id as Id<"users">);
},
},
callbacks: {
async signIn({ user, account }) {
if (account?.provider !== "credentials") return true;

if (!user.email) return false;

const existingUser = await getUserByEmail(user.email);

if (!existingUser?.emailVerified) return false;

return true;
},
async session({ session }) {
console.log(session);
const privateKey = await importPKCS8(
process.env.CONVEX_AUTH_PRIVATE_KEY!,
"RS256"
);
const convexToken = await new SignJWT({
sub: session.userId,
})
.setProtectedHeader({ alg: "RS256" })
.setIssuedAt()
.setIssuer(CONVEX_SITE_URL)
.setAudience("convex")
.setExpirationTime("1h")
.sign(privateKey);
return { ...session, convexToken };
},
},
adapter: ConvexAdapter,
...authConfig,
});

declare module "next-auth" {
interface Session {
convexToken: string;
}
}
I feel like I need to access the token callback to pass theJWT. Have I missed something in the docs?
19 replies
CCConvex Community
Created by Rayy on 5/20/2024 in #support-community
How to access mutations/queries from authAdapters in next auth convex setup?
I followed through this https://stack.convex.dev/nextauth-adapter and was able to setup adapters for convex, and my application works just fine. Currently I am using mutations I created to createUser or stuff like that, but I can see that most of the queries or mutations that I require are already present in the authAdadpter.ts file, so I was wondering how can I use it in my applicaiton? I try to call it, but I am presented with this error.
Expected 2 arguments, but got 1.ts(2554)
registration.d.ts(202, 36): An argument for 'args' was not provided.
(alias) createUser(ctx: GenericMutationCtx<any>, args: {
user: {
password?: string | undefined;
name?: string | undefined;
emailVerified?: number | undefined;
image?: string | undefined;
email: string;
};
secret: string;
}): Promise<...>
import createUser

const user = await createUser({
//passing user
})
Expected 2 arguments, but got 1.ts(2554)
registration.d.ts(202, 36): An argument for 'args' was not provided.
(alias) createUser(ctx: GenericMutationCtx<any>, args: {
user: {
password?: string | undefined;
name?: string | undefined;
emailVerified?: number | undefined;
image?: string | undefined;
email: string;
};
secret: string;
}): Promise<...>
import createUser

const user = await createUser({
//passing user
})
I understand that createUser mutation is wrapped with a custom mutation in authAdapter.ts which requires a secret key (CONVEX_AUTH_SECRET_KEY) , but how can I pass that exactly? /authAdapter.ts
const adapterMutation = customMutation(mutation, {
args: { secret: v.string() },
input: async (_ctx, { secret }) => {
checkSecret(secret);
return { ctx: {}, args: {} };
},
});

function checkSecret(secret: string) {
if (secret !== process.env.CONVEX_AUTH_ADAPTER_SECRET) {
throw new Error("Adapter API called without correct secret value");
}
const adapterMutation = customMutation(mutation, {
args: { secret: v.string() },
input: async (_ctx, { secret }) => {
checkSecret(secret);
return { ctx: {}, args: {} };
},
});

function checkSecret(secret: string) {
if (secret !== process.env.CONVEX_AUTH_ADAPTER_SECRET) {
throw new Error("Adapter API called without correct secret value");
}
6 replies
CCConvex Community
Created by Rayy on 5/19/2024 in #support-community
What is the difference between preloading query vs fetching queries in server components?
I have a parent component in Next JS which is a server component, that is rendering two children components both of which are client components. I need to access the query in both the client components. After going through the docs, I can see there are two approaches to get this done. 1. Preloading the query and then accessing the preloaded query in the client componens. 2. fetching the query using fetchQuery in server component and passing the data to the client components. Also, will preloading the query and then accessing it using usePreloadedQuery call the query twice? What does it mean when it says that it will not be reactive when using fetchQuery?
3 replies
CCConvex Community
Created by Rayy on 3/2/2024 in #support-community
How to get results from paginate Queries?
I want to paginate the results of my query, I was unable to find any examples in the docs so can someone please help me with it? The query I want to paginate -
export const getChunks = internalQuery({
args: { chatId: v.id("chatbook") },
handler: async (ctx, args) => {
const chunks = await ctx.db
.query("chatbookChunks")
.withIndex("by_chatId", (q) => q.eq("chatId", args.chatId))
.take(50);
return chunks;
},
});
export const getChunks = internalQuery({
args: { chatId: v.id("chatbook") },
handler: async (ctx, args) => {
const chunks = await ctx.db
.query("chatbookChunks")
.withIndex("by_chatId", (q) => q.eq("chatId", args.chatId))
.take(50);
return chunks;
},
});
I want to get all the results of a specific chatId till we have reached the end, how can I get the results in here?
const chunks = await ctx.runQuery(internal.helper.chunks.getChunks, {
chatId: args.chatId,
});
const chunks = await ctx.runQuery(internal.helper.chunks.getChunks, {
chatId: args.chatId,
});
3 replies
CCConvex Community
Created by Rayy on 2/28/2024 in #support-community
How to handle errors in scheduled functions and display that error to the frontend?
Suppose I have a mutation which is called from the client which inserts data to a table.
export const createChatbook = mutation({
args: {
//Pass arguments
},
handler: async (ctx, args) => {
const chatId = await ctx.db.insert("chatbook", {
//Insert data
});
await ctx.scheduler.runAfter(
0,
internal.chatbook.chunks.youtube.createChunks,
{
chatId,
url,
}
);

return chatId;
},
});
export const createChatbook = mutation({
args: {
//Pass arguments
},
handler: async (ctx, args) => {
const chatId = await ctx.db.insert("chatbook", {
//Insert data
});
await ctx.scheduler.runAfter(
0,
internal.chatbook.chunks.youtube.createChunks,
{
chatId,
url,
}
);

return chatId;
},
});
It then calls a scheduled function which creates chunks from the youtube url.
export const createChunks = internalAction({
args: {
//Pass args
},
handler: async (ctx, args) => {
const chunks = await extractAudioAndSplitChunks(args.url);

//Add chunks to a table
await ctx.runMutation(internal.helper.chunks.addChunks, {
chatId: args.chatId,
chunks: chunks.chunkArr,
});

await ctx.scheduler.runAfter(
0,
internal.chatbook.embedding.generateEmbeddings,
{
chatId: args.chatId,
title: chunks.title,
}
);
},
});
export const createChunks = internalAction({
args: {
//Pass args
},
handler: async (ctx, args) => {
const chunks = await extractAudioAndSplitChunks(args.url);

//Add chunks to a table
await ctx.runMutation(internal.helper.chunks.addChunks, {
chatId: args.chatId,
chunks: chunks.chunkArr,
});

await ctx.scheduler.runAfter(
0,
internal.chatbook.embedding.generateEmbeddings,
{
chatId: args.chatId,
title: chunks.title,
}
);
},
});
After creating the chunks, another scheduled function is called which generates the embeddings from the chunks and then adds it to the vector database and updates the "chatbook" table saying that the embeddings has been generated. Till the embeddings is being generating, a loading spinner is displayed in the frontend. And as soon as the table updates that the embedding has been generated, the chatbot is shown. So, the problem I am facing is if there is an error in the createChunks action even then the loading spinner is being displayed in the frontend. The error is being caught but I cannot figure out how can I show that error to the frontend. What would be the efficient way to handle and display errors in such cases? If I am unable to explain my situation, then please feel free to further ask any questions regarding the same.
13 replies
CCConvex Community
Created by Rayy on 2/16/2024 in #support-community
Is there a way to run mutations in bulk?
I am trying to create embeddings from chunk and then store those embedding in the convex vector store. But rather than calling one mutation for each embedding response, is there a way I can add them in bulk? I am trying to minimize addEmbedding calls.
export const generateEmbeddings = internalAction({
args: {...},
handler: async (ctx, args) => {
//Batching chunks

const responses = await Promise.all(
batches.map((batch) => fetchEmbedding(batch))
);

responses.forEach(async (response) => {
for (let i = 0; i < response.data.length; i++) {
const chunk =
typeof args.chunks[0] !== "string"
? (args.chunks[i] as Chunk)
: undefined;
await ctx.runMutation(internal.chatbook.addEmbedding, {
chatId: args.chatId,
content: chunk?.content ?? (args.chunks[i] as string),
source: chunk?.source ?? "",
embedding: response.data[i].embedding,
title: args.title,
type: args.type,
});
}
});
},
});

export const addEmbedding = internalMutation({
args: {
chatId: v.id("chatbook"),
content: v.string(),
source: v.optional(v.string()),
embedding: v.array(v.number()),
title: v.string(),
type: v.union(v.literal("code"), v.literal("doc"), v.literal("video")),
},
handler: async (ctx, args) => {
const chatEmbeddingId = await ctx.db.insert("chatEmbeddings", {
embedding: args.embedding,
content: args.content,
chatId: args.chatId,
source: args.source,
});
const existingChatDocument = await ctx.db.get(args.chatId);
const embeddingIds = existingChatDocument?.embeddingId || [];

embeddingIds.push(chatEmbeddingId);

await ctx.db.patch(args.chatId, {
embeddingId: embeddingIds,
title: args.title,
type: args.type,
});
},
});
export const generateEmbeddings = internalAction({
args: {...},
handler: async (ctx, args) => {
//Batching chunks

const responses = await Promise.all(
batches.map((batch) => fetchEmbedding(batch))
);

responses.forEach(async (response) => {
for (let i = 0; i < response.data.length; i++) {
const chunk =
typeof args.chunks[0] !== "string"
? (args.chunks[i] as Chunk)
: undefined;
await ctx.runMutation(internal.chatbook.addEmbedding, {
chatId: args.chatId,
content: chunk?.content ?? (args.chunks[i] as string),
source: chunk?.source ?? "",
embedding: response.data[i].embedding,
title: args.title,
type: args.type,
});
}
});
},
});

export const addEmbedding = internalMutation({
args: {
chatId: v.id("chatbook"),
content: v.string(),
source: v.optional(v.string()),
embedding: v.array(v.number()),
title: v.string(),
type: v.union(v.literal("code"), v.literal("doc"), v.literal("video")),
},
handler: async (ctx, args) => {
const chatEmbeddingId = await ctx.db.insert("chatEmbeddings", {
embedding: args.embedding,
content: args.content,
chatId: args.chatId,
source: args.source,
});
const existingChatDocument = await ctx.db.get(args.chatId);
const embeddingIds = existingChatDocument?.embeddingId || [];

embeddingIds.push(chatEmbeddingId);

await ctx.db.patch(args.chatId, {
embeddingId: embeddingIds,
title: args.title,
type: args.type,
});
},
});
4 replies
CCConvex Community
Created by Rayy on 2/15/2024 in #support-community
Exceeded monthly free plan resource
No description
3 replies
CCConvex Community
Created by Rayy on 2/5/2024 in #support-community
What is the best approach to call query when they depend on scheduled functions?
I am calling useMutation from client where it inserts data in the db for that particular chat Id, and then run certain scheduled functions based on the data, and I am returning the chat Id to the client when the data has been inserted to the db. My application is working fine, but I feel like I am making unnecessary query requests, when all the scheduled functions have not yet been completed. I am currently showing a loading state to the user till the embeddingId is undeifned, since the last scheduled function will populate the embeddingId.
export const useQueryChatProps = ({ chatId }: { chatId: Id<"chatbook"> }) => {
const data = useQuery(api.chatbook.getEmbeddingId, {
chatId,
});

const loading = data === undefined || data?.embeddingId === undefined;

return {
data,
loading,
};
};
export const useQueryChatProps = ({ chatId }: { chatId: Id<"chatbook"> }) => {
const data = useQuery(api.chatbook.getEmbeddingId, {
chatId,
});

const loading = data === undefined || data?.embeddingId === undefined;

return {
data,
loading,
};
};
Is this okay? Or is there a better way to achieve this? Here is my code for inserting data to the db and calling the scheuled functions
const user = await ctx.db
.query("users")
.filter((q) => q.eq(q.field("_id"), args.userId));
if (!user) {
throw new ConvexError("You need to login first.");
}

const url = args.storageId
? await ctx.storage.getUrl(args.storageId)
: args.url;

if (!url) {
throw new ConvexError("No URL found.");
}

const chatId = await ctx.db.insert("chatbook", {
userId: args.userId,
url,
});

await ctx.scheduler.runAfter(0, internal.github.getFilesFromRepo, {
repoUrl: url,
filePath: extractPathFromUrl(url),
chatId,
});

return chatId;
const user = await ctx.db
.query("users")
.filter((q) => q.eq(q.field("_id"), args.userId));
if (!user) {
throw new ConvexError("You need to login first.");
}

const url = args.storageId
? await ctx.storage.getUrl(args.storageId)
: args.url;

if (!url) {
throw new ConvexError("No URL found.");
}

const chatId = await ctx.db.insert("chatbook", {
userId: args.userId,
url,
});

await ctx.scheduler.runAfter(0, internal.github.getFilesFromRepo, {
repoUrl: url,
filePath: extractPathFromUrl(url),
chatId,
});

return chatId;
The scheduled function is dependant on other scheduled functions.
11 replies
CCConvex Community
Created by Rayy on 1/29/2024 in #support-community
Is there a way around to call actions from non functional components?
I am trying to build a chatbot with Vercel AI Sdk for streaming the response from OpenAI. I was wondering if there is a way to call actions in my route.ts? I do not want to call the action unless my POST function is called, so (In my action, I am doing vector search)
export async function POST(req: Request) {
const { messages, chatId } = await req.json();
const previousMessage: string = messages[messages.length - 1].content;

const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
stream: true,
messages,
});

// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);

// Respond with the stream
return new StreamingTextResponse(stream);
}
export async function POST(req: Request) {
const { messages, chatId } = await req.json();
const previousMessage: string = messages[messages.length - 1].content;

const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
stream: true,
messages,
});

// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);

// Respond with the stream
return new StreamingTextResponse(stream);
}
And also I was thinking to move this from route.ts to httpAction and then use that to do the vector search, but will it be efficient? Another reason to refrain from using httpAction is that it will be exposed and it just doesn't make any sense to use it like that.
9 replies
CCConvex Community
Created by Rayy on 1/29/2024 in #support-community
How to empty an optional field in the table?
chatbook: defineTable({
userId: v.id("users"),
url: v.string(),
type: v.optional(v.string()),
title: v.optional(v.string()),
length: v.optional(v.number()),
chat: v.optional(v.array(Message)),
embeddingId: v.optional(v.array(v.id("chatEmbeddings"))),
}).index("by_embedding", ["embeddingId"]),
chatbook: defineTable({
userId: v.id("users"),
url: v.string(),
type: v.optional(v.string()),
title: v.optional(v.string()),
length: v.optional(v.number()),
chat: v.optional(v.array(Message)),
embeddingId: v.optional(v.array(v.id("chatEmbeddings"))),
}).index("by_embedding", ["embeddingId"]),
This is my schema and I want to empty the chat field when this mutation is called.
export const deleteMessageHistory = mutation({
args: { chatId: v.id("chatbook") },
handler: async (ctx, args) => {
await ctx.db.replace(args.chatId, {chat: null})
},
});
export const deleteMessageHistory = mutation({
args: { chatId: v.id("chatbook") },
handler: async (ctx, args) => {
await ctx.db.replace(args.chatId, {chat: null})
},
});
I cannot initialise null to it since I am getting validation error, so how can I achieve this?
7 replies
CCConvex Community
Created by Rayy on 1/20/2024 in #support-community
Error while trying to import third party libraries.
I have no idea why am I getting this error
Error: Unable to push deployment config to https://content-tortoise-560.convex.cloud
400 Bad Request: InvalidModules: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Uncaught Failed to analyze openai.js: ENOENT: no such file or directory, open './test/data/05-versions-space.pdf'
Error: Unable to push deployment config to https://content-tortoise-560.convex.cloud
400 Bad Request: InvalidModules: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Uncaught Failed to analyze openai.js: ENOENT: no such file or directory, open './test/data/05-versions-space.pdf'
when trying to import the below libraries
"use node"

const fs = require("fs");
const pdf = require("pdf-parse");
"use node"

const fs = require("fs");
const pdf = require("pdf-parse");
I am already using the "use node" declarative as mentioned in the docs.
5 replies
CCConvex Community
Created by Rayy on 1/15/2024 in #support-community
How can I define dynamic keys for nested objects in schema?
v.object({
question: v.string(),
answer: v.string(),
yourAnswer: v.optional(v.object({property: v.string()})),
options: v.optional(v.array(v.string())),
})
v.object({
question: v.string(),
answer: v.string(),
yourAnswer: v.optional(v.object({property: v.string()})),
options: v.optional(v.array(v.string())),
})
This is my object, I want the property key to be dynamic of type string, how can I achieve the same in Convex? The yourAnswer object will have this value
{
1: "abc",
2: "def",
...
}
{
1: "abc",
2: "def",
...
}
3 replies
CCConvex Community
Created by Rayy on 9/17/2023 in #support-community
Unable to setup pinecone with convex
Why am I getting this error, when trying to setup Pinecone under a lib folder in my convex. Error:
Error: Unable to push deployment config to https://....
400 Bad Request: UnableToPush: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Failed to analyze lib/pinecone.js: Uncaught EvalError: Code generation from strings disallowed for this context
at new Function (<anonymous>)
Error: Unable to push deployment config to https://....
400 Bad Request: UnableToPush: Hit an error while pushing:
Loading the pushed modules encountered the following
error:
Failed to analyze lib/pinecone.js: Uncaught EvalError: Code generation from strings disallowed for this context
at new Function (<anonymous>)
This is how I am setting up my pinecone.
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY as string,
environment: process.env.PINECONE_ENVIRONMENT as string,
});
const pinecone = new Pinecone({
apiKey: process.env.PINECONE_API_KEY as string,
environment: process.env.PINECONE_ENVIRONMENT as string,
});
5 replies