gymmonk-schema
v0.1.3
Published
Shared Zod domain schemas for Gym SaaS (frontend + backend).
Maintainers
Readme
🏋️ gymmonk-schema
Central Zod v4 schema library for the GymMonk / Gym SaaS ecosystem.
📋 Table of Contents
- Overview
- Installation
- Core Concepts
- Library Structure
- How to Import
- Usage Examples
- Development & Publishing
- Versioning
🎯 Overview
This package is the single source of truth for:
- ✅ Domain models (
User,Organisation,Center,Membership,Workout, etc.) - ✅ API DTOs (create/update payloads)
- ✅ Public view models (safe objects to send to frontend)
- ✅ Shared primitives (
ObjectId, dates, pagination, etc.)
🚀 Designed for Multi-Platform Use
It is designed to be used by:
- Backend (Node.js / Express)
- Web frontend (Next.js)
- Mobile app (React Native)
- API-doc project (OpenAPI/Swagger via zod-to-openapi)
So all layers share the same contracts.
📦 Installation
In any consumer project (backend, web, app, api-doc):
npm install gymmonk-schema
# or
yarn add gymmonk-schema
# or
pnpm add gymmonk-schema💡 Core Concepts
Schemas vs Types
Each entity is represented by a Zod schema and a corresponding TypeScript type:
import { z } from "zod";
export const RoleSchema = z.object({
name: z.string().min(1),
description: z.string().trim().optional(),
permissions: z.array(z.string()).default([]),
});
export type Role = z.infer<typeof RoleSchema>;Two Different Layers
| Layer | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Schema (RoleSchema) | • Real runtime object• Validates and parses data• Used in controllers, services, forms, API-doc generation |
| Type (Role) | • Compile-time only (erased at runtime)• Used by TypeScript for static checking and autocomplete |
You will see this pattern throughout the library.
Naming Conventions
Inside each domain:
- Schemas are named:
XxxSchema - Types are named:
Xxx(viaz.infer<typeof XxxSchema>) - DTOs (request/response payloads) are named:
XxxCreateDtoSchema,XxxUpdateDtoSchema, etc. - Public view models (safe to send to frontend):
PublicXxxSchema,type PublicXxx
🏗️ Library Structure
At the top level, the package exposes namespaced domains:
import {
Common,
Auth,
Gym,
Workout,
Nutrition,
Subscription,
Marketing,
Notification,
Community,
Feedback,
Support,
Landing,
Audit,
} from "gymmonk-schema";Each domain has its own folder + barrel file and follows the same structure: schemas + types grouped by feature.
Below is an overview of what each domain represents.
🔧 Common
Building blocks used everywhere.
Includes:
Primitives
ObjectIdSchemaIsoDateStringSchemaIsoDateTimeStringSchemaNonEmptyStringSchema,OptionalStringSchema, etc.
Value Objects
EmailSchemaPhoneSchemaAddressSchemaNutritionalValuesSchemaPlanVariantSchema(for flexible pricing options)MfaSchema,SocialLoginSchema, etc.
Enums
- Gender enum
- EntityStatus enum
- NotificationType enum
Pagination
PaginationSchema(common page, limit, etc.)
Usage Example
const id = Common.Primitives.ObjectIdSchema.parse(someString);
const pagination = Common.Pagination.PaginationSchema.parse(req.query);ℹ️ Exact nested names may vary slightly; check
src/common/index.tsfor the authoritative exports.
🔐 Auth
Authentication & authorization related models.
Includes (names representative):
UserSchema,PublicUserSchema,UserRegisterDtoSchemaetc.RoleSchema(RBAC roles)PermissionSchema(fine-grained permissions)ResourceSchema(RBAC resources)TokenSchema(access/refresh tokens, device tokens)OtpSchema(one-time passwords)DeviceSchemaPasswordHistorySchemaUserActivitySchema(login, logout, etc.)
Common Usage:
- Backend: validate incoming auth requests (register, login, password reset)
- Frontend: type-safe forms for login/register, and PublicUser types for UI
🏢 Gym
All gym/organisation/center management models.
Includes:
Organisation / Centers
OrganisationSchemaCenterSchemaCenterFacilitySchemaCenterServiceSchema
Catalogs
EquipmentCatalogSchemaFacilityCatalogSchemaServiceCatalogSchema
Operational Models
AttendanceSchemaClassScheduleSchemaEquipmentInventorySchemaGymLayoutSchemaSessionBookingSchemaGoalSchemaAchievementBadgeSchema
Used to Define and Validate:
- How the gym, centers, facilities, services are modeled
- Attendance tracking
- Booking / layout / goals / badges
💪 Workout
Exercise and workout planning & tracking.
Includes:
ExerciseSchema(name, muscle groups, media, equipment)WorkoutPlanSchema(list of exercises with sets/reps/duration)WorkoutTrackingEntrySchema(what a member actually did, per set, per day)
Used By:
- Backend: workout CRUD, tracking endpoints
- Frontend/App: typed workout UI, tracking screens
🥗 Nutrition
Ingredient, recipes, diet plans, hydration.
Includes:
IngredientSchemaRecipeSchema,RecipeIngredientSchemaDietPlanSchema,DietPlanMealSchema,DietPlanItemSchemaDietTrackingEntrySchemaHydrationTrackingSchema
Use This For:
- Building diet plans and logging diets
- Aggregating nutrition
- Hydration tracking
💳 Subscription
Subscription, membership, coupons, payments.
Includes:
SubscriptionPlanSchemaSubscriptionSchemaMembershipPlanSchemaMembershipSchemaCouponSchemaPaymentTransactionSchema
Used For:
- Defining product plans and membership plans
- Linking members to plans (Membership, Subscription)
- Applying coupons and tracking payments
📢 Marketing
Branding, ads, campaigns, referrals.
Includes:
BrandSchemaAdvertisementSchemaMarketingCampaignSchemaReferralSchema
Used For:
- Dashboard banners, campaigns, referral systems
- Representing marketing data in a unified way
🔔 Notification
Notifications and templates.
Includes:
NotificationSchema(delivered notification entries)NotificationTemplateSchema(reusable templates per channel)
Supports channels like: in_app, push, email, sms.
👥 Community
Internal community / communication layer.
Includes:
ConversationSchemaMessageSchemaPostSchema
Used For:
- Chats
- Group conversations
- Community posts
💬 Feedback
User feedback, bug reports, ratings.
Includes:
FeedbackSchema(type, message, metadata, rating)
🎫 Support
Support tickets.
Includes:
SupportTicketSchema(priority, status, category, assignments, metadata)
🌐 Landing
Contact requests from marketing/landing pages.
Includes:
ContactRequestSchema(name, email/phone, subject, message, metadata)
📝 Audit
Audit logs.
Includes:
AuditLogSchema(who, what, when, before/after, metadata)
📥 How to Import
Top-Level Domains (Recommended)
Prefer importing from the root in most cases:
import {
Auth,
Gym,
Subscription,
Workout,
Nutrition,
Common,
} from "gymmonk-schema";
// Auth
const dto = Auth.UserRegisterDtoSchema.parse(body);
// Gym
const center = Gym.CenterSchema.parse(payload);
// Subscription
type Plan = Subscription.SubscriptionPlan;
const planSchema = Subscription.SubscriptionPlanSchema;💡 If you want very fine-grained imports, you can also import directly from subpaths as long as your build/exports are configured for it (check your
package.json"exports" if you add that later).
🎨 Usage Examples
Backend (Express)
Validate Request Body (DTO)
import { Auth } from "gymmonk-schema";
import type { Request, Response } from "express";
export async function registerHandler(req: Request, res: Response) {
try {
const dto = Auth.UserRegisterDtoSchema.parse(req.body);
// dto is fully validated and typed
const user = await createUser(dto);
const publicUser = Auth.PublicUserSchema.parse(user);
return res.status(201).json(publicUser);
} catch (err: any) {
return res.status(400).json({
message: "Invalid request",
errors: err.errors ?? err,
});
}
}Validate Query Params
import { Common } from "gymmonk-schema";
app.get("/centers", (req, res) => {
const pagination = Common.Pagination.PaginationSchema.parse(req.query);
// use pagination.page / pagination.limit
});Frontend (Next.js)
Validate Data Before Sending to Backend
"use client";
import { Auth } from "gymmonk-schema";
async function submit(formData: any) {
const dto = Auth.UserRegisterDtoSchema.parse(formData);
const res = await fetch("/api/auth/register", {
method: "POST",
body: JSON.stringify(dto),
headers: { "Content-Type": "application/json" },
});
return res.json();
}Strongly Typed UI Components
import { Gym } from "gymmonk-schema";
type Center = Gym.Center;
function CenterCard({ center }: { center: Center }) {
return (
<div>
<h2>{center.name}</h2>
<p>{center.address?.city}</p>
</div>
);
}Forms (React Hook Form + Zod)
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Subscription } from "gymmonk-schema";
const schema = Subscription.SubscriptionPlanSchema;
type PlanForm = Subscription.SubscriptionPlan;
export function PlanFormComponent() {
const form = useForm<PlanForm>({
resolver: zodResolver(schema),
});
// form.register, form.handleSubmit, etc.
}API-doc / OpenAPI
In your API-doc project (contract-first design), you can introspect schemas and generate OpenAPI:
import {
OpenAPIRegistry,
generateOpenApiDocument,
} from "@asteasolutions/zod-to-openapi";
import { Auth, Gym } from "gymmonk-schema";
const registry = new OpenAPIRegistry();
// Register schemas
registry.register("User", Auth.PublicUserSchema);
registry.register("Center", Gym.CenterSchema);
// Register routes and use the schemas in request/response definitions…
const openapi = generateOpenApiDocument(registry.definitions, {
openapi: "3.0.0",
info: {
title: "GymMonk API",
version: "1.0.0",
},
});
// Then write openapi.json and serve via swagger-ui-expressThis way API docs are always in sync with your code and frontend types.
🛠️ Development & Publishing
In the gymmonk-schema repo:
Build
npm run buildTypical script:
"build": "tsc"This compiles src/**/*.ts → dist/**/*.js + .d.ts.
Lint + Format
Using Biome:
npm run check # type-check + lint + format (depending on your script)
npm run lint # if you have a separate lint script
npm run format # if you have a separate format scriptVersion Bump
Use semantic versioning:
npm version patch # 0.1.0 -> 0.1.1
npm version minor # 0.1.0 -> 0.2.0
npm version major # 0.1.0 -> 1.0.0This updates package.json and creates a git commit + tag (if configured).
Publish
npm login
npm run build
npm publish --access publicThen consumers can install:
npm install gymmonk-schema📌 Versioning
Follow standard SemVer:
| Version Type | Pattern | Description |
| ------------ | ------- | ---------------------------------------------------------------- |
| PATCH | x.y.Z | Internal fixes, no breaking changes |
| MINOR | x.Y.z | New schemas/fields added in a backwards-compatible way |
| MAJOR | X.y.z | Breaking changes (removed fields, renamed, incompatible changes) |
Guidelines:
- ✅ Adding optional fields: minor
- ⚠️ Making a field required or changing its type: major
- ⚠️ Removing schemas or renaming them: major
📝 Keep a simple
CHANGELOG.mdto track what changed between versions, especially when schemas or DTOs change in ways that affect consumers.
Built with ❤️ for the GymMonk ecosystem
