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:
Caching
Rate Limiting
Elevated Privileges
Idempotency
Verified
Subscribed
Caching
Routes can be cached by adding this directive in the route definition.
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()
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` })
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 })
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)
await reply.uncache(key: string, options?: CacheOptions)
This is a decorator to reply that you can use to manually purge a cached response.
CacheOptions
CacheOptions
ttl - Time to live of the cached object. default
5
secondskey - (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)
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
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(key: string, limit?: number, options?: RateLimitOptions)
await request.throttled(limit: number, options?: RateLimitOptions)
await request.throttled(limit: number, options?: RateLimitOptions)
The signature for throttled allow different usage for convenience.
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)
await request.unthrottle(key?: string, options?: RateLimitOptions)
If you need to explicitly reset the rate limit for a specific key.
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');
}
}
Example usage: auth-passwordless/index.ts
RateLimitOptions
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()
instance.sudo()
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
{
...rest of your schema.json
"body": {
"type": "object",
"required": [...other fields, "sudo_password"],
"properties": {
...other fields
"sudo_password": {
"type": "string"
}
}
}
}
See Schema →
Idempotency
If you want to enable idempotency for a route. You can add the idempotency directive for that definition
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-key
header generated by the client.
await fetch('/api/v1/accounts', {
method: 'POST',
headers: {
'idempotency-key': uuid.v1(),
},
});
instance.idempotent(options?: IdempotencyOptions)
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
IdempotencyOptions
ttl - the amount of time before the idempotency key can be reused. default 86400
Verified
instance.verified()
instance.verified()
Check if the currently logged in user has a verified
status.
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()
instance.subscribed()
Check if the currently logged in organization has an active subscription.
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
Was this helpful?