{"id":21215775,"url":"https://github.com/betahuhn/deta-base-orm","last_synced_at":"2025-06-11T17:34:04.948Z","repository":{"id":38215611,"uuid":"394051997","full_name":"BetaHuhn/deta-base-orm","owner":"BetaHuhn","description":"🗃🪐 Basic ORM for Deta Base","archived":false,"fork":false,"pushed_at":"2023-08-23T14:03:21.000Z","size":329,"stargazers_count":22,"open_issues_count":8,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-13T16:46:04.422Z","etag":null,"topics":["database","deta","deta-base","nodejs","orm","orm-library","typescript"],"latest_commit_sha":null,"homepage":"https://readme.fish/BetaHuhn/deta-base-orm","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BetaHuhn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"ko_fi":"betahuhn","github":"betahuhn"}},"created_at":"2021-08-08T19:23:28.000Z","updated_at":"2024-10-28T00:53:09.000Z","dependencies_parsed_at":"2024-01-06T01:03:48.380Z","dependency_job_id":"6b4f6d6d-aa7e-473f-ba0c-f811e8b29f05","html_url":"https://github.com/BetaHuhn/deta-base-orm","commit_stats":{"total_commits":40,"total_committers":1,"mean_commits":40.0,"dds":0.0,"last_synced_commit":"34be748cb1dfcb3bbac024bdfee9751c331f11e1"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaHuhn%2Fdeta-base-orm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaHuhn%2Fdeta-base-orm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaHuhn%2Fdeta-base-orm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaHuhn%2Fdeta-base-orm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BetaHuhn","download_url":"https://codeload.github.com/BetaHuhn/deta-base-orm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225635323,"owners_count":17500349,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","deta","deta-base","nodejs","orm","orm-library","typescript"],"created_at":"2024-11-20T21:45:09.798Z","updated_at":"2024-11-20T21:45:10.317Z","avatar_url":"https://github.com/BetaHuhn.png","language":"JavaScript","funding_links":["https://ko-fi.com/betahuhn","https://github.com/sponsors/betahuhn","https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=394RTSBEEEFEE","https://ko-fi.com/F1F81S2RK"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \n# Deta Base ORM\n\n[![Node CI](https://github.com/BetaHuhn/deta-base-orm/workflows/Node%20CI/badge.svg)](https://github.com/BetaHuhn/deta-base-orm/actions?query=workflow%3A%22Node+CI%22) [![Release CI](https://github.com/BetaHuhn/deta-base-orm/workflows/Release%20CI/badge.svg)](https://github.com/BetaHuhn/deta-base-orm/actions?query=workflow%3A%22Release+CI%22) [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/BetaHuhn/deta-base-orm/blob/master/LICENSE) ![David](https://img.shields.io/david/betahuhn/deta-base-orm)\n\nBasic ORM for Deta Base\n\n\u003c/div\u003e\n\n---\n\n**🚧 Under development 🚧**\n\nThis project is under heavy development and not yet suitable for production use!\n\n---\n\n## 👋 Introduction\n\nDeta Base ORM is a JavaScript/TypeScript library which lets your work with [Deta Base](https://docs.deta.sh/docs/base/about) in a object relational model. Define a schema for your Base, cross reference items between Bases and interact with your data in a more functional way. It is heavily inspired by [mongoose](https://mongoosejs.com/docs/index.html) and helps you write high quality, maintainable applications in a more productive way then with the standard [Deta Base SDK](https://docs.deta.sh/docs/base/sdk/) (it uses it under the hood). Additionally it features a offline mode which mocks the remote Deta Base and replaces it with a local JSON file for better access during development.\n\n## 🚀 Get started\n\n*First be sure you have Node and NPM installed.*\n\nNext install Deta Base ORM from the command line:\n\n```shell\nnpm install deta-base-orm --save\n```\n\nThen import it into your project:\n\n```ts\nimport * as DetaOrm from 'deta-base-orm'\n```\n\nOr:\n\n```ts\nconst DetaOrm = require('deta-base-orm')\n```\n\n### Example\n\nHere's a quick example to help you get started!\n\nImagine we like kittens and want to record every kitten we ever meet in Deta Base: \n\n```ts\nimport * as DetaOrm from 'deta-base-orm'\n\n// ✨ Define a schema for the kittens\nconst KittenSchema = new DetaOrm.Schema({\n    name: 'string',\n    cuteness: 'number'\n})\n\n// 🛌 Create our Kitten base\nconst Kitten = new DetaOrm.Base('Kitten', KittenSchema)\n\n// 🐱 Create a new kitten\nconst line = Kitten.create({\n    name: 'Line',\n    cuteness: 8\n})\n\n// 🔖 Access the kittens name\nconsole.log(line.name) // 'Line'\n\n// 💾 Save our kitten to the Deta Base\nawait line.save()\n\n// 🔍 Find all kittens\nconst kittens = await Kitten.find()\nconsole.log(kittens) // [{name: 'Line', cuteness: 8}, ...]\n\n// 🔑 Find a kitten by its key\nconst sameKitten = await Kitten.findByKey(line.key)\nconsole.log(sameKitten) // {name: 'Line', cuteness: 8}\n\n// 🧵 Find a kitten by its cuteness\nconst cutest = await Kitten.find({ cuteness: 8 })\nconsole.log(cutest) // [{name: 'Line', cuteness: 8}]\n\n// 💘 Delete a kitten\nawait sameKitten.delete()\n```\n\n\u003c/details\u003e\n\nPretty cool right? Congratulations, you now know how almost everything works! 🎉\n\nSee below for a more detailed guide.\n\n## 📚 Usage\n\n### Defining a schema\n\nBefore you can properly use the other methods you need to define a schema for your Base. It specifies what values to expect and in what format. You can set a property to required (`false` by default) or specify a default value:\n\n```ts\nconst schema = new DetaOrm.Schema({\n    name: 'string',\n    age: {\n        type: 'number',\n        required: true\n    },\n    hungry: {\n        type: 'boolean',\n        default: false\n    }\n})\n```\n\n### Creating a Base\n\nCreating a new Base is similar to how you would do it with the regular [Deta Base SDK](https://docs.deta.sh/docs/base/sdk) except that it also accepts a schema. The schema can either be a instance of the Schema class or an object defining a schema (like above):\n\n```ts\nconst Base = new DetaOrm.Base('name', schema, options)\n```\n\n### Creating a new document\n\nCreating a new document/item in your Base is very easy. Just call the `.create()` method of your Base instance and pass it some new data. Make sure the data matches your schema, else it will throw and error telling you what's wrong.\n\n```ts\nconst document = Base.create({\n    name: 'Maxi',\n    age: 5,\n    hungry: true\n})\n```\n\n\u003e Creating a document doesn't save it to Deta Base automatically. It only exists locally until you call `.save()`\n\n### Saving a document\n\nTo save a document to your Deta Base, call `.save()`:\n\n```ts\nawait document.save()\n```\n\nYou can also create and save a document in one go by calling `.save()` on your Base instance instead of `.create()`:\n\n```ts\nconst document = await Base.save({\n    name: 'Maxi',\n    age: 5,\n    hungry: true\n})\n```\n\n### Retrieving documents\n\nThere are multiple ways to retrieve documents from your Base:\n\n### Multiple documents\n\n```ts\nconst documents = await Base.find(query)\n```\n\nThe query should be a JavaScript object specifing attributes to filter for defined in the schema. See [Filtering](#Filtering) below.\n\n### Retrieving a single document\n\n```ts\nconst document = await Base.findOne(query)\n```\n\nThe query should be a JavaScript object specifing attributes to filter for defined in the schema. See [Filtering](#Filtering) below.\n\n### Updating a document\n\n```ts\nawait document.update(changes)\n```\n\nThe changes should be a JavaScript object defining the changes.\n\n### Deleting a document\n\n```ts\nawait document.delete()\n```\n\n### Cross referencing\n\nYou can cross reference documents from one Base to another:\n\n```js\n// 🏃‍♂️ Declare Base for humans\nconst Human = new DetaOrm.Base('Human', {\n    name: 'string'\n})\n\n// 🛌 Declare Base for kittens\nconst Kitten = new DetaOrm.Base('Kitten', {\n    name: 'string',\n    owner: Human\n})\n\n// 👶 Create new human\nconst human = await Human.save({\n    name: 'Maxi'\n})\n\n// 🐱 Create new kitten\nconst cat = await Kitten.save({\n    name: 'Line',\n    owner: human.key // Reference id of human\n})\n\nconsole.log(cat) // =\u003e { name: 'Line', owner: '17be07f6ee7zNXWw' }\n\n// ✨ Replace id of the owner with actual data from another Base\nawait cat.populate('owner')\n\nconsole.log(cat) // =\u003e { name: 'Line', owner: { name: 'Maxi' } }\n```\n\n`.populate()` takes a path to a field in your schema and returns a document matching the value of that path from another Base. It only stores the key of the document in the Base, the acutal data is resolved from the other Base. For this to work you need to define the other Base in your schema:\n\n```js\nconst Human = new DetaOrm.Base('Human', {\n    name: 'string'\n})\n\nconst Kitten = new DetaOrm.Base('Kitten', {\n    name: 'string',\n    owner: Human, // This tells the library that this path is a cross reference to another Base\n\n    // Does the same as above\n    owner: {\n        type: 'base',\n        base: Human\n    }\n})\n```\n\n### Filtering\n\nWhen getting a document from a base with `.find()` and `.findOne()` you can specify a query object or list of queries to filter the documents with. In the object you can select paths from your schema and define filters and queries to match it against:\n\n```js\nconst Kitten = new DetaOrm.Base('Kitten', {\n    name: 'string',\n    cuteness: 'number'\n})\n\nconst garfield = await Kitten.findOne({\n    name: 'Garfield' // Name equals value\n})\n\nconst cutest = await Kitten.find({\n    name: {\n        $con: 'e' // All kittens with the letter e in their name\n    },\n    cuteness: {\n        $gt: 8 // All kittens with a cuteness greater than 8\n    }\n})\n```\n\nThis library supports all of Deta Bases [queries](https://docs.deta.sh/docs/base/sdk#queries):\n\n| Name                  | Property | Description                                      |\n|-----------------------|----------|--------------------------------------------------|\n| Equal                 | $eq      | Equal to value                                   |\n| Not Equal             | $ne      | Not equal to value                               |\n| Less Than             | $lt      | Less than number                                 |\n| Greater Than          | $gt      | Greater than number                              |\n| Less Than Or Equal    | $lte     | Less than or equal to number                     |\n| Greater Than Or Equal | $gte     | Greater than or equal to number                  |\n| Prefix                | $pfx     | Prefix of value                                  |\n| Range                 | $rg      | Range of numbers i.e. [22,30]                    |\n| Contains              | $con     | String contains substring or array contains item |\n| Not Contains          | $ncon    | Opposite of contains                             |\n\nThey can be combined together in one query or used in different queries. All queries in an object are interpreted as `AND`:\n\n```js\nawait Base.find({\n    name: {\n        $con: 'repo',\n        $pfx: 'gh'\n    }\n})\n```\n\n\u003e Here: name starts with the prefix `gh` AND contains the substring `repo`\n\nQueries in separate objects are treated as `OR`:\n\n```js\nawait Base.find([\n    {\n        name: 'Hello'\n    },\n    {\n        cuteness: {\n            $gt: 8\n        }\n    }\n])\n```\n\n\u003e Here: The name must be `Hello` OR the cuteness greater than `8`\n\n### Paging\n\nDeta Base ORM supports the same paging as the normal Base SDK. Just specify a limit and optionally the key of the last item as parameters: `.find(query, limit, last)`\n\nExample:\n\n```js\nconst items = await Base.find({ age: 24 }, 5) // Limit number of items to 5\n\nconst nextFive = await Base.find({ age: 24 }, 5, last: items[4].key) // Use the key of the last item\n```\n\n### Runtime variables\n\nDuring the execution of your Deta Micro there are a few different environment variables available which give your information about the micro itself and the environment it is running in. For ease of use, `deta-base-orm` provides them and a few other useful environment variables to you via the `Runtime` class:\n\n```js\nimport { Runtime } from 'deta-base-orm'\n\nconsole.log(Runtime.isMicro)\nconsole.log(Runtime.region)\nconsole.log(Runtime.memory)\nconsole.log(Runtime.domain)\n```\n\nHere is an overview of the available variables:\n\n| Key        | Description                                | Type    | Micro Only |\n|------------|--------------------------------------------|---------|------------|\n| isMicro    | Is the program running in a Deta micro     | boolean | No         |\n| isDev      | Is the program running in development mode | boolean | No         |\n| isOffline  | Is the offline mode enabled                | boolean | No         |\n| isSpace    | Is the micro running inside Space          | boolean | No         |\n| projectKey | Deta project key                           | string  | No         |\n| subdomain  | Subdomain of the micro                     | string  | Yes        |\n| domain     | Full domain of the micro                   | string  | No         |\n| region     | AWS region the micro is running in         | string  | Yes        |\n| memory     | Amount of memory available to the micro    | number  | Yes        |\n| logLevel   | Deta micro log level                       | boolean | No         |\n\n### Offline Mode\n\nYou can optionally enable the offline mode which uses locally stored JSON files instead of the live remote Deta Base service. This is very helpful during development as it allows you to run your queries against a test database and then use the live one in production. And of course, it works offline so you can develop your application with all of the benefits of Base without internet access.\n\nYou can enable the offline mode via the environment variable `DETA_OFFLINE` or using the Base option `offline`:\n\n```ts\nconst OfflineBase = new DetaOrm.Base('name', schema, {\n    offline: true\n})\n```\n\n\u003e It will store the JSON files in a `.deta-base-orm` folder by default\n\n## ⚙️ Options\n\nYou can optionally pass a options object to the the Base contructor:\n\n```ts\nconst options = { /* optional */ }\nconst Base = new DetaOrm.Base(name, schema, options)\n```\n\nHere are all the available options:\n\n### Key generation\n\nBy default this library will generate random keys which are in an ascending order, meaning that new documents will always be at the bottom. You can change this by setting `descending` to true. New documents will then be at the top of your Base.\n\nIn ascending mode it will use the Unix timestamp and in descending order `maxDateNowValue (8.64e15) - Unix timestamp` to make sure the key is sequential. The key will be appended with a random id to make sure two documents created at the same time do not have the same key. The final key will consist of the sequential timestamp in hex plus a 5 char random id.\n\n\u003e Because the timestamp is in ms, the key is only sequential until a certain point i.e two keys generated in the same ms may not be in the right order\n\n### Offline Mode\n\nSet `offline` to true to enable the offline mode. In offline mode it will use a locally stored JSON files instead of the live remote Deta Base service.\n\nThe JSON files will by default be stored in a `.deta-base-orm` directory but this can be changed with the `storagePath` option. For each Base you create, a JSON file with the same name will be created in that folder. E.g the data for a Base with the name Kittens will be stored in `Kittens.json`.\n\n**Important Note:** The offline mode is currently still in development and doesn't yet support everything that is available with the normal Base service. Here are all the things that are missing/don't fully work yet:\n\n- Advanced [query operators](#filtering): The query currently only supports direct matching of values i.e. `{ name: 'Maxi', age: 18 }`\n- Limits/paging: Currently all items are returned that match the query\n- Cross referencing: Running `.populate()` to cross reference items across Bases doesn't work and returns an error\n\n### Timestamp\n\nThis library can optionally add a `createdAt` field to each new document containing the timestamp of when it was created. To enabled this, set `timestamp` to true.\n\n### Custom Base\n\nIf you already have a Deta Base instance you can pass it to Deta Base ORM as the `db` option to reuse it:\n\n```ts\nconst deta = Deta()\nconst db = deta.Base(name)\n\nconst Base = new DetaOrm.Base(name, schema, { db })\n```\n\n## 💡 Planned features/To Do\n\n- Add custom methods/actions to the Base\n- Make populate work offline\n- Add populate as chained command to `find()`\n- Populate multiple paths at once\n\n## 💻 Development\n\n- run `yarn lint` or `npm run lint` to run eslint.\n- run `yarn watch` or `npm run watch` to watch for changes.\n- run `yarn build` or `npm run build` to produce a compiled version in the `lib` folder.\n\n## ❔ About\n\nThis project was developed by me ([@betahuhn](https://github.com/BetaHuhn)) in my free time. If you want to support me:\n\n[![Donate via PayPal](https://img.shields.io/badge/paypal-donate-009cde.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=394RTSBEEEFEE)\n\n[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/F1F81S2RK)\n\n## 📄 License\n\nCopyright 2021 Maximilian Schiller\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetahuhn%2Fdeta-base-orm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbetahuhn%2Fdeta-base-orm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetahuhn%2Fdeta-base-orm/lists"}