Skip to content

CLoaKY233/Yoink

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

URL Shortener (Axum + SurrealDB)

A minimal URL shortener written in Rust using Axum for HTTP and SurrealDB as the primary data store with namespaced, document-graph records.

What this is

  • 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.

How it works

  • 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 model

  • 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.

Project layout

  • 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.

API endpoints

  • 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.

Environment variables

  • 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.

Quick start (local)

  1. 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
  1. Ensure your SurrealDB instance is available and reachable over ws/wss (Cloud or self-hosted).

  2. Build and run:

cargo run --release
  1. 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"}'
  1. Use the redirect:
curl -i http://localhost:3000/example
  1. Fetch stats:
curl http://localhost:3000/api/stats/example

SurrealDB connection details

  • 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.

Data operations used

  • 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.

Validation and IDs

  • 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.

Operational notes

  • 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.

Running on Ubuntu

  • 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.

Testing checklist

  • 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.

Why SurrealDB here

  • 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.

Troubleshooting

  • 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.

About

Simple proof of concept URL Shortener using Rust-lang

Topics

Resources

Stars

Watchers

Forks

Languages