Skip to main content

Ctx

Struct Ctx 

Source
pub struct Ctx { /* private fields */ }
Expand description

Per-request application context: the Worker environment with typed access to its bindings.

serve builds one per request and passes it to the router as axum state, so handlers take State(ctx): State<Ctx>. Queue consumers, cron runs and inbound email handlers receive one too. Cloning is cheap (the environment is a JavaScript handle) and the type is Send, so it can be held across .await in plain axum handlers.

Creating a Ctx costs nothing on the free plan; each binding call (db, ocre::cache, ocre::storage…) only looks the binding up.

A Ctx and its clones share two per-request memories, like Rails’ query cache and local cache: identical SELECTs through db are answered from memory until a write (see Db), and ocre::cache reads each KV key at most once. Each request, queued job and cron run gets its own.

§Examples

use axum::{Router, extract::State, routing::get};
use ocre::{ApiResult, Ctx};

async fn count(State(ctx): State<Ctx>) -> ApiResult<String> {
    let db = ctx.db()?;
    let rows: Vec<serde_json::Value> = db.all("SELECT COUNT(*) AS n FROM posts", ocre::params![]).await?;
    Ok(rows[0]["n"].to_string())
}

fn routes() -> Router<Ctx> {
    Router::new().route("/count", get(count))
}

Implementations§

Source§

impl Ctx

Source

pub fn log(&self) -> &Logger

The logger of this request, job batch or cron run (see ocre::log).

In a request its lines carry request_id, method and path, the same request id as the RequestId extractor and the X-Request-Id response header. Add fields with Logger::with. No binding call; each line is one Workers Logs event.

§Examples
use axum::extract::State;
use ocre::Ctx;

async fn import(State(ctx): State<Ctx>) -> &'static str {
    let log = ctx.log().with("import_id", 12);
    log.info("import started");
    log.warn(format_args!("{} rows skipped", 3));
    "OK"
}
Source

pub fn errors(&self) -> &Reporter

The error reporter of this request, job batch or cron run (see ocre::errors).

Reports are logged at once and sent to the registered subscribers when the response is ready (or the job or cron run ends).

§Examples
use axum::extract::State;
use ocre::{Ctx, Result};

async fn refresh(State(ctx): State<Ctx>) -> Result<&'static str> {
    ctx.errors().set_context("feed", "rss");
    let body: Option<String> = ctx.errors().handle("<rss/>".parse::<String>());
    Ok(if body.is_some() { "refreshed" } else { "kept the old feed" })
}
Source

pub fn events(&self) -> &Events

Structured events of this request or job (Rails’ Rails.event), see ocre::events.

§Examples
use axum::extract::State;
use ocre::Ctx;

async fn signup(State(ctx): State<Ctx>) -> &'static str {
    ctx.events().notify("user.signed_up", serde_json::json!({ "plan": "free" }));
    "welcome"
}
Source

pub fn config<T: DeserializeOwned>(&self) -> Result<T>

The app’s settings, read from Worker variables and secrets into T (see ocre::config).

Each field reads the variable of the same name in upper case, or else the secret. No binding call: one environment lookup per field.

§Errors

Error::Internal (500, logged) naming the variable when a required one is missing or does not convert to its field’s type.

§Examples
use axum::extract::State;
use ocre::{Ctx, Result};
use serde::Deserialize;

#[derive(Deserialize)]
struct Settings {
    support_email: String,
    max_uploads: Option<u32>,
}

async fn contact(State(ctx): State<Ctx>) -> Result<String> {
    let settings: Settings = ctx.config()?;
    Ok(settings.support_email)
}
Source

pub fn env(&self) -> &Env

The raw Workers environment, for bindings Ocre does not wrap yet.

Use it for vars, secrets and bindings such as AI or Vectorize; prefer the Ocre helpers when one exists, since their errors name the cloudflare.config.ts fix.

§Examples
use axum::extract::State;
use ocre::{Ctx, Result};

async fn app_name(State(ctx): State<Ctx>) -> Result<String> {
    let name = ctx.env().var("APP_NAME")?;
    Ok(name.to_string())
}
Source

pub fn secret( &self, name: &str, ) -> impl Future<Output = Result<String>> + Send + use<>

The secret name, wherever it is kept: a Worker secret (ocre secrets push), a .dev.vars value in ocre dev, or a secret of the account’s Secrets Store bound to the Worker in cloudflare.config.ts (NAME: bindings.secretsStoreSecret({ storeId, secretName }), which ocre secrets push NAME --store writes). Code reads it the same way wherever it lives, so moving a secret to the store changes no code.

A Worker secret is read without I/O; a Secrets Store secret costs one call to the store. Secrets Ocre reads without awaiting (SECRET_KEY_BASE, R2_*) must stay Worker secrets.

§Errors

Error::Internal (500, logged) when the secret is missing or empty, naming where to set it.

§Examples
use axum::extract::State;
use ocre::{Ctx, Result};

async fn rates(State(ctx): State<Ctx>) -> Result<String> {
    let key = ctx.secret("RATES_API_KEY").await?;
    Ok(format!("{} characters", key.len()))
}
Source

pub fn db(&self) -> Result<Db>

The application database: the D1 binding DB.

Looking the binding up runs no query and costs no D1 rows; see Db for what each query reads and writes.

§Errors

Error::Internal (500, logged) when the Worker has no DB binding; the message says to add DB: bindings.d1({ name: "<app>" }) to cloudflare.config.ts.

§Examples
use axum::extract::State;
use ocre::{Ctx, Result};

async fn handler(State(ctx): State<Ctx>) -> Result<String> {
    let db = ctx.db()?;
    let changed = db.execute("DELETE FROM sessions WHERE expires_at < ?1", ocre::params![ocre::now()]).await?;
    Ok(format!("{changed} expired"))
}
Source

pub fn db_named(&self, binding: &str) -> Result<Db>

Another D1 database of the app, by its binding name (Rails’ multiple databases).

Each database is a KEY: bindings.d1({ name: "..." }) entry in cloudflare.config.ts with its own binding key and database name (see the Models guide, “Several databases”). Queries cannot join across databases: load ids from one, then find_many in the other.

§Free plan

Up to 10 databases per account, 5 GB of storage and the daily row quotas shared by all of them.

§Errors

Error::Internal (500, logged) when the Worker has no D1 binding named binding; the message names the cloudflare.config.ts entry to add.

§Examples
use axum::extract::State;
use ocre::{Ctx, Result, params};

async fn track(State(ctx): State<Ctx>) -> Result<()> {
    let analytics = ctx.db_named("ANALYTICS")?;
    analytics.execute("INSERT INTO page_views (path) VALUES (?1)", params!["/"]).await?;
    Ok(())
}

Trait Implementations§

Source§

impl Clone for Ctx

Source§

fn clone(&self) -> Ctx

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl Freeze for Ctx

§

impl RefUnwindSafe for Ctx

§

impl Send for Ctx

§

impl Sync for Ctx

§

impl Unpin for Ctx

§

impl UnsafeUnpin for Ctx

§

impl UnwindSafe for Ctx

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V