winsoroaks
winsoroaksโ€ข12mo ago

what's the proper way to query an SDK client in node env?

whenever a user navigates to the billing page, i want to check stripe if they have cancelled the pay, essentially, im trying to do
const plan: Stripe.Subscription = await stripe.subscriptions.retrieve(
stripeSubscriptionId
)
const plan: Stripe.Subscription = await stripe.subscriptions.retrieve(
stripeSubscriptionId
)
since stripe SDK is in the node env, i need to wrap it in an "action"
export const getStripePlan = action({
handler: async (ctx) => {
const stripe = new Stripe(process.env.STRIPE_KEY!, {
apiVersion: "2023-10-16",
typescript: true,
})

const stripeSubscriptionId = ...

const plan: Stripe.Subscription = await stripe.subscriptions.retrieve(
stripeSubscriptionId
)
return plan.cancel_at_period_end
},
})
export const getStripePlan = action({
handler: async (ctx) => {
const stripe = new Stripe(process.env.STRIPE_KEY!, {
apiVersion: "2023-10-16",
typescript: true,
})

const stripeSubscriptionId = ...

const plan: Stripe.Subscription = await stripe.subscriptions.retrieve(
stripeSubscriptionId
)
return plan.cancel_at_period_end
},
})
and i was thinking that i could do
// /api/stripe/route.ts
export async function GET(req: NextApiRequest, res: NextApiResponse) {
const plan = await convex.query(api.stripe.actions.getStripePlan, {})
res.status(200).json({ plan })
}
// /api/stripe/route.ts
export async function GET(req: NextApiRequest, res: NextApiResponse) {
const plan = await convex.query(api.stripe.actions.getStripePlan, {})
res.status(200).json({ plan })
}
and on the page.tsx, do a fetch to my API above. but im running into
formatDate
Argument of type 'FunctionReference<"action", "public", EmptyObject, boolean>' is not assignable to parameter of type 'FunctionReference<"query">'.
formatDate
Argument of type 'FunctionReference<"action", "public", EmptyObject, boolean>' is not assignable to parameter of type 'FunctionReference<"query">'.
or is there a better approach to what im trying to achieve? thanks!
8 Replies
lee
leeโ€ข12mo ago
convex's three types of functions are queries, mutations, and actions. Since this is an action, you would call convex.action instead of convex.query
winsoroaks
winsoroaksOPโ€ข12mo ago
got it, thanks! would u say this is the easiest way to do what i try to accomplish above? apparently useAction requiries something to trigger it, so i'd need to make it live in the api route
lee
leeโ€ข12mo ago
Can you share more of the flow? As written, the /api/stripe route seems a little redundant since the client can call the action directly
winsoroaks
winsoroaksOPโ€ข12mo ago
the goal is to check if the customer has cancelled the subscription plan. when my page renders, im currently doing
// this doesnt run when the page renders
const stripePlan = useAction(api.stripe.actions.getStripePlan)
console.log("๐Ÿš€ ~ file: billing-form.tsx:34 ~ stripePlan:", stripePlan)

// this runs when the pay renders
React.useEffect(() => {
if (isLoading) return
const fetchData = async () => {
const data = await fetch("/api/stripe", {}).then((res) => {
console.log("๐Ÿš€ ~ file: page.tsx:31 ~ fetchData ~ res:", res)
})
}
fetchData()
}, [isLoading])
// this doesnt run when the page renders
const stripePlan = useAction(api.stripe.actions.getStripePlan)
console.log("๐Ÿš€ ~ file: billing-form.tsx:34 ~ stripePlan:", stripePlan)

// this runs when the pay renders
React.useEffect(() => {
if (isLoading) return
const fetchData = async () => {
const data = await fetch("/api/stripe", {}).then((res) => {
console.log("๐Ÿš€ ~ file: page.tsx:31 ~ fetchData ~ res:", res)
})
}
fetchData()
}, [isLoading])
hence, i needed to add the route at api/stripe
export async function GET(req: NextApiRequest, res: NextApiResponse) {
const plan = await convex.action(api.stripe.actions.getStripePlan, {})
res.status(200).json({ plan })
}
export async function GET(req: NextApiRequest, res: NextApiResponse) {
const plan = await convex.action(api.stripe.actions.getStripePlan, {})
res.status(200).json({ plan })
}
jamwt
jamwtโ€ข12mo ago
yeah, you can say something like const myAction = useAction(api.stripe.actions.getStripePlan);' const plan = myAction(); in your app the path /api/stripe/route.ts implies this is a vercel function. you don't need a seprate vercel function, you can just directly invoke actions from your app using the above syntax
jamwt
jamwtโ€ข12mo ago
our demo project giphy-action shows this. here's the action definition: https://github.com/get-convex/convex-demos/blob/main/giphy-action/convex/messages.ts#L24 and here's the app binding the action hook ( https://github.com/get-convex/convex-demos/blob/main/giphy-action/src/App.tsx#L10 ) and calling the action at the appropriate time and getting the result from the convex backend ( https://github.com/get-convex/convex-demos/blob/main/giphy-action/src/App.tsx#L18 )
GitHub
convex-demos/giphy-action/convex/messages.ts at main ยท get-convex/c...
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
GitHub
convex-demos/giphy-action/src/App.tsx at main ยท get-convex/convex-d...
Demo apps built on Convex. Contribute to get-convex/convex-demos development by creating an account on GitHub.
jamwt
jamwtโ€ข12mo ago
so, based on your code snippet, you can just call the action in your useEffect directly without the vercel function useAction() doesn't directly invoke the action, it gives you a handle to the action you can call as many times as you want small rewrite of yours:
// Get a reference to the action.
const getStripePlan = useAction(api.stripe.actions.getStripePlan)

// this runs when the pay renders
React.useEffect(() => {
if (isLoading) return
const fetchData = async () => {
const stripePlan = await getStripePlan();
}
fetchData()
}, [isLoading, getStripePlan]) // note this is added as a hook dependency
// Get a reference to the action.
const getStripePlan = useAction(api.stripe.actions.getStripePlan)

// this runs when the pay renders
React.useEffect(() => {
if (isLoading) return
const fetchData = async () => {
const stripePlan = await getStripePlan();
}
fetchData()
}, [isLoading, getStripePlan]) // note this is added as a hook dependency
winsoroaks
winsoroaksOPโ€ข12mo ago
got it, makes sense. thanks! yes i can confirm it's working ๐Ÿ™‚

Did you find this page helpful?