JE Joseph Edward Lagos, Nigeria

Type to filter. Use the up and down arrows to move between results and Enter to open the highlighted one.

SpendWise

Personal finance app

Role
Full-stack
Year
2026
Status
in development

Expense tracking and budgeting as a Django REST API with an Expo client. It handles recurring transactions, splits group expenses between people, and settles up the balances that fall out of it.

  • JWT auth with rotating, blacklisted refresh tokens
  • Budgets, recurring transactions and spending analysis
  • Group expenses with flexible splitting and settlements
  • Financial reports and data export

A Django REST backend paired with an Expo client, with the auth flow designed so the app can reach its home screen without a second round trip after registering or signing in.

  • Django
  • DRF
  • PostgreSQL
  • Expo
  • React Native
  • TypeScript
Screen recording

How it is built

A Django REST backend paired with an Expo client, with the auth flow designed so the app can reach its home screen without a second round trip after registering or signing in.

Stack

  • Django 4.2 backend
  • DRF 3.16 backend
  • Simple JWT backend
  • django-filter backend
  • django-environ backend
  • Expo 54 client
  • React Native 0.81 client
  • React Query client
  • React Hook Form client
  • Zod client

Architecture

  • Expo client client
    • Expo Router
    • React Query
    • React Hook Form
    • Zod
  • Django REST API service
    • DRF
    • Simple JWT
    • django-filter

Flow

  • Expo client Django REST API HTTPS, bearer token

API

REST

  • Register and login both return the token pair plus the user, so the client can skip a follow-up profile request
  • POST /api/register/ Returns user plus token pair
  • POST /api/token/ Login, returns token pair plus full profile
  • POST /api/token/refresh/ Rotates the refresh token
  • POST /api/token/logout/ Blacklists the refresh token
  • GET /api/users/me/ Current user profile

Authentication

JSON Web Tokens

  • Bearer tokens via Simple JWT rather than DRF's plain token auth
  • Refresh tokens rotate on every use and the spent one is blacklisted
  • A stolen refresh token is good for a single refresh before it is dead

Notable decisions

  • Rotate and blacklist refresh tokens on every use

    Caps the damage from a stolen refresh token at one refresh

  • Return the user object alongside the token pair

    Removes a round trip on app startup and after registration