Reputation: 398
I am very new to Next.js. I have created login page and I want to call API on submit button click. There are few issues which i am getting
I am already adding "use server"
in the action.ts still getting first error
Login Page:
import Image from "next/image";
import React from "react";
import lady from "../../../public/landing-girl.svg";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { signInUser } from "../actions/action";
export default function Login() {
const callAPI = async (params: FormData) => {
console.log(params);
await signInUser("[email protected]", "Qwerty1@");
};
return (
<div className="flex flex-1 flex-col justify-center items-center">
<Image
src={lady}
alt="lady"
fill={true}
placeholder="empty"
priority={false}
style={{ objectFit: "cover" }}
></Image>
<div className="w-screen h-screen bg-black/20 z-10" />
<div className="w-fit min-w-[500px] h-auto bg-[#F2F2F2]/95 z-20 absolute rounded-lg items-center flex flex-1 px-10 py-5 flex-col">
<span className="text-primary text-3xl font-semibold">Login</span>
<span className="text-text-primary mt-3">
Welcome back! Please enter your details.
</span>
<form action={callAPI} className="mt-5 w-full">
<Input label="Email Id" placeholder="Enter your email"></Input>
<Input label="Password" placeholder="Enter your passoword"></Input>
<Button className="w-full" type="submit">
Sign In
</Button>
</form>
<span className="mt-5 text-text-primary text-sm">
{"Don't have account? "}
<Link href={"/signup"}>
<span className="text-primary font-semibold">Sign Up</span>
</Link>
</span>
</div>
</div>
);
}
action.ts
"use server";
import { callSignInUser } from "@/lib/server";
export async function signInUser(email: string, password: string) {
const res = await callSignInUser(email, password);
return res;
}
index.ts
export const API_BASE_URL = "http://xxxxxx/api/";
export const createAPIEndpoint = (path: string) => {
return `${API_BASE_URL}${path}`;
};
server.ts
import { isOKResponse, returnError, returnSuccess } from "@/types";
import { createAPIEndpoint } from ".";
export const callSignInUser = async (email: string, password: string) => {
const json = {
email: email,
password: password,
};
const response = await fetch(createAPIEndpoint(`/v1/auth/jwt/create/`), {
method: "POST",
body: JSON.stringify(json),
});
try {
const data = await response.json();
if (isOKResponse(response)) {
return returnSuccess<{}>(data);
}
} catch {
console.log("error");
}
return returnError(new Error("Error fetching newest news"));
};
Please can someone explain me what is wrong here? Please let me know what shall I change to make it work.
Upvotes: 0
Views: 1279
Reputation: 81
On re-reading through I think the issue is probably something happening in your fetch call that you're not handling the error. The error will be logged out in the terminal you're running next from, not your browser console, because it's happening server side. Your browser console and network call (the screenshot you have) are only going to show you client side events.
That POST to /login
you're seeing is not the fetch call. That is the signInUser
server action being invoked from the login page. All server actions are POST calls under the hood, and always return 200
regardless of what actually happens in the server side function (see discussion here).
Yes, you've got a try catch
block in the code below, but it won't actually catch the first function calls:
const response = await fetch(createAPIEndpoint(`/v1/auth/jwt/create/`), {
method: "POST",
body: JSON.stringify(json),
});
try {
const data = await response.json();
if (isOKResponse(response)) {
return returnSuccess<{}>(data);
}
} catch {
console.log("error");
}
If you notice your fetch call is outside of the try catch
block, and so if either there's an error in your fetch
call or your createAPIEndpoint
it will just throw an error then and there and not be caught by the catch
. You'd want to move that fetch call inside the try catch
block.
I've created a stripped down version here to show you simplified flow.
In short, it just removes the extra server actions calls. I don't have your auth API, so I've just substituted a test one, but that call to an external API is there.
When going through your code I noticed a couple of other things:
<Image
src={lady}
alt="lady"
fill={true}
placeholder="empty"
priority={false}
style={{ objectFit: "cover" }}/>
rather than having to have the additional </Image>
after it.
I can't see what is in your type file, but this returnSuccess<{}>(data);
seems a little odd. You've got returnSuccess as a generic with a type of an empty object.
isOKResponse
and returnError
are being imported from your types file too, but you're invoking them as functions?
You're defining a function inside and then exporting from index.ts
, this is fine but it's an odd use of an index file. Typically, these are used in a directory as central export point for other files in that directory. Eg, you'd have a directory of auth.ts
, utils.ts
and others. And then in the index.ts
file you'd just have export {authFunction1...} from './auth.ts'
. As I say, it's just a file really you can do what you want with it but just thought I'd mention it.
return returnError(new Error("Error fetching newest news"));
I don't know what that returnError
function is doing. But as this is an async call you probably don't want to be returning an error; instead just throw it. Because it's going to resolve as a promise, it would require you to check the value of the resolved promise directly.
It could be fine here, as I don't know what that returnError
function is doing. But it's an odd pattern you will want to avoid if you are actually returning an error - it will get even worse if you ever have to chain any promises. Typically you only want to use return a value where it's a sync function because you'll be using the value anyway.
Upvotes: 1