A DocType is one record definition. You write it once, as a JSON file in an app or as a Custom DocType in the Desk, and Frappe builds four things from it: a database table, a form and list view, a REST endpoint, and a place to hang permissions. The rest of this book is about what Frappe does with that definition.
The module below shows the first three for any field type. Pick one, and switch the database.
Try it
Field type → what Frappe builds
Pick a field type and a database. MariaDB is Frappe's default.
Group
Field type
Database
- Database column
decimal(21,9)- Form control
ControlCurrency- What the API sends
- number, for example
1250.5
Width and precision are configurable; the default is 21,9.
Sample values are illustrative. Frappe v16.52.0; Check differs on v15.123.0.
| Field type | MariaDB | PostgreSQL | Form control | API sends |
|---|---|---|---|---|
| Data | varchar(140) | varchar(140) | ControlData | string |
| Small Text | text | text | ControlSmallText | string |
| Text | text | text | ControlText | string |
| Long Text | longtext | text | ControlLongText | string |
| Text Editor | longtext | text | ControlTextEditor | string |
| Markdown Editor | longtext | text | ControlMarkdownEditor | string |
| HTML Editor | longtext | text | ControlHTMLEditor | string |
| Code | longtext | text | ControlCode | string |
| JSON | json | json | ControlJSON | string |
| Password | text | text | ControlPassword | string |
| Read Only | varchar(140) | varchar(140) | ControlReadOnly | string |
| Int | int(11) | int | ControlInt | number |
| Long Int | bigint(20) | bigint | ControlLongInt | number |
| Float | decimal(21,9) | decimal(21,9) | ControlFloat | number |
| Currency | decimal(21,9) | decimal(21,9) | ControlCurrency | number |
| Percent | decimal(21,9) | decimal(21,9) | ControlPercent | number |
| Check | tinyint(4) | smallint | ControlCheck | number 0 or 1 |
| Rating | decimal(3,2) | decimal(3,2) | ControlRating | number |
| Duration | decimal(21,9) | decimal(21,9) | ControlDuration | number |
| Date | date | date | ControlDate | string |
| Datetime | datetime(6) | timestamp | ControlDatetime | string |
| Time | time(6) | time(6) | ControlTime | string |
| Link | varchar(140) | varchar(140) | ControlLink | string |
| Dynamic Link | varchar(140) | varchar(140) | ControlDynamicLink | string |
| Select | varchar(140) | varchar(140) | ControlSelect | string |
| Autocomplete | varchar(140) | varchar(140) | ControlAutocomplete | string |
| Phone | varchar(140) | varchar(140) | ControlPhone | string |
| Color | varchar(140) | varchar(140) | ControlColor | string |
| Icon | varchar(140) | varchar(140) | ControlIcon | string |
| Attach | text | text | ControlAttach | string |
| Attach Image | text | text | ControlAttachImage | string |
| Signature | longtext | text | ControlSignature | string |
| Barcode | longtext | text | ControlBarcode | string |
| Geolocation | longtext | text | ControlGeolocation | string |
| Table | no column | no column | ControlTable | array of row objects |
| Table MultiSelect | no column | no column | ControlTableMultiSelect | array of row objects |
| Section Break | no column | no column | layout (no input control) | not in the API |
| Column Break | no column | no column | layout (no input control) | not in the API |
| Tab Break | no column | no column | layout (no input control) | not in the API |
| HTML | no column | no column | ControlHTML | not in the API |
| Button | no column | no column | ControlButton | not in the API |
| Image | no column | no column | ControlImage | not in the API |
| Heading | no column | no column | ControlHeading | not in the API |
| Fold | no column | no column | ControlFold | not in the API |
| Attachment Gallery | no column | no column | ControlAttachmentGallery | not in the API |
What does one field become?
The field type decides everything. Frappe keeps a map from field type to column type, and names the form control after the type. A Currency field is a decimal(21,9) column on MariaDB, a ControlCurrency in the form, and a number in the API. A Check is tinyint(4) on v16 and int(1) on v15, a tick box in the form, and 0 or 1 in the API. A Link is a varchar(140) that holds the other document's name, and the API returns that name, not the document. A Date is a date column that the API sends as a string such as 2026-04-01.
Text types hold more or less. Data, Select, Link and Phone are varchar(140) by default. Small Text and Text are text. Long Text, Code and the editors are longtext. Set a length on a Data field and the column follows, never below 64. A Link to a DocType that is named by UUID becomes a uuid column. PostgreSQL has its own map, which the module shows.
Which fields have no column?
Layout fields: Section Break, Column Break, Tab Break, HTML, Button and Image. A Table field has no column either. Its rows belong to another DocType, the child table, which has its own table with parent, parentfield and parenttype columns, and the API nests those rows under the field's name. A Password value goes to a separate __Auth table, and the field itself holds asterisks.
What does every table have?
Every table also gets name, creation, modified, modified_by, owner, docstatus and idx. The table is named tab plus the DocType's name, so Sales Invoice is tabSales Invoice. A Single DocType, used for settings, has no table of its own: its values are rows in tabSingles.
How does it reach the API?
GET /api/resource/Sales Invoice/SINV-00001 returns the document inside a data key, with its child rows nested. POST runs insert() and PUT runs save(), so an API write goes through the same lifecycle as a click in the Desk (chapter 2). A read also checks read permission and removes the fields the user may not read (chapter 4).
Where does the definition live?
For a standard DocType it is a JSON file in the app, under <app>/<module>/doctype/<name>/<name>.json. bench migrate reads it and brings the table in line. You can create a standard DocType only in developer mode. On a live site you create a Custom DocType, which has no Python class of its own, so its logic comes from a Server Script or a doc_events hook (chapter 3). Changes to someone else's DocType are not edits to its file. They are rows layered on top (chapter 7).
Where this comes from
Read at Frappe v16.52.0 and ERPNext v16.50.0. “(v15)” marks Frappe v15.123.0 and “(ERPNext v15)” marks ERPNext v15.122.0. Each link opens that file at that tag, on that line.
database/schema.py:25 · database/database.py:97–98 · mariadb/database.py:175–210 · database/database.py:91 · postgres/database.py:133–168 · mariadb/database.py:181 · mariadb/database.py:179 (v15) · database/schema.py:13–15 · database/schema.py:411–464 · controls/control.js:48–55 · controls/data.js:336 · controls/int.js:28 · controls/text.js:23 · utils/response.py:222–241 · api/__init__.py:33–44 · api/__init__.py:63–68 · api/v1.py:44–58 · api/v1.py:74–88 · model/__init__.py:53–65 · model/base_document.py:1352–1372 · model/document.py:782–796 · model/document.py:649–690 · doctype/doctype.py:328–337 · model/base_document.py:103–107 · frappe/migrate.py:144–153
Official page: Understanding DocTypes, read 14 October 2026. Platform view: what Frappe gives a business, and what still gets built.