Decorators
Decorators are a a powerful feature of Pylon, that allow you to secure, modifiy or extend your service functions.
Overview
Pylon provides a decorator factory createDecorator that allows you to create custom decorators for your service functions. Decorators can be used to add additional functionality to your service functions, such as authentication, logging, or other concerns.
Creating a Decorator
To create a decorator, you can use the createDecorator function from the @getcronit/pylon package. This function takes a callback function as an argument, which will be executed when the decorator is applied to a service function.
import {app, createDecorator} from '@getcronit/pylon'
const log = createDecorator(async () => {
console.log('Executing service function')
})
class Foo {
@log
foo = async () => {
return 'foo'
}
}
export default appIn this example, the log decorator logs a message when the foo service function is executed.
Applying a Decorator
To apply a decorator to a service function, you can use the decorator syntax provided by TypeScript. Decorators can be applied to class methods, static methods, or properties.
import {app, createDecorator} from '@getcronit/pylon'
const log = createDecorator(async () => {
console.log('Executing service function')
})
class Foo {
@log
foo = async () => {
return 'foo'
}
@log
static bar = 'bar'
@log
static baz() {
return 'baz'
}
@log
static qux = () => {
return 'qux'
}
@log
bar = 'bar'
@log
baz() {
return 'baz'
}
@log
qux = () => {
return 'qux'
}
}
const foo = new Foo()
export const graphql = {
Query: {
instanceFoo: foo.foo,
staticBar: Foo.bar,
staticBaz: Foo.baz,
staticQux: Foo.qux,
instanceBar: foo.bar,
instanceBaz: foo.baz,
instanceQux: foo.qux
}
}
export default appYou can also apply decorators by calling the decorator function directly.
import {app, createDecorator} from '@getcronit/pylon'
const log = createDecorator(async () => {
console.log('Executing service function')
})
export const graphql = {
Query: {
welcome: log((firstName: string, lastName: string) => {
return `Hello ${firstName} ${lastName}`
})
}
}Arguments Validation
You can use decorators to validate the arguments of a service function. This can be useful for enforcing e.g. password requirements.
import {app, createDecorator, ServiceError} from '@getcronit/pylon'
const validatePassword = createDecorator(
async (username: string, password: string) => {
if (password.length < 8) {
throw new ServiceError('Password must be at least 8 characters long', {
code: 'INVALID_PASSWORD',
statusCode: 400
})
}
}
)
export const graphql = {
Query: {
hello: 'Hello World!'
},
Mutation: {
login: validatePassword((username: string, password: string) => {
return `Welcome, ${username}!`
})
}
}
export default appIn this example, the validatePassword decorator checks if the password provided to the login service function is at least 8 characters long. If the password is too short, a ServiceError is thrown with a status code of 400.
Hono Middleware as Decorators
All Hono middleware functions can be used inside decorators. This allows you to apply middleware to service functions, instead of routes.
For example, you can use the basicAuth middleware to require authentication for a service function.
import {app, createDecorator, getContext} from '@getcronit/pylon'
import {basicAuth} from 'hono/basic-auth'
const authMiddleware = basicAuth({
username: 'admin',
password: 'password'
})
const requireBasicAuth = createDecorator(async () => {
const ctx = getContext()
await authMiddleware(ctx, async () => {})
})
export const graphql = {
Query: {
secure: requireBasicAuth(() => {
return 'You are authenticated!'
})
}
}
app.get('/', authMiddleware, c => {
return new Response('Hello World')
})
export default appIn this example, the requireBasicAuth decorator applies the basicAuth middleware to the secure service function, requiring the user to provide a username and password to access the function.
Now you can navigate to http://localhost:3000 and you will be prompted to enter a username and password. Afterwards you can access the secure service function.
Take a look at the Hono documentation (opens in a new tab) for more information on available middleware functions.
Decorators as Middleware
You can also use decorators as middleware functions for routes. This allows you to apply the same middleware to multiple routes.
import {app, createDecorator, getContext} from '@getcronit/pylon'
import {basicAuth} from 'hono/basic-auth'
const requireBasicAuth = createDecorator(async () => {
const ctx = getContext()
await basicAuth({
username: 'admin',
password: 'password'
})(ctx, async () => {})
})
export const graphql = {
Query: {
hello: 'Hello World!'
}
}
app.get(
'/',
requireBasicAuth(async (c, next) => {
return new Response('Hello World')
})
)In this example, the requireBasicAuth decorator is used as middleware for the / route, requiring the user to provide a username and password to access the route.