{"id":50607579,"url":"https://github.com/peterkyle01/rust-neon-template","last_synced_at":"2026-06-06T00:30:30.782Z","repository":{"id":361610585,"uuid":"1252332339","full_name":"peterkyle01/rust-neon-template","owner":"peterkyle01","description":"A production-ready Rust API template built with Axum and Neon (serverless Postgres with built-in Auth and Data APIs).","archived":false,"fork":false,"pushed_at":"2026-05-31T12:33:33.000Z","size":28,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T14:16:34.835Z","etag":null,"topics":["axum","neon-auth","neon-data-api","neondb","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/peterkyle01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T12:16:38.000Z","updated_at":"2026-05-31T12:33:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/peterkyle01/rust-neon-template","commit_stats":null,"previous_names":["peterkyle01/rust-neon-template"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/peterkyle01/rust-neon-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterkyle01%2Frust-neon-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterkyle01%2Frust-neon-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterkyle01%2Frust-neon-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterkyle01%2Frust-neon-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peterkyle01","download_url":"https://codeload.github.com/peterkyle01/rust-neon-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterkyle01%2Frust-neon-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33965591,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["axum","neon-auth","neon-data-api","neondb","rust"],"created_at":"2026-06-06T00:30:29.376Z","updated_at":"2026-06-06T00:30:30.775Z","avatar_url":"https://github.com/peterkyle01.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rust Neon Template\n\nA Rust API starter template built with [Axum](https://github.com/tokio-rs/axum) and [Neon](https://neon.tech) (serverless Postgres with built-in Auth and Data APIs).\n\n\u003e **⚠️ Not production-ready out of the box.** This starter template is intended\n\u003e to accelerate development. Before deploying to production, review authentication,\n\u003e authorization, rate limiting, input validation, error handling, secrets management,\n\u003e and security hardening for your specific use case.\n\n## Features\n\n- **Axum web framework** – fast, ergonomic, and async-first\n- **Neon Auth** – sign-up, sign-in, and sign-out via the Neon Auth API\n- **Neon Data API CRUD** – generic `create`/`get_all`/`get_one`/`update`/`delete` on `NeonClient` — works for any table\n- **`NeonClient` extractor** – automatically pulls the JWT from `Authorization: Bearer` via Axum's `FromRequestParts`\n- **Auto-generated types** – `utility-types` reduces boilerplate (e.g. `SignInRequest` derived from `SignUpRequest`)\n- **Standard API envelope** – all responses follow `{ \"data\": ... }` / `{ \"error\": { \"code\": \"...\", \"message\": \"...\" } }`\n- **Smart health checks** – verifies both Auth and Data API endpoints are reachable\n- **Level-based logging** – `INFO` for 2xx, `WARN` for 4xx, `ERROR` for 5xx\n- **Comprehensive tests** – integration tests covering the full CRUD flow and error scenarios\n- **No system OpenSSL** – uses `rustls` for TLS\n\n## How it works\n\n```\nClient                    Axum Server                      Neon Auth                  Neon Data API\n  │                          │                                │                           │\n  ├─ POST /api/v1/auth/sign-in                                │                           │\n  │                          ├─ POST /sign-in/email ─────────►│                           │\n  │                          │◄── session token + cookie ─────┤                           │\n  │                          ├─ GET /get-session (cookie) ───►│                           │\n  │                          │◄── set-auth-jwt: \u003cJWT\u003e ───────┤                           │\n  │◄─ { \"data\": { \"token\": \u003cJWT\u003e } }                          │                           │\n  │                          │                                │                           │\n  ├─ GET /api/v1/notes (Bearer JWT)                           │                           │\n  │                          ├─ GET /notes (Bearer JWT) ──────┼───────────────►───────────┤\n  │◄─ { \"data\": [...] } ─────┤◄──────────────────────────────┼───────────[...]──────────┤\n```\n\n## Project Structure\n\n```\nsrc/\n├── lib.rs            # Library root — routes(), TraceLayer, LogOnResponse\n├── main.rs           # Binary entry point — calls into lib\n├── response.rs       # Standard API envelope (AppError, ok(), created())\n├── config/\n│   ├── mod.rs        # Config struct (environment settings)\n│   └── client.rs     # NeonClient (struct + impl + FromRequestParts)\n│                     # + auth types (SignUpRequest, SignInRequest, Session)\n└── handlers/\n    ├── mod.rs\n    ├── auth.rs       # Handler functions (sign_up, sign_in, sign_out)\n    ├── notes.rs      # Note model + handler functions (create, list, get, update, delete)\n    └── health.rs     # Health check with component-level status\ntests/\n└── api.rs            # Full integration tests (4 tests, no warnings)\n```\n\n## Prerequisites\n\n- [Rust](https://www.rust-lang.org/tools/install) 1.85+ (edition 2024)\n- A [Neon](https://neon.tech) project with **Auth** and **Data API** enabled\n\n## Getting Started\n\n### 1. Set environment variables\n\nCreate a `.env` file in the project root:\n\n```env\nAUTH_URL=https://\u003cyour-project\u003e.neonauth.\u003cregion\u003e.aws.neon.tech/neondb/auth\nDATA_API_URL=https://\u003cyour-project\u003e.apirest.\u003cregion\u003e.aws.neon.tech/neondb/rest/v1\nPORT=8080\nHOST=0.0.0.0\n```\n\n| Variable        | Description                                    | Required |\n|-----------------|------------------------------------------------|----------|\n| `AUTH_URL`      | Your Neon Auth URL (from Console → Auth)       | Yes      |\n| `DATA_API_URL`  | Your Data API URL (from Console → Data API)    | Yes      |\n| `PORT`          | Port the server listens on (default `8080`)    | No       |\n| `HOST`          | Host the server binds to (default `0.0.0.0`)   | No       |\n\n### 2. Create the notes table\n\nRun this SQL in your Neon console's SQL editor:\n\n```sql\nCREATE TABLE notes (\n    id      SERIAL PRIMARY KEY,\n    title   TEXT NOT NULL,\n    content TEXT NOT NULL DEFAULT ''\n);\n\n-- Disable RLS so the Data API can read/write freely.\nALTER TABLE notes DISABLE ROW LEVEL SECURITY;\n```\n\n### 3. Run the server\n\n```bash\ncargo run\n```\n\nEvery request is logged with level-appropriate detail:\n\n```\n INFO listening on 0.0.0.0:8080\n INFO request{method=GET uri=/health}: ok status=200 latency_ms=1310\n INFO request{method=POST uri=/api/v1/auth/sign-in}: ok status=200 latency_ms=937\n WARN request{method=GET uri=/api/v1/notes/9999}: client error status=404 latency_ms=599\n WARN request{method=GET uri=/api/v1/notes}: client error status=401 latency_ms=0\n```\n\n### 4. Run the tests\n\n```bash\ncargo test\n```\n\nAll 4 integration tests pass, covering health, auth errors, and the full CRUD lifecycle.\n\n## API Response Format\n\nEvery endpoint returns one of two shapes:\n\n**Success (2xx):**\n```json\n{ \"data\": \u003cpayload\u003e }\n```\n\n**Error (4xx/5xx):**\n```json\n{ \"error\": { \"code\": \"NOT_FOUND\", \"message\": \"note 9999 not found\" } }\n```\n\n**Error codes:**\n\n| Code            | HTTP Status | When                      |\n|-----------------|-------------|---------------------------|\n| `BAD_REQUEST`   | 400         | Invalid input             |\n| `UNAUTHORIZED`  | 401         | Missing/wrong credentials |\n| `NOT_FOUND`     | 404         | Resource doesn't exist    |\n| `INTERNAL_ERROR`| 500         | Server error              |\n\n## API Endpoints\n\n### Health\n\n```bash\ncurl http://localhost:8080/health\n# {\"data\":{\"status\":\"ok\",\"checks\":{\"auth\":\"ok\",\"data_api\":\"ok\"}}}\n```\n\n### Auth\n\nAll auth endpoints are nested under `/api/v1/auth`.\n\n```bash\n# Sign up\ncurl -X POST http://localhost:8080/api/v1/auth/sign-up \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"email\": \"alice@example.com\", \"name\": \"Alice\", \"password\": \"s3cret\"}'\n\n# Sign in — returns a JWT\nTOKEN=$(curl -s -X POST http://localhost:8080/api/v1/auth/sign-in \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"email\": \"alice@example.com\", \"password\": \"s3cret\"}' | jq -r '.data.token')\n\n# Sign out\ncurl -X POST http://localhost:8080/api/v1/auth/sign-out \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\n### Notes CRUD\n\nAll notes endpoints require `Authorization: Bearer \u003ctoken\u003e`.\n\n```bash\n# Create\ncurl -X POST http://localhost:8080/api/v1/notes \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\": \"Hello\", \"content\": \"Neon!\"}'\n\n# List\ncurl http://localhost:8080/api/v1/notes \\\n  -H \"Authorization: Bearer $TOKEN\"\n\n# Get by ID\ncurl http://localhost:8080/api/v1/notes/1 \\\n  -H \"Authorization: Bearer $TOKEN\"\n\n# Update\ncurl -X PATCH http://localhost:8080/api/v1/notes/1 \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\": \"Updated\", \"content\": \"Edited!\"}'\n\n# Delete\ncurl -X DELETE http://localhost:8080/api/v1/notes/1 \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\nStandard response examples:\n\n```json\n// GET /notes (list)\n{ \"data\": [{ \"id\": 1, \"title\": \"Hello\", \"content\": \"Neon!\" }] }\n\n// GET /notes/1 (single)\n{ \"data\": { \"id\": 1, \"title\": \"Hello\", \"content\": \"Neon!\" } }\n\n// POST /notes (create — 201)\n{ \"data\": { \"id\": 2, \"title\": \"New\", \"content\": \"Note\" } }\n\n// PATCH /notes/1 (update)\n{ \"data\": [{ \"id\": 1, \"title\": \"Updated\", \"content\": \"Yes\" }] }\n\n// DELETE /notes/1\n{ \"data\": { \"message\": \"deleted\" } }\n\n// 404 — non-existent note\n{ \"error\": { \"code\": \"NOT_FOUND\", \"message\": \"note 9999 not found\" } }\n\n// 401 — missing auth\n{ \"error\": { \"code\": \"UNAUTHORIZED\", \"message\": \"missing or invalid Authorization header\" } }\n```\n\n## NeonClient\n\n[`NeonClient`](src/config/client.rs) is the shared HTTP client for both the Neon Auth API and the Neon Data API. It handles the full token lifecycle:\n\n1. **Sign-in / Sign-up** – calls the Better Auth REST API, extracts the session cookie\n2. **JWT exchange** – calls `/get-session` with the cookie, extracts the JWT from the `set-auth-jwt` response header\n3. **Data API calls** – uses the JWT as `Authorization: Bearer` for all CRUD operations\n\nIn handlers, `client: NeonClient` is extracted directly from the request — the `FromRequestParts` implementation pulls the `Authorization: Bearer` header automatically.\n\n### Available methods\n\n| Category | Method                                      | Description                        |\n|----------|---------------------------------------------|------------------------------------|\n| Auth     | `sign_up`                                   | Register a new user                |\n| Auth     | `sign_in`                                   | Sign in an existing user           |\n| Auth     | `get_session`                               | Refresh / validate the session     |\n| Auth     | `sign_out`                                  | Sign out and clear the token       |\n| Data API | `get_all::\u003cT\u003e(resource)`                    | List all records of a resource     |\n| Data API | `get_one::\u003cT\u003e(resource, id)`                | Get a single record by ID          |\n| Data API | `create::\u003cT\u003e(resource, body)`               | Create a record                    |\n| Data API | `update::\u003cT\u003e(resource, id, body)`           | Update a record                    |\n| Data API | `delete(resource, id) -\u003e bool`              | Delete a record (returns false if missing) |\n\nThe generic CRUD methods work with any Neon Data API table — just pass the resource name (e.g. `\"notes\"`, `\"users\"`) and the return type `T`.\n\n## Adding a new resource\n\n1. **Create the model** — define your struct with `#[derive(Serialize, Deserialize)]` in a new handler file (e.g. `handlers/items.rs`)\n2. **Write handlers** — use `client: NeonClient` with the generic CRUD methods; return `response::ok()` or `response::created()`\n3. **Wire routes** — add the paths to `pub fn routes()` in `src/lib.rs`\n\n## Development\n\n```bash\ncargo check       # Check for compilation errors (fast)\ncargo build       # Build the project\ncargo run         # Run the server\ncargo test        # Run 4 integration tests\ncargo fmt         # Format code\ncargo clippy      # Lint\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterkyle01%2Frust-neon-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeterkyle01%2Frust-neon-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterkyle01%2Frust-neon-template/lists"}