A minimal URL shortener written in Rust using Axum for HTTP and SurrealDB as the primary data store with namespaced, document-graph records.
- A small web service that shortens long URLs and tracks usage metrics like click count and last access time.
- The service exposes three primary endpoints: create a short URL, redirect by ID, and fetch per-link statistics.
- SurrealDB is used as the database, taking advantage of namespaces and databases to organize application data cleanly.
- The service is built with Axum and runs an HTTP server that mounts routes for creation, redirection, and stats retrieval.
- Records are stored in a urls table with fields: short_id, original_url, click_count, created_at, and last_accessed.
- Creation inserts a record with a stable ID using SurrealDB’s create(...).content(...) API.
- Redirect increments click_count and updates last_accessed via update(...).content(...).
- Stats retrieval selects the record by ID and returns a typed JSON response.
- SurrealDB organizes data into namespaces (NS) and databases (DB), which is helpful for multi-tenant or environment separation.
- The application connects to SurrealDB over ws/wss, authenticates, selects a namespace and database, and then performs CRUD operations.
- Authentication commonly uses Root credentials at bootstrap before you define scoped users for namespace/database access.
- src/handlers: Request handlers for creating, redirecting, and reading stats.
- src/database: Connection and initialization logic for SurrealDB.
- src/models: Typed request/response DTOs and the UrlRecord model.
- src/utils: Utilities like short ID generation using nanoid.
- POST /: Create a short URL from a JSON body containing url and optional custom_id.
- GET /{id}: Redirect to the original URL and increment click_count.
- GET /api/stats/{id}: Return stats including click_count, created_at, and last_accessed.
- DATABASE_URL: SurrealDB endpoint, e.g. wss://<INSTANCE_ENDPOINT>.[6]
- SURREAL_NS and SURREAL_DB: Namespace and database to select via use_ns(...).use_db(...).
- SURREAL_USERNAME and SURREAL_PASSWORD: Credentials used with Root signin for initial access.
- SERVER_ADDRESS: Address to bind the Axum server, e.g. 0.0.0.0:3000.
- BASE_URL: The public base used to render short URLs, e.g. https://your-domain.tld.
- Prepare a .env file:
DATABASE_URL=wss://<INSTANCE_ENDPOINT>
SURREAL_NS=app
SURREAL_DB=prod
SURREAL_USERNAME=root
SURREAL_PASSWORD=secret
SERVER_ADDRESS=0.0.0.0:3000
BASE_URL=http://localhost:3000
-
Ensure your SurrealDB instance is available and reachable over ws/wss (Cloud or self-hosted).
-
Build and run:
cargo run --release
- Create a short link:
curl -X POST http://localhost:3000 \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com/some/very/long/path","custom_id":"example"}'
- Use the redirect:
curl -i http://localhost:3000/example
- Fetch stats:
curl http://localhost:3000/api/stats/example
- Connect using the Rust SDK’s any::connect(...), then authenticate and select your NS/DB.
- Typical sequence: connect → signin(Root { username, password }) → use_ns(...).use_db(...).
- The USE statement defines the active namespace and database context for subsequent operations.
- Create: db.create(("urls", &id)).content(url_record) creates a record with a specific ID.
- Update: db.update(("urls", &id)).content(record) replaces the record, used to increment click_count and set last_accessed.
- Select: db.select(("urls", &id)) reads a single record by table and ID.
- Short IDs are generated with nanoid by default; clients may supply custom_id with length checks.
- Original URLs are validated with the url crate to ensure proper formatting before persistence.
- Namespaces and databases let you split environments (dev/staging/prod) within one SurrealDB instance.
- For SurrealDB Cloud, use your wss endpoint and Root/system credentials to bootstrap access.
- When moving beyond bootstrap, define scoped users at the namespace or database level per least-privilege best practices.
- The service builds and runs reliably on Ubuntu with a standard Rust toolchain and Cargo.
- Ensure outbound connectivity to your SurrealDB endpoint (Cloud or self-hosted) and that credentials are set in .env.
- Health: GET /health returns a simple JSON indicating the service is healthy.
- Create → Redirect → Stats flow: Verify click_count increments after GET /{id}.
- Namespace/DB selection: Confirm SURREAL_NS and SURREAL_DB are correct for the target environment.
- It combines document flexibility with graph relationships and real-time capabilities, while keeping simple CRUD ergonomic in Rust.
- The Rust SDK offers first-class methods for connect, signin, use_ns/use_db, create, select, and update with a straightforward API surface.
- Authentication errors: Re-check SURREAL_USERNAME/SURREAL_PASSWORD and that Root or the scoped user exists.
- NS/DB issues: Confirm they exist and are selected via use_ns(...).use_db(...), and that your user has access.
- Connectivity: Ensure the DATABASE_URL uses the correct ws/wss scheme and endpoint for your deployment.