Fullstack Development

261497: Fullstack Development

Stack Overflow 2024 Developer Survey #

261497: Fullstack Development

Fullstack Landscape

Diagram

Inspired from this VDO.

261497: Fullstack Development

Case Study

How to over-engineer "todo" apps

261497: Fullstack Development

5 Ways to make Todo apps

  • Multi-Page Applications (MPA)
  • Single-Page Applications (SPA)
  • React Server Components (RSC)
  • RSC + Client Components (React's New Architecture)
  • HTMX
261497: Fullstack Development

Round 1

Multi-Page Application (MPA) vs Single-Page Application (SPA)

261497: Fullstack Development

Multi-page application

  • Loads a new page every time you perform an action.
  • Traditional web applications.
  • Use server-side technologies
    • PHP, Ruby on Rails, ASP.NET, Java, and Node JS.
  • Can include JavaScript (script) for client-side interactivity
261497: Fullstack Development

261497: Fullstack Development

Todo app (MPA)

  • Express JS + Pug (renderer)
261497: Fullstack Development

Get started

  • git clone https://github.com/fullstack-68/landscape-mpa.git mpa
  • cd mpa
  • pnpm install
  • pnpm run build
  • pnpm run start
261497: Fullstack Development

Endpoint

./src/index.ts

app.get("/", async (req, res) => {
  const message = req.query?.message ?? "";
  const todos = await getTodos();
  // Output HTML
  res.render("pages/index", {
    todos,
    message,
    mode: "ADD",
    curTodo: { id: "", todoText: "" },
  });
});
261497: Fullstack Development

Renderer

./view/pages/index.pug

body
    main(class="container")
        a(href="/")
            h1 Todo (MPA)
        div(id="todoform")
            include ../components/inputform.pug
        div(id="todolist")
            include ../components/todolist.pug
261497: Fullstack Development

Use incognito mode to avoid loading chrome extensions.

261497: Fullstack Development

Note

  • Every button is wrapped in a separate form
    • Need to trigger different endpoints.
  • Need to use input(type="hidden) to encode additional information.
form(action="/delete" method="POST" style="display: contents")
  input(type="hidden" value=`${todo.id}` name="curId")
  button(type="submit" class="contrast" style="margin-bottom: 0") πŸ—‘
form(action="/edit" method="POST" style="display: contents")
  input(type="hidden" value=`${todo.id}` name="curId")
  button(type="submit" class="secondary" style="margin-bottom: 0") πŸ–Š
261497: Fullstack Development

261497: Fullstack Development

Single-page application

  • Single-page application
    • Loads a single HTML page and dynamically updates the content as the user interacts with the app.
  • Use frontend and backend frameworks separately.
261497: Fullstack Development

261497: Fullstack Development

Todo app (SPA)

  • Express JS + React
261497: Fullstack Development

Get started

  • git clone https://github.com/fullstack-68/landscape-spa.git spa
  • Backend
    • cd spa/backend
    • pnpm install
    • pnpm run build
    • pnpm run start
261497: Fullstack Development

Get started (cont.)

  • Frontend
    • cd spa/frontend
    • pnpm install
    • pnpm run build
    • pnpm run preview
261497: Fullstack Development

261497: Fullstack Development

261497: Fullstack Development

Global store

  • zustand
261497: Fullstack Development

App comparison (UX)

Item MPA SPA
No page-refresh ❌ βœ…
Spinner ❌ βœ…
Element disabling ❌ βœ…
261497: Fullstack Development

App comparison (technical)

Item MPA SPA
Amount of JS loaded βœ… ❌
HTML content (SEO) βœ… ❌
State in URL βœ… ❌
261497: Fullstack Development

DX

Item MPA SPA
Number of frameworks 1 βœ… 2 ❌
Complexity Less βœ… More ❌
Lines of code Less βœ… More ❌
Type Safety Less ❌ More βœ…
Hot reloading Partial ❌ Full βœ…
261497: Fullstack Development

Amount of Codes

Dir # Files Total Lines
MPA
./src 2 161
./views 3 42
SPA
./backend/src 2 144
./frontend/src 10 😭 360

Total: MPA=203, SPA=504

261497: Fullstack Development

Round 2

Back to the server. (Next JS)

261497: Fullstack Development

Server-Side Rendering (SSR)

  • SSR
    • Generating the HTML for a web page on the server before sending it to the client’s browser.
  • CSR (Client-Side Rendering)
    • Browser loads a minimal HTML file and fetches and renders the content using JavaScript.
  • See this explanation.
261497: Fullstack Development

Next JS

  • V12
    • "Full SSR" (with DB query) can only be done through top-level component (page level).
    • Use getServerSideProps function.
  • V13 (and above)
261497: Fullstack Development

Server component

  • Run exclusively on the server.
  • Generate static HTML.
    • No interactivity (event handlers)
  • It's code isn't included in the JS bundle.
    • Never re-render.
    • Output is static without change in router level.
  • No hook
    • useState, useEffect 😍
261497: Fullstack Development

Server component


Link

261497: Fullstack Development

Client component

  • The "standard" React components we're familiar with.
  • Client Components render on both the client and the server.
    • Still have SSR.
Render on server? Render on client?
Server Component βœ…
Client Component βœ… βœ…
261497: Fullstack Development

Why server component?

  • First "official" way to run server-exclusive code in React.
  • Performance
  • Less complications
    • Dependency arrays, stale closures, memoization, ...
    • (All of these are caused by things changing.)
261497: Fullstack Development

Missing piece

React

If RSC output is static, how can I mutate data then?

261497: Fullstack Development

Missing piece

React

If RSC output is static, how can I mutate data then?

PHP

I had solved this problem before you were born, kid.

261497: Fullstack Development


Link

261497: Fullstack Development

React's new architecture

  • Client component
  • Server component
  • Server action
    • Asynchronous functions that are executed on the server.
    • Alternative to API routes.
261497: Fullstack Development

Server Action vs API Route

261497: Fullstack Development

Form action

  • No need to define new endpoints.
  • Accept form-data
  • Colocation
    • Type safe
    • Can "bind" data that is passed thought components' props. 🀯
  • Can trigger RSC update without refreshing page. πŸ‘
261497: Fullstack Development

Todo App (RSC Only)

  • git clone https://github.com/fullstack-68/landscape-rsc-only.git rsc-only
  • cd rsc-only
  • pnpm install
  • pnpm run build
  • pnpm run start
261497: Fullstack Development

No blank HTML

261497: Fullstack Development

Page refresh is back

alt text

261497: Fullstack Development

Server components

./src/app/page.tsx

export default async function Home({ params, searchParams }: PageProps) {
  const todos = await getTodos();
  //...
  return <main className="container">...</main>;
}
  • Async function
  • Data fetching without useEffect.
    • It only runs once on the server. (Try console.log)
  • Returns HTML to client.
261497: Fullstack Development

Server Action

./src/components/FormInput.tsx (Slightly modified)

export const FormInput: FC<Props> = async ({ message, mode, curId }) => {
  async function actionCreateTodo(formData: FormData) {
    "use server";
    const todoText = formData.get("todoText") as string; // Receive form-data
    await createTodos(todoText); // DB stuff
    redirect("/?message=&curId=&mode=ADD"); // Update content without refreshing page (Nice!)
  }

  return (
    <form action={actionCreateTodo} style={{ display: "contents" }}>
      <input type="hidden" name="curId" value={curId ?? ""} />
      <button type="submit">{mode === "ADD" ? "Submit" : "Update"}</button>
    </form>
  );
};

No need to create endpoint manually.

261497: Fullstack Development

Automatic binding

./src/components/TodoList.tsx

  • "Binding" todo in the server action
  • No need to use form-data. Type safety!
  • No need to include hidden input field.
261497: Fullstack Development

Automatic binding

const ButtonDelete: FC<{ todo: Todo }> = ({ todo }) => {
  async function actionDeleteTodo(formData: FormData) {
    "use server";
    await deleteTodo(todo.id); //πŸ‘ˆπŸ‘ˆπŸ‘ˆπŸ‘ˆ
    revalidatePath("/");
  }
  return (
    <form action={actionDeleteTodo}>
      <button type="submit">πŸ—‘</button>
    </form>
  );
};
261497: Fullstack Development

Side note

(for my future self)

  • revalidatePath
    • Used when there is not change in url (params).
  • redirect
    • Used when you need to change the url (change client states in the url).
261497: Fullstack Development

Missing UX/DX

  • UX
    • No loading spinner
  • DX
    • Client state is accessed from url. (searchParam in page.tsx)
      • Not type safety. Need validation.
    • Way too complicated (compared with MPA)
261497: Fullstack Development

Hybrid (RSC + RCC)

  • git clone https://github.com/fullstack-68/landscape-hybrid.git rsc-client
  • cd rsc-client
  • pnpm install
  • pnpm run build
  • pnpm run start
261497: Fullstack Development

Structure

261497: Fullstack Development

Client component

./src/components/TodoList.tsx

"use client";  //πŸ‘ˆ Mark component as client component
//...
export const TodoList: FC<Props> = ({ todos }) => {
  const { curTodo } = useStore((state) => state);
  return (
    <>
      {...}
    </>
  );
};
261497: Fullstack Development

SSR

261497: Fullstack Development

SSR

./src/app/page.tsx

export default function Home({ params, searchParams }: PageProps) {
  const todosPromise = getTodos();
  return (
    <main className="container">
      <a href="/">
        <h1>Todo (RSC + RCC)</h1>
      </a>
      <FormInput />
      <Suspense fallback={<div>Loading...</div>}>
        <TodoList todosPromise={todosPromise} /> //πŸ‘ˆ Inject data to client
        component (SSR)
      </Suspense>
      <Spinner />
    </main>
  );
}
261497: Fullstack Development

No page refresh

261497: Fullstack Development

Interactivity

  • Elements disabled
  • Spinner
261497: Fullstack Development

Server action in a separate file

./src/app/actionAdnDb.ts (Slightly modified)

"use server"; //πŸ‘ˆ Mark functions in this file as server actions.
//...
export async function actionCreateTodo(todoText: string) {
  //...
  await createTodos(todoText);
  //...
  revalidatePath("/");
  return { message: "" };
}
  • You can import this function into client component.
  • You don't have to use form-data anymore.
  • You can return data back to client component.
261497: Fullstack Development

Server action in client component

./src/components/FormInput.tsx

const ButtonSubmit: FC<PropsButtonSubmit> = ({ setMessage }) => {
  // ...
  function handleClick() {
    startTransition(async () => {
      const res = await actionCreateTodo(inputText);
      setMessage(res.message);
      setInputText("");
    });
  }
  // ...
  return <button onClick={handleClick}>Submit</button>;
};
  • I can trigger server action anywhere, not just form.
261497: Fullstack Development

Type safety

./src/components/FormInput.tsx

alt text

  • You get type safety from client component to database levels πŸ‘.
261497: Fullstack Development

Line of codes

Type # Line
MPA 203
SPA 504
RSC 292
RSC + RCC 😭 527

(In Next JS project, I counted src dir.)

261497: Fullstack Development

Have we gone too far?

261497: Fullstack Development

Round 3

Back to basic

261497: Fullstack Development

HTMX

261497: Fullstack Development

What exactly is HTMX?

Small JS library that can swap parts of UI with HTML response from a server.

2023 JavaScript Rising Stars

261497: Fullstack Development

Get started

  • git clone https://github.com/fullstack-68/landscape-htmx.git htmx
  • cd htmx
  • pnpm install
  • pnpm run build
  • pnpm run start
261497: Fullstack Development

Todo app

  • The stack is very similar to MPA (express + pug).
  • SSR enabled (duh!)
  • No reloading
  • Spinner enabled
  • Form input disabled during submission.
261497: Fullstack Development

Code count

Type # Line
MPA 203
SPA 504
RSC 292
RSC + RCC 527
HTMX ☺️ 259

Take that NextJS!

261497: Fullstack Development

In addition

261497: Fullstack Development

Summary

Architecture Usage
MPA Backend-critical (financial, ERP, ΰΈ—ΰΈ°ΰΉ€ΰΈšΰΈ΅ΰΈ’ΰΈ™)
SPA Dashboards, editor
RSC Blogs, brochure sites
RSC + RCC Highly interactive website with latest technology (to justify high price)
HTMX Apps that finish on time
261497: Fullstack Development