Introducing backhooks: hooks for the backend (nodeJS)
Mathieu Marteau
Posted on January 24, 2023
I wrote my first real open source project last week and I got 40 stars on Github, I'm so excited!
React has hooks, Vue has composables, but what does backend has?
When building a back-end app with NodeJS, we always struggle with context. How to inject dependencies etc...
NestJS came with a good solution and is now widely used for their strong typed dependency injection.
But! NestJS also relies on some specificities like decorators, that are not easy to work with in different environments.
Wouldn't you like to just write hooks for your application, and access the data you need when you need it?
Imagine this express app route:
import { useHeaders, hooksMiddleware } from '@backhooks/http'
app.use(hooksMiddleware())
app.use('/foo', (req, res) => {
const headers = useHeaders() // <- this is a hook
res.send(headers)
})
As you see, the useHeaders
function is a global, unified function that could work for multiple runtimes, not only nodeJS - expressJS apps.
This makes it really easy to write functions, that are really easy to unit test:
export function useAuthorizationHeader () {
const headers = useHeaders()
return headers['authorization']
}
And the good news is that the library is entirely type safe!
Creating hooks
Creating hooks is really straightforward. You can create hooks that rely on other hooks, or create a hook that holds a state for a particular run context (a request in general in an http context)
Stateful hooks
Sometimes, you have the need to share data between hooks call during a request execution.
You could create a useCount()
that would increment at each call in a specific request:
import { createHook } from "@backhooks/core";
const [useCount, setCount] = createHook({
// Here, we define a `data` function that will return
// the initial state of the hook. This function must
// be synchronous.
data() {
return {
count: 0,
};
},
// The `execute` function uses the state, can mutate the state
// and returns a value. This function can be asynchronous.
execute(state) {
state.count++;
return state.count;
},
});
That would result in the following:
app.get('/count', (req, res) => {
console.log(useCount()) // Always 1
console.log(useCount()) // Always 2
console.log(useCount()) // Always 3
res.send(useCount()) // Always sends 4
})
You can also use the setCount
function to override any state on the hook:
app.get('/count', (req, res) => {
setCount({
count: 50
})
console.log(useCount()) // Always 51
console.log(useCount()) // Always 52
console.log(useCount()) // Always 53
...
})
Platform agnostic
You can use backhooks with any nodeJS application. Just run a context when you need it:
import { runHookContext } from '@backhooks/core'
import { useHeaders, configureHeadersHook } from '@backhooks/http'
runHookContext(async () => {
setHeaders({
headers: {
authorization: 'Bearer abcd'
}
})
const headers = useHeaders()
console.log(headers.authorization) // Bearer abcd
})
Each hook call must be run into a runHookContext
callback in order to correctly attach hooks to the correct context.
The middleware provided for express
or fastify
actually does that automagically for each request.
So if you want to create a library, that relies on http requests informations, and want it to be compatible with every runtime, like express, fastify or even h3, you can just write hooks and it should work!
Imagine how easy it would be to write a useUser()
function and be able to use it through all of your application?
Class injection
With backhooks
, you can even do class injection really easily. That would be the equivalent of a request scoped provider for NestJS:
import { useLogger } from "./useLogger";
export class MyProvider {
constructor(private readonly logger = useLogger());
foo() {
this.logger.debug("bar!"); // Type safe
}
}
and to register the hook:
import { createHook } from "@backhooks/core";
import { MyProvider } from "./MyProvider";
export const [useMyProvider] = createHook({
data() {
return new MyProvider();
},
execute(state) {
return state;
},
});
And then use it anywhere, not just in other hooks function.. It's entirely plug and play!
app.use("/", (req, res) => {
const myProvider = useMyProvider();
myProvider.foo(); // Logs "bar!", while being type safe ;)
res.send("ok!");
});
If you like it, I invite you to leave a star on the repository on github to encourage me to go further with it, or comment something here!
https://github.com/coderhammer/backhooks
You can read the README of the repo if you want more informations and use case.
Cheers!
Posted on January 24, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024
November 28, 2024