Skip to content

Commit

Permalink
blog: trpc server actions (#5735)
Browse files Browse the repository at this point in the history
* initial draft

* chore: apply lint and formatting fixes

* path extactor helper

* link to github

* Apply suggestions from code review

Co-authored-by: Alex / KATT <alexander@n1s.se>

* { meta }

* finalizing

* example rate limited action

* reorder

* console.log

* banner

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Alex / KATT <alexander@n1s.se>
  • Loading branch information
3 people committed May 26, 2024
1 parent c4770bc commit 4a672dd
Show file tree
Hide file tree
Showing 7 changed files with 485 additions and 14 deletions.
45 changes: 44 additions & 1 deletion packages/server/src/adapters/next-app-dir.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initTRPC } from '../@trpc/server';
import { experimental_trpcMiddleware, initTRPC } from '../@trpc/server';
import { nextAppDirCaller } from './next-app-dir/nextAppDirCaller';

test('experimental caller', async () => {
Expand Down Expand Up @@ -48,3 +48,46 @@ test('with context', async () => {
),
);
});

test('with path extractor', async () => {
interface Meta {
span: string;
}
const t = initTRPC.meta<Meta>().create();

// Some external middleware that relies on procedure paths
const loggerMiddleware = experimental_trpcMiddleware().create(
async (opts) => {
const start = new Date().getTime();
const result = await opts.next();
const duration = new Date().getTime() - start;

// eslint-disable-next-line no-console
console.log(`${opts.path} took ${duration}ms`);

return result;
},
);

const base = t.procedure
.experimental_caller(
nextAppDirCaller({
pathExtractor: ({ meta }) => (meta as Meta).span,
}),
)
.use(loggerMiddleware);

const loggerSpy = vi.spyOn(console, 'log').mockImplementation(() => {
// no-op
});

{
const proc = base.meta({ span: 'hello' }).query(async () => 'hello');
const result = await proc();
expect(result).toBe('hello');

expect(loggerSpy).toHaveBeenCalledWith(
expect.stringContaining('hello took'),
);
}
});
14 changes: 10 additions & 4 deletions packages/server/src/adapters/next-app-dir/nextAppDirCaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ import { rethrowNextErrors } from './rethrowNextErrors';
/**
* Create a caller that works with Next.js React Server Components & Server Actions
*/
export function nextAppDirCaller<TContext>(
export function nextAppDirCaller<TContext, TMeta>(
config: Simplify<
{
/**
* Extract the path from the procedure metadata
*/
pathExtractor?: (opts: { meta: TMeta }) => string;
/**
* Transform form data to a `Record` before passing it to the procedure
* @default true
Expand All @@ -43,6 +47,8 @@ export function nextAppDirCaller<TContext>(
return config?.createContext?.() ?? ({} as TContext);
};
return async (opts) => {
const path =
config.pathExtractor?.({ meta: opts._def.meta as TMeta }) ?? '';
const ctx: TContext = await createContext().catch((cause) => {
const error = new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
Expand All @@ -60,7 +66,7 @@ export function nextAppDirCaller<TContext>(
ctx,
error,
input: opts.args[0],
path: '',
path,
type: opts._def.type,
});

Expand All @@ -86,7 +92,7 @@ export function nextAppDirCaller<TContext>(
type: opts._def.type,
ctx,
getRawInput: async () => input,
path: '',
path,
input,
})
.then((data) => {
Expand All @@ -102,7 +108,7 @@ export function nextAppDirCaller<TContext>(
type: opts._def.type,
ctx,
getRawInput: async () => input,
path: '',
path,
input,
})
.then((data) => {
Expand Down
81 changes: 80 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4a672dd

Please sign in to comment.