Creating an API Key for Secure Data Access Between Next.js Server and Convex Backend
Hey everyone! 👋
I'm working on a project where I have a Next.js server and a Convex backend. I want my server to access specific data from Convex without needing to set up a user account or similar authentication method on the backend. Ideally, I'd like to create an API key that my Next.js server could use to access this data securely.
My Goals:
1. Simplify access: I don’t want to manage an additional user account for server access.
2. Security: I want to ensure the connection is secure and properly authenticated.
3. Minimal overhead: Trying to keep this as lightweight as possible.
My Questions:
1. Is there a way to create an API key for my server to use with Convex?
2. What are the best practices for securing this kind of server-to-server connection?
3. Has anyone implemented something similar with a Convex backend, or are there any known limitations I should be aware of?
17 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
@Kenni I'd use a simple shared secret here, you can build an API key system but e.g. an environment variable that you set on both sides (to include in requests from the Next.js backend and the Convex server to check) works well.
What's the best way to pass the shared secret to the API? There's no way I can find to examine the token from
ctx.auth
. An optional argument for a token on each function would work, but is much more clunkyI mean generate a secret, then stick that secret in an environment variable in convex deployment and stick that secret in the Nextjs server via environment variable
What do you mean here, something about reading values from the identity JWT? Or using the JWT for something else?
I'm interested in how the secret is sent to the API. I see there's a way to look at the claims from the result of
ctx.auth.getUserIdentity()
, but this requires the auth provider to have a valid user set up.
In my case I'm using Clerk and I'm trying to see if there's a way to avoid creating a special user within Clerk.What API are you talking about?
Yeah agree, it's only from a browser or server where you're performing actions on behalf of a user that the identity token from Clerk makes sense. This thread is about something else,
access specific data from Convex without needing to set up a user account or similar authentication method on the backendso there's no relevant user. So instead of using
ConvexHttpClient.setAuth()
, I'm suggesting passing a secret value as an argument to the function.
This doesn't have anything to do with Clerk, unless you wanted to use Clerk to generate these secrets somehow. I'm suggesting generating random secrets, like MY_APP_CONVEX_SECRET=THIS_IS_MY_SUPER_SECRET_TOKEN_SFKLJSQWRUSODIFUSYSDXCUVEWHFOIUSDNXLCOIWEURKLSDF
(but don't just keyboard spam, to be the most secure you should generate a random secret)Ah sorry, API = my convex actions/queries/mutations
Got it, thanks! Not ideal but I think this should work
Great! What would be ideal here, with goals of being the most convenient and giving confidence in the security of the solution?
Or maybe better, what feels bad about this solution?
can this be a convex helper or sm? I'd really like to use already defined internal endpoints without much modifications to handle a case when a key matches in every function
it's not considered so secure to pass the secret as parameter.
is it possible to put your server behind the Convex’s authentication providers - Clerk, and have the server call the function with a backend JWT that browsers cannot mint?
So the next.js server code has the api-key of Clerk and issue a JWT, and inside the public Convex mutation - enforce this JWT.
is it possible? recommended?
@ballingt the secret parameter is not ideal because:

Yeah that works, add another auth provider for your new JWT and check that the token is for that one and has eg a subject you trust. You can add as many auth providers as you need.
Hi @ballingt
I meant using only one auth provide (Clerk), and inside the convex mutation, distinguish wether the request came from the frontend or from the Next.js backend, based on the JWT - if it contains some custom claim (e.g. role=backend) - the request was originated on the backend, otherwise - it was originated on the frontend.
similar to what explain here:
https://clerk.com/docs/backend-requests/custom-session-token
or here:
https://clerk.com/docs/backend-requests/jwt-templates
Backend Requests: Customize your session token
Learn how to customize the session token that is generated for you by Clerk.
Backend Requests: JWT templates
Learn how to create custom JWT templates to generate JSON Web Tokens with Clerk.
By provider here I mean the entries in convex/auth.config.ts; if the JWTs you use for the server auth are different (ie there aren't OIDC) you migh need another entry in that file
Do you have an example for how to do it in such secure way?
I'm struggling with this. I think many folks who uses Vercel functions will benefit from this example.
(of how to restrict Convex mutation to the Next.js server side, and block the frontend side)
I also created a dedicated post:
https://discord.com/channels/1019350475847499849/1389494809646993438
Hey! Can you share the link to this? I'm curious about this topic
I feel like there needs to be a more general solution for letting convex know when a parameter needs to be secure
especially considering there are plenty of other types of secrets than api keys and jwts
The dedicated post: https://discord.com/channels/1019350475847499849/1389494809646993438
Exactly, I feel the same way - there must be a better approach then sending a shared secret as parameter
I think it would be ideal if Convex will supply api-key which enables accessing intenal mutation from external code