Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types: improve typing to allow custom request and reply #211

Merged
merged 1 commit into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import {
ContextConfigDefault,
RouteGenericInterface,
FastifyInstance,
FastifyPluginCallback,
FastifyReply,
FastifyRequest,
FastifySchema,
RouteGenericInterface,
preHandlerHookHandler
} from 'fastify';

declare module 'fastify' {
interface FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider> {
auth(
functions: fastifyAuth.FastifyAuthFunction[] | (fastifyAuth.FastifyAuthFunction | fastifyAuth.FastifyAuthFunction[])[],
auth<
Request extends FastifyRequest = FastifyRequest,
Reply extends FastifyReply = FastifyReply
Comment on lines +15 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looks not correct. Why is Request and Reply not depending on the Generic Types from FastifyInstance?

Suggested change
Request extends FastifyRequest = FastifyRequest,
Reply extends FastifyReply = FastifyReply
Request extends FastifyRequest = Request extends FastifyRequest = FastifyRequest<RawServerDefault, RawRequest, FastifySchema, TypeProvider, ContextConfigDefault, Logger>,
Reply extends FastifyReply = FastifyReply<RawServer, RawRequest, RawReply, RouteGenericInterface, ContextConfigDefault, FastifySchema, TypeProvider>

Untested but I would expect this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FastifyRequest and FastifyReply interfaces have changed from your example.
The default should be equal to the old value to prevent breaking the previous code.
Furthermore, now the FastifyRequest accepts other generic types, for instance, RouteGeneric, and in this way, the users can pass the request as they prefer.
If you take a look at the tests, I tried to break nothing and include your request to leave the power to the user to choose the request type.

>(
functions: fastifyAuth.FastifyAuthFunction<Request, Reply>[] | (fastifyAuth.FastifyAuthFunction<Request, Reply> | fastifyAuth.FastifyAuthFunction<Request, Reply>[])[],
options?: {
relation?: fastifyAuth.FastifyAuthRelation;
run?: 'all';
Expand All @@ -26,10 +29,13 @@ type FastifyAuth = FastifyPluginCallback<fastifyAuth.FastifyAuthPluginOptions>
declare namespace fastifyAuth {
export type FastifyAuthRelation = 'and' | 'or'

export type FastifyAuthFunction = (
export type FastifyAuthFunction<
Request extends FastifyRequest = FastifyRequest,
Reply extends FastifyReply = FastifyReply
> = (
this: FastifyInstance,
request: FastifyRequest,
reply: FastifyReply,
request: Request,
reply: Reply,
done: (error?: Error) => void
) => void;

Expand Down
61 changes: 59 additions & 2 deletions types/index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import fastify, { FastifyRequest, FastifyReply, preHandlerHookHandler, FastifyInstance } from 'fastify';
import fastify, { FastifyInstance, FastifyReply, FastifyRequest, preHandlerHookHandler } from 'fastify';
import fastifyAuth from '..'
import { expectType } from 'tsd';
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
Expand Down Expand Up @@ -57,4 +57,61 @@ jsonSchemaToTS.route({
url: '/',
preHandler: jsonSchemaToTS.auth([]),
handler: () => {}
})
})

declare module "fastify" {
interface FastifyRequest {
identity: {actorId: string};
}

interface FastifyInstance {
authenticate: (request: FastifyRequest) => Promise<void>;
}
}

export const usersMutationAccessPolicy =
(fastify: FastifyInstance) =>
async (
request: FastifyRequest<{
Params: { userId: string }
}>,
): Promise<void> => {
const { actorId } = request.identity;
const isOwner = actorId === request.params.userId;

if (isOwner) {
return;
}

fastify.log.warn("Actor should not be able to see this route");

throw new Error(request.params.userId);
};

async function usersController(fastify: FastifyInstance): Promise<void> {
fastify.patch<{
Params: { userId: string };
Body: { name: string };
}>(
"/:userId",
{
onRequest: fastify.auth([
usersMutationAccessPolicy(fastify),
]),
},
async (req, res) => ({ success: true }),
);
}

async function usersControllerV2(fastify: FastifyInstance): Promise<void> {
fastify.patch<{
Params: { userId: string };
Body: { name: string };
}>(
"/:userId",
{
onRequest: usersMutationAccessPolicy(fastify),
},
async (req, res) => ({ success: true }),
);
}