# Welcome to Novel

{% hint style="danger" %}
🚧 **Documentation is still under construction** 🚧

Please send us a message in our community or email us for direct answers to questions.
{% endhint %}

{% hint style="info" %}
Current Stable Version: **2025.1.0** (December 2024)
{% endhint %}

Thank you for purchasing Novel. We hope you find value in this software as much as we do from your purchase.

Novel is a full-stack SaaS starter kit built with Node.js, Next.js, React, PostgreSQL, and best practices that can get your team up and running as fast as possible.

Novel was built to scratch an itch on our experience of building and re-building blocks of features for different companies and sites. This is the amalgamation of those.

The product suite includes:

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td><h3>Novel Server</h3></td><td>API Server</td><td><a href="/pages/ki7DxkneUcxBSynhjHzM">/pages/ki7DxkneUcxBSynhjHzM</a></td></tr><tr><td></td><td><h3>Novel Web</h3></td><td>Open Source Next.js UI</td><td><a href="/pages/heRGiusLo6iAzqeoB45S">/pages/heRGiusLo6iAzqeoB45S</a></td></tr><tr><td><p></p><h3>Novel Studio</h3></td><td>Admin Dashboard</td><td></td><td><a href="/pages/AxiNL25ismUGRiTqhUem">/pages/AxiNL25ismUGRiTqhUem</a></td></tr><tr><td></td><td><h3>Novel Native</h3></td><td>Mobile App. <em>Coming Soon!</em></td><td></td></tr></tbody></table>

***

The documentation is split based on the layers that you will be interacting with as you develop. If you have purchased a standard license, all products should be available to you.

## Accept Invites

You would have been invited to a private Discord Community and the private Github repository under <https://github.com/madewithnovel>. Make sure you have accepted these invites. If you have any problems, send us a message at <hello@novel.dev>.

* [ ] [Discord Community](https://discord.com/channels/889773464809000970)
* [ ] [Madewithnovel Github Repository](https://github.com/madewithnovel)

## Quick Start

Get up and running by running the code in your terminal

```sh
mkdir your-new-project
cd your-new-project
curl -fsSL https://novel.dev/install.sh | bash
```

Continue to [Getting Started](/start) to finish your setup.

{% embed url="<https://docs.novel.dev/start>" %}

## Work on something

Head over to [Guides](https://docs.novel.dev/guides/) for other useful tips.

{% embed url="<https://docs.novel.dev/guides>" %}

### Configuring your Instance

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I define my Pricing?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-pricing">https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-pricing</a></td></tr><tr><td>How can I send emails?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/sending-emails">https://docs.novel.dev/guides/knowledge-base/novel-server/sending-emails</a></td></tr><tr><td>What configurations are available?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-novel">https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-novel</a></td></tr></tbody></table>

### Working with the API

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I create a new endpoint?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/creating-a-rest-endpoint">https://docs.novel.dev/guides/knowledge-base/novel-server/creating-a-rest-endpoint</a></td></tr><tr><td>How do I secure endpoints?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint">https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint</a></td></tr><tr><td>How can I write my own middleware?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/writing-a-middleware">https://docs.novel.dev/guides/knowledge-base/novel-server/writing-a-middleware</a></td></tr><tr><td>How do I use background jobs?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/background-jobs">https://docs.novel.dev/guides/knowledge-base/novel-server/background-jobs</a></td></tr><tr><td>How can I set up a scheduled cron job?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/cron-jobs">https://docs.novel.dev/guides/knowledge-base/novel-server/cron-jobs</a></td></tr><tr><td>How do I setup my Social Providers</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/setting-up-oauth">https://docs.novel.dev/guides/knowledge-base/novel-server/setting-up-oauth</a></td></tr><tr><td>How do I write tests for my features?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/writing-end-to-end-tests">https://docs.novel.dev/guides/knowledge-base/novel-server/writing-end-to-end-tests</a></td></tr></tbody></table>

### Working with Data

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I create a new Table?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/creating-a-table">https://docs.novel.dev/guides/knowledge-base/database/creating-a-table</a></td></tr><tr><td>How do I override an existing Model?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/overriding-a-model">https://docs.novel.dev/guides/knowledge-base/novel-server/overriding-a-model</a></td></tr><tr><td>How can I query my new tables? </td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/writing-queries">https://docs.novel.dev/guides/knowledge-base/database/writing-queries</a></td></tr><tr><td>How do I make updates?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/writing-mutations-updates-deletes">https://docs.novel.dev/guides/knowledge-base/database/writing-mutations-updates-deletes</a></td></tr><tr><td>How does validation work?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/using-validations">https://docs.novel.dev/guides/knowledge-base/database/using-validations</a></td></tr></tbody></table>

### Working with the UI

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I make a new Page?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/creating-a-marketing-page">https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/creating-a-marketing-page</a></td></tr><tr><td>How do I make a request to my API?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/making-a-request-to-the-api">https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/making-a-request-to-the-api</a></td></tr><tr><td>How can I add my own styles?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/applying-styles">https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/applying-styles</a></td></tr><tr><td>How do I add shadcn/ui to my project?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/using-shadcn-ui">https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/using-shadcn-ui</a></td></tr><tr><td>How can I write tests for my UI?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/writing-tests-with-playwright">https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/writing-tests-with-playwright</a></td></tr></tbody></table>

### Going to Production

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>Production Checklist</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/production-checklist">https://docs.novel.dev/guides/production-checklist</a></td></tr><tr><td>Deploying with AWS ECS, Fargate, and RDS</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/deployment/deploying-to-aws-ecs-fargate-and-rds">https://docs.novel.dev/guides/deployment/deploying-to-aws-ecs-fargate-and-rds</a></td></tr><tr><td>Deploying the UI with Vercel</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/deployment/deploying-novel-web-to-vercel">https://docs.novel.dev/guides/deployment/deploying-novel-web-to-vercel</a></td></tr><tr><td>Deploying with AWS EC2 and RDS</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/deployment/deploying-to-aws-ec2-and-rds">https://docs.novel.dev/guides/deployment/deploying-to-aws-ec2-and-rds</a></td></tr><tr><td>Deploying with a VPS</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/deployment/deploying-to-digital-ocean">https://docs.novel.dev/guides/deployment/deploying-to-digital-ocean</a></td></tr></tbody></table>

***

## Contributing

You or your team can send over pull requests for any immediate changes you need to see in Novel. It will be reviewed according to Novel's technical direction and capabilities. You will be advised by Novel on whether PRs can be merged or if it is a custom implementation that should live in your application instead.

Please abide by the provided [Software License](https://novel.dev/legal/license).

## Support

If you require support and training on this software. Send us a message at <hello@novel.dev> and we will get back to you within 1 business day.


# Start

{% hint style="danger" %}
🚧 **Documentation is still under construction** 🚧

Please send us a message in our community or email us for direct answers to questions. If there are missing pages, or broken links. Don't hesitate to let us know!
{% endhint %}

{% hint style="info" %}
**Disclaimer**

You are expected to know the basics of web development as well as working with Node.js, Next.js, React, and some database and UI design.

The software will get you 90% of the way there, you will still need to perform some manual procedures and Novel will be there to help.
{% endhint %}

## Express Installation

You can install Novel via the following commands and it will get you an almost-ready Novel instance.&#x20;

{% stepper %}
{% step %}

### <mark style="background-color:yellow;">Run the installation script</mark>

{% code title="Unix / OSX / Powershell" %}

```sh
mkdir your-new-project
cd your-new-project
curl -s https://novel.dev/install.sh | bash
```

{% endcode %}

If you have access and the right permissions in your system, it'll install everything needed for the project.
{% endstep %}

{% step %}

### Run the development server

You can then run the development server.

{% code title="Unix / OSX / Powershell" %}

```sh
novel dev
```

{% endcode %}
{% endstep %}

{% step %}

### You are ready :tada:

Go to <http://localhost:7634> to see the default screens and API.
{% endstep %}

{% step %}

### Set up Stripe and Postmark

Novel will run just fine without these settings, but you wont be able to create accounts and send email.&#x20;

[Get your Stripe Keys →](https://docs.novel.dev/guides/knowledge-base/general-information/getting-stripe-keys)

[Get your Postmark Keys →](https://docs.novel.dev/guides/knowledge-base/general-information/getting-postmark-keys)
{% endstep %}
{% endstepper %}

## Manual Installation

If at any point the express installation does not work for you, you may refer to the installation.

This assumes you are in a unix environment: Unix / OSX / Powershell

{% stepper %}
{% step %}

### Install Git / Homebrew / NVM

You will need to install the package and runtime managers needed by Novel

#### Install Homebrew

Reference: [https://brew.sh](https://brew.sh/)

```sh
curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh
```

#### Install NVM

Reference: <https://github.com/nvm-sh/nvm#installing-and-updating>

```sh
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
```

#### Install Git

You can follow the instruction here <https://git-scm.com/downloads/mac> or run

```sh
brew install git
```

{% endstep %}

{% step %}

### Install node.js

Now that NVM is available, you can install the latest version of node easily

```
nvm install 23
```

{% hint style="warning" %}
If this is not available, try to close your terminal and open it again. If all else fails, copy below into the terminal
{% endhint %}

```sh
# ONLY IF NVM FAILS TO RUN
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
```

{% endstep %}

{% step %}

### Accept the Github Organization Invite

Head over to the <https://github.com/madewithnovel> repository and accept the Invitation.

If you do not see this, please send us a message at <hello@novel.dev>
{% endstep %}

{% step %}

### Clone the Novel codebase

Git should also be available in your machine. Before running this, make sure you have your key exchange is set up with github

You can see this manual if you havent

{% embed url="<https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account>" %}

Once you have done this, you can now clone the repository by doing this in your terminal

```sh
mkdir [YOUR SAAS DIRECTORY]
cd [YOUR SAAS DIRECTORY]
git clone git@github.com:madewithnovel/novel.git
rm -rf web
git clone git@github.com:madewithnovel/next.js.git web
npm install
mv .env.sample .env
```

You should have the API and WEB codebase available to you.
{% endstep %}

{% step %}

### Set-up your  Database

Now you need to set up your database, you have 2 options: Docker or native install.

#### Install Docker

If you dont have docker available. Install it via this page&#x20;

<https://docs.docker.com/desktop/setup/install/linux/>

#### Docker Available

If you have docker available, you can run the following command in your terminal

```sh
docker run --name novel-db \
            -e POSTGRES_PASSWORD=postgres \
            -e POSTGRES_USER=postgres \
            -e POSTGRES_DB=novel \
            -p 5433:5433 \
            -d postgres:17
```

### Add the Connection String to the Environment file \`.env\`

Replace the DB\_HOST on line 4 of the new .env file

&#x20;<img src="/files/8ALeIoSfI6aCXxGrUNP1" alt="" data-size="original">

To the value below

<pre><code><strong>DB_HOST=postgres://postgres:postgres@localhost:5433/novel
</strong></code></pre>

{% endstep %}

{% step %}

### Start the Development Server

You should have every part of the system ready. You are now able to run your instance by doing

```sh
novel dev
```

Proceed to the next steps to continue your development.
{% endstep %}
{% endstepper %}

## Next steps

You can go to our guides to start a SaaS speedrun

[SaaS Speedrun →](https://docs.novel.dev/guides/shipping-fast/speedrun)

[Designing your SaaS →](https://docs.novel.dev/guides/shipping-fast/designing-your-saas)

[Creating a Marketing Page →](https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/creating-a-marketing-page)

[Creating an Application Screen →](https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/creating-an-application-screen)


# Philosophy

The whole motivation behind Novel was to make it easy for engineers to start and maintain convention but allow enough escape hatches for growth. The development of Novel follows the same vein.

## **Monolith-First**

Learn the domains of the business as safe as possible

## Low Overhead

Make use of as little dependencies and managed service as possible

## **Security-first**

Be paranoid of data coming in or out

## **Simple Interfaces**

Consistent and small interfaces

## **Transparent Tooling**

Wrestling with tooling costs money

## **Development-team Friendly**

The code should be easy to jump into


# Tech Stack

Each component within the various apps make use of vetted 3rd party libraries. They are pinned versions so you get the behaviour expected based on the versions in your codebase. Subsequent updates to the codebase will let you know when these versions get updated and what their impacts are.

The application makes use of Typescript and ESLint.

NPM is the default package manager for Novel.

{% tabs %}
{% tab title="Novel API" %}

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3>Fastify</h3></td><td></td><td></td></tr><tr><td><h3>Knex / Objection</h3></td><td></td><td></td></tr><tr><td><h3>PG-Boss</h3></td><td></td><td></td></tr></tbody></table>

* AJV
* React-email
* Socket.io
* Swagger/OpenAPI
* Grant
* CASL
* Nodemailer
* Postmark
* Stripe
* Undici

You can see the full list at <https://github.com/madewithnovel/novel/blob/main/packages/novel/package.json>
{% endtab %}

{% tab title="Novel Web" %}

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3>Next.js</h3></td><td></td><td></td></tr><tr><td><h3>Shadcn/UI</h3></td><td></td><td></td></tr><tr><td><h3>TailwindCSS</h3></td><td></td><td></td></tr></tbody></table>

* Lucide
* React Hook Form
* React Dropzone
* React Hot Toast
* Next-intl
* Zod
* Socket.io

You can see the full list at <https://github.com/madewithnovel/next.js/blob/main/packages/next/package.json>
{% endtab %}
{% endtabs %}

## Changelog

* 2024-12-20 - Initial Documentation


# Releases

## Schedule

Novel will have a quarterly major release schedule and an indefinite number of minor patches.&#x20;

| January 2025 | March 2025 | June 2025 | September 2025 |
| ------------ | ---------- | --------- | -------------- |
| 2025.1.0     | 2025.2.0   | 2025.3.0  | 2025.4.0       |

Nightly releases will be available on the relevant repositories under the `nightly`branches.

## Upgrading

{% hint style="warning" %}
THIS FEATURE IS SCHEDULED FOR 2025.2.0
{% endhint %}

## Support Coverage <a href="#upgrade-support-coverage" id="upgrade-support-coverage"></a>

This document outlines the upgrade process and scope of support for the software. For full details on warranty and support coverage, refer to the `WARRANTY.md` file.

Upgrades to the software are supported as follows:

### Covered Directories <a href="#covered-directories" id="covered-directories"></a>

The following directories are covered under the warranty as specified in `WARRANTY.md`. Upgrades in these directories will be fully supported and aligned with the upgrade process detailed at <https://docs.novel.dev/upgrading>:

* **`deploy`**: All deployment-related configurations and files.
* **`packages`**: Core software logic and modules within this directory.
* **`test`**: Tests provided within the covered directories.

### Userland Directories <a href="#userland-directories" id="userland-directories"></a>

The following directories fall outside the scope of full warranty coverage. Any upgrades affecting these directories will be handled on a best-effort basis. Code generation tools may be provided where feasible to assist with integration:

* **`app`**: Application-specific files, user customization, and logic.
* **`components`**: User-defined or customized UI components.
* **`test`** (outside covered files): User-modified test cases in unrelated directories.

### Important Notes <a href="#important-notes" id="important-notes"></a>

1. Modifications to files within the `packages` directory will void warranty coverage for those files.
2. It is recommended to maintain backups of your customizations before proceeding with upgrades.
3. For a step-by-step guide and resources for upgrading supported directories, visit <https://docs.novel.dev/updates>.


# Versions

Novel uses a versioning scheme that clearly communicates the development cycle and stability of the software.&#x20;

The format follows `YYYY.M.P`, where `YYYY` represents the release year, `M` denotes the major version number for significant updates or milestone features within that year, and `P` indicates patches or smaller incremental updates to address issues or refine features.

## Major Releases

**Major releases**, such as `2024.1.0`, introduce substantial changes, enhancements, or entirely new features. These updates are planned and released a few times annually, depending on the roadmap and development progress. The middle segment increment signals the introduction of significant improvements, ensuring users can anticipate and distinguish major updates.

## Patch Versions

**Patch versions** increment the last segment (e.g., `2024.1.1`) and are typically released for bug fixes, stability improvements, or minor enhancements. These updates are rolled out as needed to maintain the software's reliability and performance, ensuring that critical issues are resolved promptly without waiting for the next major version.


# Changelog

The novel team announces new releases via all known social channels under @madewithnovel

***

### 2025.1

**December 2024**. This is the official release of Novel Web and Novel API.

* New: Open-Sourced Novel Next.js
* New: Novel API
* New: Novel Studio


# Warranty

Updated November 2024

THIS WARRANTY AGREEMENT ("WARRANTY") is provided for the Software as defined below. By using the Software, you accept and agree to the terms of this Warranty.

### 1. DEFINITIONS <a href="#id-1-definitions" id="id-1-definitions"></a>

* "Software" refers to the SaaS boilerplate/starter kit and all its components.
* "Packages Directory" refers to the directory named "packages" within the Software.
* "Author" refers to the creator(s) and copyright holder(s) of the Software.
* "Customer" refers to any person or entity that obtains and uses the Software.

### 2. SCOPE OF WARRANTY <a href="#id-2-scope-of-warranty" id="id-2-scope-of-warranty"></a>

The Author provides warranty coverage exclusively for:

* Components within the Packages Directory in their unmodified form
* Documented APIs and interfaces as explicitly specified in the documentation

### 3. LIMITED SUPPORT <a href="#id-3-limited-support" id="id-3-limited-support"></a>

Limited technical support is provided for:

* Customer modifications outside the Packages Directory
* Integration issues with third-party software
* General usage queries and implementation guidance

### 4. WARRANTY EXCLUSIONS <a href="#id-4-warranty-exclusions" id="id-4-warranty-exclusions"></a>

This warranty becomes void if:

* Any modifications are made to files within the Packages Directory
* The Software is used in violation of the accompanying license agreement
* The Software is deployed in violation of provided specifications
* The Software is combined with incompatible third-party software

### 5. DISCLAIMER OF LIABILITY <a href="#id-5-disclaimer-of-liability" id="id-5-disclaimer-of-liability"></a>

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

### 6. LIMITATION OF LIABILITY <a href="#id-6-limitation-of-liability" id="id-6-limitation-of-liability"></a>

The Author's total liability for any and all claims related to the Software shall not exceed the amount paid by the Customer for the Software. The Author is not liable for any indirect, incidental, special, or consequential damages.

### 7. SUPPORT TERMS <a href="#id-7-support-terms" id="id-7-support-terms"></a>

Support is provided on a reasonable-effort basis and is limited to:

* Documentation clarification
* Bug fixes for covered components
* Security updates for covered components
* Implementation guidance within the scope of covered components

### 8. MODIFICATION OF WARRANTY <a href="#id-8-modification-of-warranty" id="id-8-modification-of-warranty"></a>

The Author reserves the right to modify this Warranty at any time. Continued use of the Software following any modification constitutes acceptance of the modified Warranty.

### 9. GOVERNING LAW <a href="#id-9-governing-law" id="id-9-governing-law"></a>

This Warranty shall be governed by and construed in accordance with the laws of the jurisdiction in which the Author is established, without regard to conflicts of law principles.

### 10. SEVERABILITY <a href="#id-10-severability" id="id-10-severability"></a>

If any provision of this Warranty is held to be unenforceable or invalid, such provision shall be changed and interpreted to accomplish the objectives of such provision to the greatest extent possible under applicable law, and the remaining provisions shall continue in full force and effect.


# Security Policy

Updated November 2024

This security policy outlines the procedures for handling security vulnerabilities and maintaining the security of the software following OWASP security principles.

### 1. SUPPORTED VERSIONS <a href="#id-1-supported-versions" id="id-1-supported-versions"></a>

Below are the versions of the software currently receiving security updates:

| Version | Support Status | End of Support |
| ------- | -------------- | -------------- |
| 2025.1  | Full Support   | Current        |

Future versions will be announced with their respective support periods. Legacy versions will be listed here when available.

### 2. SECURITY COVERAGE <a href="#id-2-security-coverage" id="id-2-security-coverage"></a>

Security support is provided for:

* All unmodified components within the Packages Directory
* Core APIs and interfaces as documented
* Authentication and authorization systems
* Data handling routines in unmodified code

Limited or no security coverage is available for:

* Modified components in the Packages Directory
* Custom code outside the Packages Directory
* Third-party integrations
* Custom deployment configurations

### 3. REPORTING A VULNERABILITY <a href="#id-3-reporting-a-vulnerability" id="id-3-reporting-a-vulnerability"></a>

We take all security vulnerabilities seriously. Please follow these steps to report a security issue:

* DO NOT report security vulnerabilities through public GitHub issues
* Email <security@novel.dev> with detailed information
* Include the following information in your report:
  * Type of vulnerability
  * Full path to source file(s)
  * Step-by-step reproduction instructions
  * Impact of the vulnerability
  * Suggested fix (if available)

### 4. DISCLOSURE POLICY <a href="#id-4-disclosure-policy" id="id-4-disclosure-policy"></a>

Our disclosure process follows these principles:

* Reporter receives confirmation within 48 hours
* Issue is verified and assessed within 7 days
* Fix development begins for verified issues
* Security advisory is published once patch is ready
* Public disclosure after customers have update window

### 5. SECURITY UPDATE PROCESS <a href="#id-5-security-update-process" id="id-5-security-update-process"></a>

Security updates follow OWASP risk assessment methodology:

* Critical vulnerabilities: 24-48 hour response
* High severity: 1 week response
* Medium severity: 2 week response
* Low severity: Next scheduled release

### 6. OWASP SECURITY PRINCIPLES <a href="#id-6-owasp-security-principles" id="id-6-owasp-security-principles"></a>

Our security implementation follows OWASP principles:

* Defense in Depth
* Principle of Least Privilege
* Secure by Default
* Fail Securely
* Security Through Design
* Input Validation and Output Encoding
* Keep Security Simple (KISS)

### 7. SECURITY FEATURES <a href="#id-7-security-features" id="id-7-security-features"></a>

The software implements OWASP recommended security controls:

* Strong Authentication System
* Role-based Access Control (RBAC)
* API Rate Limiting
* Comprehensive Input Validation
* XSS Protection
* CSRF Protection
* SQL Injection Prevention
* Security Headers
* Audit Logging
* Secure Session Management

### 8. SECURITY BEST PRACTICES <a href="#id-8-security-best-practices" id="id-8-security-best-practices"></a>

We recommend following these OWASP-aligned practices:

* Regular dependency updates
* Security feature enablement as documented
* Environment variable usage for sensitive data
* Rate limiting implementation
* Audit logging enablement
* Security scanning integration
* Access control implementation
* Secure communication protocols

### 9. INCIDENT RESPONSE <a href="#id-9-incident-response" id="id-9-incident-response"></a>

In case of a security incident:

* Isolate affected systems
* Report to <security@novel.dev>
* Preserve logs and evidence
* Wait for response team instructions
* Document all actions taken

### 10. SECURITY MONITORING <a href="#id-10-security-monitoring" id="id-10-security-monitoring"></a>

We recommend implementing:

* System activity monitoring
* Error and access log monitoring
* Failed authentication monitoring
* API usage monitoring
* Database access monitoring

### 11. SECURITY ASSESSMENTS <a href="#id-11-security-assessments" id="id-11-security-assessments"></a>

We conduct annual security assessments including:

* Third-party penetration testing
* Professional security code audit
* Automated security scanning
* Dependency vulnerability monitoring
* Architecture security reviews

Results of these assessments inform our security roadmap and improvements. Details about our latest security audit can be requested by verified customers.

### 12. MODIFICATION OF POLICY <a href="#id-12-modification-of-policy" id="id-12-modification-of-policy"></a>

This security policy may be updated from time to time. Check the version number and modification date to ensure you have the latest version.

For any questions regarding this security policy, please contact: <security@novel.dev>


# Errors

This page is a catalog of known errors in the Novel Software. Check the errors listed as sub pages or the error directory in Novel API

<https://github.com/madewithnovel/novel/tree/main/packages/novel/errors>

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/creating-an-error-file>" %}


# Cannot start Novel


# Unauthorized


# Invalid Session


# Validation Failed


# Getting Started

Your license grants you access to the <https://github.com/madewithnovel/novel> repository where the Novel API codebase can be cloned and installed from.

{% embed url="<https://docs.novel.dev/start#express-installation>" %}

Novel Web, Novel Studio, and Novel Native(Soon) all make use of the Novel API to function. You will not be able to use any of these other products without Novel API.

You can start by checking any of the guides below or searching with the documentation search box.

## Configuring your Instance

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I define my Pricing?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-pricing">https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-pricing</a></td></tr><tr><td>How can I send emails?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/sending-emails">https://docs.novel.dev/guides/knowledge-base/novel-server/sending-emails</a></td></tr><tr><td>What configurations are available?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-novel">https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-novel</a></td></tr></tbody></table>

## Working with the API

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I create a new endpoint?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/creating-a-rest-endpoint">https://docs.novel.dev/guides/knowledge-base/novel-server/creating-a-rest-endpoint</a></td></tr><tr><td>How do I secure endpoints?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint">https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint</a></td></tr><tr><td>How can I write my own middleware?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/writing-a-middleware">https://docs.novel.dev/guides/knowledge-base/novel-server/writing-a-middleware</a></td></tr><tr><td>How do I use background jobs?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/background-jobs">https://docs.novel.dev/guides/knowledge-base/novel-server/background-jobs</a></td></tr><tr><td>How can I set up a scheduled cron job?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/cron-jobs">https://docs.novel.dev/guides/knowledge-base/novel-server/cron-jobs</a></td></tr><tr><td>How do I setup my Social Providers</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/setting-up-oauth">https://docs.novel.dev/guides/knowledge-base/novel-server/setting-up-oauth</a></td></tr><tr><td>How do I write tests for my features?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/writing-end-to-end-tests">https://docs.novel.dev/guides/knowledge-base/novel-server/writing-end-to-end-tests</a></td></tr></tbody></table>

## Working with Data

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>How do I create a new Table?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/creating-a-table">https://docs.novel.dev/guides/knowledge-base/database/creating-a-table</a></td></tr><tr><td>How do I override an existing Model?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/novel-server/overriding-a-model">https://docs.novel.dev/guides/knowledge-base/novel-server/overriding-a-model</a></td></tr><tr><td>How can I query my new tables? </td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/writing-queries">https://docs.novel.dev/guides/knowledge-base/database/writing-queries</a></td></tr><tr><td>How do I make updates?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/writing-mutations-updates-deletes">https://docs.novel.dev/guides/knowledge-base/database/writing-mutations-updates-deletes</a></td></tr><tr><td>How does validation work?</td><td></td><td></td><td><a href="https://docs.novel.dev/guides/knowledge-base/database/using-validations">https://docs.novel.dev/guides/knowledge-base/database/using-validations</a></td></tr></tbody></table>


# Project Structure

Novel adopts a monorepo approach made available by NPM. Workspaces for pnpm and yarn are different to NPM as everything is hoisted to the root node\_modules. This allows to reference code easily in imports.

You can inspect Novel Web's project structure here <https://github.com/madewithnovel/next.js>

Novel API follows a similar structure

<details>

<summary>App</summary>

This is where your application lives. All business features and logic reside here. You have full control over these folders

* API - where your route handlers live. See [Routing](/novel-server/routing)
* Auth - where your auth handlers live. See [Authentication](/novel-server/authentication)
* Errors - collection of errors for your app. See [Errors](/novel-server/errors)
* Events - collection of events that is emitted from your app. See [Events](/novel-server/events)
* Features - a generic folder where you can collate features/services for your app.
* Migrations - definition of your database. See [Migrations](/novel-server/migrations)
* Models - a collection of automatically generated models based on your database. See [Models](/novel-server/models)
* Schema - a generic folder where you can store jsonschema, zod, or typebox definitions.
* Tasks - where your background tasks and cron jobs live. See [Background Jobs](/novel-server/background-jobs)
* Templates - where email templates are compiled from. See [Mail](/novel-server/mail)

You can make use of fastify conventions to add additional middlewares, plugins by exposing an `index.js`in this directory.

</details>

<details>

<summary>Config</summary>

This is where configuration of your instance lives. These are non-environment variable dependent configuration.

See [Configuration](/novel-server/configuration)

</details>

<details>

<summary>Deploy</summary>

These are utility and Infrastructure-as-code scripts useful for deployment.

See [Deployment](/novel-server/deployment)

</details>

<details>

<summary>Packages</summary>

This is where Novel core code lives. It is not advisable to modify any packages that come from this folder. Warranty covers the directories that contain the Warranty.md file.

Telemetry is sent to Novel based on errors that stem from these packages. It helps us develop Novel and determine which features need work.

See [Warranty](/warranty)

</details>

<details>

<summary>Storage</summary>

If you are not using Cloudflare or S3 for your file uploads, the default storage is the storage folder. Do not delete the .gitignore in this folder.

See [Uploading Files](/novel-server/uploading-files)

</details>

<details>

<summary>Test</summary>

Test helpers and suites live in this folder. You have full control on whether you want to collocate tests or group them in this folder.

See [Testing](/novel-server/testing)

</details>

<details>

<summary>Web</summary>

This is a placeholder directory that you can install Novel web into, and Novel API will pick up and run for you. If you wish to keep these 2 codebases separate, it is allowed.

See [With Novel Web](/novel-server/with-novel-web)

</details>

## Changelog

* 2024-12-20 - Initial Documentation


# With Novel Web

Novel can be used with or without Novel Web. However, If you would like to use it with Novel Web, or if you have used the express installation method; you would have Novel Web inside your `/web` directory.

Normally, the software would be able to detect if this is inside that directory.

But if you would want to deploy Novel Web separately into Vercel, like the tutorial below

{% embed url="<https://docs.novel.dev/guides/deployment/deploying-to-vercel>" %}

You will need to perform a couple more steps.

## In different server

{% stepper %}
{% step %}

### Configure Novel API

Make sure you set the Novel API to know where the Novel Web instance is set. This is usually the domain for that instance

{% code title=".env" lineNumbers="true" %}

```sh
HOST=https://canary.novel.dev
WEB_HOST=https://demo.novel.dev

...rest of your `.env`
```

{% endcode %}
{% endstep %}

{% step %}

### Configure Novel Web

Add this environment variable in the server that you set up Novel Web on.

{% code title=".env" lineNumbers="true" %}

```sh
NEXT_PUBLIC_API_HOST=https://canary.novel.dev
...rest of your `.env`
```

{% endcode %}

For example, with Vercel

<figure><img src="/files/p5xI5wffQtErmj8EJcE7" alt=""><figcaption></figcaption></figure>

{% embed url="<https://docs.novel.dev/guides/deployment/deploying-to-vercel>" %}
{% endstep %}
{% endstepper %}

## In the same server

As long as Novel Web is inside the `/web` directory, Novel API will make sure it will serve it in the same host as the API.&#x20;

No additional set up is required.

## Changelog

* 2024-12-20 - Initial Documentation


# Configuration

You have access to different configuration that change the behaviour of Novel.

These configuration live in the `/config`directory of your Novel instance. Different files annotate which parts of the app the configuration impacts.

{% hint style="danger" %}
**ATTENTION!**

Do not commit sensitive information like passwords and API keys to your repository.
{% endhint %}

## Usage

You can use configuration from the config directory like below

<pre class="language-typescript" data-title="app/feature.ts" data-line-numbers><code class="lang-typescript"><strong>import config from 'novel/config';
</strong><strong>
</strong>config('app.host');
</code></pre>

Quick guide on how to modify these files are discussed here

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-novel>" %}

### `config(path: string, defaultValue: string)`

You can fetch any configuration from the configuration registry using JSON path. The values are available in the Configuration reference below

<pre class="language-typescript" data-title="app/feature.ts" data-line-numbers><code class="lang-typescript"><strong>import config from 'novel/config';
</strong><strong>// Using default value
</strong>config('app.host', 'some-other-host-if-it-doesnt-exist.com');
</code></pre>

### `set(path: string, value: any)`

You can also modify any setting in runtime. **This is not advisable** but you may have a need for it regardless.

<pre class="language-typescript" data-title="app/feature.ts" data-line-numbers><code class="lang-typescript"><strong>import { set } from 'novel/config';
</strong><strong>
</strong>config('custom.setting', 'john@doe.com');
</code></pre>

It is advisable to do this in a place before your app starts preferrably in `/app/index.ts`because in production mode, multiple processes will be spun up, and your setting may not be applied in different processes.

## Environment

{% hint style="info" %}
Environment variables are loaded during startup-time.
{% endhint %}

Your instance will respect the values available in your current terminal context as well as the supplied `.env`file in the project root.

You can refer to the sample file in <https://github.com/madewithnovel/novel/blob/main/.env.sample>

### Minimum Required Environment Variables

<table><thead><tr><th width="288">Environment Variable</th><th>Description</th></tr></thead><tbody><tr><td><code>HOST</code></td><td>The full domain that is used to access the instance</td></tr><tr><td><code>DB_HOST</code></td><td>The connection string used by <code>/config/database.js</code></td></tr><tr><td><code>STRIPE_API_TOKEN</code></td><td>The API token used by Stripe</td></tr><tr><td><code>POSTMARK_API_TOKEN</code></td><td>The API token used by Postmark</td></tr></tbody></table>

{% hint style="danger" %}
**ATTENTION!**

Do not commit sensitive information like passwords and API keys to your repository.
{% endhint %}

{% embed url="<https://docs.novel.dev/guides/knowledge-base/general-information/using-sentry>" %}

## Configuration

{% hint style="info" %}
Configuration is loaded during startup-time.
{% endhint %}

The default configuration that you can reference lives in `/packages/novel/lib/default.js`

The configuration files in the `/config`directory are in `.js`files and not typescript files. They do, however, can be written in esm format. They are not transpiled.

{% hint style="warning" %}
Configuration in these files are not dependent on the environment except if the config values reference environment variables.
{% endhint %}

### App

This file controls application level flags. This file is located in `/config/app.js`

<table data-full-width="false"><thead><tr><th width="175">Key</th><th width="348">Description</th><th>Default Value</th></tr></thead><tbody><tr><td>host</td><td>The hostname that this instance will be run from. In production, it is advisable to use your domain name here</td><td><ul><li>process.env.HOST</li><li>http://localhost:7634</li></ul></td></tr><tr><td>web</td><td>If you are using Novel Web standalone in vercel, you need to modify this to point to the hostname you use in vercel. See <a data-mention href="/pages/8xYJS5bBL2pVnM67IchA">/pages/8xYJS5bBL2pVnM67IchA</a></td><td><ul><li>process.env.WEB_HOST</li><li>process.env.HOST</li><li>http://localhost:7634</li></ul></td></tr><tr><td>secure</td><td>Controls whether the instance should run in https</td><td>false</td></tr><tr><td>cors.origin</td><td>Controls where requests can come from, this can be an array or a boolean, or a string of the domain that is trying to access Novel API. See <a href="https://github.com/fastify/fastify-cors">https://github.com/fastify/fastify-cors</a> for more information</td><td>true</td></tr><tr><td>session.lifetime</td><td>Controls how long the session lasts for logged in users</td><td>1h</td></tr><tr><td>socket</td><td>Controls whether sockets are enabled in the instance. See <a data-mention href="/pages/8NeTFbbhUZFuSX47A7f9">/pages/8NeTFbbhUZFuSX47A7f9</a> for more information</td><td>false</td></tr><tr><td>notify_webhook</td><td>Used internally if you would like various internal events to be emitted to the destination of your choice. Accepts slack or discord endpoints for now.</td><td>process.env.NOTIFY_WEBHOOK</td></tr><tr><td>devtools</td><td>Enables Novel devtool inspection </td><td>true</td></tr><tr><td>telemetry</td><td>Enables telemetry collection of transactions/errors within the packages/novel directory. Used for product improvements. </td><td>true</td></tr></tbody></table>

### Database

This file controls database settings. This file is located in `/config/database.js`

<table data-full-width="false"><thead><tr><th width="175">Key</th><th width="348">Description</th><th>Default Value</th></tr></thead><tbody><tr><td>connection</td><td>Connection string of the PostgreSQL database.</td><td>process.env.DB_HOST</td></tr><tr><td>cloudsql.enabled</td><td>Experimental. Enable CloudSQL endpoints</td><td>false</td></tr><tr><td>cloudsql.tables</td><td>Experimental. The tables that are allowed to be exposed by CloudSQL.</td><td>[]</td></tr></tbody></table>

### Auth

This file controls authentication related settings. This file is located in `/config/auth.js`

<table data-full-width="false"><thead><tr><th width="219">Key</th><th width="355">Description</th><th>Default Value</th></tr></thead><tbody><tr><td>mfa</td><td>Enable the use of MFA</td><td>true</td></tr><tr><td>register_on_oauth</td><td>Register the account if it doesnt exist if coming from an Oauth flow</td><td>false</td></tr><tr><td>allow_unverified</td><td>Allow accounts to access the app even if they are unverified</td><td>false</td></tr><tr><td>verification_expiry</td><td>The time the verification link is available.</td><td>3d</td></tr><tr><td>routes.login</td><td>The URL in the front end for use in redirects</td><td>/login</td></tr><tr><td>routes.mfa</td><td>The URL in the front end for use in redirects</td><td>/login/mfa</td></tr><tr><td>routes.link</td><td>The URL in the front end for use in redirects</td><td>/login/link</td></tr><tr><td>routes.forget</td><td>The URL in the front end for use in redirects</td><td>/login/forget</td></tr><tr><td>routes.verify</td><td>The URL in the front end for use in redirects</td><td>/mail/verify</td></tr><tr><td>routes.signup</td><td>The URL in the front end for use in redirects</td><td>/signup</td></tr><tr><td>routes.after_login</td><td>The URL in the front end for use in redirects</td><td>/dashboard</td></tr><tr><td>routes.logout</td><td>The URL in the front end for use in redirects</td><td>/logout</td></tr><tr><td>roles</td><td>Roles available in the app. See <a data-mention href="/pages/YnCI9EvKEucRMliY1P0r">/pages/YnCI9EvKEucRMliY1P0r</a></td><td></td></tr></tbody></table>

### Mail

This file controls mail delivery. This file is located in `/config/mail.js`

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/email-providers>" %}

{% embed url="<https://docs.novel.dev/guides/knowledge-base/general-information/getting-postmark-keys>" %}

<table data-full-width="false"><thead><tr><th width="175">Key</th><th width="348">Description</th><th>Default Value</th></tr></thead><tbody><tr><td>driver</td><td>Driver used for sending mail. smtp or postmark</td><td>postmark</td></tr><tr><td>credentials.token</td><td>The token used by postmark</td><td>process.env.POSTMARK_SERVER_TOKEN</td></tr><tr><td>credentials.host</td><td>The connection string of your SMTP server</td><td>process.env.SMTP_HOST</td></tr><tr><td>defaults.from</td><td>Default from address and name to sign off your emails</td><td>process.env.DEFAULT_MAIL_FROM</td></tr></tbody></table>

### SaaS

This file controls how your SaaS behaves. This file is located in `/config/saas.js`

{% embed url="<https://docs.novel.dev/guides/knowledge-base/general-information/getting-stripe-keys>" %}

<table data-full-width="false"><thead><tr><th width="201">Key</th><th width="348">Description</th><th>Default Value</th></tr></thead><tbody><tr><td>driver</td><td>Driver used to manage your SaaS</td><td>stripe</td></tr><tr><td>credentials.token</td><td>The API token used by Stripe</td><td>process.env.STRIPE_API_TOKEN</td></tr><tr><td>credentials.webhook</td><td>The secret used by Stripe to validate the webhook payload</td><td>process.env.STRIPE_WEBHOOK_SECRET</td></tr><tr><td>sync</td><td>Recommended. If changes in the plans config are applied to Stripe automatically</td><td>true</td></tr><tr><td>archive_on_remote</td><td>If the plans are removed in the plans config, remove them in Stripe as well</td><td>true</td></tr><tr><td>tax_included</td><td>If tax is included for any automatically generated products/prices</td><td>false</td></tr><tr><td>currencies</td><td>Available currencies you support</td><td>['USD']</td></tr><tr><td>aggregate_usage</td><td>If usage based billing is used. See <a href="https://stripe.com/billing/usage-based-billing">Usage Based BIlling</a></td><td>sum</td></tr><tr><td>upfront</td><td>Enable if upfront collection is required for subscriptions to be created.</td><td>false</td></tr><tr><td>plans</td><td>See Plans below</td><td>[]</td></tr></tbody></table>

#### Plans

{% code title="config/saas.js" lineNumbers="true" %}

```typescript
{
    // Unique ID for the plan
    id: 'standard-2024',
    
    // A human-readable name for the plan
    name: 'Standard',
    
    // Show this plan on the plans endpoint
    frontpage: true,
    
    // The monthly price for the plan
    monthly: [29],
    
    // The yearly price for the plan
    yearly: [290],
    
    // If the plan supports a trial period
    trial: 30,
    
    // The meter used for usage based billing
    meter: null,
    
    // Flags that are enabled to users/orgs that have this plan
    flags: ['projects'],
    
    // Limits applied to the users/orgs that have this plan
    limits: {
        projects: 5,
        seats: 2,
    },
}
```

{% endcode %}

See <https://github.com/madewithnovel/novel/blob/main/config/saas.js>

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/configuring-pricing>" %}

### Filesystem

This file controls how files are handled during uploads and management. This file is located in `/config/filesystem.js`

<table data-full-width="false"><thead><tr><th width="175">Key</th><th width="348">Description</th><th>Default Value</th></tr></thead><tbody><tr><td>driver</td><td>Driver to use for file management</td><td>s3</td></tr><tr><td>public_url</td><td>The URL assets use to generate their public URLs</td><td>process.env.FILESYSTEM_PUBLIC_URL</td></tr><tr><td>default_bucket</td><td>A bucket that files belong to. To comply with S3 schemes.</td><td><ul><li>process.env.FILESYSTEM_DEFAULT_BUCKET</li><li>default</li></ul></td></tr><tr><td>credentials.endpoint</td><td>URL where uploads can be sent to</td><td>process.env.FILESYSTEM_ENDPOINT</td></tr><tr><td>credentials.client_id</td><td>Credentials of the remote file system</td><td>process.env.FILESYSTEM_ACCESS_KEY_ID</td></tr><tr><td>credentials.secret_key</td><td>Credentials of the remote file system</td><td>process.env.FILESYSTEM_SECRET_ACCESS_KEY</td></tr><tr><td>types</td><td>Types supported by the upload. See <a href="https://www.npmjs.com/package/mime">https://www.npmjs.com/package/mime</a></td><td>['image', 'audio', 'video', 'application/pdf', 'application/vnd.', 'text']</td></tr></tbody></table>

{% embed url="<https://docs.novel.dev/guides/knowledge-base/general-information/using-aws-s3>" %}

{% embed url="<https://docs.novel.dev/guides/knowledge-base/general-information/using-cloudflare-r2>" %}

{% hint style="danger" %}
**ATTENTION!**

Do not commit sensitive information like passwords and API keys to your repository.
{% endhint %}

## Changelog

* 2024-12-20 - Initial Documentation


# Novel CLI

The Novel API includes a CLI that can be used as a shortcut to perform certain functionalities available with Novel. Check them below:

{% embed url="<https://docs.novel.dev/novel-server/novel-cli/novel-dev>" %}

{% embed url="<https://docs.novel.dev/novel-server/novel-cli/novel-start>" %}

{% embed url="<https://docs.novel.dev/novel-server/novel-cli/novel-new>" %}

## Changelog

* 2024-12-20 - Initial Documentation


# novel dev

You can run the Novel Development server by running

```sh
novel dev
```

This command does the following things

1. Loads the configuration and environment files
2. Runs the migration scripts from the relevant migration folders
3. Creates the base model files
4. Syncs the pricing configuration with Stripe
5. Set up Stripe CLI for local webhook usage
6. Compiles the mail templates
7. Runs the scheduler for task and background processing
8. Loads the API endpoints
9. Runs the server
10. Listens for file changes and restarts the server accordingly

{% hint style="info" %}
Debug and diagnostic information is available in the console when this command is running.
{% endhint %}

## Changelog

* 2024-12-20 - Initial Documentation


# novel start

You can run the Novel Production server by running

```sh
novel start
```

This command does the following things

1. Loads the configuration and environment files
2. Runs the migration scripts from the relevant migration folders
3. Syncs the pricing configuration with Stripe
4. Compiles the mail templates
5. Runs the scheduler for task and background processing
6. Loads the API endpoints
7. Runs the server in cluster mode

{% hint style="info" %}
Diagnostic information is available when this command is running. Your logging utility will be able to capture this information.
{% endhint %}

## Changelog

* 2024-12-20 - Initial Documentation


# novel new

Provides scaffolding methods for easy feature development.&#x20;

## Use with Anthropic

It is possible to use Anthropic's Claude to create features for your instance. Novel scaffolding will make sure that the code generated will work within the Novel codebase.

You can get your Anthropic keys from the guide below

{% embed url="<https://docs.novel.dev/guides/knowledge-base/general-information/getting-anthropic-api-keys>" %}

{% hint style="warning" %}
**DISCLAIMER**

It is important that you review the code that is generated by your scaffold before you commit and push to production.

Do your due diligence!
{% endhint %}

## `novel new api`

This command will create an api endpoint for you and put it in `/app/api`. You can then modify and enrich this endpoint once its generated.

You can start by running

```bash
novel new api
```

You will be asked the following

1. The identifier of this new api, this will be used for the function name in the api spec
2. The path of this new api, it will be registered under the path you provide&#x20;
3. The HTTP method this api will respond with
4. If the endpoint require authorization from a cookie session or api key. See [Sessions](/novel-server/sessions)
5. <mark style="background-color:orange;">If Anthropic is enabled</mark>: a prompt that describes what the endpoint should do. This generates the following: Implementation, API Spec, Helper, Test File.
6. Your files are now available on the `/app/api/identifier-in-step-1`

<div align="left"><figure><img src="/files/KEyLv5dA7Zk7Y9W0hxHJ" alt="" width="563"><figcaption></figcaption></figure></div>

## `novel new model`

This command will create migrations for you that in turn, generate model files during development.

It will ask you multiple times until you tell it to stop.

You can start this by running

```bash
novel new model
```

You will be asked the following

1. The name of the table you are creating
2. The name of the column used as a primary key
3. <mark style="background-color:orange;">If Anthropic is enabled</mark>: a prompt that describes what the table should do.
4. It will ask you if you want to add more tables
5. Your files are now available on the `/app/migrations`&#x20;

<div align="left"><figure><img src="/files/OjIas9jVdNsWG2QQFgjL" alt="" width="563"><figcaption></figcaption></figure></div>

## `novel new task`

This command creates a background job or a cron job based on your intent.

You can start this by running

```bash
novel new task
```

You will be asked the following

1. The task identifier that is used to register this task
2. If it is a cron job or not
   1. If it is a cron job, it will ask the schedule
3. <mark style="background-color:orange;">If Anthropic is enabled</mark>: a prompt that describes what the task should do.
4. Your files are now available on the `/app/tasks/id-of-the-task-in-step1`&#x20;

<div align="left"><figure><img src="/files/Fxja3lzFtiYswLMy0eiR" alt="" width="563"><figcaption></figcaption></figure></div>

## Changelog

* 2024-12-20 - Initial Documentation


# Novel API

The Novel API is used by all Novel Products to handle and manage your SaaS.&#x20;

Novel uses [Fastify](https://fastify.dev/) for its underlying routing framework.

## OpenAPI

Novel uses OpenAPI to shape the requests as much as possible. The built-in routes will have described the input and output of each endpoint. This is useful for the web client as request files are generated automatically using the spec.

[See Requests →](/novel-web/requests)

You can also check the stable API Reference here

{% embed url="<https://docs.novel.dev/novel-server/novel-api/api-reference>" %}

## Postman

A spec is available in Postman if you would like to explore

{% hint style="info" %}
This is always up-to-date and includes canary changes
{% endhint %}

{% embed url="<https://api.postman.com/collections/30329062-a6849217-4bf6-4b1d-aa17-491fc7e3a08a?access_key=PMAT-01JFH5YBZQFKWKQB8YNNXSJNG5>" %}
Public Postman Collection
{% endembed %}


# API Reference

You can download your own API reference by going to your instance's host and accessing it via `/docs/openapi/json` . example: <https://canary.novel.dev/docs/openapi/json>

{% hint style="info" %}
This document is up to date for Release 2025.1.0
{% endhint %}

## Auth

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}/callback" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/forgot" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}/mfa" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/passwordless/verify" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/signup/intent" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/reset" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/signup" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Application

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/plans" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/csrf" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/session" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/session/end" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/session/switch" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Accounts

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/events" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account" method="patch" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/security" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/sessions" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/sessions" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/settings" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/settings" method="patch" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/password" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/deactivate" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/mfa" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/mfa" method="put" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/account/mfa" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Organizations

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization" method="patch" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/events" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/invite" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/invite" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/invite/accept" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/invite/reject" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organizations" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/members" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/members" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/promote" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/organization/settings" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Subscription

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/subscription" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/subscription/cancel" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/subscription/cards" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/subscription/methods" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/subscription/methods" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/subscription/upgrade" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## API Keys

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/api-keys" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/api-keys" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/api-keys" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/api-keys/events" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Files

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/files/presign" method="put" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/files/{key}" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/files{\*}" method="put" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Notifications

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/notifications/archive" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/notifications/register" method="delete" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/notifications/register" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/notifications/register" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/notifications" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/api/v1/notifications/read" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Changelog

* 2024-12-20 - Initial Documentation


# Database

Novel makes use of PostgreSQL exclusively because it has very wide adoption across cloud and a vibrant plugin community.

Knex.js with Objection.js is the ORM of choice for the project.

{% embed url="<https://www.postgresql.org/>" %}

To install PostgreSQL, follow the [Start](/start#manual-installation) tutorial.

{% embed url="<https://knexjs.org/>" %}

{% embed url="<https://vincit.github.io/objection.js/>" %}

## Setup

If you have not followed the Getting started tutorial, it would be best to start with that

{% embed url="<https://docs.novel.dev/start>" %}

Your Novel instance will use the environment variable below to create a connection pool against your database.

{% code title=".env" lineNumbers="true" %}

```sh
DB_HOST=postgres://username:password@postgres-server:5432/novel
```

{% endcode %}

## Why not Drizzle/Prisma/TypeORM/Kysely?

* **Prisma** uses a different DSL that you most likely would not touch frequently. It also has abstraction overhead and troubleshooting issues due to having a different runtime engine.
* **Drizzle** has a smaller community but with comparable pedigree as knex
* **TypeORM** is more of an ORM but also suffers from abstraction overhead. It has better support for typescript than knex.
* **Kysely** is a newer player and have a smaller community similar to Drizzle.

## Managing the Data

There are a lot of tools out there you can use to connec to your database. What we recommend is [Datagrip by Jetbrains](https://www.jetbrains.com/datagrip/), or [pgAdmin for OSX](https://www.pgadmin.org/download/pgadmin-4-macos/).

Novel ships with default database tables that function as a foundation for all the relevant features you have access to.

These are located in `/packages/novel/migrations`.

These migrations are executed during development runtime as well as when you start the server.

{% embed url="<https://docs.novel.dev/guides/knowledge-base/database/creating-a-table>" %}

## Usage

You can use the internal database connection like below

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import db from 'novel/db';

await db('accounts').select();
```

{% endcode %}

You can use knex methods here.

## Request Bound Transactions

You can create a request bound transaction

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import db from 'novel/db';

function handler(request, reply) {
    await db().session();
    // this will run in a transaction
    await db('accounts').where('id', 1).update({ test: 1 });
    // and any db calls from required functions
    await updateAccount();
}
```

{% endcode %}

All database calls within the request lifecycle uses the transaction.

## Base Models

These models follows the multi-tenancy principle. These are also covered under warranty and not advised to be overwritten.

These models are located in `/packages/novel/models/`.

<div data-full-width="false"><figure><img src="/files/gWTSuwVepXJNtMKvHpSb" alt=""><figcaption><p>Overview of the Base Models included with Novel</p></figcaption></figure></div>

A deeper discussion and explanation of the way models work are explained below

{% embed url="<https://docs.novel.dev/novel-server/models>" %}

## Changelog

* 2024-12-20 - Initial Documentation


# Caching

Novel's philosophy of low overhead affects the function of caching because it do not use traditional caching services.

Caching in Novel makes use of PostgreSQL's `SKIP UNLOGGED` feature. So the caching layer is within the database layer itself.

The cache is located in the `cache` table.&#x20;

## Usage

You can use the cache through

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import cache from 'novel/cache';

await cache.set('key', value);

await cache.get('key');
```

{% endcode %}

The cache key is hashed and stored in the cache table.

### `cache.get(key: string)`

You can get a cached value from the database through a simple function.

### `cache.set(key: string, value: any, ttl?: number)`

You can set the value as well as a TTL for the key you want to set

### `cache.clear(key: string)`

Remove a key from the cache.

### `cache.touch(key: string, ttl: number)`

If you want to extend the TTL of a cached key.


# Migrations

Migrations in Novel API are handled by Knex.js

{% embed url="<https://knexjs.org/guide/migrations.html>" %}

The big difference with Novel is that migrations are auto executed during runtime. This means any changes to the migrations are applied immediately.

During development time, Novel auto-generates Models and the accompanying validation and schema files to be registered within the server as well as made available to you for import.

You can check how models work below

{% embed url="<https://docs.novel.dev/novel-server/models>" %}

## Best Practices

Start by following the tutorial below

{% embed url="<https://docs.novel.dev/guides/knowledge-base/database/creating-a-table>" %}

1. Name your table in plural form.
2. Be careful of your indices and primary keys. Maybe a composite primary key is more useful.
3. There is no need to add a maximum length to strings.
4. Make use of `.references('table_name.foreign_key')` when defining relationships.
5. Make use of timestamps for boolean fields.
6. Make sure you add references to organization ID or account ID where needed.
7. Avoid using UUID v4s for your Primary Keys. Consider UUID v1/v7, ULID, or normal Big Int.

## Prototyping

When prototyping, you may perform trial-and-error on tables and columns.&#x20;

{% hint style="warning" %}
At 2025.1.0, there is no automatic way to temporarily try your database changes without data loss.&#x20;
{% endhint %}

If you need to prototype your database tables temporarily, you will need to:

1. Delete the migration file row in the `_migrations` table in your database
2. Run `knex migrate:rollback` to rollback the change that was made to the database.
3. Run the migration again with your changes.

## Changelog

* 2024-12-20 - Initial Documentation


# Models

{% hint style="danger" %}
**Attention!**

Do not update or delete models in the `/packages/novel/models` directory. These files are covered by warranty. Updating them may cause unforeseen problems.
{% endhint %}

Make sure you have a quick look on how Models work with Objection.js below

{% embed url="<https://vincit.github.io/objection.js/api/model/>" %}

When Novel development starts, model files are generated based on the migration files present in your app directory.

You can start with these tutorials

{% embed url="<https://docs.novel.dev/guides/knowledge-base/database/creating-a-table>" %}

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/creating-a-custom-model>" %}

During development, whenever migration changes. These are rebuilt and linted.

Make sure you commit and push these files to your repository.

{% hint style="info" %}
Production does not generate new model files, it merely loads what's in the directories already.
{% endhint %}

## Base Models

There are tables and models that come out of the box with Novel API. Below is a representation of the whole default schema.

<figure><img src="/files/gWTSuwVepXJNtMKvHpSb" alt=""><figcaption></figcaption></figure>

Here are the most important models you should be familiar with

<details>

<summary>Accounts</summary>

These are models related to working with accounts and entities that require them. These are extendable by creating your own models in your app directory.

The following are features that require this model.

1. Verifications
2. Identities
3. Events
4. Notification Inbox
5. API Keys

</details>

<details>

<summary>Organizations</summary>

These are models related to working with organizations. These are extendable by creating your own models in your app directory.

The following are features that require this model.

1. Events
2. Account Memberships
3. Subscriptions

Most models have a reference to this model.

</details>

<details>

<summary>Subscriptions</summary>

These are models related to working with subscriptions. These are extendable by creating your own models in your app directory.

The following are features that require this model.

1. Charges
2. Usage Reporting

</details>

{% hint style="info" %}
Remember that Novel uses Multi-Tenancy by default.

Check [Organizations](/novel-server/organizations) for more information.
{% endhint %}

## Model Files

Models that are generated by Novel come with a couple of files that serve specific purposes.

These files live in your `/app/models` directory as well as `packages/novel/models.`

<table><thead><tr><th width="166">Filename</th><th width="432">Description</th><th>Editable</th></tr></thead><tbody><tr><td><code>index.ts</code></td><td><p>File that you can build your specific extensions from. </p><p>This is not overwritten during startup.</p></td><td>✅</td></tr><tr><td><code>base.ts</code></td><td><p>Generated based on your migration file. </p><p>Contains typescript definitions and Objection specific properties like relationships and id and table identifiers. This is overwritten every time the server starts.</p></td><td></td></tr><tr><td><code>schema.json</code></td><td><p>Generated based on your migration file and represents your migration file as JSON schema. </p><p>Can directly be inferred via typescript. This is overwritten every time the server starts.</p></td><td></td></tr><tr><td><code>zod.ts</code></td><td><p>Generated based on your migration file and represents your migration file as a Zod definition. </p><p>Can directly be inferred via typescript. This is overwritten every time the server starts.</p></td><td></td></tr></tbody></table>

## Conventions

From the model files discussed above, a couple of conventions are applied so it works well with Novel.

1. Each model class includes a class property of their columns and relationships.
2. Table names are defined as plural.
3. IDs can be both a single key or a composite one.
4. Zod definitions are exposed in the model itself access via the `.z`getter.
5. JSON Schema validation is built in during insert and update.
6. Each model exposes it's relationship via Objection's `.relationMappings`getter.
7. Types are inferred from both the model's properties and zod inferred definition.

## Extending Models

You can extend an existing model by using ESM/Typescript's `extends` feature.

This will let you inherit all the base model's properties and methods and apply your own from a different file.

You can start by extending like below

{% code title="app/models/accounts/index.ts" lineNumbers="true" %}

```typescript
import AccountModel from 'novel/models/accounts';

export default class Account extends AccountModel {
	async someMethod() {
		// something specific to your app
		// follow 
	}
}
```

{% endcode %}

And you can use this new model in your app like below

{% code title="app/services/some-feature.ts" lineNumbers="true" %}

```typescript
import Account from 'app/models/accounts';

await Account.someMethod();
```

{% endcode %}

{% hint style="danger" %}
**Attention!**

Do not update or delete models in the `/packages/novel/models` directory. These files are covered by warranty. Updating them may cause unforeseen problems.
{% endhint %}

## Model Helpers

Novel Models have access to the following helpers provided by Objection as well as useful lodash ones below.

Take a look at Objection's Model implementation for the helpers available by default.

{% embed url="<https://vincit.github.io/objection.js/guide/models.html>" %}

### `pick(paths)`

Provided by lodash via [.pick()](https://lodash.com/docs/4.17.15#pick). Creates an object composed of the picked `object` properties.

<pre class="language-typescript" data-line-numbers><code class="lang-typescript"><strong>const data = await SomeModel.select();
</strong>
// => [ { id: 1, name: 'john' } ]
const picked = data.map(row => row.pick('id'));

console.log(picked);
// => [ { id: 1 } ]
</code></pre>

### `omit(paths)`

Provided by lodash via [.omit()](https://lodash.com/docs/4.17.15#omit). The opposite of [`_.pick`](https://lodash.com/docs/4.17.15#pick); this method creates an object composed of the own and inherited enumerable property paths of `object` that are not omitted.

{% hint style="warning" %}
This method is considerably slower than [`_.pick`](https://lodash.com/docs/4.17.15#pick).
{% endhint %}

<pre class="language-typescript" data-line-numbers><code class="lang-typescript"><strong>const data = await SomeModel.select();
</strong>
// => [ { id: 1, name: 'john' } ]
const omitted = data.map(row => row.omit('id'));

console.log(omitted);
// => [ { name: 'john' } ]
</code></pre>

### `at(paths)`

Provided by lodash via [.at()](https://lodash.com/docs/4.17.15#at). Creates an array of values corresponding to `paths` of `object`.

<pre class="language-typescript" data-line-numbers><code class="lang-typescript"><strong>const data = await SomeModel.select();
</strong>
// => [ { id: 1, name: 'john', settings: { utc: true } } ]
const at = data.map(row => row.at('settings.utc'));

console.log(at);
// => [ true ]
</code></pre>

### `entries()`

Provided by lodash via [.toPairs()](https://lodash.com/docs/4.17.15#toPairs). Creates an array of own enumerable string keyed-value pairs for `object` which can be consumed by [`_.fromPairs`](https://lodash.com/docs/4.17.15#fromPairs). If `object` is a map or set, its entries are returned.

<pre class="language-typescript" data-line-numbers><code class="lang-typescript"><strong>const data = await SomeModel.select();
</strong>
// => [ { id: 1, name: 'john' } ]
const entries = data.map(row => row.entries());

console.log(entries);
// => [ ['id', 1], ['name', 'john'] ]
</code></pre>

### `keys()`

Provided by lodash via [.keys()](https://lodash.com/docs/4.17.15#keys). Creates an array of the own enumerable property names of `object`.\
\
**Note:** Non-object values are coerced to objects. See the [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) for more details.

<pre class="language-typescript" data-line-numbers><code class="lang-typescript"><strong>const data = await SomeModel.select();
</strong>
// => [ { id: 1, name: 'john' } ]
const keys = data.map(row => row.keys());

console.log(keys);
// => [ ['id', 'name'] ]
</code></pre>

## Validations

Objection has built-in validation via JSON schema. It is applied automatically using Objection's `jsonSchema` getter.

In-depth explanation on how validation is performed is available below.

{% embed url="<https://vincit.github.io/objection.js/guide/validation.html>" %}

During generation, a JSON schema file is generated along with the base model. This schema is also fed into fastify's AJV schema registry.

## Using Zod

Zod is available for the model through the `.z`getter. You can use it like below

<pre class="language-typescript" data-line-numbers><code class="lang-typescript"><strong>const zodModel = SomeModel.z;
</strong>
// validate
zodModel.safeParse({ someInput: true });

// get the types via infer
type zodModelType = z.infer&#x3C;typeof zodModel>
</code></pre>

Types are also available via the model's directory. eg, `novel/models/accounts/zod`.

## Referencing a Model

You are able to reference a schema in your routes by using this shorthand when defining your route schema in Fastify

{% code lineNumbers="true" %}

```json
{
    ...
    "$ref": "https://novel.dev/entities/accounts#"
}
```

{% endcode %}

## Changelog

* 2024-12-20 - Initial Documentation


# Routing

## File-based Routing

The APIs are located in your `/app/api`directory.

It autoloads all the files that live in this directory if it follows the format below.

{% code title="app/api/accounts/index.ts" lineNumbers="true" %}

```typescript
export default async function Route (instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    
    async function handler(request, reply) {
        console.log(request);
        reply.status(204);
    }
}
```

{% endcode %}

This file is automatically loaded when novel starts.&#x20;

It still needs you to specify the endpoint of the API via [Fastify's routing](https://fastify.dev/docs/latest/Reference/Routes/).

> `instance`is an instance of a Fastify plugin. All Fastify interfaces are available here and additional ones that are discussed below.

Below are all the examples of valid route registrations

{% code lineNumbers="true" %}

```typescript
instance.get('/api/v1/account', handler);
instance.post('/api/v1/account', handler);
instance.patch('/api/v1/account', handler);
instance.head('/api/v1/account', handler);
instance.put('/api/v1/account', handler);
instance.delete('/api/v1/account', handler);
instance.options('/api/v1/account', handler);
instance.any('/api/v1/account', handler);

// you can add fastify route options as well
instance.get('/api/v1/account', fastifyRouteOptions, handler);
```

{% endcode %}

The routes are attached in their own scope&#x20;

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/creating-a-rest-endpoint>" %}

## Built-in Routes

Novel comes with plenty of built-in routes that are created to glue together various SaaS functionalities.

These routes are available under `/app/api/internal/v1`and exposed under `/api/v1`.&#x20;

You can inspect these routes using the API reference in this documentation, or the files in the Novel API repository

[See API Reference →](/novel-server/novel-api/api-reference)

See <https://github.com/madewithnovel/novel/tree/main/app/api/internal/v1>

## Protecting Routes

You can secure your endpoints by making use of the various ways Novel scopes requests. There are 2 standard modes: `cookie` and `api keys`.

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint>" %}

The functions below are called [Route Directives](/novel-server/routing/route-directives). Check other route directives available to you.

## Cookie

The cookie session allows all requests into the Novel API with the `session`cookie to be acknowledged and keep a session related to a user.

### `instance.authenticated()`

You can access this session via the request variable in your handler.

{% code title="app/api/accounts/index.ts" lineNumbers="true" %}

```typescript
export default async function Route (instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    instance.authenticated();
    
    async function handler(request, reply) {
        console.log(request.session);
        reply.status(204);
    }
}
```

{% endcode %}

{% hint style="info" %}
These sessions are the same in both cookie and api key contexts.
{% endhint %}

There are also additional request variables available to you for convenience.

#### `request.account`&#x20;

This includes details on which user is accessing that request.

```typescript
request.account = {
    id: string,
    role: string,
    verified: boolean,
};
```

#### `request.org`

This includes details on which organization is being used by the current request

```typescript
request.org = {
    id: string,
}
```

{% embed url="<https://docs.novel.dev/novel-server/sessions>" %}

## Request  Context

Each route has access to a request context that uses Node's `async_hooks`, and `@fastify/request-context` library.

This allows Novel to tap into relevant context of the request outside of the request handler. For example, retrieving an account ID in a database model.

## Using API Keys

If you want to restrict endpoints based on API keys, you can use the directive below:

### `instance.authorized()`

{% code title="app/api/accounts/index.ts" lineNumbers="true" %}

```typescript
export default async function Route (instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    instance.authorized();
    
    async function handler(request, reply) {
        console.log(request.session);
        reply.status(204);
    }
}
```

{% endcode %}

{% hint style="info" %}
These sessions are the same in both cookie and api key contexts.
{% endhint %}

This will check if the API key belongs to a specific user and fill in the relevant session variables like the above.

{% hint style="info" %}
You can use both `authorized` and `authenticated` directives in the same route definition.
{% endhint %}

## Changelog

* 2024-12-20 - Initial Documentation


# Route Directives

Directives are declarative decorators available to route definitions. They provide additional functionality to the route definitions.

There are a number of active directives available below:

1. Caching
2. Rate Limiting
3. Elevated Privileges
4. Idempotency
5. Verified
6. Subscribed

## Caching

Routes can be cached by adding this directive in the route definition.

<pre class="language-typescript" data-title="app/api/accounts/index.ts" data-line-numbers><code class="lang-typescript">export default async function Route (instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    <a data-footnote-ref href="#user-content-fn-1">instance.cached();</a>
    
    async function handler(request, reply) {
        console.log(request.session);
        reply.status(204);
    }
}
</code></pre>

The directive adds an additional header to the response based on the cached behaviour

`x-cache: hit` if the content is cached

`x-cache: miss` if the content is supposed to be cached, but wasnt.

### `instance.cached()`

The default behaviour and will cache the request based on the user, url, and session of the user.

### ``instance.cached({ key: (request) ⇒ `custom-id` })``&#x20;

This overrides the key assigned in the caching table. The current request is available for use to derive a specific id.

### `instance.cached({ ttl: 60 })`

This changes the ttl (Time to live) of the cached object. This ttl is `5`seconds by default.

### `await reply.uncache(key: string, options?: CacheOptions)`

This is a decorator to reply that you can use to manually purge a cached response.

### `CacheOptions`

* ttl - Time to live of the cached object. default`5`seconds
* key - (request: Request) function to generate a cache key

## Rate Limiting

Rate limiting is provided by <https://github.com/fastify/fastify-rate-limit>

### `instance.throttled(limit: number, window: string, options?: RateLimitOptions)`

This is a separate directive that has a different signature from the throttling shown after. You can use it like any other directive

{% code title="app/api/route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    instance.throttled();
    
    async function handler(request, reply) {
        reply.status(204);
    }
}
```

{% endcode %}

### `await request.throttled(key: string, limit?: number, options?: RateLimitOptions)`

### `await request.throttled(limit: number, options?: RateLimitOptions)`

The signature for throttled allow different usage for convenience.

{% code title="app/api/route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    
    async function handler(request, reply) {
        await request.throttled('throttled');
        reply.status(204);
    }
}
```

{% endcode %}

### `await request.unthrottle(key?: string, options?: RateLimitOptions)`

If you need to explicitly reset the rate limit for a specific key.&#x20;

{% code title="app/api/route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    
    async function handler(request, reply) {
        await request.throttled('throttled');
        reply.status(204);
        await request.unthrottle('throttled');
    }
}
```

{% endcode %}

Example usage: [auth-passwordless/index.ts](https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-passwordless/index.ts#L25)

### `RateLimitOptions`

* timeWindow - the time in seconds on how long the rate limit should last
* max - the maximum amount before the throttling kicks in
* session - custom option used in generating the key

## Elevated Privileges

If you require actions to be verified by the user again, you can make use of the sudo middleware that exposes a password check before letting the action go through.

### `instance.sudo()`

<pre class="language-typescript" data-title="app/api/route/index.ts" data-line-numbers><code class="lang-typescript">export default async function Route(instance: FastifyInstance) {
    instance.post('/api/v1/accounts', handler);
    <a data-footnote-ref href="#user-content-fn-2">instance.sudo();</a>
    
    async function handler(request, reply) {
        console.log(request);
        reply.status(204);
    }
}
</code></pre>

{% hint style="warning" %}
**Important Note**

You need to add this piece of code in your schema so it can be picked up by Novel Web
{% endhint %}

{% code title="app/api/route/schema.json" lineNumbers="true" %}

```json
{
    ...rest of your schema.json
    "body": {
	"type": "object",
	"required": [...other fields, "sudo_password"],
	"properties": {
		...other fields
		"sudo_password": {
			"type": "string"
		}
	}
    }
}
```

{% endcode %}

See [Schema →](/novel-server/routing/schema)

## Idempotency

If you want to enable idempotency for a route. You can add the idempotency directive for that definition

{% code title="app/api/route/index.ts" lineNumbers="true" %}

```typescript
export default async function Route(instance: FastifyInstance) {
    instance.post('/api/v1/accounts', handler);
    instance.idempotent();
    
    async function handler(request, reply) {
        // or await request.idempotent();
        console.log(request.headers['idempotency-key']);
        reply.status(204);
    }
}
```

{% endcode %}

Requests to this endpoint need to have a unique `idempotency-key`header generated by the client.

{% code lineNumbers="true" %}

```javascript
await fetch('/api/v1/accounts', {
    method: 'POST',
    headers: {
        'idempotency-key': uuid.v1(),
    },
});
```

{% endcode %}

### `instance.idempotent(options?: IdempotencyOptions)`

If the endpoint has processed the request and a similar request with the same idempotency key is sent, it will return the response of the previous action.

### `IdempotencyOptions`

* ttl - the amount of time before the idempotency key can be reused. default 86400

## Verified

### `instance.verified()`

Check if the currently logged in user has a `verified`status.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.authorized();
    instance.verified();
    instance.get('/your/route', handler);
    
    async function handler (request, reply) {
        reply.send('ONLY FOR AUTHENTICATED API KEYS');
    }
}
```

{% endcode %}

## Subscribed

### `instance.subscribed()`

Check if the currently logged in organization has an active subscription.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.authorized();
    instance.subscribed();
    instance.get('/your/route', handler);
    
    async function handler (request, reply) {
        reply.send('ONLY FOR SUBSCRIBED ORGANIZATIONS');
    }
}
```

{% endcode %}

## Changelog

* 2024-12-20 - Initial Documentation

[^1]: This is the caching directive

[^2]: will look for `sudo_password`in the request body.


# Middleware

{% hint style="info" %}
Middlewares are called Plugins in Fastify
{% endhint %}

You can create your own middlewares by following Fastify's Plugin architecture

{% embed url="<https://fastify.dev/docs/latest/Reference/Plugins/>" %}

You can write these in your `/app/index.ts` file, or in your middlewares, or in your routes if you wish to apply route level

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/writing-a-middleware>" %}

## Route Middlewares

Route middlewares are middleware files that are applied to a group of routes in the api directory

<pre class="language-asciidoc"><code class="lang-asciidoc">app/
├── api/
│   └── v1/
│       └── accounts-search/
│           └── index.ts
│           └── schema.json
│       └── accounts-update/
│           └── index.ts
│           └── schema.json
│       └── <a data-footnote-ref href="#user-content-fn-1">middleware.ts</a>
│   └── webhooks/
│       └── stripe-webhook/
│           └── index.ts
│       └── <a data-footnote-ref href="#user-content-fn-1">middleware.ts</a>
└── ...other files
</code></pre>

From the example above, the middleware.ts file is **ONLY** applied to the route within that directory.

A minimum implementation of a middleware file is below:

{% code title="app/api/v1/middleware.ts" lineNumbers="true" %}

```typescript
export default async function Middleware (instance: FastifyInstance) {
    // `instance` is an instance of FastifyInstance
    // All interfaces available to FastifyInstance is available here
    instance.register(async function anotherPlugin() {});
    
    instance.addHook('onReply', async function (request, reply) {});
}
```

{% endcode %}

{% hint style="info" %}
**Be careful with scope**

Hooks, plugins, decorators defined in `app/api/v1/middleware.ts` will not be available to routes defined in `app/api/webhooks`.
{% endhint %}

Fastify rules for Plugins apply to this file.

{% embed url="<https://fastify.dev/docs/latest/Reference/Plugins/>" %}

Route middlewares that are available out of the box are discussed below:

## CSRF

All `POST`, `PUT`, `PATCH`, and `DELETE`methods automatically check the csrf of the incoming requests.

The CSRF is in both the local storage of the browser and cookie. If this does not exist in the request, the request will throw a `InvalidCsrfTokenError`

References:

<https://github.com/madewithnovel/novel/blob/main/packages/novel/lib/csrf.js>

<https://github.com/madewithnovel/novel/blob/main/packages/novel/errors/invalid-csrf-token.js>

## Changelog

* 2024-12-20 - Initial Documentation

[^1]: This is where your middleware sits


# Request Helpers

This is a catalog of available convenience methods available in Novel registered using Fastify's request and reply decorators.

## Request

### `request.account`&#x20;

This includes details on which user is accessing that request.

```typescript
request.account = {
    id: string,
    role: string,
    verified: boolean,
};
```

### `request.org`

This includes details on which organization is being used by the current request

```typescript
request.org = {
    id: string,
}
```

{% hint style="warning" %}
`request.account`and `request.org` are both [getters and setters](https://fastify.dev/docs/latest/Reference/Decorators/#getters-and-setters) and will trigger errors if accessed outside of `instance.authorized()`or `instance.authenticated()`directives.
{% endhint %}

### `await request.can(action: string, subject: any, fields: string[])`

Discussed in [Authorization](/novel-server/authorization)

A convenience method that allows you to check if the currently logged in user is allowed to take `action`towards the specific `subject`.

This uses the CASL `can`signature.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.authenticated();
    instance.get('/your/route', handler);
    
    async function handler (request) {
        await request.can('read', 'projects');
        reply.send('ONLY FOR SESSIONS WITH projects:read PERMISSION');
    }
}
```

{% endcode %}

See <https://github.com/madewithnovel/novel/blob/main/packages/novel/lib/session.js#L196>

### `await request.cannot(action: string, subject: any, fields: string[])`

Discussed in [Authorization](/novel-server/authorization)

This is the inverse of `await request.can()` above.

### `await request.verified()`

Check if the currently logged in user has a `verified`status.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.authorized();
    instance.get('/your/route', handler);
    
    async function handler (request) {
        await request.verified();
        reply.send('ONLY FOR AUTHENTICATED API KEYS');
    }
}
```

{% endcode %}

### `await request.subscribed()`

Check if the currently logged in organization has an active subscription.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.authorized();
    instance.get('/your/route', handler);
    
    async function handler (request) {
        await request.subscribed();
        reply.send('ONLY FOR SUBSCRIBED ORGANIZATIONS');
    }
}
```

{% endcode %}

### `request.ua()`

A convenience method that exposes an object that has the user-agent parsed by the `my-ua-parser`library.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.get('/your/route', handler);
    
    async function handler (request) {
        return request.ua();
    }
}
```

{% endcode %}

Check the[ library definition for the ua object](https://github.com/mcollina/my-ua-parser#constructor).

## Reply

### `reply.error(error: FastifyError)`

A response builder that consolidates errors and produces an error format unique to Novel.

See <https://github.com/madewithnovel/novel/blob/main/packages/novel/lib/server.js#L158>

### `await reply.uncache(key: string, options?: CacheOptions)`

This is a decorator to reply that you can use to manually purge a cached response.

{% code title="app/api/v1/your-route/index.ts" lineNumbers="true" %}

```typescript
export default function Route(instance) {
    instance.get('/your/route', handler);
    
    async function handler (request, reply) {
        await request.cache('custom-key');
        // do some specific logic
        reply.send('OK');
        await reply.uncache('custom-key');
    }
}
```

{% endcode %}

{% embed url="<https://docs.novel.dev/novel-server/routing/route-directives#cacheoptions>" %}

## Changelog

* 2024-12-20 - Initial Documentation


# Schema

Novel makes use of different schemas across the server and the frontend. It utilizes JSON Schema as well as Zod to enforce different ways of validation and shape.

{% embed url="<https://json-schema.org/>" %}

## OpenAPI

Novel uses OpenAPI to shape the requests as much as possible. The built-in routes will have described the input and output of each endpoint. This is useful for the web client as request files are generated automatically using the spec.

[See Requests →](/novel-web/requests)

You can also check the stable API Reference here

{% embed url="<https://docs.novel.dev/novel-server/novel-api/api-reference>" %}

## Route Schemas

Novel automatically picks up a `schema.json`inside your route directories. If this exists, it will try to apply it to the route options and itwill be available to the automated openapi spec.

Example

* If you have `/app/api/user-create/index.ts` , adding a `schema.json` beside index.ts will automatically load it for that route.

## Automatic Generation

The public API registered from `/app/api` is available under the docs endpoint.

```
https://localhost:7634/docs/openapi/json
```

### Swagger UI

```
https://localhost:7634/docs/openapi
```

### Scalar Documentation UI

```
https://localhost:7634/docs/reference
```

The admin API is also available from the `/admin`  route prefix. Same UI are available under these.

```
http://localhost:7634/admin/docs/openapi/json
```

## Validation / Serialization

Novel routes follow Fastify's validation and serialization strategy via AJV.&#x20;

{% embed url="<https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/>" %}

When requests and responses do not follow the spec, a ValidationFailedError is thrown.

<https://github.com/madewithnovel/novel/blob/main/packages/novel/errors/validation-failed.js>

## Models

You are able to reference a schema in your routes by using this shorthand when defining your route schema in Fastify

{% code lineNumbers="true" %}

```json
{
    ...
    "$ref": "https://novel.dev/entities/accounts#"
}
```

{% endcode %}

## Changelog

* 2024-12-20 - Initial Documentation


# Sessions

Novel maintains a session for each client that comes in via a session cookie. This is handled by <https://github.com/fastify/fastify-secure-session>. Both application and admin context's are supported.&#x20;

You can access this session via the request variable in your handler.

{% code title="app/api/accounts/index.ts" lineNumbers="true" %}

```typescript
export default async function Route (instance: FastifyInstance) {
    instance.get('/api/v1/account', handler);
    instance.authenticated();
    
    async function handler(request, reply) {
        console.log(request.session);
        reply.status(204);
    }
}
```

{% endcode %}

{% hint style="info" %}
These sessions are the same in both cookie and api key contexts.
{% endhint %}

There are also additional request variables available to you for convenience.

#### `request.account`&#x20;

This includes details on which user is accessing that request.

```typescript
request.account = {
    id: string,
    role: string,
    verified: boolean,
};
```

#### `request.org`

This includes details on which organization is being used by the current request

```typescript
request.org = {
    id: string,
}
```

## Cookie Session

Available under

```typescript
export default function Route(instance) {
    instance.authenticated();
    instance.get('/your/route', handler);
    
    async function handler () {
        reply.send('ONLY FOR AUTHENTICATED USERS');
    }
}
```

also available under request via

```
await request.authenticated();
```

you can also provide an array of roles where the endpoint only responds to the session if they have the role.

## API Session

When using an API key, you can respond to requests and scope routes under

```typescript
export default function Route(instance) {
    instance.authorized();
    instance.get('/your/route', handler);
    
    async function handler () {
        reply.send('ONLY FOR AUTHENTICATED API KEYS');
    }
}
```

also available under request via

```
await request.authorized();
```

You can provide an array of scopes specific to the key if you need to have a more granular control.

## Changelog

* 2024-12-20 - Initial Documentation


# Authentication

Novel uses industry best-practices for authenticating users. This is made possible by the `grant`library and it's support with Fastify.

We don't use an external library like OpenAuth, Auth.js, or Better Auth, as it does not fit the need of the Novel framework.

{% hint style="info" %}
This article discusses authentication coming from the server.
{% endhint %}

Novel supports the following authentication modes:

1. Email / Password
2. Magic Links
3. OAuth2

Forget Password, Email Verification, and MFA is also supported.

## Signups

You can modify how signups are handled by modifying the built in sign up endpoints and the onboarding lifecycle features in the features directory.

By default these are stored in&#x20;

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-signup/index.ts>

and the flow goes into

<https://github.com/madewithnovel/novel/blob/main/app/features/lifecycle/signup.ts>

Learn more about it below

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/setting-up-signups>" %}

## User Sessions

User sessions are the sessions usually used by your user when they interact with the frontend. These are cookie sessions.

You can have a look at what sessions are below

{% embed url="<https://docs.novel.dev/novel-server/sessions>" %}

## API Keys

API Keys are also supported when developing your endpoints. These are protected by the API Keys feature.

You can have a look at the feature below

{% embed url="<https://docs.novel.dev/novel-server/api-keys>" %}

## Single-Sign On (SSO)

{% hint style="warning" %}
SSO is not yet available as of 2025.1.0.
{% endhint %}

## Changelog

* 2024-12-20 - Initial Documentation


# Passwords

Email and password login is a traditional authentication method where users provide a registered email address and a secure password to access an application.

It offers a straightforward setup for both developers and users, with the flexibility to incorporate password policies, multi-factor authentication, and password reset workflows.&#x20;

{% hint style="info" %}
While familiar to most users, this method requires robust security measures to protect against common threats like password breaches and phishing attacks.
{% endhint %}

You can see the implementation below

<https://github.com/madewithnovel/novel/blob/main/app/auth/password.ts>

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-strategy/index.ts>

You can create a different Password flow by following this guide

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/integrating-an-oauth-provider>" %}

## Confugration

You will need to set up the `after_login` setting if you have changed the default page structure in Novel Web.

If you changed `/web/app/(app)/dashboard` to `/web/app/(app)/apps` for example, you will need to change the configuration in `/config/auth.js` .

<pre class="language-typescript" data-title="" data-line-numbers><code class="lang-typescript">/**
 * Override the redirects here with the custom pages that you have so you can reference
 * them easily in your server code.
 */
routes: {
	login: '/login',
	mfa: '/login/mfa',
	forget: '/login/forget',
	link: '/login/link',
	verify: '/mail/verify',
	signup: '/signup',
	<a data-footnote-ref href="#user-content-fn-1">after_login: '/apps',</a>
	logout: '/logout',
},
</code></pre>

## API

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}/callback" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Changelog

* 2024-12-20 - Initial Documentation

[^1]: Changed from /dashboard


# Magic Links

Magic links are secure, time-sensitive URLs that enable passwordless authentication. When users request access, they receive a link via email that logs them in upon clicking. This eliminates the need for passwords, enhancing security and simplifying the user experience.

You can see the implementation below

<https://github.com/madewithnovel/novel/blob/main/app/auth/passwordless.ts>

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-passwordless/index.ts>

You can create a different Magic Links flow by following this guide

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/integrating-an-oauth-provider>" %}

## Confugration

You will need to set up the `after_login` setting if you have changed the default page structure in Novel Web.

If you changed `/web/app/(app)/dashboard` to `/web/app/(app)/apps` for example, you will need to change the configuration in `/config/auth.js` .

<pre class="language-typescript" data-title="config/auth.js" data-line-numbers><code class="lang-typescript">/**
 * Override the redirects here with the custom pages that you have so you can reference
 * them easily in your server code.
 */
routes: {
	login: '/login',
	mfa: '/login/mfa',
	forget: '/login/forget',
	link: '/login/link',
	verify: '/mail/verify',
	signup: '/signup',
	<a data-footnote-ref href="#user-content-fn-1">after_login: '/apps',</a>
	logout: '/logout',
},
</code></pre>

## API

You can use `/auth/passwordless`  to perform a Magic Link flow.

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/passwordless/verify" method="get" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Changelog

* 2024-12-20 - Initial Documentation

[^1]: Changed from /dashboard


# Two-Factor Authentication

Two-factor authentication (2FA) using Time-based One-Time Passwords (TOTP) adds an extra layer of security to user authentication.&#x20;

These codes expire after a short period, making it difficult for attackers to gain unauthorized access even if passwords are compromised.&#x20;

You can see the implementation below

<https://github.com/madewithnovel/novel/blob/main/packages/novel/auth.js#L142>

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-mfa/index.ts>

## Confugration

You will need to set up the `mfa` setting to true in `config/auth.js`.

{% code title="config/auth.js" lineNumbers="true" %}

```typescript
export default {
	/**
	 * Allows the use of MFA for users in the application.
	 */
	mfa: true,
}
```

{% endcode %}

## API

You can use `/auth/password/mfa`  to perform a Magic Link flow.

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/{strategy}/mfa" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Changelog

* 2024-12-20 - Initial Documentation


# Forget Password

The forgot password flow allows users to securely reset their password if they cannot log in. Users enter their registered email address to initiate the process, and an email with a time-sensitive reset link is sent to them.&#x20;

Clicking the link directs the user to a secure form where they can set a new password. This flow ensures security by verifying the user’s identity through email and expiring the link after a short duration to prevent unauthorized access.

You can see the implementation below

<https://github.com/madewithnovel/novel/blob/main/app/features/lifecycle/forgot.ts>

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-forgot/index.ts>

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-reset/index.ts>

## API

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/reset" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

{% openapi src="/files/hpwpvNShDhKbLsaTo1Zx" path="/auth/forgot" method="post" %}
[openapi.json](https://1230772123-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgjPdizpdFKu8XPFFoBRj%2Fuploads%2FBSDtmhW0VYiMTiBBhApj%2Fopenapi.json?alt=media\&token=6bf741e5-9e70-4443-9b94-5c062e5c697c)
{% endopenapi %}

## Changelog

* 2024-12-20 - Initial Documentation


# Email Verification

In the email verification flow, users with an upfront payment and active subscription are automatically verified for seamless access.&#x20;

For unpaid users, an email with a time-sensitive verification link is sent. Clicking the link confirms their email address, ensuring security and a smooth onboarding process.

## Usage

A link is sent to `/mail/verify?token=[verification token]`.&#x20;

Verification happens when this token matches with a record in the database.

<https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/auth-verify/index.ts>

<https://github.com/madewithnovel/novel/blob/main/app/features/lifecycle/verify.ts>

## Changelog

* 2024-12-20 - Initial Documentation


# OAuth2 Support

Before proceeding, it would be good to check the tutorial below

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/integrating-an-oauth-provider>" %}

Integrating social OAuth2 providers is streamlined with **fastify-grant** and **grant**, enabling users to log in via platforms like Google, Facebook, or GitHub.&#x20;

With minimal effort, developers can offer secure and user-friendly login options, enhancing accessibility and reducing friction during authentication.

## Supported Providers

### Github

Implementation: <https://github.com/madewithnovel/novel/blob/main/app/auth/github.ts>

You will need to provide the Github Oauth2 App Credentials in your `.env` file.

<table><thead><tr><th width="272"></th><th>Description</th></tr></thead><tbody><tr><td>GITHUB_CLIENT_ID</td><td>The client ID for the github app</td></tr><tr><td>GITHUB_SECRET_KEY</td><td>The secret key for the github app</td></tr></tbody></table>

You can get them here: <https://github.com/settings/applications/new>

Need more? Check out the roadmap or request it in our Community.

## Changelog

* 2024-12-20 - Initial Documentation


# Authorization

Novel uses the CASL library to provide authorization checks for all the routes and actions you need for your SaaS.

{% embed url="<https://casl.js.org/v6/en>" %}

{% hint style="info" %}
**Important**

It is important to remember that authorization is an intentional exercise and not automatic. You need to put these checks in place where they are needed.
{% endhint %}

## Configuration

In `/config/auth.js` there is a roles object that you can modify to add role IDs and permission keys to.

{% code title="config/auth.js" lineNumbers="true" %}

```typescript
/**
 * These are roles within the app that can be assigned to users.
 *
 * They have permissions that can be applied per route. Additional permission checks can be added
 * per transaction via casl/ability.
 *
 * Make sure you update app/(app)/organization/team/members.tsx if you modify this.
 */
roles: {
	superuser: ['all', 'admin'],
	admin: ['read', 'write', 'delete'],
	user: ['read', 'write'],
},
```

{% endcode %}

You can add as many roles you need here, and as many permissions applied to the role here.

These are then auto-loaded during start up time and applied to a global authorizer and a session based authorizer.

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/configure-roles-and-permissions>" %}

## Permission

The `Permission` object is a class that you can use to organize permissions in your application.

{% code title="feature.ts" lineNumbers="true" %}

```typescript
const permit = new Permission(anyActorWithAnId);

permit.allow('read', Post);
permit.deny('delete', Post);

await permit.persist();

// or fetch a stored permission
const permit = await Permission.fetch(anActorWithAnId);
```

{% endcode %}

### `new Permission(ObjectWithId)`

This creates a new Permission object that can be serialized later on. You can use this to temporarily create permissions in your feature code.

You can persist the permissions to database using `.persist()` below.

### `permit.allow(action: String, subject: String | ObjectWithId)`

This defines what you can use in the Permission object using the CASL Ability `.can` or `.cannot`. The signature for this is the same.

{% embed url="<https://docs.novel.dev/novel-server/routing/request-helpers#await-request.can-action-string-subject-any-fields-string>" %}

### `permit.deny(action: String, subject: String | ObjectWithId)`

This is the opposite of allow you can use in the Permission object using the CASL Ability `.can` or `.cannot`. The signature for this is the same.

{% embed url="<https://docs.novel.dev/novel-server/routing/request-helpers#await-request.cannot-action-string-subject-any-fields-string>" %}

### `permit.build(): Ability`

This is an internal abstraction to CASL's Ability.build function. This makes it so that you can perform CASL functions against the Permission Object

{% code title="feature.ts" lineNumbers="true" %}

```typescript
const permit = new Permission(anyActorWithAnId);

permit.allow('read', Post);
permit.deny('delete', Post);

const permissions = permit.build();

permission.can('read', Post); // returns true
```

{% endcode %}

### `await permit.persist()`

This persists the permissions defined during the lifecycle of the Permission object. It generates a serialized permission records, one row per action/scope into the `access_control` table.

### `await Permission.fetch(actor: String | ObjectWithId)`&#x20;

This is similar to `new Permission(ObjectWithId)`  but for an actor that has been persisted.

{% code title="feature.ts" lineNumbers="true" %}

```typescript
const permissions = await Permission.fetch(anyActorWithAnId);

permission.can('read', Post); // returns true
```

{% endcode %}

## Using with an API

It is important to be familiar with both authorization modes of Novel API. Discussed below:

{% embed url="<https://docs.novel.dev/novel-server/routing#protecting-routes>" %}

Combine the above with the request helpers associated with `permit.allow`and `permit.deny`.

{% embed url="<https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint>" %}

## Changelog

* 2024-12-20 - Initial Documentation


# Users

## Data Model

## Sign up

## User Lifecycle

## Activities

An account may have activities recorded against actions made to it.

This is stored in the `account_events` model. It is also available under the `novel/activity` module.

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import * as activity from 'novel/activity';

await activity.account('SOMETHING_NEW', 'Something happened');
```

{% endcode %}

This will get the account ID through the request context.

## Changelog

* 2024-12-20 - Initial Documentation


# Organizations

Before we go into how Novel handles multi-tenancy, We'd love for you to check out this wonderful article by Andrew Culver, creator of Bullettrain, a rails starter kit

{% embed url="<https://blog.bullettrain.co/teams-should-be-an-mvp-feature/>" %}
If you prefer to use rails, check out bullettrain.co instead
{% endembed %}

Novel comes with multi-tenancy enabled by default. This allows your SaaS customers to scope their actions based on an organization they belong with.

What this means is that any feature related to your SaaS should be developed with multi-tenancy in mind.

## Data Model

There are 3 data models relevant to multi-tenancy

1. [Organization ](https://github.com/madewithnovel/novel/blob/main/packages/novel/models/organizations/base.ts)- This is the organization that can have multiple users.
2. [Accounts ](https://github.com/madewithnovel/novel/blob/main/packages/novel/models/accounts/base.ts)- A user account
3. [Organization Membership](https://github.com/madewithnovel/novel/blob/main/packages/novel/models/organization-members/base.ts) - The membership detail of an account to an organization.

Novel implements a similar data model to the article written by Andrew Culver. A big difference is that subscriptions are by default, tied to an organization instead of the membership.

## Signup

Novel will create an organization and an acount during sign up. This will also create a stripe customer and create a subscription for it against the organization created.

When the user logs in the next time, the organization will be attached to the session.

Reference: <https://github.com/madewithnovel/novel/blob/main/app/features/lifecycle/signup.ts>

## Sessions

{% embed url="<https://docs.novel.dev/novel-server/sessions>" %}

Sessions in Novel include both the currently logged in user and the organization it is currently using. They are accessible using the `request.org` and `request.account` property of the current request.

{% hint style="info" %}
**Important**

Any action you want to perform should only apply to the current account and organization of that session.
{% endhint %}

### Switch Organizations

You are able to switch organizations in the same session by using the [**Switch Organization**](https://docs.novel.dev/novel-server/novel-api/api-reference#api-v1-session-switch) endpoint in the API reference below.

{% embed url="<https://docs.novel.dev/novel-server/novel-api/api-reference#api-v1-session-switch>" %}

## Subscriptions

When the user is created, an organization is created and a stripe customer is created along with it.

This is important so that there is representation of the organization in stripe.

Any changes to the subscription is attached to the organization only. This can only be done by users with the correct permission.

An example of this can be found here: <https://github.com/madewithnovel/novel/blob/main/app/api/internal/v1/subscriptions-subscribe/index.ts>

<pre class="language-typescript" data-title="/app/api/internal/v1/subscriptions-subscribe/index.ts" data-line-numbers><code class="lang-typescript">import * as upgrade from 'app/features/lifecycle/upgrade';

export default async function Route (instance) {
	instance.authenticated();
	instance.post('/api/v1/subscription/upgrade', handler);

	async function handler (request, reply) {
		await request.throttle();
		<a data-footnote-ref href="#user-content-fn-1">await request.can('write', request.org.id);</a>
		const orgId = request.org.id;

		/**
		 * See the implementation of the strategy in /app/features/lifecycle/upgrade.ts
		 *
		 * These may have additional requirements during creation like upfront payment
		 * and storing data. You may pass these to your upgrade lifecycle.
		 */

		const { plan, intent, method, interval } = request.body;
		await upgrade.start(request.session.id, { customer: orgId, plan, intent, method, interval });

		reply.status(204);
	}
}
</code></pre>

## Actions

It is important to always check the association of the current user against its membership of the target organization it is trying to perform an action on.

This is available with the Organizations Model.

{% content-ref url="/pages/YnCI9EvKEucRMliY1P0r" %}
[Authorization](/novel-server/authorization)
{% endcontent-ref %}

<pre class="language-typescript" data-title="app/api/v1/your-route.ts" data-line-numbers><code class="lang-typescript">import * as Organization from 'novel/organization';

export default async function Route (instance) {
	instance.authenticated();
	instance.cached();
	instance.get('/api/v1/organizations', handler);

	async function handler (request) {
		await request.throttle();
		await request.can('read', request.org.id);
		<a data-footnote-ref href="#user-content-fn-2">await Organization.isMember(request.org.id, request.account.id);</a>
		const organizations = await Organization.isMemberOf(request.account.id);
		return { organizations };
	}
}
</code></pre>

## Activities

An organization may have activities recorded against actions made to it.

This is stored in the `organization_events` model. It is also available under the `novel/activity` module.

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import * as activity from 'novel/activity';

await activity.org('SOMETHING_NEW', 'Something happened');
```

{% endcode %}

This will get the organization ID through the request context.

## Single-Tenancy

{% hint style="warning" %}
Single tenancy is not fully supported as of release 2025.1.0.
{% endhint %}

It is possible to do this however by removing the organization related endpoints from the `/app/api/v1`directory.

This will force the user to sign up and interact with only organization.

If you feel that this is a feature that you would like to see, send us an email at <hello@novel.dev>.

## Changelog

* 2024-12-20 - Initial Documentation

[^1]: This makes sure that the user is allowed to perform an action against this organization.

[^2]: You need to check whether the account is a member of that organization before performing an action.


# Subscriptions

##

## Metered vs License

## Managing Renewals

## MRR & ARR


# Pricing

## Pricing Workflow


# Validation

Novel makes use of 2 validation mechanisms: JSON Schema and Zod. both serve specific purpose, for shaping requests and responses and for verifying input.

Although these are optional, it is recommended that you build your system utilizing these tools as much as possible.

## API Specs and Database

**JSON Schema**, used within fastify via AJV, makes it easy to enforce how your api should behave before, during, and after processing.&#x20;

This, along with the fast performance brought by fastify, makes your system more resilient when dealing with APIs.

You can see the libraries below

{% embed url="<https://ajv.js.org/>" %}

{% embed url="<https://json-schema.org/>" %}

The standards above makes it easier to generate clients via OpenAPI/Swagger. These definitions are generated and used internally by the Novel Web client to easily expose routes as an RPC and provide an easy development experience.

JSON Schema is recommended to be used for API Request/Response shapes. Client-side validation should use zod instead.

[Using Validations →](https://docs.novel.dev/guides/knowledge-base/database/using-validations)

[Securing an Endpoint →](https://docs.novel.dev/guides/knowledge-base/novel-server/securing-an-endpoint)

For client-side validation. refer to [Validation](/novel-web/validation).

## Changelog

* 2024-12-20 - Initial Documentation


# Mail

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Notifications

### Web Push

Setting up web push requires FCM

Go to <https://console.firebase.google.com/u/0/project/\\[yourprojectid]/messaging/onboarding>

<figure><img src="/files/jqyjgrrON3xLxxdQUMfK" alt=""><figcaption></figcaption></figure>

### Web Push

You are required to set up the following environment variables

```
VAPID_FCM_KEY=***
VAPID_PUBLIC_KEY=***
VAPID_PRIVATE_KEY=***
```

You can use the novel cli to get these via

```
novel add vapid
```

and it will walk you through the steps to get these variables

You can then use the api from your controllers via

```
import * as push from 'novel/push';

await push.web({ to: ["account_ID"] } }, { body: "Your Message" });
```

### Mail

Similarly, you can use the same api to send an email notification

```
import * as push from 'novel/push';

await push.mail({ to: ["account_ID"] } }, { body: "Your Message" });
```

You can modify the mail template under `app/templates/mail/notification/message.tsx`


# API Keys


# Events

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Errors

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Feature Flags

## Custom Flags

## Subscription Lifecycle

## Customer Lifecycle


# Uploading Files

{% hint style="warning" %}
This article is still under construction
{% endhint %}

How to accept uploads

Adding new types


# Testing

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Scheduled Cron Jobs

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Background Jobs

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Sockets

{% hint style="warning" %}
This article is still under construction
{% endhint %}

In the client, you can use&#x20;

```
const socket = useSocket(namespace);

socket.on('event', () => null);
socket.emit('event', 'toServer');
```

in the server, you can create a route handler in the api directory

```
export default function Route (instance) {
    instance.socket('namespace', {}, handler);
    
    async function handler(socket) {
        socket.on('event', () => null);
    }
}
```


# Logging

Effective logging enables developers to monitor API behavior, diagnose issues, and improve application performance. It provides insights into application flow, helps in identifying bottlenecks, and ensures that the API responds correctly to requests.

{% embed url="<https://getpino.io>" %}

Pino is a fast, low-overhead logging library that is commonly used with Fastify, a web framework that prioritizes speed and efficiency. It offers minimal performance impact while providing rich logging capabilities essential for robust application monitoring.

In development environments, `pino-pretty` is often used in conjunction with Pino to format logs for better readability.

## Usage

You can use logging by using the `novel/logger` library

{% code title="feature.ts" lineNumbers="true" %}

```typescript
import logger from 'novel/logger';

logger.info('Something happened');
logger.error('Something is wrong');
logger.fatal('HELP!');
```

{% endcode %}

It follows the pino logging signature.

If you are looking for Events or Account Activities or Organization Activities, check it in their own documentation below

[Events →](https://docs.novel.dev/novel-server/events)

[Account Activities →](https://docs.novel.dev/novel-server/users#activities)

[Organization Activities →](https://docs.novel.dev/novel-server/organizations#activities)

## Changelog

* 2024-12-20 - Initial Documentation


# Cloud SQL (Experimental)

{% hint style="warning" %}
This article is still under construction
{% endhint %}

Cloud SQL is an easy way to access your database using only your client. This approach is similar to GraphQL or PostgREST but instead of a proprietary query language, it uses JSON and knex.js/objection.js conventions.

{% hint style="warning" %}
Cloud SQL is not a direct replacement for GraphQL or PostgREST. If you have these technologies in place, please continue to use them.
{% endhint %}

CloudSQL works by exposing the tables and their downstream relationships through a REST API provided by Novel. The following features are available:

* SELECT and GRAPH SELECT
* INSERT and GRAPH INSERT
* DELETE with CASCADE
* UPDATE and GRAPH UPDATE
* ORDER, LIMIT, OFFSET
* SELECT FUNCTIONS: MAX, MIN, AVG, ETC...
* TRANSACTIONS

These features are enough for you to develop a fully functioning feature without ever touching server code.

If you require more features, head over to our feature request page.


# Getting Started


# Queries


# Mutations


# Transactions


# Errors


# Telemetry

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Deployment


# Getting Started

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Configuration

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Project Structure

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Routing

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Layout and Styles

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Authentication

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Authorization

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Requests

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Request Files

Requests files are generated automatically


# Validation

Novel makes use of 2 validation mechanisms: JSON Schema and Zod. both serve specific purpose, for shaping requests and responses and for verifying input.

Although these are optional, it is recommended that you build your system utilizing these tools as much as possible.

## Forms and Requests

Any active validations that you need during development can be supported by Zod. All database models have an accompanying zod definition that can be reused and extended.

Validations within the client should make use of zod as much as possible.

{% embed url="<https://zod.dev/>" %}

You can do more on the client side:

[Using Zod in your Forms →](https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/using-zod-in-forms)

[Using Generated Requests →](https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/using-generated-requests)

[Making a Request to the API →](https://docs.novel.dev/guides/knowledge-base/novel-web-next.js/making-a-request-to-the-api)

For server-side validation. refer to [Validation](/novel-server/validation).

## Changelog

* 2024-12-20 - Initial Documentation


# Components

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Button


# Alerts


# Copybox


# Inline Notify


# Input


# Select


# Toast


# Toggle


# Upload


# Stripe Card


# Hooks

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# useSession


# useMobile


# getSession


# useFeature


# useAuthorized


# useNotification


# useSocket


# Localstorage

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Errors

{% hint style="warning" %}
This article is still under construction
{% endhint %}


# Internationalization (i18n)


# Constants




---

[Next Page](/llms-full.txt/1)

