npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@roarexclamation/mobile-base

v0.1.0

Published

Reusable base library for React Native apps using Supabase with domain-driven repositories and services.

Readme

@roar/mobile-base

Reusable base library for React Native apps using Supabase with domain-driven repositories and services. It helps you:

  • Configure a single Supabase client for your app
  • Implement typed repositories and services per domain
  • Use generic CRUD that covers 90% of cases
  • Keep features consistent across apps and update from one place

Install

Add as a dependency in each app (recommended via workspace or package registry):

# if using npm workspaces or local path
npm install @roar/mobile-base --save

# or add via Git URL until published
npm install github:roar/mobile-base

This package expects @supabase/supabase-js v2 as a peer dependency.

Quick start

  1. Configure Supabase (once at app startup):
import { configureSupabase } from "@roar/mobile-base";

configureSupabase({
  url: process.env.EXPO_PUBLIC_SUPABASE_URL!,
  anonKey: process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
});
  1. Create a typed repository and service for a domain (Users example):
// users.repository.ts
import { BaseRepository } from "@roar/mobile-base";

type UserRow = {
  id: string;
  email: string;
  full_name: string | null;
  created_at: string;
};
interface UserInsert {
  email: string;
  full_name?: string | null;
}
interface UserUpdate {
  full_name?: string | null;
}

export class UsersRepository extends BaseRepository<
  UserRow,
  UserInsert,
  UserUpdate
> {
  constructor() {
    super({ table: "users", primaryKey: "id" });
  }
}

// users.service.ts
import { BaseService } from "@roar/mobile-base";
import { UsersRepository } from "./users.repository";

export class UsersService extends BaseService<
  Parameters<UsersRepository["create"]>[0] extends infer I ? any : any,
  Parameters<UsersRepository["create"]>[0],
  Parameters<UsersRepository["update"]>[1]
> {
  constructor(repo = new UsersRepository()) {
    super(repo);
  }
}
  1. Use in your screens/hooks (with React Query example):
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { UsersRepository } from "../data/users.repository";

const usersRepo = new UsersRepository();

export function useUsersList() {
  return useQuery({
    queryKey: ["users"],
    queryFn: () => usersRepo.list({ withCount: true }),
  });
}

export function useCreateUser() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (payload: { email: string; full_name?: string | null }) =>
      usersRepo.create(payload),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["users"] }),
  });
}

API Overview

  • configureSupabase({ url, anonKey, options? }) — set up a shared client
  • setSupabaseClient(client) — provide your own (typed) client instance
  • getSupabaseClient() — retrieve the shared client

BaseRepository<Row, Insert = Row, Update = Partial>

Constructor: new BaseRepository({ table, primaryKey = "id", softDeleteColumn? }, client?)

Methods:

  • getById(id, { select? })
  • findOne({ where, select? })
  • list({ where?, orderBy?, limit?, offset?, select?, withCount?, page?, pageSize? })
  • create(payload, { select? })
  • update(id, patch, { select? })
  • delete(id) — soft deletes when softDeleteColumn is provided

BaseService<Row, Insert, Update>

Thin wrapper around a repository; extend to add validation and orchestration.

Expo adapter and Auth

Use an Expo-friendly setup that chooses SecureStore on native and AsyncStorage on web:

import {
  configureSupabaseExpo,
  AuthProvider,
  useAuth,
} from "@roar/mobile-base";

configureSupabaseExpo({
  url: process.env.EXPO_PUBLIC_SUPABASE_URL!,
  anonKey: process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
});

// In your root component
function AppRoot() {
  return <AuthProvider>{/* your app */}</AuthProvider>;
}

// In screens
const { user, session, signIn, signOut, loading } = useAuth();

Styling with NativeWind (Tailwind for RN)

This package ships a Tailwind preset. In each app:

  1. Install deps in the app:
npm i nativewind tailwindcss
  1. Configure Babel (app babel.config.js):
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: ["nativewind/babel"],
  };
};
  1. Create tailwind.config.js in the app:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  presets: [require("@roar/mobile-base/tailwind-preset")],
  content: [
    "./App.{js,jsx,ts,tsx}",
    "./src/**/*.{js,jsx,ts,tsx}",
    "./components/**/*.{js,jsx,ts,tsx}",
  ],
  theme: { extend: {} },
};
  1. Use prebuilt components or className in RN components:
import { Button, Input, Screen, Card, Text } from "@roar/mobile-base";

<Screen className="bg-neutral-50">
  <Card>
    <Text className="text-xl font-semibold mb-2">Sign in</Text>
    <Input label="Email" placeholder="email" className="mb-3" />
    <Input
      label="Password"
      placeholder="password"
      secureTextEntry
      className="mb-4"
    />
    <Button title="Continue" />
  </Card>
</Screen>;

Filters & pagination

Use where with operators: eq, neq, gt, gte, lt, lte, like, ilike, is, in, contains, containedBy, overlaps.

repo.list({
  where: [
    { column: "email", op: "ilike", value: "%@company.com" },
    { column: "status", op: "eq", value: "active" },
  ],
  orderBy: { column: "created_at", ascending: false },
  page: 1,
  pageSize: 20,
  withCount: true,
});

Type-safety with Supabase

For full type-safety, generate your Supabase Database types and create a typed client in your app, then pass it to the library:

import { createClient } from "@supabase/supabase-js";
import type { Database } from "../types/supabase"; // generated
import { setSupabaseClient } from "@roar/mobile-base";

const client = createClient<Database>(SUPABASE_URL, SUPABASE_ANON);
setSupabaseClient(client);

You can then extend BaseRepository with strongly-typed row/insert/update interfaces derived from Database["public"]["Tables"]["table_name"].

Keeping apps up to date

Recommended strategies:

  • Package: publish @roar/mobile-base to a private npm registry; apps just bump the version.
  • Monorepo: use workspaces (pnpm/npm/yarn) so apps depend on base locally.
  • Git submodule: link this repo as a submodule; update by pulling submodule changes.

This base avoids React or Expo dependencies so it works across RN apps. UI helpers or hooks can live in a separate adapter package if needed.

ORM-like workflow with migrations

Supabase is Postgres under the hood. For an ORM-like developer experience (models + migrations), use the Supabase CLI to manage SQL migrations and generate types. This keeps your schema as code and works great with the repository/service pattern in this base.

See db/README.md for commands. Highlights:

  • npm run db:migration:new -- <name>: new SQL migration file
  • npm run db:reset: apply migrations to local db
  • npm run db:push: push migrations to remote project
  • npm run db:types:local or db:types:remote: generate TypeScript types

You can also scaffold repository/service files quickly:

node ./scripts/scaffold-model.mjs fraud_reports FraudReport ./scaffold

Then move the generated files into your app and replace the row/insert/update types with your generated Database types for full type-safety.

Migration tips for existing apps (fraud-tracker-app, marle)

  • Replace lib/supabase.ts with a call to configureSupabaseExpo in your app startup.
  • Swap local AuthContext usage to the exported AuthProvider and useAuth.
  • Move direct Supabase calls out of screens into repositories/ and services/ extending the provided base classes.
  • Adopt NativeWind by adding the Babel plugin and Tailwind config using the preset, then gradually replace StyleSheet styles with className utilities or shared UI components.

License

MIT