Novel
Get NovelGuidesAPI Reference
Latest - 2025.1.0
Latest - 2025.1.0
  • Welcome to Novel
  • Start
  • Philosophy
  • Tech Stack
  • Releases
  • Versions
  • Changelog
  • License
  • Privacy
  • Warranty
  • Security Policy
  • Errors
    • Cannot start Novel
    • Unauthorized
    • Invalid Session
    • Validation Failed
  • Novel Server
    • Getting Started
    • Project Structure
    • With Novel Web
    • Configuration
    • Novel CLI
      • novel dev
      • novel start
      • novel new
    • Novel API
      • API Reference
    • Database
      • Caching
    • Migrations
    • Models
    • Routing
      • Route Directives
      • Middleware
      • Request Helpers
      • Schema
    • Sessions
    • Authentication
      • Passwords
      • Magic Links
      • Two-Factor Authentication
      • Forget Password
      • Email Verification
      • OAuth2 Support
    • Authorization
    • Users
    • Organizations
    • Subscriptions
    • Pricing
    • Validation
    • Mail
    • Notifications
    • API Keys
    • Events
    • Errors
    • Feature Flags
    • Uploading Files
    • Testing
    • Scheduled Cron Jobs
    • Background Jobs
    • Sockets
    • Logging
    • Telemetry
    • Deployment
  • Novel Web
    • Getting Started
    • Configuration
    • Project Structure
    • Routing
    • Layout and Styles
    • Authentication
    • Authorization
    • Requests
    • Request Files
    • Validation
    • Components
      • Button
      • Alerts
      • Copybox
      • Inline Notify
      • Input
      • Select
      • Toast
      • Toggle
      • Upload
      • Stripe Card
    • Hooks
      • useSession
      • useMobile
      • getSession
      • useFeature
      • useAuthorized
      • useNotification
      • useSocket
    • Localstorage
    • Errors
    • Internationalization (i18n)
    • Constants
    • Feature Flags
    • Testing
    • Telemetry
    • Deployment
    • Devtools (Alpha)
Powered by GitBook
On this page
  • Caching
  • instance.cached()
  • instance.cached({ key: (request) ⇒ `custom-id` })
  • instance.cached({ ttl: 60 })
  • await reply.uncache(key: string, options?: CacheOptions)
  • CacheOptions
  • Rate Limiting
  • instance.throttled(limit: number, window: string, options?: RateLimitOptions)
  • await request.throttled(key: string, limit?: number, options?: RateLimitOptions)
  • await request.throttled(limit: number, options?: RateLimitOptions)
  • await request.unthrottle(key?: string, options?: RateLimitOptions)
  • RateLimitOptions
  • Elevated Privileges
  • instance.sudo()
  • Idempotency
  • instance.idempotent(options?: IdempotencyOptions)
  • IdempotencyOptions
  • Verified
  • instance.verified()
  • Subscribed
  • instance.subscribed()
  • Changelog

Was this helpful?

  1. Novel Server
  2. Routing

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.

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

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` })

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 5seconds 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. default5seconds

  • key - (request: Request) function to generate a cache key

Rate Limiting

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

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

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.

app/api/route/index.ts
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(key?: string, options?: RateLimitOptions)

If you need to explicitly reset the rate limit for a specific key.

app/api/route/index.ts
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');
    }
}

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()

app/api/route/index.ts
export default async function Route(instance: FastifyInstance) {
    instance.post('/api/v1/accounts', handler);
    
    
    async function handler(request, reply) {
        console.log(request);
        reply.status(204);
    }
}

Important Note

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

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

Idempotency

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

app/api/route/index.ts
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);
    }
}

Requests to this endpoint need to have a unique idempotency-keyheader generated by the client.

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

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 verifiedstatus.

app/api/v1/your-route/index.ts
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');
    }
}

Subscribed

instance.subscribed()

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

app/api/v1/your-route/index.ts
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');
    }
}

Changelog

  • 2024-12-20 - Initial Documentation

Last updated 5 months ago

Was this helpful?

Rate limiting is provided by

Example usage:

See

https://github.com/fastify/fastify-rate-limit
auth-passwordless/index.ts
Schema →