Skip to content

Repository files navigation

HR Zen — Backend (Rails API)

Rails 7.2 API-only backend for HR Zen. PostgreSQL 16, Devise + JWT, Pundit, acts_as_tenant multi-tenancy, Solid Queue/Cache/Cable.

Screenshots

Screenshot 2026-09-29 at 7 14 35 AM Screenshot 2026-09-29 at 7 15 40 AM Screenshot 2026-09-29 at 7 16 05 AM Screenshot 2026-09-29 at 7 16 10 AM Screenshot 2026-09-29 at 7 16 15 AM Screenshot 2026-09-29 at 7 16 18 AM Screenshot 2026-09-29 at 7 16 25 AM Screenshot 2026-09-29 at 7 16 29 AM Screenshot 2026-09-29 at 7 17 10 AM Screenshot 2026-09-29 at 7 17 15 AM Screenshot 2026-09-29 at 7 17 19 AM Screenshot 2026-09-29 at 7 17 23 AM Screenshot 2026-09-29 at 7 17 45 AM Screenshot 2026-09-29 at 7 17 49 AM Screenshot 2026-09-29 at 7 17 54 AM Screenshot 2026-09-29 at 7 17 57 AM Screenshot 2026-09-29 at 7 18 12 AM Screenshot 2026-09-29 at 7 18 17 AM Screenshot 2026-09-29 at 7 18 21 AM Screenshot 2026-09-29 at 7 18 25 AM Screenshot 2026-09-29 at 7 18 28 AM Screenshot 2026-09-29 at 7 18 31 AM Screenshot 2026-09-29 at 7 18 34 AM Screenshot 2026-09-29 at 7 18 37 AM Screenshot 2026-09-29 at 7 18 44 AM Screenshot 2026-09-29 at 7 18 52 AM Screenshot 2026-09-29 at 7 18 56 AM Screenshot 2026-09-29 at 7 19 28 AM Screenshot 2026-09-29 at 7 19 34 AM

Endpoints

Verb Path Auth Purpose
POST /api/v1/sign_up public Create organization + owner user
POST /api/v1/sign_in public Authenticate, returns user + org + JWT
DELETE /api/v1/sign_out public No-op (stateless JWT)
GET /api/v1/users/me Bearer JWT Current user + organization

All responses use the envelope:

{ "success": true, "data": { }, "meta": { "message": "..." } }
{ "success": false, "error": "...", "details": { "field": ["msg"] } }

Sign in

curl -X POST http://localhost:3001/api/v1/sign_in \
  -H 'Content-Type: application/json' \
  -d '{"user":{"email":"owner@acme.test","password":"password123"}}'

Returns data.token (raw JWT). Send it back as Authorization: Bearer <token>.

Request flow

request → Api::V1::BaseController (Secured + JsonRenderer + RescueExceptions + Pundit)
        → controller action → Api::V1::*Service → serializer → render_jsonapi
  • Controllers stay thin; business logic lives in app/services/api/v1/.
  • Errors: Api::Error::* mapped to status codes in RescueExceptions.
  • SetupWorkspaceJob (Solid Queue, :priority) provisions the Auth Matrix after sign-up.

Auth notes

  • devise-jwt with the Null revocation strategy (stateless; client discards the token on sign-out).
  • devise_for :users, skip: :all registers the mapping (needed for authenticate_user! / current_user) without exposing Devise HTTP routes.
  • Interim: config.allow_unconfirmed_access_for = nil so unconfirmed users can authenticate until the confirmation email flow is wired up.

Local (Docker)

docker compose up -d --build      # app: http://localhost:3001, postgres: 5433
docker compose exec app bin/rails c

Rebuild after Gemfile changes so the bundler volume picks up new gems.

Local (no Docker app)

docker compose up -d db
bin/rails db:prepare
bin/rails s -p 3001
bin/jobs            # Solid Queue worker

Env in .env (DB_URL, CORS_ORIGINS, MAILER_SENDER, DEVISE_JWT_SECRET_KEY).

Quality

bundle exec rspec
bundle exec rubocop
bin/rails zeitwerk:check

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages