Yes, I know that this is a borderline absurd web stack for the ubiquitous TODO application but I had a lot of trouble getting this all to work. I started using these things for a more ambitious project and I'd love to spare you the trouble. So here's some basic boilerplate to get you up and running.
Here's what does what:
Component | Tool/lib |
---|---|
Web server | actix-web |
Database | PostgreSQL |
SQL engine | Diesel |
GraphQL library | Juniper |
GraphQL UI | GraphQL Playground |
Before you get started, make sure that you have PostgreSQL, Rust, Cargo, and the Diesel CLI installed and that you have Postgres running somewhere.
# Fetch the repo
git clone https://github.com/lucperkins/rust-actix-diesel-postgres-juniper
cd rust-actix-diesel-postgres-juniper
# If you would like to run the postgres server in docker
docker compose up
# Set up the database
cp .env.example .env # Modify this file to match your Postgres installation
diesel setup
diesel migration run
cargo run # could take a while!
The
DATABASE_URL
can be any Postgres installation. For my purposes, I have it set topostgres://localhost:5432/todos
.
Once the server is running, you can access the GraphQL Playground UI at http://localhost:4000/graphql.
The server implements the following GraphQL schema:
type Todo {
id: ID!
task: String!
done: Boolean!
}
input CreateTodoInput {
task: String!
done: Boolean
}
type Query {
allTodos: [Todo!]!
getTodoById(id: Int): Todo
}
type Mutation {
createTodo(input: CreateTodoInput): Todo
markTodoAsDone(id: Int): Todo
markTodoAsNotDone(id: Int): Todo
}
schema {
Query
Mutation
}
File | What it provides |
---|---|
context.rs |
The GraphQL context that handles query execution |
data.rs |
A Todos struct and some helper functions encapsulate the Diesel-powered Postgres querying logic |
db.rs |
The connection pool that handles the Postgres connection |
endpoints.rs |
The /graphql HTTP endpoint that makes GraphQL and the GraphQL Playground work |
graphql.rs |
The Query , Mutation , and Schema objects that undergird the GraphQL interface |
lib.rs |
Just the standard lib.rs |
main.rs |
Actix HTTP server setup |
models.rs |
All of the data types used for querying Postgres and providing GraphQL results |
schema.rs |
The Diesel-generated table schema |
Get it? Anyway, here's some areas for improvement (pull requests very much welcome):
- Error handling — Right now errors basically propagate directly from Diesel/Postgres into the GraphQL JSON output, which is subpar. If any of you can point me to good educational resources on this, please file an issue!
- Better execution engine — The server uses the extremely powerful actix-web but the actual DB interactions don't use Actix actors and it'd take this setup to the next level if they did.
- Use macros for schema generation — The powerful
juniper_from_schema
macro could help reduce boilerplate and improve development velocity.
I'm basically a beginner with Rust and would not have been able to put this together without peeking long and hard at the example projects and blog posts listed below. The lower-level bits you see here are basically stolen from BrendanBall. All that I've added is the Todos
data construct for executing queries.