Skip to content
CareSewa
The engine

Your data model is data, not code.

Most healthcare software hard-codes its schema. Change a form and you change the codebase, which means a release, a migration and a wait. CareSewa reads your models at request time, from documents you control. Add a field, and the form, the table, the validator and the API all change together. Immediately.

18
field types
text to relation
1
record collection
every model, every tenant
0
migrations
to add a model
0s
to roll out
next render, not next release
The difference

One extra field. Two very different weeks.

This is not a story about developers being slow. It is a story about an architecture that puts your schema on the wrong side of a build step.

Schema in the codebase

Six weeks

  1. 1You file a change request for one extra field
  2. 2It is scoped, quoted and scheduled into a release
  3. 3A developer edits a schema and writes a migration
  4. 4QA regression-tests a form that gained a text input
  5. 5It ships in six weeks, and by then the protocol moved

So the field lives in a spreadsheet instead, and the truth is now in two places.

Schema in the database

This afternoon

  1. 1You open Studio and add the field
  2. 2You set its type, label, order and whether it is required
  3. 3You save
  4. 4Your staff see it on their next render
  5. 5The API accepts it, the table shows it, the audit log records it

The truth stays in one place, because nothing had to route around the system.

How it thinks

Four ideas hold the whole engine up

None of them are clever. They are just applied consistently, which is the part most systems get wrong.

A field is a database record

Not a column, not a class property, not a line in a migration. It is a row inside a ModelDefinition document, and it can be created, reordered and removed like any other row.

Records live in one generic store

Every record of every model lands in DynamicRecord, keyed by tenant and model. Adding a model is an insert, not a schema change — which is why it takes a second instead of a sprint.

The UI is a function of the definition

DynamicForm, DynamicTable and DynamicPage take a definition and render it. Nobody hand-writes a form for your Patient model, so nobody has to hand-edit one when it changes.

The rules live in the engine

Validation, tenancy, permissions and audit are enforced where the write happens, not where the form is drawn. A curl request and a button click are the same request by the time it matters.

Anatomy

This is a model. That is all one is.

A key, a label, and a list of fields. Save it and the engine has everything it needs to validate writes, render forms and serve an API for it.

  • modelKey is immutable once created — records are keyed by it
  • Fields carry a type, a label, an order and their own rules
  • Required, unique and select options are properties, not code
  • A relation field names another model in your tenant
  • The definition is scoped to one tenant and one portal
ModelDefinitionstored, not compiled
modelKeypatientimmutable
labelPatient
tenantIdyour accountalways
portalhospital-owner
fields[]
  • Full nametextrequired
  • Phonephoneunique
  • Date of birthdate
  • Blood groupselect4 options
  • Primary doctorrelation→ doctor
  • Consent formfile
Field types

Eighteen types. Enough for a hospital.

Not a generic form builder’s list. These are the shapes clinical and operational data actually takes, and each one carries its own validation into every write.

Text

textstringtextarea

Short lines, long notes, and the difference between them.

Numbers

numberintegercurrency

Currency is stored as a number and formatted per market — never with a baked-in symbol.

Time

datedatetimetime

Dates of birth, appointment slots, and shift start times are three different problems.

Contact

emailphoneurl

Validated on write, not just masked in the input.

Choice

selectmultiselectboolean

Options are part of the definition, so changing them changes every form at once.

Structure

relationfilejson

Relation points at another model in your tenant. JSON is the escape hatch for shapes nobody predicted.

All 18, in the engine’s own order: text · string · number · integer · currency · boolean · date · datetime · email · phone · url · select · multiselect · relation · file · json · textarea · time

The loop

What happens between you and the record

Six steps, and you only author the first one. Everything after it is the engine doing what your definition told it to.

1

You define the model

One ModelDefinition — a key, a label, fields with types and rules. The only authoring step.

2

A write arrives

From a form, the app, or a curl against /data/:modelKey. All three land in one handler.

3

The engine validates it

Against your definition: types, required, options, uniqueness, and relation targets in your tenant.

4

The record is stored

Into DynamicRecord, stamped with your tenantId, and written to the append-only audit log.

5

The API already exists

List, read, create, update, delete — live the moment you saved. No endpoint was written.

6

The UI already exists

Forms and tables render from the same definition. Staff, app and integrations all see it.

There is no code generation step in this flow. That is deliberate. Generated code has to be regenerated, reviewed and redeployed — which is the exact problem we were removing.

Try it

Add a field. Watch the form change.

This is the loop, running in your browser. Add, reorder or remove on the left; the form your staff would use re-renders on the right. Nothing here is a video.

caresewa · studio / patient
Live

Model structure

4 fields
  • Full namereq
  • Phonereq
  • Date of birth
  • Blood group

Add a field

In the real Studio this saves to your ModelDefinition and every user sees it on their next render. No deploy, no migration.

Relations & validation

Dynamic is not the same as loose

Every rule you set in the definition is enforced on the server, on every write, from every source. The form is a convenience. The engine is the authority.

Relations resolve inside your tenant

A relation field points at another model you defined. On write, the engine checks the target record exists — and that it belongs to your tenant. A record cannot reference something it should not be able to see.

Uniqueness is enforced, not suggested

Mark a field unique and the engine rejects the duplicate at the write, scoped to your tenant and model. Two hospitals can both have a patient with the same national ID. One hospital cannot have them twice.

Type and required checked server-side

A date field will not take a sentence. A required field will not take an empty string. Bypassing the UI and calling the API directly gets you the same 422 with the same field errors.

Select options are part of the contract

Options live on the field, so the form, the API and the validator agree by construction. Change the list and every surface changes with it — including the one your integration is writing to.

Multi-tenancy

Isolation you cannot forget to apply

Tenancy is not a checkbox on a settings page. It is a field on every document and a filter on every query, which is the only version of it that survives contact with a growing codebase.

  • Every ModelDefinition carries a tenantId
  • Every DynamicRecord carries a tenantId
  • Every query filters by it before anything else runs
  • Relations are resolved within the tenant, never across
  • Cross-tenant reads require an explicit super-admin assertion
  • Two tenants can define the same modelKey and never meet

One collection · three tenants

tnt_a1·patientDynamicRecord
tnt_b7·patientDynamicRecord
tnt_a1·appointmentDynamicRecord
tnt_c3·vehicleDynamicRecord
tnt_b7·sampleDynamicRecord
tnt_a1·patientDynamicRecord

find({ tenantId, modelKey }) — the tenant filter is not optional, and there is no query path that omits it.

Permissions

Thirteen roles, and grants down to the model

Roles decide what kind of user someone is. Grants decide what they can touch. Both are read from the database on every request, which is what makes revocation mean something.

A staff grant, in full

You pick the portals a person can enter, then the models inside them, then the rights on each model. Nothing is inherited by accident.

portal: labgranted
sample
CRUD
report
CRUD
test
CRUD
technician
CRUD

This technician can log samples and read the catalog. They cannot delete anything, and they cannot see the technician directory at all — the model is not in their grant, so it is not in their navigation either.

13 roles

From account owner to portal staff to patient. Your role decides which surfaces exist for you at all.

BUILDER_ROLES edit models

Only builder roles can change a ModelDefinition. Staff use the models; they do not reshape them.

Read fresh, every request

Grants are not baked into a token at login. They are read from the database each request, so revoking access takes effect on the next one — not at the next sign-in.

Audit

Every mutation, written down and never rewritten

Clinical data attracts questions months later. The audit log exists so the answer is a query rather than an argument.

  • Written on every create, update and delete
  • Records the actor, the action, the model and the time
  • Append-only — there is no edit path, for anyone
  • Tenant-scoped like everything else
  • Soft delete keeps medical records recoverable
  • Removing a field stops collection, it does not erase history
audit_logappend-only
  • updatepatient09:42
  • createsample09:41
  • updatemodel:patient09:38
  • deleteappointment09:31
  • createreport09:24

Model edits are audited too. Changing the shape of your data is itself a mutation.

Shipping

Three kinds of change, three very different waits

Most of what you want to change is the first kind, and the first kind has no wait at all. That is the whole design goal.

Instant

Schema push

You change a model. The definition updates and every surface reads the new one on its next render — web and mobile alike, because both render from the same definitions.

No build. No store. No version skew.

Minutes

Over-the-air update

You change the app’s JavaScript — a screen, a flow, a fix. EAS Update publishes it and devices pick it up on next launch.

No app-store review in the path.

Store review

Native build

Only when native code changes — a new permission, a new SDK. This is the slow path, and it is the one we deliberately keep rare.

Rare by design, not by luck.

Auto-API

Define a model, get an API

Not as a feature you enable. As a consequence of how the engine reads your definition. If the model exists, the endpoint exists.

Every model you build is reachable at /api/v1/data/:modelKey with list, read, create, update and delete. Same auth, same envelope, same validation, same audit log as the UI — because it is the same code underneath.

  • GET/api/v1/data/patient
  • POST/api/v1/data/patient
  • GET/api/v1/data/patient/:id
  • PATCH/api/v1/data/patient/:id
  • DELETE/api/v1/data/patient/:id

Swap patient for any model you defined. It was never a special case.

Honest limits

What the engine does not do

Every platform has edges. Ours are worth knowing before you plan around them, not after.

A modelKey is forever

Records are keyed by it, so it cannot be renamed after creation. Labels change freely; the key does not. Pick it once, and pick it carefully.

It is not a code generator

You will not find a generated repo to fork. If your requirement genuinely needs bespoke server logic, that is a conversation with us, not a Studio setting.

JSON is the last resort

The json field type holds shapes the other seventeen cannot. It is a real escape hatch, but the engine cannot validate inside it — so use it when you mean it.

Questions

What engineers ask about the engine

The architecture questions, answered the way we would answer them in a technical review.

No. There is no generated code to escape to. A model is a document in the database, and the application reads it at request time. Nothing is compiled, nothing is scaffolded, and there is no file on disk that has to agree with your schema.

See the engine against your own workflow

Bring the form your vendor said would take six weeks. We will model it on the call.

Multi-country by design · tenant-isolated · every change audit-logged