Skip to content

Commit d63f217

Browse files
committed
Initial Commit
0 parents  commit d63f217

39 files changed

+17994
-0
lines changed

.changeset/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Changesets
2+
3+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4+
with multi-package repos, or single-package repos to help you version and publish your code. You can
5+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6+
7+
We have a quick list of common questions to get you started engaging with this project in
8+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

.changeset/config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
3+
"changelog": "@changesets/cli/changelog",
4+
"commit": false,
5+
"fixed": [],
6+
"linked": [],
7+
"access": "restricted",
8+
"baseBranch": "master",
9+
"updateInternalDependencies": "patch",
10+
"ignore": []
11+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

README.md

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# Backhooks
2+
3+
Backhooks is a new way to write backend applications by using global hooks scoped to a specific context.
4+
5+
It can be very useful for an HTTP application, for writing reusable and easily testable code.
6+
7+
## Get started
8+
9+
### Install dependency
10+
11+
```
12+
npm install @backhooks/core
13+
```
14+
15+
### Write your first hook
16+
17+
```ts
18+
import { createHook } from "@backhooks/core";
19+
20+
const [useCount, updateCountState] = createHook({
21+
data() {
22+
return {
23+
count: 0,
24+
};
25+
},
26+
execute(state) {
27+
state.count++;
28+
return state.count;
29+
},
30+
});
31+
```
32+
33+
### Execute your hook from a context
34+
35+
```ts
36+
import { runHookContext } from "@backhooks/core";
37+
38+
runHookContext(() => {
39+
console.log(useCount()); // 1
40+
console.log(useCount()); // 2
41+
console.log(useCount()); // 3
42+
});
43+
44+
runHookContext(() => {
45+
console.log(useCount()); // 1
46+
console.log(useCount()); // 2
47+
console.log(useCount()); // 3
48+
});
49+
```
50+
51+
## Usage with HTTP frameworks
52+
53+
### Usage with ExpressJS
54+
55+
```
56+
npm install @backhooks/http
57+
```
58+
59+
```ts
60+
import * as express from "express";
61+
import { useHeaders, hooksMiddleware } from "@backhooks/http";
62+
63+
const app = express();
64+
65+
app.use(hooksMiddleware());
66+
67+
app.get("/", async (req, res) => {
68+
const headers = useHeaders();
69+
res.send(headers);
70+
});
71+
72+
app.listen(3000, () => {
73+
console.log("Listening on http://localhost:3000");
74+
});
75+
```
76+
77+
### Usage with Fastify
78+
79+
```
80+
npm install @backhooks/http
81+
```
82+
83+
```ts
84+
import Fastify from "fastify";
85+
import { hooksMiddleware, useHeaders } from "@backhooks/http";
86+
87+
const fastify = Fastify({
88+
logger: true,
89+
});
90+
91+
fastify.addHook("preHandler", hooksMiddleware());
92+
93+
// Declare a route
94+
fastify.get("/", () => {
95+
const headers = useHeaders();
96+
return headers;
97+
});
98+
99+
// Run the server!
100+
fastify.listen({ port: 3000 }, function (err, address) {
101+
if (err) {
102+
fastify.log.error(err);
103+
process.exit(1);
104+
}
105+
// Server is now listening on ${address}
106+
});
107+
```
108+
109+
## Writing hooks
110+
111+
Writing hooks is very straightforward. Each hook holds a `state` for a particular context.
112+
113+
Let's try out to write a `useAuthorizationHeader` hook:
114+
115+
```ts
116+
import { createHook } from "@backhooks/core";
117+
import { useHeaders } from "@backhooks/http";
118+
119+
const [useAuthorizationHeader, updateAuthorizationHeaderHookState] = createHook(
120+
{
121+
data() {
122+
// This function is called the first time the hook
123+
// is used to create the hook state within a specific
124+
// context
125+
126+
// Note that in that function, you can use other hooks like `useHeaders`
127+
const headers = useHeaders();
128+
129+
// this function MUST be synchronous.
130+
return {
131+
header: headers["authorization"],
132+
};
133+
},
134+
execute(state) {
135+
// This function is called every time the application
136+
// calls the hook. It must return the value for this hook
137+
138+
// Note that you can also call other hooks in this function.
139+
140+
// This function CAN be asynchronous. You will have to await
141+
// the response of the hook if you make it asynchronous.
142+
return state.header;
143+
},
144+
}
145+
);
146+
```
147+
148+
Now, let's see how we could update our hook state during a context execution:
149+
150+
```ts
151+
import { runHookContext } from "@backhooks/core";
152+
153+
runHookContext(() => {
154+
updateAuthorizationHeaderHookState((state) => {
155+
return {
156+
...state,
157+
header: "abc",
158+
};
159+
});
160+
const authorizationHeader = useAuthorizationHeader();
161+
expect(authorizationHeader).toBe("abc");
162+
});
163+
```
164+
165+
This makes it really easy to test our code. You can even test your hooks by leveraging third party hooks update state function. Let's see how we could test our new hook:
166+
167+
```
168+
import { runHookContext } from "@backhooks/core";
169+
import { configureHeadersHook } from '@backhooks/http';
170+
import { useAuthorizationHeader } from './hooks/useAuthorizationHeader'
171+
172+
test('it should return the authorization header', async () => {
173+
runHookContext(() => {
174+
configureHeadersHook(state => {
175+
return {
176+
...state,
177+
headers: {
178+
authorization: 'def'
179+
}
180+
}
181+
})
182+
const authorizationHeader = useAuthorizationHeader()
183+
expect(authorizationHeader).toBe('def') // true
184+
})
185+
})
186+
```
187+
188+
## Global context
189+
190+
We have seen that for hooks to work, it should be run in a context.
191+
192+
Note that there also is a global context in your application. So that you **can** but should not really use hooks outside of a context.
193+
194+
## Applications
195+
196+
Hooks can have a variety of applications that are yet to be discovered. Here are some examples:
197+
198+
### Logger hook: To log a requestId for each log entry
199+
200+
```ts
201+
export default function () {
202+
const logger = useLogger();
203+
logger.debug("I'm a log entry"); // {"requestId": "abcd", "message": "I'm a log entry"}
204+
}
205+
```
206+
207+
### Authentication hooks: To retrieve the authenticated user during a function execution
208+
209+
```ts
210+
export default function () {
211+
const user = await useUserOrThrow();
212+
// Do something with the authenticated user
213+
}
214+
```
215+
216+
### Validation hooks: To validate body
217+
218+
```ts
219+
export default function () {
220+
const zodSchema = z.object({
221+
foo: z.string().min(3),
222+
});
223+
224+
const parsedBody = useValidatedBody(zodSchema);
225+
226+
console.log(parsedBody.foo); // Type safe, returns the foo property of the body
227+
}
228+
```
229+
230+
## Contribute
231+
232+
This is really, really early stage. Everything is subject to change. The best way to help me with that is just to communicate me in some way that you are interested in this. You can open an issue or join me on my completely empty Discord Server, I'll be happy to interact.

0 commit comments

Comments
 (0)