I know that perhaps a dedicated server would be better for this, however I'm not familiar with server only frameworks. But I'm very familiar with SvelteKit api routes. I decided to create a route named "/API" Where I have a handler class that will determine which method to run in the server based on the string passed as the func parameter in the post request. This does return the data properly when I'm using the svelte client. However, after publishing the file to Vercel It was not able to fetch the data. I will include the error message at the end since it's very long.Here is the "/API/+server.ts" file
import { json, type RequestEvent } from '@sveltejs/kit'; export async function POST({request}:RequestEvent){ const body = await request.json() const handler = new API_service(body.func); const params =body.params let data; if (Array.isArray(params)) { data = await handler.function_handler(params); } else { // Handle the case where body.params is not an array throw new Error("No parameters array") } return json( data );}class API_service{ private func:string; private config = { api_key:"&x_cg_demo_api_key=Your coingecko API Key", //<--- Change accordingly base_url:"https://api.coingecko.com/api/v3", currency:"?vs_currency=usd", ids:"&ids=bitcoin", days:"&days=30" } private coins_endpoint = { markets:"/coins/markets", } private root_endpoint = { ping:"/ping" } constructor(func:string){ this.func=func } public function_handler(params:any[]){ switch(this.func){ case "get_btc_ohlc": return this.get_btc_ohlc(params[0]) } } private async get_btc_ohlc(id:string){ const response = await fetch(this.config.base_url+`/coins/${id}/ohlc`+this.config.currency+this.config.days) return await response.json() }}
I tested the endpoint with the following client side code and I got the expected response
<script> import {onMount} from "svelte"; onMount(async ()=>{ try { const response = await fetch('/API',{ method:"POST", body:JSON.stringify({ func:"get_btc_ohlc", params:["bitcoin"] }) }) if(!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } console.log(await response.json()); } catch (error) { console.error('There was a problem with the fetch operation: ', error); } })</script>
After using copilot extensively to solve this problem as well as looking online, I was unable to find a solution after several hour. Here are the suggestions copilot gave me
- Create a vercel.json file
- Create a hook.server.ts file
- Create a meta tag in app.htmlHere they are acordingly
vercel.json
{"headers": [ {"source": "/(.*)","headers": [ {"key": "Content-Security-Policy","value": "default-src *; connect-src 'self' https://private-coingecko-api.vercel.app; script-src 'self''unsafe-inline'; style-src 'self''unsafe-inline'; img-src 'self' data:;" } ] } ] }
hook.server.ts
import type { Handle, RequestEvent } from '@sveltejs/kit';export const handle: Handle = async ({ event, resolve } ) => { const response = await resolve(event); // Ensure response.headers exists if (response.headers) { response.headers.set('Content-Security-Policy',"default-src 'self'; script-src 'self''unsafe-inline'; style-src 'self''unsafe-inline'; img-src 'self' data:; connect-src 'self' https://private-coingecko-api.vercel.app;" ); } return response;};
The Meta tag in app.html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self''unsafe-inline'; style-src 'self''unsafe-inline'; img-src 'self' data:; connect-src 'self' https://private-coingecko-api.vercel.app;">
The following snippet is of the actual fetch to my endpoint which returns the error following it (I Ran this from the console in the default edge blank page)
await fetch("https://private-coingecko-api.vercel.app/API",{ method:"POST", body:JSON.stringify({ func:"get_btc_ohlc", params:["bitcoin"], apikey: "Your api key" //<-- I removed the middleware in the previous snippet but its still there in production })}).catch(error => console.error('Error:', error));
Here is another error from postman
Here are the Logs from my vercel server. As you can see, the data from coingecko works just fine, however it fails to complete because of the cross site POST request. I modified my hooks file accordingly to a tutorial and here is how it looks, and it's still not working on postman.
import type { Handle } from '@sveltejs/kit';export const handle: Handle = async ({ resolve, event }) => { // Apply CORS header for API routes if (event.url.pathname.startsWith('/API')) { // Required for CORS to work if(event.request.method === 'OPTIONS') { return new Response(null, { headers: {'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS','Access-Control-Allow-Origin': '*','Access-Control-Allow-Headers': '*', } }); } } const response = await resolve(event); if (event.url.pathname.startsWith('/API')) { response.headers.append('Access-Control-Allow-Origin', `*`); } return response;};
Edit:I'm currently reading this, it seems useful so far I still haven't figured it outhttps://www.programonaut.com/cross-site-post-form-submissions-are-forbidden-in-sveltekit/
Here is more detailed documentation about this topichttps://kit.svelte.dev/docs/adapter-node
this information also seems relevanthttps://snippets.khromov.se/configure-cors-in-sveltekit-to-access-your-api-routes-from-a-different-host/