appwrite / appwrite

Your backend, minus the hassle.
https://appwrite.io
BSD 3-Clause "New" or "Revised" License
45.43k stars 4.02k forks source link

🐛 Bug Report: e._formData.get is not a function #8596

Closed TomasioLT closed 1 month ago

TomasioLT commented 2 months ago

👟 Reproduction steps

I'm using latest nextjs version, typescript, tailwind and shadcn-ui components.

Dependencies ```json "dependencies": { "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "appwrite": "^14.0.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.429.0", "next": "14.2.6", "next-themes": "^0.3.0", "node-appwrite": "^13.0.0", "react": "^18", "react-dom": "^18", "recharts": "^2.12.7", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7" }, ```

Last week worked on authentication, does work properly ( Email sign-up, email sign-in, google oauth). This week started to work with database

If I Sign-in to application ( Middleware prevent absolutely all pages from access if not authorized ) and / or sign out after that, everything works well. Until... I go to /payments page and click on button - to add new payment:

image

After upload to database ( at the moment database accepts request from public access - from anyone, without restrictions), I get response: image

And after when I tried to upload several payments to the database - I cannot log out from the application: image

Console.error: image

Server console error ``` Internal error: TypeError: e._formData.get is not a function at ti (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:311006) at t.decodeReply (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:35:314558) at C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:39:286 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async rS (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:38:7978) at async r4 (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:41:1256) at async doRender (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:1418:30) at async cacheEntry.responseCache.get.routeKind (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:1579:28) at async DevServer.renderToResponseWithComponentsImpl (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:1487:28) at async DevServer.renderPageComponent (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:1911:24) at async DevServer.renderToResponseImpl (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:1949:32) at async DevServer.pipeImpl (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:916:25) at async NextNodeServer.handleCatchallRenderRequest (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\next-server.js:272:17) at async DevServer.handleRequestImpl (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\base-server.js:812:17) at async C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\dev\next-dev-server.js:339:20 at async Span.traceAsyncFn (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\trace\trace.js:154:20) at async DevServer.handleRequest (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\dev\next-dev-server.js:336:24) at async invokeRender (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\lib\router-server.js:173:21) at async handleRequest (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\lib\router-server.js:350:24) at async requestHandlerImpl (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\lib\router-server.js:374:13) at async Server.requestListener (C:\Users\tomas\Documents\Code\appwrite\next-appwrite-transport-order\node_modules\next\dist\server\lib\start-server.js:141:13) digest: "3179946304" POST /payments 500 in 25ms ```

And even If I try to clean all the localhost cookies, now im signed out of application, on try to sign in, i got the same error:

image

But If I delete in vscode .next whole folder and start the server again. Whole authentication proccess work without any error. Until I start to interact with the appwrite cloud database again...

👍 Expected behavior

Working sign-in(email and with oauth providers), sign-out options without additional errors. Also with appwrite cloud database functionality.

👎 Actual Behavior

Screenshots and problem provided in reproduction section.

Code Basis.

/app/sign-in/page.tsx ```typescript "use client"; import {useEffect, useState} from "react"; import {toast} from "sonner"; import Link from "next/link"; import {Button} from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import {Input} from "@/components/ui/input"; import {Label} from "@/components/ui/label"; import { createAdminClient, createSessionClient, signInWithEmail, } from "@/lib/server/appwrite"; import {ID} from "node-appwrite"; import {cookies} from "next/headers"; import {redirect} from "next/navigation"; import {Loader2} from "lucide-react"; import { signUpWithDiscord, signUpWithGithub, signUpWithGoogle, signUpWithMicrosoft, } from "@/lib/server/oauth"; import {Client, Account, OAuthProvider} from "appwrite"; import {Separator} from "@/components/ui/separator"; import ForgotPassword from "@/components/ForgotPassword"; import ProviderButton from "./ProviderButton"; // Define the type for the logged-in user, which might include other properties based on your backend interface User { name: string; email: string; [key: string]: any; // Add additional properties as needed } const LoginPage: React.FC = () => { const [buttonLoading, setButtonLoading] = useState(false); const [buttonGoogleLoading, setButtonGoogleLoading] = useState(false); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const handleLogin = async (e: any) => { e.preventDefault(); setButtonLoading(true); console.log(`Email: ${email}, password: ${password}`); const session = await signInWithEmail(email, password) .then(() => { toast.success("Signed in succesffully!"); setButtonLoading(false); }) .catch((err) => { toast.error(`Error: ${err.message}`); setButtonLoading(false); }); console.log("Sign in session: ", session); }; const hanleLoginGoogle = async () => { await signUpWithGoogle(); }; const hanleLoginGithub = async () => { await signUpWithGithub(); }; const handleLoginMicrosoft = async () => { await signUpWithMicrosoft(); }; const handleLoginDiscord = async () => { await signUpWithDiscord(); }; return ( Login Enter your email below to login to your account
setEmail(e.currentTarget.value)} />
setPassword(e.currentTarget.value)} />
Don't have an account?{" "} Sign up
); }; export default LoginPage; ```
/app/sign-in/ProviderButton.tsx ```typescript import {Button} from "@/components/ui/button"; import {Loader2} from "lucide-react"; import React, {useState} from "react"; export default function ProviderButton({ title, handleLogin, }: { title: string; handleLogin: () => Promise; }) { const [loading, setLoading] = useState(false); const onClickHandler = async () => { setLoading(true); try { await handleLogin(); // Await the handleLogin function if it's asynchronous } finally { setLoading(false); } }; return ( ); } ```
/lib/server/appwrite.ts ```typescript // src/lib/server/appwrite.js "use server"; import {Client, Account, ID, OAuthProvider} from "node-appwrite"; import {cookies} from "next/headers"; import {redirect} from "next/navigation"; export async function createSessionClient() { const client = new Client() .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string) .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string); const session = cookies().get("__sessionIsh"); if (!session || !session.value) { throw new Error("No session"); } client.setSession(session.value); return { get account() { return new Account(client); }, }; } export async function createAdminClient() { console.log("Appwirte Endpoint: ", process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT); const client = new Client() .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string) .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string) .setKey(process.env.NEXT_APPWRITE_KEY as string); return { get account() { return new Account(client); }, }; } export async function getLoggedInUser() { try { const {account} = await createSessionClient(); return await account.get(); } catch (error) { return null; } } export async function getEnvProjectId() { return process.env.NEXT_PUBLIC_APPWRITE_PROJECT; } export async function signInWithEmail(email: string, password: string) { //"use server"; const {account} = await createAdminClient(); console.log("ADM account: ", account); const session = await account.createEmailPasswordSession(email, password); cookies().set("__sessionIsh", session.secret, { path: "/", httpOnly: true, sameSite: "lax", secure: true, }); redirect("/?status=ok"); } export async function signUpWithEmail( email: string, password: string, name: string ) { const {account} = await createAdminClient(); await account.create(ID.unique(), email, password, name); const session = await account.createEmailPasswordSession(email, password); cookies().set("__sessionIsh", session.secret, { path: "/", httpOnly: true, sameSite: "lax", secure: true, }); redirect("/?status=ok"); } export async function signOut() { const {account} = await createSessionClient(); cookies().delete("__sessionIsh"); await account.deleteSession("current"); redirect("/sign-in"); } export async function signUpWithGoogle() { const client = new Client() .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT as string) .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT as string); } ```
/lib/server/oauth.ts ```typescript // src/lib/server/oauth.js "use server"; import {createAdminClient} from "@/lib/server/appwrite"; import {redirect} from "next/navigation"; import {headers} from "next/headers"; import {OAuthProvider} from "node-appwrite"; export async function signUpWithGithub() { const {account} = await createAdminClient(); const origin = headers().get("origin"); const redirectUrl = await account.createOAuth2Token( OAuthProvider.Github, `${origin}/api/oauth`, `${origin}/sign-up` ); return redirect(redirectUrl); } export async function signUpWithGoogle() { const {account} = await createAdminClient(); const origin = headers().get("origin"); console.log("Origin: ", origin); const redirectUrl = await account.createOAuth2Token( OAuthProvider.Google, `${origin}/api/oauth`, `${origin}/sign-in`, ["openid"] ); console.log("Redirect url: ", redirectUrl); return redirect(redirectUrl); } export async function signUpWithMicrosoft() { const {account} = await createAdminClient(); const origin = headers().get("origin"); console.log("Origin: ", origin); const redirectUrl = await account.createOAuth2Token( OAuthProvider.Microsoft, `${origin}/api/oauth`, `${origin}/sign-in`, ["openid"] ); console.log("Redirect url: ", redirectUrl); return redirect(redirectUrl); } export async function signUpWithDiscord() { const {account} = await createAdminClient(); const origin = headers().get("origin"); console.log("Origin: ", origin); const redirectUrl = await account.createOAuth2Token( OAuthProvider.Discord, `${origin}/api/oauth`, `${origin}/sign-in`, ["openid"] ); console.log("Redirect url: ", redirectUrl); return redirect(redirectUrl); } ```
/app/api/oauth/route.ts ```typescript // src/app/oauth/route.js import {createAdminClient} from "@/lib/server/appwrite"; import {cookies} from "next/headers"; import {NextRequest, NextResponse} from "next/server"; export async function GET(request: NextRequest) { const userId = request.nextUrl.searchParams.get("userId") as string; const secret = request.nextUrl.searchParams.get("secret") as string; console.log("API USERID: ", userId); console.log("API SECRET: ", secret); const {account} = await createAdminClient(); const session = await account.createSession(userId, secret); cookies().set("__sessionIsh", session.secret, { path: "/", httpOnly: true, sameSite: "lax", secure: true, }); return NextResponse.redirect(`${request.nextUrl.origin}/?status=ok`); } ```

🎲 Appwrite version

Appwrite Cloud

💻 Operating system

Windows

🧱 Your Environment

No response

👀 Have you spent some time to check if this issue has been raised before?

🏢 Have you read the Code of Conduct?

TomasioLT commented 2 months ago

Additional information: when I deployed to vercel host, got this message after the same reproductions steps.

image

stnguyen90 commented 2 months ago

@TomasioLT, for server-side, please make sure you're only using the node-appwrite SDK. The e._formData.get is not a function error is caused by an incompatibility of the web (appwrite) SDK in Next.js's server-side runtime.

github-actions[bot] commented 1 month ago

This issue has been labeled as a 'question', indicating that it requires additional information from the requestor. It has been inactive for 7 days. If no further activity occurs, this issue will be closed in 14 days.

github-actions[bot] commented 1 month ago

This issue has been closed due to inactivity. If you still require assistance, please provide the requested information.