How to create a custom API Endpoint to check subscriptionStatus and subscriptionTier
I am building a browser extension that should only be accessible to the users that have an account with my webapp and are logged in with the same browser.
My wasp version seems to be 0.13.2 as it returns that when I run wasp version.
I have noticed that wasp documentation mentions this in the documentation for wasp 0.13:
https://wasp-lang.dev/docs/advanced/apis#api-reference
api fooBar { // APIs and their implementations don't need to (but can) have the same name.
fn: import { fooBar } from "@src/apis",
httpRoute: (GET, "/foo/bar")
}
and for queries:
query getAllTasks {
fn: import { getAllTasks } from "@src/queries.js"
}
query getFilteredTasks {
fn: import { getFilteredTasks } from "@src/queries.js"
}
Anyhow, I have noticed that my default set up queries use a different path:
query getDailyStats {
fn: import { getDailyStats } from "@src/server/queries.js",
entities: [User, DailyStats]
}
I can't make custom endpoints work and have been trying for an entire day now.
Calls like this one work without a problem though: http://localhost:3001/auth/email/login
My Questoins are the following:
Is it correct to have quereis.ts and actions.ts at these paths when on wasp 0.13?
app/src/server/queries.ts
app/src/server/actions.ts
Is there already a build in way to check if somebody is a subscribed user?
Am i missing something obvious here?
Thank you in advance for any help! This is doing my head in.
Custom HTTP API Endpoints | Wasp
In Wasp, the default client-server interaction mechanism is through Operations. However, if you need a specific URL method/path, or a specific response, Operations may not be suitable for you. For these cases, you can use an api. Best of all, they should look and feel very familiar.
31 Replies
Wohooo @fred-123, you just became a Waspeteer level 1!
Hi @fred-123. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
Based on the information provided in the knowledge sources, here are the answers to your questions:
1. Is it correct to have queries.ts and actions.ts at these paths when on wasp 0.13?
Yes, it is correct to have
queries.ts
and actions.ts
in the src/server
directory. The path you use to import these files in your Wasp code should match their location in your project structure. For example, if your queries.ts
file is located in src/server
, you would import it in your Wasp code like this: import { getDailyStats } from "@src/server/queries.js"
.
2. Is there already a built-in way to check if somebody is a subscribed user?
Wasp provides built-in authentication functionality, which you can use to restrict access to certain parts of your application to authenticated users. You can check if a user is authenticated in your queries and actions by checking the context.user
object. If context.user
is null
, the user is not authenticated. Here's an example from the Wasp tutorial (https://wasp-lang.dev/docs/tutorial/auth#updating-operations-to-check-authentication):
However, if by "subscribed user" you mean a user who has an active subscription (for example, a paid subscription), you would need to implement this functionality yourself. You could do this by adding a subscriptionStatus
field to your User
entity, and then checking this field in your queries and actions.
3. Am I missing something obvious here?
Without more information about the specific problems you're encountering, it's hard to say what might be going wrong. However, here are a few things you could check:
- Make sure you've declared your API in your Wasp code using the api
declaration, and that the fn
field of this declaration points to the correct location of your API's implementation. For example:
- Make sure your API's implementation is correctly defined and exported in your apis.js
(or apis.ts
) file.
- If you're trying to access your API from the client, make sure you're using the Axios wrapper from wasp/client/api
to make the request. For example:
If you're still having trouble, you might find it helpful to look at the Wasp documentation (https://wasp-lang.dev/docs), or to ask for help on the Wasp Discord server (https://discord.com/invite/rzdnErX).
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?@kapa.ai why would i have to add a subscriptionStatus to the user entity?
isn't that what this is for?
subscriptionTier String?
subscriptionStatus String?
Let me check the knowledge base for you. One moment! :books:
----------------------
You're correct. If your
User
entity already includes subscriptionTier
and subscriptionStatus
fields, you can use these fields to track the subscription status of your users.
In this case, you wouldn't need to add a new subscriptionStatus
field. Instead, you could check the value of the existing subscriptionStatus
field to determine whether a user is a subscribed user.
Here's an example of how you might do this in a query or action:
In this example, the getSubscribedContent
function checks whether the user is authenticated and whether their subscriptionStatus
is 'active'
. If either of these conditions is not met, the function throws a 401 error.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?@kapa.ai how can i set up a jwt token upon login?
Let me check the knowledge base for you. One moment! :books:
----------------------
To set up a JWT token upon login, you can create a custom API endpoint in your Wasp application that generates a JWT when the user logs in. Here's an example of how you might do this:
First, define the API endpoint in your
main.wasp
file:
Then, implement the generateJwt
function in a generateJwt.js
file in your src/server/api
directory. This function should authenticate the user, generate a JWT containing the user's data, and send the JWT back to the client:
In this example, the authenticateUser
function is a placeholder for your actual user authentication logic. You should replace this function with code that verifies the provided username and password against your user database.
The generateJwt
function uses the jsonwebtoken
library to generate a JWT. The JWT is signed with a secret key, which is retrieved from the environment variables. The JWT contains the user's username and user ID, and expires after 1 hour.
Once the JWT is generated, it's sent back to the client in the response body.
You can then store this JWT in your client application (for example, in local storage), and include it in the headers of subsequent API requests to authenticate the user.
Please note that this is a basic example and might need to be adjusted based on your specific requirements and security considerations.
For more information, you can refer to the Wasp documentation (https://wasp-lang.dev/docs) or the Wasp Discord community (https://discord.com/channels/686873244791210014/1223328118278983680) for more specific discussions on this topic.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?@kapa.ai I have a few questions.
The file src/server/api does not exist in the default open saas template. Should i just add it or is this set up differently in opensaas?
Is the secret key that is retrieved from the environment variables set up on default or do i need to set this up?
Let me check the knowledge base for you. One moment! :books:
----------------------
1. The file src/server/api does not exist in the default open saas template. Should i just add it or is this set up differently in opensaas?
The
src/server/api
directory is not included by default in the OpenSaaS template. If it doesn't exist, you can create it yourself. This directory is meant to contain your custom server-side API implementations. The structure of your project can vary depending on your needs, so feel free to organize your files and directories in a way that makes sense for your application.
2. Is the secret key that is retrieved from the environment variables set up on default or do I need to set this up?
The secret key used for signing JWTs is typically stored in an environment variable for security reasons. This environment variable is not set up by default. You will need to set it up yourself.
You can do this by adding a line to your .env
file (which should be in the root directory of your project) like this:
Replace your-secret-key
with a strong, random string. This string should be kept secret, as anyone who knows it could use it to generate valid JWTs for your application.
After adding this line to your .env
file, you can access the secret key in your code using process.env.JWT_SECRET_KEY
.
Remember not to include your .env
file in your version control system (e.g., by adding it to your .gitignore
file) to prevent it from being exposed publicly.
For more information, you can refer to the Wasp documentation (https://wasp-lang.dev/docs) or the Wasp Discord community (https://discord.com/channels/686873244791210014/1223328118278983680) for more specific discussions on this topic.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?@kapa.ai can the login logic be customized at all and if yes where can i find it?
Let me check the knowledge base for you. One moment! :books:
----------------------
Yes, the login logic can be customized in Wasp. You can use the
login
action provided by Wasp to create your own custom login flow.
Here is an example of how you can use the login
action in a React component:
In this example, the login
action is called with the username and password entered by the user. If the login is successful, the user is redirected to the home page. If the login fails, an error is displayed.
You can find more information about customizing the login flow in the Wasp documentation (https://wasp-lang.dev/docs/auth/username-and-pass#login).
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?Here's a human to help as well. Could you give me a TLDR of what is your current issue?
If some of the Wasp concepts are still confusing to you, I'd recommend going through the simple tutorial app to iron out any missing fundmentals: https://wasp-lang.dev/docs/tutorial/create
1. Creating a New Project | Wasp
You'll need to have the latest version of Wasp installed locally to follow this tutorial. If you haven't installed it yet, check out the QuickStart guide!
Hi @miho,
I am trying to get the following information from the User entity through an api endpoint so i can retrieve it from outside the webapp.
subscriptionTier
subscriptionStatus
I was reading through the documentation quite a lot but can't seem to figure it out exactly. Can you give me a macro overview of what I would need to do. What confuses me a bit is the entire auth process, as I can't seem to find out where it actually happens apart from the auth declaration in the main.wasp. I managed to do api calls with the endpoint http://localhost:3001/auth/email/login to check if a given username and password combination is a user present in the DB. @kapa.ai how do I create a custom api endpoint to retrieve the following information from the user entity to access it from outside: subscriptionTier subscriptionStatus
subscriptionStatus
I was reading through the documentation quite a lot but can't seem to figure it out exactly. Can you give me a macro overview of what I would need to do. What confuses me a bit is the entire auth process, as I can't seem to find out where it actually happens apart from the auth declaration in the main.wasp. I managed to do api calls with the endpoint http://localhost:3001/auth/email/login to check if a given username and password combination is a user present in the DB. @kapa.ai how do I create a custom api endpoint to retrieve the following information from the user entity to access it from outside: subscriptionTier subscriptionStatus
Let me check the knowledge base for you. One moment! :books:
----------------------
To create a custom API endpoint that retrieves
subscriptionTier
and subscriptionStatus
from the User
entity, you can follow these steps:
1. Declare the API in your Wasp code:
2. Implement the getUserSubscriptionInfo
function in a getUserSubscriptionInfo.js
file in your src/server/api
directory. This function should fetch the user's subscriptionTier
and subscriptionStatus
from the database and send them back to the client:
In this example, the getUserSubscriptionInfo
function expects the user ID to be provided as a query parameter (e.g., /api/user-subscription-info?userId=123
). It uses this ID to fetch the user's subscriptionTier
and subscriptionStatus
from the database, and sends these values back to the client in the response body.
Please note that this is a basic example and might need to be adjusted based on your specific requirements and security considerations. For example, you might want to add authentication to this endpoint to ensure that only authenticated users can access it.
For more information, you can refer to the Wasp documentation (https://wasp-lang.dev/docs/advanced/apis) or the Wasp Discord community (https://discord.com/channels/686873244791210014/1223328118278983680) for more specific discussions on this topic.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?I got it working now.
For anybody else that wants to retrieve subscriptionStatus and subscriptionTier through API Endpoint here is how i have it set up.
In main.wasp add this under the custom Wasp Api Endpoints
api getUserSubscriptionInfo {
fn: import { getUserSubscriptionInfo } from "@src/server/api/getUserSubscriptionInfo.js",
entities: [User],
httpRoute: (GET, "/api/user-subscription-info")
}
I have then created a new folder called api at this path:
app/src/server/
inside the new api folder i have created a function called getUserSubscriptionInfo.js containing the following:
import { HttpError } from 'wasp/server';
export async function getUserSubscriptionInfo(req, res, context) {
const userId = req.query.userId;
if (!userId) {
throw new HttpError(400, 'User ID is required');
}
const user = await context.entities.User.findUnique({ where: { id: parseInt(userId, 10) } });
if (!user) {
throw new HttpError(404, 'User not found');
}
const { subscriptionTier, subscriptionStatus } = user;
res.json({ subscriptionTier, subscriptionStatus });
}
It then can be tested with postman:
get http://localhost:3001/api/user-subscription-info
body-> raw -> json:
{
"email": "a valid user email that is present inside the db",
"password": "the password associated with said user email"
} params key: userId value: the sames users id from the db it will then return the information in the body. For example: { "subscriptionTier": "Pro", "subscriptionStatus": "past_due" } this is how you can make it work only with the user email so you don't need the userid: app/src/server/api/getUserSubscriptionInfo.js import { HttpError } from 'wasp/server'; export async function getUserSubscriptionInfo(req, res, context) { const email = req.query.email; console.log('Received email:', email); // Debugging line if (!email) { throw new HttpError(400, 'Email is required'); } const user = await context.entities.User.findUnique({ where: { email: email } }); console.log('Queried user:', user); // Debugging line if (!user) { throw new HttpError(404, 'User not found'); } const { subscriptionTier, subscriptionStatus } = user; res.json({ subscriptionTier, subscriptionStatus }); }
} params key: userId value: the sames users id from the db it will then return the information in the body. For example: { "subscriptionTier": "Pro", "subscriptionStatus": "past_due" } this is how you can make it work only with the user email so you don't need the userid: app/src/server/api/getUserSubscriptionInfo.js import { HttpError } from 'wasp/server'; export async function getUserSubscriptionInfo(req, res, context) { const email = req.query.email; console.log('Received email:', email); // Debugging line if (!email) { throw new HttpError(400, 'Email is required'); } const user = await context.entities.User.findUnique({ where: { email: email } }); console.log('Queried user:', user); // Debugging line if (!user) { throw new HttpError(404, 'User not found'); } const { subscriptionTier, subscriptionStatus } = user; res.json({ subscriptionTier, subscriptionStatus }); }
Wohooo @fred-123, you just became a Waspeteer level 2!
@kapa.ai its all clear now. How can i mark this thread as solved?
Let me check the knowledge base for you. One moment! :books:
----------------------
I'm an AI and don't have the ability to mark threads as solved. However, you can usually mark a thread as solved by editing the original post or title to include something like "[SOLVED]" at the beginning. The exact method can vary depending on the platform you're using. If you're on a platform like Discord or a forum, you might also be able to select a "Mark as solved" option somewhere. If you're not sure, you can ask a moderator or other community member for help.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?
@fred-123 nice! Thank you for the solutions, I'll mark it as solved. Oh you already did, good job 😊
how would this make sure the chrome extension user is not still able to acces it when their they are no longer paying?
Are you just constantly retrieving the tier/status in a background script or at any action they try to do with the chrome extension?
Also how would i make this extension that access an endpoint from the SaaS only when a user is authenticated?
I am retrieving the status in the background. You can trigger it when an event is triggered that requires paid subscription.
Once the status of the user changes in the db he can't use the extension or certain features of the extension anymore depending on how you set it up. You should include two statuses. Subscription tier and payment status.
If the user hasn't paid in time his payment status will be set to past_due. Make sure your logic checks that and your good.
You can just trigger a subscription checking script after the user has logged in.
how are you retrieving the status in the bcakground?
could you provide a code snippet
just call the api and pass it to wherever you need it in your script
I am not sure anymore if i had to set up a custom endpoint for it or if it was already there
What endpoint are you calling the auth/me? and how do you pass anything in it and know who the user is?
you have to call
https://[your-server-url].fly.dev/auth/email/login
I have set up a login screen where the usere enters the email and password.
The info is then passed to the api endpoint for auth to check if it's an existing user.
In the same function that does this auth part I save the useres email address that was used to log in into the users browser storage.
That email address is then used to to check the subscription status and subscription tier:
app/src/server/api/getUserSubscriptionInfo.js
import { HttpError } from 'wasp/server';
export async function getUserSubscriptionInfo(req, res, context) {
const email = req.query.email;
console.log('Received email:', email); // Debugging line
if (!email) {
throw new HttpError(400, 'Email is required');
}
const user = await context.entities.User.findUnique({ where: { email: email } });
console.log('Queried user:', user); // Debugging line
if (!user) {
throw new HttpError(404, 'User not found');
}
const { subscriptionTier, subscriptionStatus } = user;
res.json({ subscriptionTier, subscriptionStatus });
}
Then you just do some basic conditional statements to set up the logic what the users is meant to have access to or not
just use something like postman to test it.
just share your code in a new thread and i am sure somebody will be happy to point you in the correct direction.
regarding the endpoint. Most likely you are working locally so it would be this for you if you use that port:
http://localhost:3001/auth/email/login
Make sure you call on port 3001 which is the server and not on 3000 which is the client
Are you testing with postman? What errors are you getting?
Are you sure you are checking the correct db and that the user is actually present?
i am not getting any erros now but just curious how i would implement this
But lets say my extebsion makes a request to an endpoint of my SaaS how would i allow only subscribed userss to do tjis
And lets say they watch the network tab, i want them to not be able to make such requests succesful
why would you care if somebody else uses the endpoint?
they can only retrieve subscription status and tier if they know the email address already and that informatoin isn't really that sensitive.
At least when still building an MVP i don't think this is that relevant just yet. But of course you could make it safer and I am sure there are plenty of resources out there on a hundred ways of building it more robust.
No but the extension in my cases retrieves data, this is really usedul data and i dont want people accessing it without having a paid subscription
Do you understand what i mean in my case?
yeah i do.
just do it the proper way with JWT tokens and you should be fine
@kapa.ai How to secure an api endpoint in wasp 0.14 with jwt tokens?
Let me check the knowledge base for you. One moment! :books:
----------------------
To secure an API endpoint in Wasp with JWT tokens, you can follow these steps:
1. First, you need to set up a JWT token upon user login. You can create a custom API endpoint in your Wasp application that generates a JWT when the user logs in. Here's an example of how you might do this:
Define the API endpoint in your
main.wasp
file:
Then, implement the generateJwt
function in a generateJwt.js
file in your src/server/api
directory. This function should authenticate the user, generate a JWT containing the user's data, and send the JWT back to the client:
2. Once the JWT is generated, it's sent back to the client in the response body. You can then store this JWT in your client application (for example, in local storage), and include it in the headers of subsequent API requests to authenticate the user.
3. To secure an API endpoint with the JWT, you can create a middleware function that verifies the JWT and includes it in all protected routes. Here's an example of how you might do this:
In this example, the jwtMiddleware
function is a middleware function that verifies the JWT included in the Authorization
header of the request. If the JWT is valid, the function saves the decoded user ID to
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @fred-123, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?Does that answer your question?
clear yea
thakns
will read better later
but i think what u asked to the bot answers so should be good