Espresso
☕
Espresso is a minimal and lightweight router for cloudflare
workers without any dependency. It
utilizes
URLPattern
to perform routing. It provides two methods before
and after
for
applying middleware both before and after processing the request (i.e.
The request processing from handler). It implements all http methods
described here MDN HTTP
Methods (i.e
head
, get
, post
, put
, patch
, delete
, connect
, trace
,
options
) are supported.
Along with routing, this library provides a optional feature to execute
callback for a cron expression. For example say one have use case to
execute a task every hour and another task which requires to be executed
on start of month. Since cloudflare
ScheduleEvent
provides a property called cron
which will have the source of event
trigger. This gives a flexibility to perform dynamic operation with
respect to cron expressions
Why Espresso
☕
- 🛫 Easy to get started
- 🧳 Zero dependency, it doesn't use any external library
- 🍔 Easy to customize and extend
- 🕐 Bind callbacks for different cron expressions
Usage
import espresso, { ServerError } from '@nerding_it/espresso';
const logger = (message) => {
console.log(message)
}
espresso
.cors({})
.setDebug() // NOTE: For development purpose
.before(async (request) => {
logger('Request URL logging middleware')
logger(`Requested URL: ${request.url}`)
})
.before(async (request, env) => {
logger('Log environment variable middleware')
logger(`Environment variables ${JSON.stringify(env)}`)
})
// Auth middle warey
.before( async () => {
// Throw error like this with custom http status, Say if user is not authorized to access
// throw new ServerError('Invalid authorization', 401)
})
.after((response) => {
logger('Response status code logging middleware')
logger(`Response status: ${response.status}`)
})
.after((response, env, context) => {
logger('Caching middleware')
})
.after((response, env, context) => {
logger('Cleanup middleware')
})
.head('/', async () => {
return {
status: 200,
body: {message: 'I am head'}
}
})
.get('/', async () => {
return {
status: 200,
body: {message: 'I am get'}
}
})
.post('/', async (request) => {
return {
status: 200,
body: { message: 'I am post'}
}
})
.put('/', async (request) => {
return {
status: 200,
body: { message: 'I am put'}
}
})
.patch('/', async (request) => {
return {
status: 200,
body: { message: 'I am patch'}
}
})
.delete('/', async (request) => {
return {
status: 200,
body: { message: 'I am delete'}
}
})
.options('/', async (request) => {
return {
status: 200,
body: { message: 'I am options'}
}
})
.connect('/', async (request) => {
return {
status: 200,
body: { message: 'I am connect'}
}
})
.trace('/', async (request) => {
return {
status: 200,
body: { message: 'I am trace'}
}
})
.get('/:name', async (request) => {
return {
status: 200,
body: {message: `Hello ${request.params.get('name')}`}
}
})
// When you pass query params in the url like ?query=How are you
// Pass headers in the response
.get('/:name/:country', async (request) => {
return {
status: 200,
body: {message: `Hello ${request.params.get('name')} from ${request.params.get('country')}, your query is ${request.query.get('query')}`},
headers: {
'Cache-Control': 'no-cache'
}
}
})
.patch('/:id', async (request) => {
// Throw an error
throw new ServerError('Bad request', 400)
})
.schedule('* * * * *', async (env) => {
// Run some task every minutes
})
.schedule('*/30 * * * *', async (env) => {
// Run some task every 30 minutes
})
.brew()
Running app locally or publish to cloudflare
You have to use cloudflare
wrangler to run
application locally, Just invoke wrangler dev
to start local instance
of the application, Or use wrangler publish
to publish into cloudflare
edge servers
Reference
-
cors
Enable cors, Optionally pass parameters to override default values for cors header. Below is the exampleconst config = { allowOrigin: 'http://localhost:3000, http://localhost:3001', allowMethods: 'GET, POST', allowHeaders: 'Authorization, Content-Type' } espresso.cors(config)
-
head
Register a handler for http head method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .head('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body return { status: 200, body: {message: 'I am http head method'} } })
-
options
Register a handler for http options method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .options('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body return { status: 200, body: {message: 'I am http options method'} } })
-
get
Register a handler for http get method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .get('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body return { status: 200, body: {message: 'I am http get method'} } })
-
post
Register a handler for http post method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .post('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body // Use request.body to use post body return { status: 200, body: {message: 'I am http post method'} } })
-
put
Register a handler for http put method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .put('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body // Use request.body to use put body return { status: 200, body: {message: 'I am http put method'} } })
-
patch
Register a handler for http patch method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .patch('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body // Use request.body to use patch body return { status: 200, body: {message: 'I am http patch method'} } })
-
delete
Register a handler for http delete method, Below is the exampleimport espresso from '@nerding_it/espresso'; espresso() .delete('/', async (request, env) => { // Do something with request // Use environment variables // Return the status code and body return { status: 200, body: {message: 'I am http delete method'} } })
-
before
Register a response middleware, Below is the example which transforms request bodyimport espresso from '@nerding_it/espresso'; espresso() .before('/', async (request, env) => { if (request.headers.get('content-type').toLowerCase() === 'application/json' || request.headers.get('content-type').toLowerCase() === 'application/json;utf-8') { request.body = await request.json() } })
-
after
Register a response middleware, Below is the example which writes data into KVimport espresso from '@nerding_it/espresso'; espresso() .after('/', async (response, env, context) => { const clone = response.clone() // Write response url and response status to KV context.waitUntil(env.KV.put(clone.url, clone.status)) })
-
schedule
Register a schedule event handler, It takes cron expression and a call back. Below is a example which register a callback for cron expression * * * * * (i.e. Every minute). And another callback which calls when cron expression is */2 * * * * (i.e. Every two minute). In this way you can perform different operations for different cron triggers. Read more about Cron Triggers. To make it work you need to configure cron triggers with respected expression for your workerimport espresso from '@nerding_it/espresso'; espresso() .schedule('* * * * *', async (env) => { console.debug('I write message to console every minute') }) .schedule('*/2 * * * *', async (env) => { console.debug('I write message to console every two minute') }) .brew()
-
brew
This wraps router instance inside FetchEvent and optionally ScheduleEvent (NOTE: It adds ScheduleEvent only if you call schedule). For example it adds ScheduleEvent for below codeimport espresso from '@nerding_it/espresso'; espresso // If it's triggered by this cron string .schedule("* * * * *", async (env) => { // List keys const { keys } = await env.KV.list() }) .brew()
-
setDebug
Enable debugging (i.e. console messages)import espresso from '@nerding_it/espresso'; espresso .setDebug() .brew()
Enable cors with custom settings
You have to pass custom parameters to cors
method as mentioned below,
const config = {
allowOrigin: '*', // For example 'http://localhost:3000, https://localhost:3001'
allowMethods: '*', // For example 'GET, POST'
allowHeaders: '*' // For example 'Authorization, Content-Type'
}
// Then pass into cors
espresso.cors(config)
// Rest of the code
FAQ
How do I have to access path parameters, For example I have route like this /user/:id
request.params
is a map with parameter name as key, Just use
request.params.get('id')
to access id
How do I have to access query parameters, For example I have route like this /user?text=web
request.query
is a map with parameter name as key, Just use
request.query.get('text')
to access text
How to perform non blocking task in after
middleware?
Middlewares get context
object, It'll have waitUntil
method. Utilize
it to perform operations async. This is useful for caching etc.