rust: a first connection to postgres

·

2 min read

The first example of code I wrote is a basic connection to postgres. It is a nice exercise because it learns you how to interact with a config file, to handle errors, to import crates and to organize the file structure.

Here is my current structure:

.
├── .env
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── Settings.toml
├── docker-compose.yml
└── src
   ├── backend
   │   ├── mod.rs
   │   └── postgres.rs
   └── main.rs

docker-compose.yml: it only contains my postgres configuration

The first step of this project was to import some crates and to search and find crates corresponding to my needs. For that, I went to crates.io. This is the repository for all rust packages.

Cargo.toml:

[package]
name = "note-sh"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
postgres = "0.19.1"
config = "0.11.0"

Currently, I have only two dependencies:

  • postgres: a synchronous client for the PostgreSQL database (I will see later to use the asynchronous client tokio-postgres).
  • config: layered configuration system for Rust applications

My file looks like this:

use config::{Config,File};

mod backend;

fn main() {
    let mut settings = Config::default();
    // reads the config file called `Settings.toml`
    settings.merge(File::with_name("Settings")).unwrap();  

    // makes the connection to the database
    let conn = backend::postgres::connect(settings.get_str("postgres_url").unwrap().as_str());

    // checks connection & error
    match conn {
       Ok(..) => println!("connected to the databse"),
       Err(error) => panic!("{}", error),
    };
}

For this little example, I wanted to understand how to call external file in main.rs.

If we zoom in the src/:

.
├── backend
│   ├── mod.rs
│   └── postgres.rs
└── main.rs

mod.rs is a specific file used to define which parts of the code must be public (pub) or not. see rust by example and Clear explanation of Rust's module system.

The backend/postgres.rs is my simplest file at the moment since it only makes the connection and returns a postgres client.

use postgres::{Client, Error, NoTls};

// postgres structure that contains the client
pub struct Postgres {
    // postgres client
    client: Client,
}

// makes the connection to the postgres database with the given url
pub fn connect(url: &str) -> Result<Postgres, Error> {
    let client = Client::connect(url, NoTls)?;

    Ok(Postgres { client: client })
}

with its backend/mod.rs:

// export postgres file
pub mod postgres;

And finally:

$ cargo run
   Compiling note-sh v0.1.0 (/Users/myuser/workspace/src/github.com/fberrez/note-sh)
    Finished dev [unoptimized + debuginfo] target(s) in 7.86s
     Running `target/debug/note-sh test`
connected to the databse