W
Wasp2w ago
NEROX

public API for Chrome Extension

I am trying to create an API to receive and send information from a Chrome extension, what would be the correct process to define it in main.wasp? Create me a complete example
20 Replies
kapa.ai
kapa.ai2w ago
Hi @NEROX. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To create a public API for a Chrome extension using Wasp, you need to define a custom HTTP API endpoint in your main.wasp file. Here's a complete example of how to do this: 1. First, define the API in your main.wasp file:
api generateJwt {
fn: import { generateJwt } from "@src/apis/generateJwt.js",
httpRoute: (POST, "/api/generate-jwt")
}
api generateJwt {
fn: import { generateJwt } from "@src/apis/generateJwt.js",
httpRoute: (POST, "/api/generate-jwt")
}
This defines an API endpoint that will be accessible at POST /api/generate-jwt. 2. Next, implement the API function in your src/apis/generateJwt.js file:
import * as jwt from 'jsonwebtoken';

export const generateJwt = (req, res, context) => {
const { username, password } = req.body;

// Here you would typically authenticate the user
// For this example, we'll assume authentication is successful

const userPayload = { username, userId: 'user123' };
const secret = process.env.JWT_SECRET;
const token = jwt.sign(userPayload, secret, { expiresIn: '1h' });

res.json({ token });
};
import * as jwt from 'jsonwebtoken';

export const generateJwt = (req, res, context) => {
const { username, password } = req.body;

// Here you would typically authenticate the user
// For this example, we'll assume authentication is successful

const userPayload = { username, userId: 'user123' };
const secret = process.env.JWT_SECRET;
const token = jwt.sign(userPayload, secret, { expiresIn: '1h' });

res.json({ token });
};
This implementation creates a JWT token for the user. In a real-world scenario, you would need to implement proper authentication logic. 3. Make sure to set up CORS for your API to allow requests from your Chrome extension. You can do this by adding a middleware config in your main.wasp file:
apiNamespace chromeExtension {
middlewareConfigFn: import { chromeExtensionMiddleware } from "@src/middleware.js",
path: "/api"
}
apiNamespace chromeExtension {
middlewareConfigFn: import { chromeExtensionMiddleware } from "@src/middleware.js",
path: "/api"
}
4. Implement the middleware in src/middleware.js:
export const chromeExtensionMiddleware = (config) => {
config.cors = {
origin: '*', // Be cautious with this in production
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
};
return config;
};
export const chromeExtensionMiddleware = (config) => {
config.cors = {
origin: '*', // Be cautious with this in production
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
};
return config;
};
This setup will create a public API endpoint that your Chrome extension can use to generate JWT tokens. The extension can then use these tokens for authentication in subsequent requests to your Wasp backend. Remember to handle security carefully, especially when dealing with extensions and public APIs. This example is simplified and you should add proper authentication, rate limiting, and other security measures in a production environment. Sources: Custom HTTP API Endpoints Making Sure CORS Works ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @NEROX, 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?
NEROX
NEROXOP2w ago
@kapa.ai main.wasp: query getSitemap { fn: import { getSitemap } from "@src/server/webScraper.js", entities: [] } query checkUrlStatus { fn: import { checkUrlStatus } from "@src/server/webScraper.js", entities: [] } query getPageContent { fn: import { getPageContent } from "@src/server/webScraper.js", entities: [] } webScraper.ts: import axios from 'axios'; import { xml2js } from 'xml-js'; import * as cheerio from 'cheerio'; import puppeteer from 'puppeteer'; import TurndownService from 'turndown'; import type { GetSitemap, CheckUrlStatus, GetPageContent } from 'wasp/server/operations'; // Define input/output types type GetSitemapArgs = { domain: string }; type CheckUrlStatusArgs = { urls: string[] }; type GetPageContentArgs = { url: string }; // Define XML types interface XMLLoc { _text: string; } interface XMLUrl { loc: XMLLoc; } interface XMLUrlset { url: XMLUrl | XMLUrl[]; } interface XMLSitemap { loc: XMLLoc; } interface XMLSitemapIndex { sitemap: XMLSitemap | XMLSitemap[]; } interface XMLResult { urlset?: XMLUrlset; sitemapindex?: XMLSitemapIndex; } // Export interfaces as plain objects to satisfy SuperJSON export type UrlStatus = { url: string; status: number; redirectUrl?: string; } export type PageContent = { title: string; content: string; contentMd: string; metadata: { description: string; keywords: string; author: string; }; styles: string[]; scripts: string[]; inlineStyles: string; }
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I apologize, but I don't have enough information in the provided knowledge sources to give a complete and accurate answer about the specific code you've shared. The code you've provided seems to be a mix of Wasp query declarations and TypeScript implementations, but I don't have detailed information about these specific queries or their implementations in the context you've provided. However, I can provide some general information about Wasp queries based on the knowledge sources: 1. Queries in Wasp are declared in the main.wasp file and implemented in separate JavaScript or TypeScript files. This is consistent with what you've shown in your code snippet. Declaring Queries 2. The syntax for declaring queries in the main.wasp file looks correct based on the examples in the documentation. API Reference 3. For TypeScript implementations, Wasp generates types that you can use to specify the query's input and output types. This is similar to what you're doing with the GetSitemap, CheckUrlStatus, and GetPageContent types. Implementing Queries 4. The use of entities: [] in your query declarations suggests that these queries don't directly interact with any Wasp entities. This is valid, but different from some of the examples in the documentation which often show queries using entities. If you have specific questions about how to implement these queries or how to use them in your Wasp application, I'd be happy to try to answer based on the general Wasp documentation. However, for the specific implementation details and types you've defined, I don't have enough context in the provided knowledge sources to comment on their correctness or provide guidance. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @NEROX, 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?
NEROX
NEROXOP2w ago
@kapa.ai main.wasp: query getSitemap { fn: import { getSitemap } from "@src/server/webScraper.js", entities: [] } query checkUrlStatus { fn: import { checkUrlStatus } from "@src/server/webScraper.js", entities: [] } query getPageContent { fn: import { getPageContent } from "@src/server/webScraper.js", entities: [] } webScraper.ts: import axios from 'axios'; import { xml2js } from 'xml-js'; import * as cheerio from 'cheerio'; import puppeteer from 'puppeteer'; import TurndownService from 'turndown'; import type { GetSitemap, CheckUrlStatus, GetPageContent } from 'wasp/server/operations'; // Define input/output types type GetSitemapArgs = { domain: string }; type CheckUrlStatusArgs = { urls: string[] }; type GetPageContentArgs = { url: string }; // Define XML types interface XMLLoc { _text: string; } interface XMLUrl { loc: XMLLoc; } interface XMLUrlset { url: XMLUrl | XMLUrl[]; } interface XMLSitemap { loc: XMLLoc; } interface XMLSitemapIndex { sitemap: XMLSitemap | XMLSitemap[]; } interface XMLResult { urlset?: XMLUrlset; sitemapindex?: XMLSitemapIndex; } // Export interfaces as plain objects to satisfy SuperJSON export type UrlStatus = { url: string; status: number; redirectUrl?: string; } export type PageContent = { title: string; content: string; contentMd: string; metadata: { description: string; keywords: string; author: string; }; styles: string[]; scripts: string[]; inlineStyles: string; } what I'm doing creating my API endpoint? fix my example completely
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the code you've provided, it appears you're trying to create query operations for web scraping functionalities in a Wasp application. However, if you're aiming to create API endpoints that can be accessed externally (e.g., from a Chrome extension as mentioned in your previous question), you should use the api declaration in Wasp instead of query. Here's how you can modify your main.wasp file to create API endpoints:
api getSitemap {
fn: import { getSitemap } from "@src/server/webScraper.js",
httpRoute: (GET, "/api/sitemap")
}

api checkUrlStatus {
fn: import { checkUrlStatus } from "@src/server/webScraper.js",
httpRoute: (POST, "/api/check-url-status")
}

api getPageContent {
fn: import { getPageContent } from "@src/server/webScraper.js",
httpRoute: (GET, "/api/page-content")
}
api getSitemap {
fn: import { getSitemap } from "@src/server/webScraper.js",
httpRoute: (GET, "/api/sitemap")
}

api checkUrlStatus {
fn: import { checkUrlStatus } from "@src/server/webScraper.js",
httpRoute: (POST, "/api/check-url-status")
}

api getPageContent {
fn: import { getPageContent } from "@src/server/webScraper.js",
httpRoute: (GET, "/api/page-content")
}
Now, let's modify your webScraper.ts file to implement these API endpoints:
import axios from 'axios';
import { xml2js } from 'xml-js';
import * as cheerio from 'cheerio';
import puppeteer from 'puppeteer';
import TurndownService from 'turndown';
import type { GetSitemap, CheckUrlStatus, GetPageContent } from 'wasp/server/api';

// ... (keep your type definitions as they are)

export const getSitemap: GetSitemap = async (req, res, context) => {
const { domain } = req.query;
// Implement your sitemap fetching logic here
// ...
res.json({ /* your sitemap data */ });
};

export const checkUrlStatus: CheckUrlStatus = async (req, res, context) => {
const { urls } = req.body;
// Implement your URL status checking logic here
// ...
res.json({ /* your URL status data */ });
};

export const getPageContent: GetPageContent = async (req, res, context) => {
const { url } = req.query;
// Implement your page content fetching logic here
// ...
res.json({ /* your page content data */ });
};
import axios from 'axios';
import { xml2js } from 'xml-js';
import * as cheerio from 'cheerio';
import puppeteer from 'puppeteer';
import TurndownService from 'turndown';
import type { GetSitemap, CheckUrlStatus, GetPageContent } from 'wasp/server/api';

// ... (keep your type definitions as they are)

export const getSitemap: GetSitemap = async (req, res, context) => {
const { domain } = req.query;
// Implement your sitemap fetching logic here
// ...
res.json({ /* your sitemap data */ });
};

export const checkUrlStatus: CheckUrlStatus = async (req, res, context) => {
const { urls } = req.body;
// Implement your URL status checking logic here
// ...
res.json({ /* your URL status data */ });
};

export const getPageContent: GetPageContent = async (req, res, context) => {
const { url } = req.query;
// Implement your page content fetching logic here
// ...
res.json({ /* your page content data */ });
};
These changes will create three API endpoints: 1. GET /api/sitemap for fetching a sitemap 2. POST /api/check-url-status for checking URL statuses 3. GET /api/page-content for fetching page content A few important points: 1. We've changed query to api in the main.wasp file. This creates HTTP endpoints instead of Wasp queries. 2. We've added httpRoute to each API declaration, specifying the HTTP method and path. 3. In the implementation file, we're now importing types from wasp/server/api instead of wasp/server/operations. 4. The implementation functions now take req, res, and context parameters, as is standard for Express.js route handlers. 5. We're using res.json() to send the response, which is the correct way to respond in an Express.js-style API. Remember to handle errors appropriately and validate input in your actual implementations. Also, you might want to add CORS configuration if these APIs will be accessed from a different origin (like a Chrome extension). You can do this as shown in the previous answer:
apiNamespace webScraperApi {
middlewareConfigFn: import { webScraperApiMiddleware } from "@src/middleware.js",
path: "/api"
}
apiNamespace webScraperApi {
middlewareConfigFn: import { webScraperApiMiddleware } from "@src/middleware.js",
path: "/api"
}
And implement the middleware in src/middleware.js:
export const webScraperApiMiddleware = (config) => {
config.cors = {
origin: '*', // Be cautious with this in production
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type']
};
return config;
};
export const webScraperApiMiddleware = (config) => {
config.cors = {
origin: '*', // Be cautious with this in production
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type']
};
return config;
};
This setup will create public API endpoints that can be accessed externally, which seems to be what you're aiming for. Custom HTTP API Endpoints </assistant_answer> ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @NEROX, 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?
NEROX
NEROXOP2w ago
@kapa.ai Is this corect? api getSitemapApi { fn: import { getSitemap } from "@src/server/webScraper.js", httpRoute: (GET, "/api/sitemap") } api checkUrlStatusApi { fn: import { checkUrlStatus } from "@src/server/webScraper.js", httpRoute: (POST, "/api/check-url-status") } api getPageContentApi { fn: import { getPageContent } from "@src/server/webScraper.js", httpRoute: (GET, "/api/page-content") } apiNamespace webScraperApi { middlewareConfigFn: import { webScraperApiMiddleware } from "@src/middleware.js", path: "/api" }
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Yes, the code you've provided is correct for defining custom API endpoints in Wasp. Let's break it down: 1. API Definitions: Your three api declarations for getSitemapApi, checkUrlStatusApi, and getPageContentApi are correctly structured. Each one specifies: - A function to handle the request (fn) - An HTTP route with method and path (httpRoute) This is consistent with the API declaration syntax described in the Custom HTTP API Endpoints documentation. 2. API Namespace: You've also correctly defined an apiNamespace called webScraperApi. This is used to apply middleware to all routes under the "/api" path. The syntax matches the example provided in the Customize Per-Path Middleware section of the Wasp documentation. Your code will create the following endpoints: - GET /api/sitemap - POST /api/check-url-status - GET /api/page-content And all of these endpoints will use the middleware defined in webScraperApiMiddleware. Remember to implement the actual functions (getSitemap, checkUrlStatus, getPageContent) in your @src/server/webScraper.js file, and the webScraperApiMiddleware function in your @src/middleware.js file. This setup will allow you to handle CORS and any other middleware concerns for all your API routes in one place, which is a good practice for maintaining consistency across your API endpoints. Custom HTTP API Endpoints Customize Per-Path Middleware ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @NEROX, 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?
NEROX
NEROXOP2w ago
Woah let's try @kapa.ai is possible to test API endpoints in development with localhost:3000?
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Yes, it is possible to test API endpoints in development using localhost:3000, but there's an important detail to note about Wasp's default port configuration. By default, Wasp uses two different ports for development: 1. Port 3000 for the web client (frontend) 2. Port 3001 for the API server (backend) This means that while your React frontend will be accessible at <http://localhost:3000,> your API endpoints will actually be served from <http://localhost:3001.> To test your API endpoints during development, you should use <http://localhost:3001> followed by your API route. For example: - <http://localhost:3001/api/sitemap> - <http://localhost:3001/api/check-url-status> - <http://localhost:3001/api/page-content> You can use tools like Postman, curl, or even your browser (for GET requests) to test these endpoints. If you need to use a different port, Wasp allows you to configure this. As mentioned in the Github issue about port configuration, you can set custom ports using environment variables: 1. For the web client, in .env.client:
PORT=8000
REACT_APP_API_URL=<http://localhost:4040>

PORT=8000
REACT_APP_API_URL=<http://localhost:4040>

2. For the API server, in .env.server:
PORT=4040
WASP_WEB_CLIENT_URL=<http://localhost:8000>

PORT=4040
WASP_WEB_CLIENT_URL=<http://localhost:8000>

Remember, when testing your API endpoints, make sure your server is running (wasp start). Also, if you're calling these APIs from your frontend code during development, Wasp should handle the correct URL configuration automatically. Make it easy to use different ports (than 3000 and 3001) during Wasp development ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @NEROX, 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?
NEROX
NEROXOP2w ago
Just built a Chrome extension with Api, thx Kapa!
NEROX
NEROXOP2w ago
;)
No description
No description
A³Learn
A³Learn2w ago
Dude!!!! You are killing it!
Vinny (@Wasp)
Vinny (@Wasp)2w ago
Cool. What is this useful for?
A³Learn
A³Learn2w ago
I think his app on product hunt is about SEO, maybe an addon
NEROX
NEROXOP2w ago
Having all URLs it's easier to automate any kind of interlink, it's very easy extension for now.
Vinny (@Wasp)
Vinny (@Wasp)2w ago
I still don't get what it's useful for, but I trust it is 🙂 It's useful for SEO stuff, right? teach me 👶
A³Learn
A³Learn2w ago
Did you build an extension with a new wasp project or is it an api? We should do another community get together
NEROX
NEROXOP2w ago
I have built the whole API with Wasp and later the extension that makes the calls, I have also created it in tool format: https://whitehatseotactics.com/website-page-counter For any content writing task with AI for example, you can pass to ChatGPT the whole list of links and ask it to create in an article the most relevant internal links in a natural way for example.
NEROX
NEROXOP2w ago
Actually I think that Ricardo has that "feature" in his https://article-generation.com/ app
No description
Vinny (@Wasp)
Vinny (@Wasp)2w ago
ok I love this tool now 🏩 great idea

Did you find this page helpful?