SvelteKit fetch Request to Authenticated CloudKit API Fails - fetch

I have a simple GET request to one of Apple's APIs (CloudKit) and it doesn't work in SvelteKit. The request is structured like this (line breaks added to make it easier to read):
https://api.apple-cloudkit.com/database/1/iCloud.com.MyAppContainer
/development/public/users/current?
ckAPIToken=abc
&ckWebAuthToken=xyz
If I run this request using cURL or in an API testing app like Paw, it works great:
==== Result ====
Status code: 200
================
{
"userRecordName" : "_abc123"
}
If I do the same request in SvelteKit in the client side, it returns the error the CloudKit API gives when you don't include a ckWebAuthToken, but I've definitely included it.
<script>
let url = 'https://api.apple-cloudkit.com/database/1/iCloud.com.MyAppContainer
/development/public/users/current?ckAPIToken=abc&ckWebAuthToken=xyz'
let res = await fetch(url)
console.log(await res.json())
</script>
==== Result ====
Status code: 421
================
{
uuid: 'abc...',
serverErrorCode: 'AUTHENTICATION_REQUIRED',
reason: 'request needs authorization',
redirectURL: 'https://idmsa.apple.com/IDMSWebAuth/auth?oauth_tok...'}
}
I also tried the same fetch in a server-side endpoint and the result is the same (421 error).
Is there something about fetch in SvelteKit that would make it incompatible with an external API like this?

Related

Unable to start Next 13 app directory (beta) in production mode

Step 1: Automatically create a new Next.js project using the new beta app directory:
npx create-next-app#latest --experimental-app
pages/api/hello.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
This file is identical to the one created automatically created by npx - there are no modifications.
I am trying to build a simple home page, which fetches data from the api which gets data from my database. Either way an await/async will be required. I am following the instructions from here.
In this example I will demonstrate that even awaiting the supplied hello api can't seem to run in production, and I can't work out why.
app/page.tsx
async function getHelloAsync() {
const res = await fetch('http://localhost:3000/api/hello', { cache: 'no-store' });
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const hello = await getHelloAsync();
return (
<main>
<h1>Hello: {hello.name}</h1>
</main>
)
}
To test the hello api works, I confirm that running pn run dev and then curl http://localhost:3000/api/hello the following successful response is received:
{"name":"John Doe"}
Next up we exit the dev server and run:
pn run build
The first headache is that the build will completely fail to build unless one adds { cache: 'no-store' } to the fetch command:
const res = await fetch('http://localhost:3000/api/hello', { cache: 'no-store' });
or adds this to the top of app/page.tsx:
export const fetchCache = 'force-no-store';
I am actually not sure how one would even build this if you wanted to cache the response or use revalidate instead and provide an initial optimistic response, because without cache: no-store it refuses to build outright. Ideally instead it should just cache the result from /api/hello and not fail. Running the dev server at the same idea as doing the build does allow the build to work, but then as soon as you exit the dev server and run pn run start then all the api calls fail anyway. So that is not a good idea.
This leads us to the next problem - why are the api calls not working in production (i.e. when calling pn run start).
Step 2:
pn run build
pn run start
Confirm that the following still works and yes it does:
curl http://localhost:3000/api/hello
Result:
{"name":"John Doe"}
Now we visit http://localhost:3000 in a browser but, surprise! We get the following error:
> next start
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
warn - You have enabled experimental feature (appDir) in next.config.js.
warn - Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.
info - Thank you for testing `appDir` please leave your feedback at https://nextjs.link/app-feedback
(node:787) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11118:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async getHelloAsync (/Users/username/nextjstest/.next/server/app/page.js:229:17)
at async Page (/Users/username/nextjstest/.next/server/app/page.js:242:19) {
cause: Error: connect ECONNREFUSED ::1:3000
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1300:16)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '::1',
port: 3000
}
}
[Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.] {
digest: '3567993178'
}
Why is it saying that the connection is refused when we know the API is available? I can't get this to run at all. I know this is beta but surely the code should actually run right? How do I make this code work?
Also if anyone knows where where the logs are that I'm supposed to be accessing to see digest '3567993178' please let me know.

Creating new document with Firestore REST API and Local Emulator Suite, Returning Error 404: Problem with Path Parameter

I’m just getting acquainted with Firebase/Firestore as a beginner coder, and I'm attempting to create an integration test for a set of callable functions a friend had written for their project. I am writing a test to automate testing using the Firebase local emulator suite.
Right now, I'm attempting to write a POSt request using Axios that will create a document in a given collection in my local emulator suite, after having received an Id Token from generating an authorized user.
The project id is called okane-crud-dev. I’ve created a collection
called test.
I have created an authenticated user with a given email and password, and generated the unique Id Token from an initial post request:
interface createPostRequest {
url: string;
data: Object;
config: Object;
};
//create an instance of a user
const createUserInstance : createPostRequest = {
url: 'http://localhost:9099/identitytoolkit.googleapis.com/v1/accounts:signUp?key=hi',
data: {
'email': 'myemail#email.com',
'password': 'mypassword',
'returnSecureToken': true
},
config: {
'headers':
{'Content-Type': 'application/json'}
},
};
const createUserResponse = await axios.post(createUserInstance.url, createUserInstance.data, createUserInstance.config);
const userIdToken = createUserResponse.data.idToken;
const userLocalId = createUserResponse.data.localId;
Up to this point, I have had no issues.
As for the second POST request to create a document, this is my code. I used this post as a reference:
Creating new collection and document with Firestore REST API returning HTTP 400
const createDocumentInstance : createPostRequest = {
url: "https://firestore.googleapis.com/v1beta1/projects/'localhost:8080/okane-crud-dev'/databases/(default)/documents/test",
data: {
"fields": {
"localId": userLocalId,
'budget': '2000',
}
},
//directly pasted IdToken as using the variable resulted in problem with ' ' error
config: {
'headers':
{
'Content-Type': 'application/json',
'Authorization': `Bearer ${userIdToken}`,
}
}};
console.log(createDocumentInstance);
const createDocument = await axios.post(createDocumentInstance.url, createDocumentInstance.data, createDocumentInstance.config);
const docReference = createDocument.data;
console.log(docReference);
When I attempted to run this, the following error was returned:
Request failed with status code 404
at createError (../../node_modules/axios/lib/core/createError.js:16:15)
at settle (../../node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (../../node_modules/axios/lib/adapters/http.js:293:11)
I'm a beginner and am just starting to learn how to code, so bear with me if this is an easy answer as I'm still figuring out how to debug.
I know that a 404 error means an issue with locating the resource -> and after making some adjustments to the headers, I figured the issue must be in my URL. I’ve tried looking around for other posts that use local emulator suite and POST requests to figure out if there was something wrong with how I wrote the path.
"https://firestore.googleapis.com/v1beta1/projects/'localhost:8080/okane-crud-dev'/databases/(default)/documents/test"
I've been looking at the Firebase documentation closely for creating a document; https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents/createDocument#path-parameters
Borrowing from the other post, I’ve tried different variations of where to include the emulator suite port: localhost:8080 and the project id “okane-crud-dev”. But haven’t seemed to figure out. I made sure that the project id was connected to my local emulator suite. Does anyone have any suggestions?
If you're using the Firestore Emulator with the REST API, you should change the base URL https://firestore.googleapis.com/v1 to your localhost http://localhost:8080/v1 then proceed with the path of your Firestore database.
http://localhost:8080/v1/projects/okrane-crud-dev/databases/(default)/documents/test

Why do I get a "502 Gateway" error from NextJs app hosted on Firebase for POST requests only?

I started to build an API using NextJs framework. I want it to be hosted on Firebase (Hosting and Functions). Everything is working as long as I send only GET requests. When I send a POST request I receive a "502 Bad Gateway" error.
It's very simple to reproduce. You just have to download and deploy the example provided by the team developing NextJs.
create a new project on Firebase console
install the "with Firebase hosting" example
change the project name in the .firebaserc (line 3) file
create a folder "api" under the folder "pages"
create a file "hello.js" under the folder "api" and add the following snippet
export default async (req, res) => {
const {
body,
method
} = req;
console.log("method :>> ", method);
console.log("body :>> ", body);
switch (method) {
case "POST":
res.status(200).end(`Method ${method} supported!`);
break;
default:
res.setHeader("Allow", ["POST"]);
res.status(405).end(`Method ${method} Not Allowed`);
}
};
deploy the app
send a GET request to "https://[project-name].web.app/api/hello" and see it works
send a POST request to "https://[project-name].web.app/api/hello" and see it does not work
Do you have a the same error as me?
I spent 2 days to read articles, watch videos and try different configurations. You can even update the firebaseFunctions to add a console.log and see the POST request is caught by the Firebase Cloud Function but the NextJs server does not pass it to our API like it does for a GET request. It's out of my skills range...
Below the output you should have. The POST request should be answered with 200 - Method POST is supported!.
This was a real pain to track down, but after poking around myself for a while, I found that the same issue crops up for PUT and PATCH requests. Which suggested that it had something to do with the body of the request. Annoyingly, after finding that out, I stumbled across the thread of Issue #7960, where they found the same problem.
Simply put, the body of the request processed once by https.onRequest() and then nextjsHandle() tries to parse it again. Because the body was handled already, the raw-body module (within nextjsHandle()) waits indefinitely for 'data' events that will never come.
Currently, there isn't a way to turn off the body parsing done by https.onRequest(), so it must be disabled on the next.js end. Unfortunately, there isn't a global off switch for body parsing that can be added in next.config.js and it must be done for each and every API route (the files in pages/api) (which may change if the proposed fix in PR #16169 is added).
To disable body parsing for a given route, you add the following to the route's file
export const config = {
api: {
// disables call to body parsing module
bodyParser: false,
}
};
However, as mentioned in Issue #7960 by #rscotten, you might also want to use next dev while developing your app, so you need to enable it while using next dev but disable it while deployed. This can be done using
export const config = {
api: {
// disables call to body parsing module while deployed
bodyParser: process.env.NODE_ENV !== 'production',
}
};
Applying these changes to hello.js gives:
export default async (req, res) => {
const {
body,
method
} = req;
console.log("method :>> ", method);
console.log("body :>> ", body);
switch (method) {
case "POST":
res.status(200).end(`Method ${method} supported!`);
break;
default:
res.setHeader("Allow", ["POST"]);
res.status(405).end(`Method ${method} Not Allowed`);
}
};
export const config = {
api: {
// disable nextjs's body parser while deployed
// (as body parsing is handled by `https.onRequest()`),
// but enable it for local development using `next dev`
bodyParser: process.env.NODE_ENV !== 'production',
}
};

"Handler crashed with error runtime error: invalid memory address or nil pointer dereference", but POSTMAN is ok! Why this happens?

I work with vue and go for frontend and backend respectively. I send post request to my server and get 403 error code message(notAllowed). But in postman I get the objects and is fine.
Vue and Vuex
My axios post request:
const response = await this.$axios.post(`http://localhost:8000/v1/org/${params.organization}/kkms/${params.kkm}/closeShift`,{
headers : {
'token' : this.state.token.value
}});
I know I should also use other properties like 'Content-Type' and etc in headers, but know it works well with only "token" property in the other requests. I want to know whether problem in backend or frontend?
It seems you have a mistake in the axios request.
You are receiving a 403, that means you are not authorized (or sometimes something else, check the comments in the question and down here ).
As can be found in axios docs, the post request looks like this:
axios.post(url[, data[, config]]).
It accepts the config (so the headers) as THIRD parameter, while you are setting it as second parameter. Add an empty FormData object as second param, and just shift your config to the third param.
const fakeData = new FormData();
const response = await this.$axios.post(`http://localhost:8000/v1/org/${params.organization}/kkms/${params.kkm}/closeShift`,
fakeData,
{
headers : {
'token' : this.state.token.value
}
});

Request has incorrect content type when using Twilio and Google Cloud Functions

I am following this Twilio tutorial on how to reply to SMS messages with my app:
https://www.twilio.com/docs/sms/tutorials/how-to-receive-and-reply-node-js
The tutorial assumes you're using Express, but I am doing this with a Cloud Function, so my code looks a bit different:
exports.sms = functions.https.onCall((req: any, res: any) => {
const twiml = new MessagingResponse();
if (req.body.Body === 'hello') {
twiml.message('Hi!');
} else if (req.body.Body === 'bye') {
twiml.message('Goodbye');
} else {
twiml.message(
'No Body param match, Twilio sends this in the request to your server.',
);
}
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(twiml.toString());
});
When I text my Twilio #, it hits that endpoint, but I get the following error:
Request has incorrect Content-Type. application/x-www-form-urlencoded
How do I get around this?
It looks like you're mixing up callable type functions and normal HTTP type functions. Please read the documentation to understand the difference. Callable functions are intended to be invoked directly from your mobile app using the provided client SDK. They provide two arguments: an input data object, and a context. Callables do NOT provide "req" and "res". If you want control over the the response, you should be using a normal HTTP function with "onRequest" instead of "onCall".

Resources