Resolver merge
The merge method provides a convenient way to combine resolvers, facilitating the creation of standalone resolvers without needing the PtsqServer.init method. This flexibility allows you to develop resolvers independently and then merge them with your existing resolver set.
When two resolvers are merged, their argument and output validation schemas are intersected, ensuring that the resulting resolver adheres to the combined validation criteria. Additionally, middlewares from the original resolver are integrated with those from the resolver being merged.
It's essential to note that the context of the original resolver must extend the root context of the resolver being merged. This ensures that the merged resolver operates within the appropriate context scope, maintaining consistency and coherence across your resolver architecture.
import { PtsqError, Resolver } from '@ptsq/server';
import { Request } from 'express';
export type Role = 'Administrator' | 'Manager' | 'User';
export const authedResolver = Resolver.createRoot<{
user?: Role;
}>().use(({ ctx /* { user?: Role } */, next }) => {
if (!ctx.user) throw new PtsqError({ code: 'UNAUTHORIZED' });
return next({
user: ctx.user,
});
});
import { PtsqServer } from '@ptsq/server';
import { Type } from '@sinclair/typebox';
import { Request, Response } from 'express';
import { authedResolver, getUserFromJWT } from './auth.ts';
const createContext = async ({ req, res }: { req: Request; res: Response }) => {
const user = await getUserFromJWT(req);
return {
req,
res,
user,
};
};
const { resolver, router, serve } = PtsqServer.init({
// The context has type { req: Request; res: Response; user?: User<Role> }
ctx: createContext,
}).create();
const administratorResolver = resolver
// ctx = { req: Request; res: Response; user?: User<Role> }
.merge(authedResolver)
// ctx = { req: Request; res: Response; user: User<Role> }
.use(
({ ctx /* { req: Request; res: Response; user: User<Role> } */, next }) => {
if (ctx.user.role !== 'Administrator')
throw new PtsqError({ code: 'FORBIDDEN' });
return next({
user: ctx.user,
});
},
);
Deep context
When using deep context objects, objects are not merged deeply, only shallowly.
import { Resolver } from '@ptsq/server';
export const deepContextChangeResolver = Resolver.createRoot<{
a: {
b: number;
};
}>().use(({ ctx /* { a: { b: number } } */, next }) => {
return next({
a: {
c: 1,
},
});
});
import { PtsqServer } from '@ptsq/server';
import { Type } from '@sinclair/typebox';
import { Request, Response } from 'express';
import { deepContextChangeResolver } from './deepContextChangeResolver.ts';
const createContext = () => {
return {
a: {
b: 1,
},
};
};
const { resolver, router, serve } = PtsqServer.init({
// The context has type { req: Request; res: Response; user?: User<Role> }
ctx: createContext,
}).create();
const deepContextChangeResolverOverwrite = resolver
// ctx = { a: { b: number } }
.merge(deepContextChangeResolver);
// ctx = { a: { c: number } }
// The b property inside a object is gone
If you want to merge them deeply, you have to do that on your own.
import { Resolver } from '@ptsq/server';
export const deepContextChangeResolver = Resolver.createRoot<{
a: {
b: number;
};
}>().use(({ ctx /* { a: { b: number } } */, next }) => {
return next({
a: {
...ctx.a,
c: 1,
},
});
});
import { PtsqServer } from '@ptsq/server';
import { Type } from '@sinclair/typebox';
import { Request, Response } from 'express';
import { deepContextChangeResolver } from './deepContextChangeResolver.ts';
const createContext = () => {
return {
a: {
b: 1,
},
};
};
const { resolver, router, serve } = PtsqServer.init({
// The context has type { req: Request; res: Response; user?: User<Role> }
ctx: createContext,
}).create();
const deepContextChangeResolverOverwrite = resolver
// ctx = { a: { b: number } }
.merge(deepContextChangeResolver);
// ctx = { a: { b: number; c: number } }