/supaorm

Supabase ORM-like library to make working with forms easier

Primary LanguageJavaScript

SupaORM (pre-alpha)

A lightweight orm-like library for easier working with relational tables via supabase.

This makes using forms much nicer (more modular) than the spaghetti code than ensues, allowing you to nest your data like an ODM in line with relations.

Warning: At this stage, supaorm is more of a concept than a finished library.

Table Structure Setup

import {TABLE_TYPES} from "supaorm";

export const TABLES = {
    profiles: { name: 'profiles', type: TABLE_TYPES.entity, get m2o() {
        return { address_id: TABLES.address.name };
    }},
    areas: { name: 'areas', type: TABLE_TYPES.entity },
    profileAreas: { name: 'profiles_areas', type: TABLE_TYPES.join, get m2m() {
        return { profile_id: TABLES.profiles.name, area_id: TABLES.areas.name }
    }},
    address: { name: 'addresses', type: TABLE_TYPES.entity },
    contacts: { name: 'contacts', type: TABLE_TYPES.entity, get m2o() {
        return { profile_id: TABLES.profiles.name };
    }},
}

Example Component (React)

import React, {useCallback, useEffect, useRef, useState} from "react";
import {Handler, groomData, mapNameAndIdToSelectValues} from "./handler";
import {Button, Card, Col, Form, Input, InputNumber, message, Popconfirm, Row, Space, Spin, Tooltip} from "antd";
import {TABLES} from "./tableData";

const ProfileEditComponent = () => {
    /** create handler for profile */
    const profileHandler = useRef(makeHandler('profiles', TABLES, supabaseDriver(supabase)));

    /** handle form submit */
    const submitForm = async () => {
        // groom our data from form ready to be passed supabase crud operations
        const fields = await groomData(profileForm, profile, {
            // map selected areas to {id, profile_id, area_id} format
            profileAreas: (fields) => fields.areas?.map(area => ({
                id: profile?.profileAreas?.find(profileArea => profileArea.area_id === area && profileArea.profile_id === fields.id)?.id || FieldGroup._newId(),
                profile_id: fields.id,
                area_id: area
            })),
            // map areas from select to area
            areas: (fields) => fields.areas?.map(areaId => areas.find(area => area.id === areaId)),
        });

        // set data to nested FieldGroup
        const profileFieldGroup = profileHandler.current.getFieldGroup(TABLES.profiles.name);
        profileFieldGroup.newData = fields;
        // submit form
        await profileHandler.current.submit();

        // get id of new record
        const id = profileFieldGroup.results?.inserts[fields.id]?.id;
    }

    return (
        <Form form={profileForm}>
            {/* Profile Fields */}
            <Form.Item label="Name" name="name" initialValue={profile.name}><Input /></Form.Item>
            <Form.Item label="Phone" name="phone" initialValue={profile.phone}><Input /></Form.Item>
            {/* Address */}
            <Form.Item name="address.address_1" label="Line 1" initialValue={profile.address?.address_1}><Input /></Form.Item>
            <Form.Item name="address.address_2" label="Line 2" initialValue={profile.address?.address_2}><Input /></Form.Item>
            <Form.Item name="address.address_3" label="Line 3" initialValue={profile.address?.address_3}><Input /></Form.Item>
            <Form.Item name="address.city" label="City" initialValue={profile.address?.city}><Input /></Form.Item>
            <Form.Item name="address.postcode" label="Postcode" initialValue={profile.address?.postcode}><Input /></Form.Item>
            <Form.Item name="address.country" label="Country" initialValue={profile.address?.country}><Input /></Form.Item>
            {/* Areas */}
            <Form.Item  label="Areas"name="areas" initialValue={profile.areas?.map(area => Number(area.id)) || []}>
                {/* ... */}
            </Form.Item>
            {/* Contacts */}
            {/* ... */}
        </Form>
    );
}

Licence: MIT