Fifthtry

Hello Rust

We are going to create a new cargo project, lets create our Cargo.toml:

[package]
name = "realm_tutorial"
version = "0.1.0"
edition = "2018"

[dependencies]
serde_derive = "~1.0.0"
serde = "~1.0.0"

realm = "0.1.18"
realm_macros = "0.1.0"

failure = "~0.1.1"
http = "0.1.17"
hyper = "0.12.33"
futures = "0.1.26"

Lets also create src and src/main.rs:

mkdir src
echo "fn main(){}" > src/main.rs

Lets run cargo check to ensure everything is working.

This will create a folders named target-nix, .cargo, and a file named Cargo.toml. target-nix and .cargo can become big, and its okay to delete them if you stop working on a project.

target-nix and .cargo must be added to .gitignore, which Cargo.lock must be checked in.

main with realm

Lets replace main.rs code with this:

realm::realm! {middleware}

pub fn middleware(ctx: &realm::Context) 
    -> realm::base::Result<realm::Response> 
{
    realm_tutorial::routes::index::get(ctx)
}

Here we are relying on a macro named realm::realm!. This macro generates the main() function of the program. If you need more flexibility, you can use realm::realm_serve! macro from your main function.

realm::realm! macro takes a function, here we called it middleware, which is called on every http request.

The middleware gets realm::Context object as input.

In our middleware, we are for now calling our index route’s get function. In future we will look at how route properly to handle different URLs.

Making realm_tutorial a library crate

With just src/main.rs our crate is a binary crate so far. Let’s make it a library crate as well to organise our code.

First we need src/lib.rs:

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate realm_macros;

pub mod routes;

We have added a few macro_use declarations as we are going to need them in our routes::index module.

realm_tutorial::routes module

Its recommended all routes in realm apps be defined in a module named routes.

Lets create it:

mkdir src/routes
echo "pub mod index;" > src/routes/mod.rs

Nothing fancy here.

realm_tutorial::routes::index, our first route handler

Most of realm apps are just middleware URL matching routes, and routes returning what we call a “realm page”:

pub use realm::Page as RealmPage;

#[realm_page(id = "Pages.Index")]
struct Page {
    hello: String,
}

pub fn get(_ctx: &realm::Context) 
    -> realm::base::Result<realm::Response> 
{
    Page {
        hello: "world".to_string(),
    }
    .with_title("Welcome")
}

We have defined a type called Page, which is supposed to contain the data that our index route wants to send to our elm file or server side render it for search engines.

In this case we are saying that the only dynamic data is hello, a String.

We have also used realm_page macro to make it an actual realm::Page, and declared that the Elm module to use with this page is Pages.Index.

In the get() function, we are constructing this Page struct and returning it.

Running Our Server

After much ado, lets finally run our server:

cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.98s
     Running `target-nix/debug/realm_tutorial`
Listening on http://0.0.0.0:3000

Yohoo! Our server is running on port 3000 (you can override by setting PORT environment variable) and listening on 0.0.0.0.

Lets use curl to interact with it:

curl 127.0.0.1:3000
<!DOCTYPE html>
        <html>
            <head>
                <meta charset="utf-8" />
                <title>Welcome</title>
                <meta name="viewport" content="width=device-width" />
                <script id="data" type="application/json">
                    {
  "id": "Pages.Index",
  "config": {
    "hello": "world"
  },
  "title": "Welcome",
  "url": "/",
  "replace": null,
  "redirect": null
}
                </script>
                <style>p {margin: 0}</style>
            </head>
            <body>
                <div id="main"></div>
                <script src='/static/elm.js'></script>
            </body>
        </html>

If you squint carefully you will see that "hello": "world", the dynamic data index::get generated is over there. Also there is Pages.Index, the “id”, which we will get to very soon.

Astute observer may notice that there is no server side rendering, the page is empty, we will get to that too soon.

Another incantation of curl:

curl "127.0.0.1:3000/?realm_mode=layout"
{"id":"Pages.Index","config":{"hello":"world"},"title":"Welcome","url":"/","replace":null,"redirect":null}

This “realm_mode” stuff is used for our SPA frontend code. Also we have:

curl "127.0.0.1:3000/?realm_mode=api"   
{
  "hello": "world"
}

In case you wanted to use the index page as an API. All realm pages are APIs too.

Let’s hook this up with frontend now.

Table Of Content

What is Realm?

A Bit On Motivation

Routing is Hard

What does Realm do?

Backend Data And Type Safety

Tutorial

Quick Start Realm Tutorial

In Depth Tutorial (not ready)

Nix
Shell
Doit
Hello Rust
Hello Elm
Hello Static Files
Hello Server Side Rendering
Pre-Commit Hooks

Routing, Request And Response

Frontend, Data, Navigation, And APIs

How To Guides

File Upload

Backend: S3 File Upload
Authenticated File Serving
Frontend: Uploading Files From Elm

How to use storybook?

How to implement “loading..”?

Docs

Realm.In

Realm.Storybook.Story

realm::In

realm::Context

realm::Result

realm::RequestConfig

Environment Variables

Internals - Only for Realm Developers, not Users

“Realm DATA”
iFrame Controller
Shutdown Routine

Change Log

Get Realm Starter Working

Transparent Offline Feature

How to make http requests in Realm?

Development

Tutorial: ToDo App

Enhance Realm Starter

Double Load Issue

Deploy To Heroku Button

End failure

Realm-Starter Github Template

Proposal: Activity Store

Proposal: Bundling

Proposal: Retry On Network Error

Backlog

Readings

Change Log

How to Publish

Testing

Code Snippets

Skip rustfmt For Some Section

Close Modal Dialog When Clicked Outside

Ignoring Lints In Python

Ignoring Lints (clippy and rustc warnings) In Rust