Skip to content

Commit 4d02618

Browse files
committed
Update docs
1 parent 8e270ad commit 4d02618

File tree

4 files changed

+103
-273
lines changed

4 files changed

+103
-273
lines changed

README.md

+1-4
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ RPC usually indicates strict typing of procedures for maximal compatibility and
1818

1919
## Installation
2020

21-
Please follow the instructions for each side:
22-
23-
Server docs: [Server README](https://github.com/fxdave/cuple/tree/main/packages/server)
24-
Client docs: [Server README](https://github.com/fxdave/cuple/tree/main/packages/client)
21+
Please check the [docs](https://fxdave.github.io/cuple/).
2522

2623
Or try the boilerplate: https://github.com/fxdave/react-express-cuple-boilerplate
2724

docs/docs/server/middlewares.mdx

+100-15
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ description: 'Middlewares, Chaining'
55

66
# Middlewares
77

8-
- Middlewares are a way to reuse code.
9-
- They can pass data down to other middlewares.
10-
- They can also prevent further execution.
11-
- They can send responses so you don't have to handle every case.
8+
Middlewares allow you to reuse logic by handling tasks like validation, access control, or response formatting. They offer several features:
129

13-
NOTE: Schema validators are also middlewares.
10+
- Passing data to other middlewares.
11+
- Preventing further execution based on conditions.
12+
- Sending responses, so you don't need to handle every case manually.
13+
14+
Note: Schema validators are also treated as middlewares.
1415

1516
## Basic
1617

@@ -28,20 +29,20 @@ builder
2829
});
2930
```
3031

31-
- If the middleware returns `{ next: true }`, it can also add something to `data` type-safely.
32-
- The above example returns `{ next: true, randomValue }` thus `data.randomValue` is accessible in the next request handler.
33-
- If the middleware returns `{ next: false }` it sends a response thus it needs `statusCode`.
34-
But having `result` is also advised for a good **discriminated union**.
32+
In this example:
33+
34+
- If the middleware returns `{ next: true }`, the execution proceeds to the next handler. You can also type-safely add data, as shown with `randomValue` in `data.randomValue`
35+
- If the middleware returns `{ next: false }`, it sends an immediate response, which requires a `statusCode`. It's recommended to include a `result: 'some-error' as const` for having a proper discriminated union in the response.
3536

3637
## Middleware Chaining
3738

38-
Some middlewares need to use schema validation.
39-
This, however, prevents you to just move the request handler out of the request handler building.
40-
To solve this, there are two concepts you can use:
41-
- `.buildLink()` Instead of the final middleware, you can use this to get a chain link.
42-
- `.chain(fooLink).chain(barLink)` to bring multiple links into the request handler building.
39+
Some middlewares need to use data coming from schema validators. In order to use the data from the schema validation you have to keep the middleware function in the builder code.
40+
This, however, prevents you to just move the middleware function out of the builder.
41+
To solve this, Cuple introduces a wrapper called **link**, and you can connect links with each other.
42+
- `.buildLink()` Creates a link from a build chain. Use this instead of the final middleware (get, post, ...).
43+
- `.chain(fooLink).chain(barLink)` to bring multiple links into the build chain
4344

44-
Here's an example:
45+
### Example:
4546

4647
```ts
4748
const authLink = builder
@@ -70,3 +71,87 @@ const routes = {
7071
}),
7172
};
7273
```
74+
75+
## Dependent chain links (Since 1.0.10)
76+
77+
Middlewares can depend on the output of previous middlewares.
78+
For example, a role-check middleware might depend on the result of an authentication middleware.
79+
80+
### Example:
81+
82+
```ts
83+
84+
const authLink = builder
85+
.middleware(async () => ({ next: true, auth: { userId: 12345 } }))
86+
.buildLink();
87+
88+
const adminRoleLink = builder
89+
.expectChain<typeof authLink>()
90+
.middleware(async ({ data }) => {
91+
const user = await getUser(data.auth.userId)
92+
if(user.role !== 'admin') {
93+
return { next: false, statusCode: 403, message: "This is admin only." }
94+
}
95+
return { next: true, user }
96+
})
97+
.buildLink();
98+
99+
return {
100+
someRoute: builder
101+
.chain(authLink)
102+
.chain(adminRoleLink)
103+
.get(async ({ data }) => {
104+
return success({
105+
message: `You have access! Good ${data.user.name}`
106+
});
107+
}),
108+
};
109+
```
110+
111+
In this example:
112+
- `adminRoleLink` depends on the output of `authLink`.
113+
114+
## Dealing with service dependencies
115+
116+
In more complex applications, middleware may need additional services like a database or external APIs.
117+
To keep the code maintainable and testable, it's best to inject these dependencies rather than import them directly.
118+
You can achieve this by creating middleware link factories.
119+
120+
### Example:
121+
```ts
122+
function createAuthLink(builder: CupleBuilder) {
123+
return builder
124+
.middleware(async () => ({ next: true, auth: { userId: 12345 } }))
125+
.buildLink()
126+
}
127+
type AuthLink = ReturnType<typeof createAuthLink>
128+
129+
function createAdminRoleLink(builder: CupleBuilder, userRepository: UserRepository) {
130+
return builder
131+
.expectChain<AuthLink>()
132+
.middleware(async ({ data }) => {
133+
const user = await getUser(data.auth.userId)
134+
if(user.role !== 'admin') {
135+
return { next: false, statusCode: 403, message: "Admins only." }
136+
}
137+
return { next: true, user }
138+
})
139+
.buildLink()
140+
}
141+
142+
function createAdminModule(builder: CupleBuilder, userRepository: UserRepository) {
143+
const authLink = createAuthLink(builder);
144+
const adminRoleLink = createAdminRoleLink(builder, userRepository);
145+
return {
146+
someRoute: builder
147+
.chain(authLink)
148+
.chain(adminRoleLink)
149+
.get(async ({ data }) => {
150+
return success({
151+
message: `You have access! Good ${data.user.name}`
152+
});
153+
}),
154+
};
155+
}
156+
```
157+
- note: `CupleBuilder` is an exported type from the `index.ts` file

packages/client/README.md

+1-87
Original file line numberDiff line numberDiff line change
@@ -8,90 +8,4 @@ Client side for @cuple RPC.
88
npm i @cuple/client
99
```
1010

11-
NOTE: **@cuple/client** and **@cuple/server** versions have to match
12-
13-
## Example
14-
15-
```ts
16-
import { createClient } from "@cuple/client";
17-
import type { routes } from "../backend/src/app";
18-
19-
const client = createClient<typeof routes>({
20-
path: "http://localhost:8080/rpc",
21-
});
22-
23-
async function changePassword() {
24-
const response = await authedClient.setUserPassword.post({
25-
body: {
26-
oldPassword: "something",
27-
password1: "newPass",
28-
password2: "newPass",
29-
},
30-
});
31-
32-
console.log(response);
33-
}
34-
35-
changePassword();
36-
```
37-
38-
# API
39-
40-
## Feature 1: Calling procedures
41-
42-
```ts
43-
// Server:
44-
const routes = {
45-
foo: builder.get(/**/),
46-
bar: builder.post(/**/),
47-
baz: builder.put(/**/),
48-
patch: builder.patch(/**/),
49-
delete: builder.delete(/**/),
50-
};
51-
52-
// Client:
53-
await client.foo.get({});
54-
await client.bar.post({});
55-
await client.baz.put({});
56-
await client.patch.patch({});
57-
await client.delete.delete({});
58-
```
59-
60-
### Parameters:
61-
62-
**body**: The request body
63-
**headers**: HTTP headers
64-
**params**: URL parameters (e.g.: in case of `/post/:id`, it can be `{ id: "1" }`)
65-
**query**: query parameters (e.g.: in case of `?id=2`, it can be `{ id: "2" }`)
66-
67-
Every parameter's type is defined based on the built request handler.
68-
69-
## Feature 2: Client chaining
70-
71-
```ts
72-
const client = createClient<typeof routes>({
73-
path: "http://localhost:8080/rpc",
74-
});
75-
76-
const authedClient = client.with(() => ({
77-
headers: {
78-
authorization: localStorage.getItem("token") || "nothing",
79-
},
80-
}));
81-
82-
// GOOD:
83-
await authedClient.getProfile.get({});
84-
85-
// THIS IS ALSO GOOD:
86-
await client.getProfile.get({
87-
headers: {
88-
authorization: localStorage.getItem("token") || "nothing",
89-
},
90-
});
91-
92-
// BAD EXAMPLE:
93-
// Calling the handler which requires auth headers without auth headers cause type error:
94-
await client.getProfile.get({}); // TYPE ERROR
95-
```
96-
97-
`with` can handle async callbacks, which will be evaluated every time you make a request.
11+
Please check the [docs](https://fxdave.github.io/cuple/)

0 commit comments

Comments
 (0)