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 app
In 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 app
You 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 app
In 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 app
In 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.