Proof of concept writing a monolith BBS using Rust, GraphQL, WASM, and SQL. WILL BE ARCHIVED ONCE PROVEN

Overview

GraphQL Forum

Important
DO NOT even think about using this in production, lest your sanity be destroyed and credentials lost!

Loosely following the awesome book Zero to Production In Rust, minus the vigorous test cases and battle tested framework.

TL;DR

Summarize backend in one line?

Good performance without even trying, massive pain keeping SQL and code in sync, and huge trouble working around corners of API.

Summarize frontend in one line?

Surprisingly pleasant to write, but documentation is scarce, and code size is large. WASM is probably not ready to be the "main site" yet.

Comment the code quality?

It would be an excellent playground to practice exploits!

How was the experience?

Extremely determined. Damn, full-stack is hard.

Will I do this again?

No, not the full stack. If I want to build anything for real, I would go for something like Preact (for frontend) + Hasura (for GQL and data) + Ory Kratos (for auth). For other backend services that don’t involve dealing with data store, I would still consider Rust Axum, tower stack, and async-graphql to be good.

How is the code quality?

TheDailyWTF kind of quality.

Building and running

Make sure you have trunk installed.

Go to gqlforum-sycamore/, then:

$ trunk build --release

Go to gqlforum-backend/, then:

$ cargo run --release

Go to http://localhost:3000 for the main page. Go to http://localhost:3000/graphql for GraphQL playground.

Admin account is admin, password is admin.

The configs in configuration.toml should be apparent.

Features

The Stack

Backend
  • async-graphql

  • Axum

  • Tower

  • sqlx

  • SQLite

Frontend
  • Sycamore

  • WASM

Backend Functionality

  • ✓ Serves a frontend with the same server

  • ✓ Backend with tracing

  • ✓ List topics

  • ✓ Topic by id

  • ✓ Create topic

  • ✓ Create post

  • ✓ Delete topic

  • ✓ Delete post

Frontend Functionality

  • ✓ Login page

  • ✓ Logout link

  • ✓ List topics

  • ✓ Topic by id

  • ✓ Create topic

  • ✓ Create post

  • Delete topic

  • Delete post

Authentication

  • ✓ Server side session management

    • ✓ Signed secure cookie on client side, hashed secret on server side

  • ✓ Password authentication with Argon2

  • ✓ Login via username/password

  • ✓ Registration (GraphQL only)

  • ✓ Change password

Authorization

  • ✓ Only admins/author can see deleted topics and posts

  • ✓ Regular users can see their own posts/topics and any public posts/topics

  • ✓ Regular users can only delete their own posts

  • ✓ Admin can delete every post

Anti-features

  • ✓ No HTTPS. Therefore, credentials are sent in clear text

    • Admittedly, this is easy to fix.

  • ✓ Frontend crashes all the time. Try refreshing

  • ✓ Horrendous UI design

  • ✓ Despite the backend can paginate, you can only see the latest 10 topics on index page

  • ✓ Despite the backend has this API, you cannot see user profiles at frontend

  • ✓ Despite the backend has this API, you cannot register at frontend

  • ✓ Despite the backend has this API, you cannot delete posts or topics at frontend

  • ✓ Terrible error messages

  • panic!, unwrap, and expect everywhere

  • ✓ Error handling scattered across front end, backend, on different layers of Result/Option, etc.

  • ✓ Barely any defenses

  • ✓ No checks what so ever in user input, but injection is guarded against

  • ✓ Spaghetti code scattered around like crazy

  • ✓ No documentation, no tests, no examples

  • ✓ Monolith repo

  • ✓ A test page that does no good except to verify my graphql implementation

  • ✓ Stale sessions are not cleaned up regularly

  • ✓ Random crashes if redirection goes too quickly

Design Choices

N+1

N+1 is not purposefully avoided. Joins are used to ensure correctness and access control, but not for performance (yet). See: https://www.sqlite.org/np1queryprob.html.

Access Control

Metadata consistency and access control are ensured on SQL queries instead of at application level. Access control comes in form of 4 views: topic_permissions, topic_public, post_permissions, and post_public.

Invariants

  • Posts are never deleted from database.

  • Post number is never changed.

  • Post metadata is always accessible, but contents can only be viewed as permitted.

These invariants are enforced by the SQL query used to access posts.

Experience Report

Warning
DO NOT IMPLEMENT PASSWORD AUTHENTICATION AND SESSIONS YOURSELF!

The Good

  • Great performance without even trying

    • While I don’t have much web experience, the backend feels exceptionally fast

    • With --release, that is

    • 12MB memory use? Yeah, pretty good.

  • Axum comes with a great collection of middleware

  • async-graphql object definition is relatively easy to use…​ once I got the basics

    • I will continue to use it if I need to write a service to do something instead of to retrieve something. The latter is better done with existing solution, like Hasura

  • The compiler is very good at catching mistakes, if I am actually using types properly

  • Trunk sets up WASM output nicely

The Bad

General
  • I have to keep the frontend/backend router in sync, manually.

    • For every route the SPA uses, I need the backend to serve the index.html

  • Cargo workspace does not work well with mixed targets

  • There are these…​ "Context" paradigm which does something like get_context::<Type>() which I don’t like, because they destroy the point of having a statically type-checked language. And they are everywhere.

Backend
  • Really, we are manually doing monad stack here by using all those Context<'_'>, Extension, Layer, …​ except without the nice do syntax Haskell provides

  • async-graphql doesn’t work very well with Axum middleware

    • Cannot use CookieJar because we cannot return extra arguments

      • Ended up rolling my own implementation to sign cookies

    • Repetition in binding middleware (in Axum and async-graphql)

  • sqlx generics are extremely hard to type check, but I managed to use some anyways

  • sqlx macros do not work well with SQLite, because it type checks SQLite bytecode at compile time. This has some bugs, and is an extremely slow process

Frontend
  • There aren’t any Rust GraphQL clients that work under WASM, so I rolled a simple one in a single file.

  • Trunk’s proxy doesn’t work. It just keeps redirecting until the browser refuses to continue.

  • Took me an enormous amount of time to figure out how to do async in WASM

  • Sycamore doesn’t have very good docs. I hacked around with terrible looking code.

  • Sycamore macros don’t work well with formatting

  • Sycamore’s routing seems a bit limited

  • Cannot figure out how to set status code for Sycamore

  • Fight the borrow checker with loads of .as_ref() and .clone()

  • Wasm is quite large, compared to JS libraries. I have practically all optimization turned to max in this project, and the size is still 327kB/129kB(gzipped). Also, it grows fast.

Conclusion

It started with me trying to make a small, monolith, self-contained forum that can run on extremely resource-limited machines. Then my sanity drained as I went along, and I cut features over and over, until I decide "this is going to be a technological study".

No, I will not continue developing this pile of diamonds. If I really want one in production, I am going for something else.

I do, however, think that this "full-stack" project contains some insights on the current ecosystem of Rust in web development, and some snippets that might be helpful to someone else.

Just don’t deploy it in production.

You might also like...
Rust / Wasm framework for building client web apps
Rust / Wasm framework for building client web apps

Yew Rust / Wasm client web app framework Documentation (stable) | Documentation (latest) | Examples | Changelog | Roadmap | 简体中文文档 | 繁體中文文檔 | ドキュメント A

Build, bundle & ship your Rust WASM application to the web.

Trunk Build, bundle & ship your Rust WASM application to the web. ”Pack your things, we’re going on an adventure!” ~ Ferris Trunk is a WASM web applic

A simple authentication flow using Rust and Actix-web, with a PostgreSQL database and a sveltekit frontend.

Rust-auth-example This repository aims to represent a simple authentication flow using Rust and Actix-web, with a PostgreSQL database and a sveltekit

A Google-like web search engine that provides the user with the most relevant websites in accordance to his/her query, using crawled and indexed textual data and PageRank.
A Google-like web search engine that provides the user with the most relevant websites in accordance to his/her query, using crawled and indexed textual data and PageRank.

Mini Google Course project for the Architecture of Computer Systems course. Overview: Architecture: We are working on multiple components of the web c

Web Application with using Rust(Actix, Diesel and etc)
Web Application with using Rust(Actix, Diesel and etc)

Santa Service App Used technology stack Web Server with using Rust (Actix, Actix-web, Diesel) Data base (Postgres) Console Application (Tokio) Tasks o

Rust HTTP API Template using PostgreSQL, Redis, RabbitMQ, and Hexagonal Architecture

Rust Template HTTP API Rust API Template using PostgreSQL, Redis, RabbitMQ, and Hexagonal Architecture The following template provides a basic structu

An API project using Rust, Actix Web and JWT. *WIP*

Actix-web REST API with JWT (WIP) A simple CRUD backend app using Actix-web, Diesel and JWT Require Rust Stable Postgres Or using Docker How to run Ma

Experiments with Rust CRDTs using Tokio web application framework Axum.

crdt-genome Synopsis Experiments with Rust CRDTs using Tokio web application framework Axum. Background Exploring some ideas of Martin Kleppmann, part

A very basic "clone" of Reddit's r/place written in Rust using warp

r(usty)/place A very basic "clone" of r/place written in Rust using warp. Instead of rendering the image on the client side, the image is encoded as P

Owner
Rongcui Dong
Rongcui Dong
Rust GraphQL server using simple type-only schema

SimpleGQL This library allows a simplified GraphQL schema to be given and will run a server with a backend store (currently only SQLite) and a set of

Daniel Cocks 5 May 10, 2023
A Rust GraphQL system with full support for subscriptions and authentication that works out of the box.

Diana is a GraphQL system for Rust that's designed to work as simply as possible out of the box, without sacrificing configuration ability.

arctic_hen7 36 Dec 19, 2022
A Rust Boilerplate server with GraphQL API, Diesel, PostgreSQL, session authentication and JWT

Canduma rust Graphql A Rust authentication server with GraphQL API, Diesel, PostgreSQL session authentication and JWT This repository contains a Graph

Julien Lenne 738 Dec 28, 2022
Rust server with Axum, GraphQL and SurrealDb

??️ Article on my web Axum server, Async-GraphQl, SurrealDB template Run without any prior setup, DB is in memory: cargo run To use routes other than

null 15 Jun 26, 2023
Diana is a GraphQL system for Rust that's designed to work as simply as possible out of the box

Diana is a GraphQL system for Rust that's designed to work as simply as possible out of the box, without sacrificing configuration ability. Unlike other GraphQL systems, Diana fully supports serverless functions and automatically integrates them with a serverful subscriptions system as needed, and over an authenticated channel. GraphQL subscriptions are stateful, and so have to be run in a serverful way. Diana makes this process as simple as possible.

null 0 Aug 3, 2021
A fast GraphQL engine.

bluejay-rb Warning This project is still very early in its development and should be considered highly unstable and experimental. It is incomplete and

Adam Petro 4 Feb 20, 2023
HTTP Proxy based solution for real-time interception and prioritization of SQL queries.

starproxy ⚠️ starproxy is a prototype: Not currently used in production, but will likely be some day. Table of Contents starproxy Table of Contents Ba

Will Eaton 5 Mar 6, 2023
A simple, modular, and fast framework for writing MEV bots in Rust.

What is Artemis? Artemis is a framework for writing MEV bots in Rust. It's designed to be simple, modular, and fast. At its core, Artemis is architect

Paradigm 1.4k Jun 26, 2023
The simplest build-time framework for writing web apps with html templates and typescript

Encoped A build-time fast af tool to write static apps with html and TypeScript Features Template-based ESLint, Prettier and Rollup integration No ext

null 1 Dec 11, 2021
Silkenweb - A library for writing reactive single page web apps

Silkenweb A library for building reactive single page web apps. Features Fine grained reactivity using signals to minimize DOM API calls No VDOM. Call

null 85 Dec 26, 2022