First Frontend to Backend Connection

Setup the Backend #

Having previously used Warp for a backend server I decided this would be a good chance to try out Axum.

To start we need to install two dependencies Axum and Tokio

cargo add axum
cargo add tokio --features full

Create a Server and Route #

The Axum page on Crates.io has a good example of how to create a server and two routes. I simplified the code to just have a single GET route and changed the port the server listens on to avoid collision with our local Next.JS app.

use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(root));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn root() -> &'static str {
    "Hello, World!"
}

Now when we start the server with

cargo run

And visit http://localhost:8080/ in a browser we see the text Hello, World!

Hello, World! backend output

To make the example a small bit more applicable I changed the text returned from the root() function.

async fn root() -> &'static str {
    "Hello from Rust and Axum"
}

Hello from Rust and Axum updated backend output

Accessing the Backend from the Frontend #

In a new terminal window we now need to be in the ./frontend directory.

In a text editor open up the file at src/app/page.tsx

To begin lets remove most of the boiler plate code and leave with a minimal component.

export default function Home() {
  return (
    <main>
      <p>Hello from Next.JS</p>
    </main>
  );
}

Start up the local dev server with

npm run dev

In a browser if we visit http://localhost:3000/ we see our Hello from Next.JS text.

Hello from Next.JS frontend output

Getting the Data from the Backend #

Based on the NextJS docs for Fetching Data on the Server we can update our home component to fetch the text from our backend route and then display it.

The updated Home component has the following code.

async function getData() {
  const result = await fetch('http://127.0.0.1:8080/')
  
  if (!result.ok) {
    throw new Error('Failed to fetch from the backend')
  }
 
  return result.text()
}

export default async function Home() {
  const content = await getData()
  return (
    <main>
      <p>{content}</p>
    </main>
  );
}

Checking our frontend app in the browser we now see the text sent from the backend displayed.

Updated frontend output

At this point we now have our frontend and backend connected allowing us to build on this pattern going forward.