0% found this document useful (0 votes)
60 views24 pages

Nextjs Server Side

Uploaded by

sadev40375
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
60 views24 pages

Nextjs Server Side

Uploaded by

sadev40375
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Projects | 100xDevs [Link]

com/pdf/nextjs-2/next-2-1

Step 1 - Backends in
[Link]
[Link] is a full stack framework

This means the same process can handle frontend and


backend code.

Why?

1 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

1. Single codebase for all your codebase

2. No cors issues, single domain name for your FE and BE

3. Ease of deployment, deploy a single codebase

Step 2 - Recap of Data


fetching in React
Let’s do a quick recap of how data fetching works in React

We’re not building backend yet


Assume you already have this backend route - [Link]
[Link]/api/v1/user/details

Code - [Link]

Website - [Link]

User card website


Build a website that let’s a user see their name and email from the given
endpoint

2 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

UserCard component

Data fetching happens on the client

3 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Step 3 - Data fetching in Next


Ref - [Link]
fetching/fetching-caching-and-revalidating

You can do the same thing as the last slide in [Link], but then you
lose the benefits of server side rendering

You should fetch the user details on the server side and pre-render the
page before returning it to the user.

4 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Let’s try to build this


1. Initialise an empty next project

npx create-next-app@latest

1. Install axios

npm i axios

1. Clean up [Link] , [Link]

2. In the root [Link] , write a function to fetch the users details

async function getUserDetails() {


const response = await [Link]("[Link]
return [Link];
}

1. Convert the default export to be an async function (yes, nextjs now


supports async components)

import axios from "axios";

async function getUserDetails() {


const response = await [Link]("[Link]
return [Link];
}

export default async function Home() {


const userData = await getUserDetails();

return (
<div>
{[Link]}
{[Link]}
</div>
);
}

5 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

1. Check the network tab, make sure there is no waterfalling

1. Prettify the UI

6 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

import axios from "axios";

async function getUserDetails() {


const response = await [Link]("[Link]
return [Link];
}

export default async function Home() {


const userData = await getUserDetails();

return (
<div className="flex flex-col justify-center h-screen">
<div className="flex justify-center">
<div className="border p-8 rounded">
<div>
Name: {userData?.name}
</div>

{userData?.email}
</div>
</div>
</div>
);
}

Good question to ask at this point - Where is the loader ?

Do we even need a loader ?

Step 4 - Loaders in Next


7 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

What if the getUserDetails call takes 5s to finish (lets say the backend is
slow). You should show the user a loader during this time

[Link] file
Just like [Link] and [Link] , you can define a [Link] file that will
render until all the async operations finish

1. Create a [Link] file in the root folder

2. Add a custom loader inside

export default function Loading() {


return <div className="flex flex-col justify-center h-screen">
<div className="flex justify-center">
Loading...
</div>
</div>
}

8 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

9 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Step 5 - Introducing api


routes in [Link]
NextJS lets you write backend routes, just like express does.

This is why Next is considered to be a full stack framework.

The benefits of using NextJS for backend includes -

1. Code in a single repo

2. All standard things you get in a backend framework like express

3. Server components can directly talk to the backend

10 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Step 6 - Let’s move the


backend into our own app
We want to introduce a route that returns hardcoded values for a user’s
details (email, name, id)

1. Introduce a new folder called api

2. Add a folder inside called user

3. Add a file inside called [Link]

4. Initialize a GET route inside it

export async function GET() {


return [Link]({ username: "harkirat", email: "harkirat@[Link]" })
}

1. Try replacing the api call in [Link] to hit this URL

async function getUserDetails() {


try {
const response = await [Link]("[Link]
return [Link];
} catch(e) {
[Link](e);
}
}

This isn’t the best way to fetch data from the backend. We’ll make
this better as time goes by

11 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Step 7 - Frontend for Signing


up
1. Create app/signup/[Link]

2. Create a simple Page

import { Signup } from "@/components/Signup"

export default function() {


return <Signup />
}

1. Create components/[Link]

▾ Code

import axios from "axios";


import { ChangeEventHandler, useState } from "react";

export function Signup() {


const [username, setUsername] = useState("");
const [password, setPassword] = useState("");

return <div className="h-screen flex justify-center flex-col">


<div className="flex justify-center">
<a href="#" className="block max-w-sm p-6 bg-white border border-gray-20
<div>
<div className="px-10">
<div className="text-3xl font-extrabold">
Sign up
</div>
</div>
<div className="pt-2">
<LabelledInput onChange={(e) => {
setUsername([Link]);

12 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

}} label="Username" placeholder="harkirat@[Link]" />


<LabelledInput onChange={(e) => {
setPassword([Link])
}} label="Password" type={"password"} placeholder="123456" />
<button type="button" className="mt-8 w-full text-white bg-gray-800
</div>
</div>
</a>
</div>
</div>

function LabelledInput({ label, placeholder, type, onChange }: LabelledInputType


return <div>
<label className="block mb-2 text-sm text-black font-semibold pt-4"
<input onChange={onChange} type={type || "text"} id="first_name" className
</div>
}

interface LabelledInputType {
label: string;
placeholder: string;
type?: string;
onChange: ChangeEventHandler<HTMLInputElement>
}

1. Convert components/[Link] to a client component

"use client"

1. Add a onclick handler that sends a POST request to /user

<button onClick={async () => {


const response = await [Link]("[Link] {
username,
password
});

}} type="button" className="mt-8 w-full text-white bg-gray-800 focus:ring-4 focus:rin

1. Route the user to landing page if the signup succeeded

13 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Ref useRouter hook - [Link]


application/routing/linking-and-navigating#userouter-hook

▾ Final [Link]

import axios from "axios";


import { useRouter } from "next/router";
import { ChangeEventHandler, useState } from "react";

export function Signup() {


const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const router = useRouter();

return <div className="h-screen flex justify-center flex-col">


<div className="flex justify-center">
<a href="#" className="block max-w-sm p-6 bg-white border border-gray-20
<div>
<div className="px-10">
<div className="text-3xl font-extrabold">
Sign up
</div>
</div>
<div className="pt-2">
<LabelledInput onChange={(e) => {
setUsername([Link]);
}} label="Username" placeholder="harkirat@[Link]" />
<LabelledInput onChange={(e) => {
setPassword([Link])
}} label="Password" type={"password"} placeholder="123456" />
<button onClick={async () => {
const response = await [Link]("[Link] {
username,
password
});
[Link]("/")
}} type="button" className="mt-8 w-full text-white bg-gray-800 focus
</div>
</div>
</a>
</div>
</div>

14 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

function LabelledInput({ label, placeholder, type, onChange }: LabelledInputType


return <div>
<label className="block mb-2 text-sm text-black font-semibold pt-4"
<input onChange={onChange} type={type || "text"} id="first_name" className
</div>
}

interface LabelledInputType {
label: string;
placeholder: string;
type?: string;
onChange: ChangeEventHandler<HTMLInputElement>
}

We still have to implement the backend route, lets do that in the


next slide

Step 8 - Backend for signing


up
Add a POST route that takes the users email and password and for now
just returns them back

1. Navigate to app/api/user/[Link]

2. Initialize a POST endpoint inside it

15 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest) {


const body = await [Link]();

return [Link]({ username: [Link], password: [Link]


}

Ref - [Link]
response

Step 9 - Databases!
We have a bunch of dummy routes, we need to add a database layer to
persist data.

Adding prisma to a [Link] project is straightforward.

Please get a free Postgres DB from either neon or aiven

16 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

1. Install prisma

npm install prisma

1. Initialize prisma schema

npx prisma init

1. Create a simple user schema

model User {
id Int @id @default(autoincrement())
username String @unique
password String
}

1. Replace .env with your own Postgres URL

DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schem

1. Migrate the database

npx prisma migrate dev --name init_schema

1. Generate the client

npx prisma generate

17 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

1. Finish the signup route

export async function POST(req: NextRequest) {


const body = await [Link]();
// should add zod validation here
const user = await [Link]({
data: {
username: [Link],
password: [Link]
}
});

[Link]([Link]);

return [Link]({ message: "Signed up" });


}

1. Update the GET endpoint

export async function GET() {


const user = await [Link]({});
return [Link]({ name: user?.username, email: user?.username })
}

We’re not doing any authentication yet. Which is why we’re not
returning a jwt (or setting a cookie) here

Step 10 - Better fetches

18 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

For the root page, we are fetching the details of the user by hitting an
HTTP endpoint in getUserDetails

Current solution
import axios from "axios";

async function getUserDetails() {


try {
const response = await [Link]("[Link]
return [Link];
} catch(e) {
[Link](e);
}
}

export default async function Home() {


const userData = await getUserDetails();

return (
<div className="flex flex-col justify-center h-screen">
<div className="flex justify-center">
<div className="border p-8 rounded">
<div>
Name: {userData?.name}
</div>

{userData?.email}
</div>
</div>
</div>
);
}

getUserDetails runs on the server. This means you’re sending a request


from a server back to the server

19 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

Better solution
import { PrismaClient } from "@prisma/client";

const client = new PrismaClient();

async function getUserDetails() {


try {
const user = await [Link]({});
return {
name: user?.username,
email: user?.username
}
} catch(e) {
[Link](e);
}
}

export default async function Home() {


const userData = await getUserDetails();

return (
<div className="flex flex-col justify-center h-screen">
<div className="flex justify-center">
<div className="border p-8 rounded">
<div>
Name: {userData?.name}

20 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

</div>

{userData?.email}
</div>
</div>
</div>
);
}

Step 11 - Singleton prisma


client
Ref - [Link]
help-articles/nextjs-prisma-client-dev-practices

1. Create db/[Link]

2. Add a prisma client singleton inside it

import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => {


return new PrismaClient()
}

declare global {
var prisma: undefined | ReturnType<typeof prismaClientSingleton>
}

const prisma = [Link] ?? prismaClientSingleton()

21 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

export default prisma

if ([Link].NODE_ENV !== 'production') [Link] = prisma

1. Update imports of prisma everywhere

import client from "@/db"

Step 12 - Server Actions


Ref - [Link]
fetching/server-actions-and-mutations

Right now, we wrote an API endpoint that let’s the user sign up

export async function POST(req: NextRequest) {


const body = await [Link]();
// should add zod validation here
const user = await [Link]({
data: {
username: [Link],
password: [Link]
}
});

[Link]([Link]);

return [Link]({ message: "Signed up" });


}

22 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

What if you could do a simple function call (even on a client component


that would run on the server?) (similar to RPC )

Under the hood, still an HTTP request would go out. But you would
feel like you’re making a function call

Steps to follow
1. Create actions/[Link] file (you can create it in a different folder)

2. Write a function that takes username and password as input and


stores it in the DB

"use server"

import client from "@/db"

export async function signup(username: string, password: string) {


// should add zod validation here
const user = await [Link]({
data: {
username: username,
password: password
}
});

[Link]([Link]);

return "Signed up!"


}

1. Update the [Link] file to do the function call

import { signup } from "@/actions/user";;

...

<button onClick={async () => {


const response = await signup(username, password);

23 of 24 10/9/24, 18:27
Projects | 100xDevs [Link]

[Link]("token", response);
[Link]("/")
}} type="button" className="mt-8 w-full text-white bg-gray-800 focus:ring-4 focus:rin

Check the network tab

Benefits of server actions


1. Single function can be used in both client and server components

2. Gives you types of the function response on the frontend (very similar
to trpc)

3. Can be integrated seamlessly with forms (ref https://


[Link]/watch?v=dDpZfOQBMaU)

24 of 24 10/9/24, 18:27

You might also like