You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(middleware): support creation of standalone functions (#229)
Code in this PR requires context to be an object, it extends it by default, and enables creation of standalone middleware functions via built-in `experimental_createMiddleware` utility.
re #222
* Creates a standalone middleware function. It accepts a generic object with optional `serverError`, `ctx` and `metadata`
5
+
* properties, if you need one or all of them to be typed. The type for each property that is passed as generic is the
6
+
* **minimum** shape required to define the middleware function, but it can also be larger than that.
7
+
*
8
+
* {@link https://next-safe-action.dev/docs/safe-action-client/middleware#create-standalone-middleware-with-createmiddleware See docs for more information}
If you chain multiple `schema` methods, as explained in the [Extend previous schema](/docs/recipes/extend-previous-schema) page, and want to override the default validation errors shape, you **must** use `handleValidationErrorsShape` inside the last `schema` method, otherwise there would be a type mismatch in the returned action result.
52
+
If you chain multiple `schema` methods, as explained in the [Extend previous schema](/docs/safe-action-client/extend-previous-schema) page, and want to override the default validation errors shape, you **must** use `handleValidationErrorsShape` inside the last `schema` method, otherwise there would be a type mismatch in the returned action result.
53
53
:::
54
54
55
55
### `flattenValidationErrors` and `flattenBindArgsValidationErrors` utility functions
Copy file name to clipboardexpand all lines: website/docs/safe-action-client/extend-a-client.md
+2-2
Original file line number
Diff line number
Diff line change
@@ -1,9 +1,9 @@
1
1
---
2
-
sidebar_position: 3
2
+
sidebar_position: 5
3
3
description: Learn how to use both a basic and an authorization client at the same time in your project.
4
4
---
5
5
6
-
# Extend base client
6
+
# Extend a client
7
7
8
8
A common and recommended pattern with this library is to extend the base safe action client, to cover different use cases that you might want and/or need in your applicaton.
Copy file name to clipboardexpand all lines: website/docs/safe-action-client/index.md
+1-1
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,6 @@ description: Safe action client is the instance that you can use to create types
5
5
6
6
# Safe action client
7
7
8
-
The safe action client instance is created by the `createSafeActionClient()` function. The instance is used to create safe actions, as you have already seen in previous sections of the documentation. You can create multiple clients too, for different purposes, as explained [in this section](/docs/recipes/extend-base-client).
8
+
The safe action client instance is created by the `createSafeActionClient()` function. The instance is used to create safe actions, as you have already seen in previous sections of the documentation. You can create multiple clients too, for different purposes, as explained [in this page](/docs/safe-action-client/extend-a-client).
9
9
10
10
You can also provide functions to the client, to customize the behavior for every action you then create with it. We will explore them in detail in the next sections.
Copy file name to clipboardexpand all lines: website/docs/safe-action-client/middleware.md
+87-2
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,7 @@ description: Learn how to use middleware functions in your actions.
5
5
6
6
# Middleware
7
7
8
-
next-safe-action, since version 7, ships with a composable and powerful middleware system, which allows you to create functions for almost every kind of use case you can imagine (authorization, logging, role based access, etc.). It works very similarly to the [tRPC implementation](https://trpc.io/docs/server/middlewares), with some minor differences.
8
+
next-safe-action, since version 7, ships with a composable and powerful middleware system, which allows you to create functions for almost every kind of use case you can imagine (authorization, logging, role based access, etc.). It works very similarly to the [tRPC implementation](https://trpc.io/docs/server/middlewares).
9
9
10
10
Middleware functions are defined using [`use`](/docs/safe-action-client/instance-methods#use) method in your action clients, via the `middlewareFn` argument.
// Here we pass the same untouched context (`userId`) to the next function, since we don't need
146
146
// to add data to the context here.
147
-
returnnext({ ctx });
147
+
returnnext();
148
148
})
149
149
.metadata({ actionName: "deleteUser" })
150
150
.action(async ({ ctx: { userId } }) => {
@@ -200,3 +200,88 @@ Note that the second line comes from the default `handleServerErrorLog` function
200
200
## `middlewareFn` return value
201
201
202
202
`middlewareFn` returns a Promise of a [`MiddlewareResult`](/docs/types#middlewareresult) object. It extends the result of a safe action with `success` property, and `parsedInput`, `bindArgsParsedInputs` and `ctx` optional properties. This is the exact return type of the `next` function, so you must always return it (or its result) to continue executing the middleware chain.
203
+
204
+
## Extend context
205
+
206
+
Context is a special object that holds information about the current execution state. This object is passed to middleware functions and server code functions when defining actions.
207
+
208
+
Starting from version 7.6.0, context is extended by default when defining middleware functions. For instance, if you want both the `sessionId` and `userId` in the context, by using two different middleware functions (trivial example), you can do it like this:
// You can also define a middleware function that doesn't extend or modify the context.
225
+
returnnext();
226
+
})
227
+
```
228
+
229
+
```typescript title="src/app/test-action.ts"
230
+
"use server";
231
+
232
+
import { actionClient } from"@/lib/safe-action";
233
+
234
+
exportconst testAction =actionClient
235
+
.action(async ({ ctx }) => {
236
+
// Context contains `sessionId` and `userId` thanks to the middleware.
237
+
const { sessionId, userId } =ctx;
238
+
});
239
+
```
240
+
241
+
## Create standalone middleware
242
+
243
+
:::info
244
+
Experimental feature
245
+
:::
246
+
247
+
Starting from version 7.6.0, you can create standalone middleware functions using the built-in `experimental_createMiddleware()` function. It's labelled as experimental because the API could change in the future, but it's perfectly fine to use it, as it's a pretty simple function that just wraps the creation of middleware.
248
+
249
+
Thanks to this feature, and the previously mentioned [context extension](#extend-context), you can now define standalone middleware functions and even publish them as packages, if you want to.
250
+
251
+
Here's how to use `experimental_createMiddleware()`:
An action defined using the `actionClientWithMyMiddleware` will contain `foo`, `baz` and `john` in its context.
286
+
287
+
\* Note that you can pass, **but not required to**, an object with two generic properties to the `experimental_createMiddleware()` function: `ctx`\[1\], and `metadata`\[2\]. Those keys are optional, and you should only provide them if you want your middleware to require **at minimum** the shape you passed in as generic. By doing that, following the above example, you can then access `ctx.foo` and `metadata.actionName` in the middleware you're defining. If you pass a middleware that requires those properties to a client that doesn't have them, you'll get an error in `use()` method.
0 commit comments