trpc: improve folders structure
This commit is contained in:
parent
1f1b3afd7b
commit
23e51a6420
@ -1,9 +1,38 @@
|
||||
/* eslint-disable jsdoc/check-tag-names */
|
||||
/* eslint-disable canonical/filename-match-regex */
|
||||
import { createContext } from '@/trpc/context';
|
||||
import appRouter from '@/trpc/routers';
|
||||
import { createContext } from '@/server/context';
|
||||
import { appRouter } from '@/server/routers/_app';
|
||||
import * as trpcNext from '@trpc/server/adapters/next';
|
||||
|
||||
export default trpcNext.createNextApiHandler({
|
||||
/**
|
||||
* Enable query batching
|
||||
*/
|
||||
batching: {
|
||||
enabled: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* @link https://trpc.io/docs/context
|
||||
*/
|
||||
createContext,
|
||||
|
||||
/**
|
||||
* @link https://trpc.io/docs/error-handling
|
||||
*/
|
||||
onError({ error }) {
|
||||
if (error.code === 'INTERNAL_SERVER_ERROR') {
|
||||
// send to bug reporting
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Something went wrong', error);
|
||||
}
|
||||
},
|
||||
|
||||
router: appRouter,
|
||||
/**
|
||||
* @link https://trpc.io/docs/caching#api-response-caching
|
||||
*/
|
||||
// responseMeta() {
|
||||
// // ...
|
||||
// },
|
||||
});
|
||||
|
||||
@ -25,7 +25,7 @@ export async function action({ store, trpcClient }) {
|
||||
|
||||
const payments = toJS($tables.payments.values);
|
||||
|
||||
trpcClient.calculate.calculate
|
||||
trpcClient.calculate
|
||||
.mutate({
|
||||
insurance: { values: insurance },
|
||||
payments: { values: payments },
|
||||
|
||||
@ -20,7 +20,7 @@ export function common({ store, trpcClient }: ProcessContext) {
|
||||
key,
|
||||
});
|
||||
|
||||
trpcClient.quote.getData
|
||||
trpcClient.getQuote
|
||||
.query({
|
||||
values: {
|
||||
quote: quote.value,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { User } from '@/api/user/types';
|
||||
import type RootStore from '@/stores/root';
|
||||
import type { TRPCPureClient } from '@/trpc/types';
|
||||
import type { TRPCPureClient } from '@/trpc/client';
|
||||
import type { ApolloClient } from '@apollo/client';
|
||||
import type { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { t } from './server';
|
||||
import { t } from './trpc';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
/**
|
||||
* @see https://trpc.io/docs/v10/middlewares
|
||||
*/
|
||||
export const userMiddleware = t.middleware(({ ctx, next }) => {
|
||||
if (process.env.NODE_ENV !== 'development' && !ctx.user) {
|
||||
throw new TRPCError({
|
||||
@ -14,3 +17,5 @@ export const userMiddleware = t.middleware(({ ctx, next }) => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
export const middleware = t.middleware;
|
||||
12
apps/web/server/procedure.ts
Normal file
12
apps/web/server/procedure.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { userMiddleware } from './middleware';
|
||||
import { t } from './trpc';
|
||||
|
||||
/**
|
||||
* Create an unprotected procedure
|
||||
*
|
||||
* @see https://trpc.io/docs/v10/procedures
|
||||
*/
|
||||
|
||||
export const publicProcedure = t.procedure;
|
||||
|
||||
export const protectedProcedure = t.procedure.use(userMiddleware);
|
||||
7
apps/web/server/routers/_app.ts
Normal file
7
apps/web/server/routers/_app.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { mergeRouters } from '../trpc';
|
||||
import { calculateRouter } from './calculate';
|
||||
import { quoteRouter } from './quote';
|
||||
|
||||
export const appRouter = mergeRouters(quoteRouter, calculateRouter);
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
73
apps/web/server/routers/calculate/index.ts
Normal file
73
apps/web/server/routers/calculate/index.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { router } from '../../trpc';
|
||||
import { createRequestData } from './lib/request';
|
||||
import { transformCalculateResults } from './lib/transform';
|
||||
import { validate } from './lib/validation';
|
||||
import { CalculateInputSchema, CalculateOutputSchema } from './types';
|
||||
import { calculate } from '@/api/core/query';
|
||||
import initializeApollo from '@/apollo/client';
|
||||
import { STALE_TIME } from '@/constants/request';
|
||||
import { protectedProcedure } from '@/server/procedure';
|
||||
import type { QueryFunctionContext } from '@tanstack/react-query';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const calculateRouter = router({
|
||||
calculate: protectedProcedure
|
||||
.input(CalculateInputSchema)
|
||||
.output(CalculateOutputSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const apolloClient = initializeApollo();
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const validationResult = await validate({
|
||||
context: {
|
||||
apolloClient,
|
||||
queryClient,
|
||||
user: ctx.user,
|
||||
},
|
||||
input,
|
||||
});
|
||||
|
||||
if (validationResult.success === false) {
|
||||
return {
|
||||
error: validationResult.error,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
const requestData = await createRequestData({
|
||||
context: {
|
||||
apolloClient,
|
||||
queryClient,
|
||||
user: ctx.user,
|
||||
},
|
||||
input,
|
||||
user: ctx.user,
|
||||
});
|
||||
|
||||
const calculateResult = await queryClient.fetchQuery(
|
||||
['calculate'],
|
||||
(context: QueryFunctionContext) => calculate(requestData, context),
|
||||
{
|
||||
staleTime: STALE_TIME,
|
||||
}
|
||||
);
|
||||
|
||||
if (calculateResult.errors?.length > 0) {
|
||||
return {
|
||||
error: calculateResult.errors[0],
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
const result = transformCalculateResults({
|
||||
calculateInput: input,
|
||||
requestCalculate: requestData,
|
||||
responseCalculate: calculateResult,
|
||||
});
|
||||
|
||||
return {
|
||||
data: result,
|
||||
success: true,
|
||||
};
|
||||
}),
|
||||
});
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import type { CalculateInput, Context } from './types';
|
||||
import type { CalculateInput, Context } from '../types';
|
||||
import type * as CoreTypes from '@/api/core/types';
|
||||
import type { User } from '@/api/user/types';
|
||||
import { ESN, NSIB_MAX, VAT } from '@/constants/values';
|
||||
@ -26,7 +26,7 @@ type AdditionalDataGetters = {
|
||||
[Key in keyof CoreTypes.AdditionalData]: () => Promise<CoreTypes.AdditionalData[Key]>;
|
||||
};
|
||||
|
||||
export async function getRequestData({
|
||||
export async function createRequestData({
|
||||
context,
|
||||
input,
|
||||
user,
|
||||
@ -1,4 +1,4 @@
|
||||
import type { CalculateInput, OutputData } from './types';
|
||||
import type { CalculateInput, OutputData } from '../types';
|
||||
import type { RequestCalculate, ResponseCalculate } from '@/api/core/types';
|
||||
import { ESN, NDFL, VAT } from '@/constants/values';
|
||||
import { last } from 'radash';
|
||||
@ -9,7 +9,10 @@ type Input = {
|
||||
responseCalculate: ResponseCalculate;
|
||||
};
|
||||
|
||||
export function convertCalculateResults({ responseCalculate, calculateInput }: Input): OutputData {
|
||||
export function transformCalculateResults({
|
||||
responseCalculate,
|
||||
calculateInput,
|
||||
}: Input): OutputData {
|
||||
const { values: inputValues } = calculateInput;
|
||||
const { postValues, columns, preparedValues } = responseCalculate;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable canonical/sort-keys */
|
||||
import { t } from '../server';
|
||||
import { publicProcedure } from '../procedure';
|
||||
import { router } from '../trpc';
|
||||
import defaultValues from '@/config/default-values';
|
||||
import * as insuranceTable from '@/config/tables/insurance-table';
|
||||
import * as addProduct from '@/process/add-product';
|
||||
@ -30,8 +30,8 @@ const defaultInsurance = {
|
||||
const defaultFingap = { keys: [] };
|
||||
const defaultPayments = { values: [] };
|
||||
|
||||
const quoteRouter = t.router({
|
||||
getData: t.procedure
|
||||
export const quoteRouter = router({
|
||||
getQuote: publicProcedure
|
||||
.input(GetQuoteInputDataSchema)
|
||||
.output(GetQuoteOutputDataSchema)
|
||||
.query(async ({ input }) => {
|
||||
@ -67,5 +67,3 @@ const quoteRouter = t.router({
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
export default quoteRouter;
|
||||
40
apps/web/server/trpc.ts
Normal file
40
apps/web/server/trpc.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* This is your entry point to setup the root configuration for tRPC on the server.
|
||||
* - `initTRPC` should only be used once per app.
|
||||
* - We export only the functionality that we use so we can enforce which base procedures should be used
|
||||
*
|
||||
* Learn how to create protected base procedures and other things below:
|
||||
*
|
||||
* @see https://trpc.io/docs/v10/router
|
||||
* @see https://trpc.io/docs/v10/procedures
|
||||
*/
|
||||
|
||||
import type { Context } from './context';
|
||||
import { initTRPC } from '@trpc/server';
|
||||
import SuperJSON from 'superjson';
|
||||
|
||||
export const t = initTRPC.context<Context>().create({
|
||||
/**
|
||||
* @see https://trpc.io/docs/v10/error-formatting
|
||||
*/
|
||||
errorFormatter({ shape }) {
|
||||
return shape;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see https://trpc.io/docs/v10/data-transformers
|
||||
*/
|
||||
transformer: SuperJSON,
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a router
|
||||
*
|
||||
* @see https://trpc.io/docs/v10/router
|
||||
*/
|
||||
export const router = t.router;
|
||||
|
||||
/**
|
||||
* @see https://trpc.io/docs/v10/merging-routers
|
||||
*/
|
||||
export const mergeRouters = t.mergeRouters;
|
||||
@ -1,10 +1,24 @@
|
||||
import type { AppRouter } from './routers';
|
||||
import getUrls from '@/config/urls';
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@/server/routers/_app';
|
||||
import { createTRPCProxyClient, httpBatchLink, loggerLink } from '@trpc/client';
|
||||
import { createTRPCNext } from '@trpc/next';
|
||||
import type { NextPageContext } from 'next';
|
||||
import SuperJSON from 'superjson';
|
||||
import { isServer } from 'tools/common';
|
||||
|
||||
export type SSRContext = NextPageContext & {
|
||||
/**
|
||||
* Set HTTP Status code
|
||||
*
|
||||
* @example
|
||||
* const utils = trpc.useContext();
|
||||
* if (utils.ssrContext) {
|
||||
* utils.ssrContext.status = 404;
|
||||
* }
|
||||
*/
|
||||
status?: number;
|
||||
};
|
||||
|
||||
const { BASE_PATH, PORT } = getUrls();
|
||||
|
||||
function getBaseUrl() {
|
||||
@ -13,40 +27,108 @@ function getBaseUrl() {
|
||||
return `http://localhost:${PORT ?? 3000}${BASE_PATH}`;
|
||||
}
|
||||
|
||||
const url = `${getBaseUrl()}/api/trpc`;
|
||||
|
||||
export const trpcClient = createTRPCNext<AppRouter>({
|
||||
config({ ctx }) {
|
||||
/**
|
||||
* If you want to use SSR, you need to use the server's full URL
|
||||
*
|
||||
* @link https://trpc.io/docs/ssr
|
||||
*/
|
||||
return {
|
||||
/**
|
||||
* @link https://trpc.io/docs/links
|
||||
*/
|
||||
links: [
|
||||
// adds pretty logs to your console in development and logs errors in production
|
||||
loggerLink({
|
||||
enabled: (opts) =>
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
(opts.direction === 'down' && opts.result instanceof Error),
|
||||
}),
|
||||
httpBatchLink({
|
||||
/**
|
||||
* Set custom request headers on every request from tRPC
|
||||
*
|
||||
* @link https://trpc.io/docs/ssr
|
||||
*/
|
||||
headers() {
|
||||
return {
|
||||
cookie: ctx?.req?.headers.cookie,
|
||||
};
|
||||
if (!ctx?.req?.headers) {
|
||||
return {};
|
||||
}
|
||||
// To use SSR properly, you need to forward the client's headers to the server
|
||||
// This is so you can pass through things like cookies when we're server-side rendering
|
||||
|
||||
const {
|
||||
// If you're using Node 18 before 18.15.0, omit the "connection" header
|
||||
connection: _connection,
|
||||
...headers
|
||||
} = ctx.req.headers;
|
||||
|
||||
return headers;
|
||||
},
|
||||
url,
|
||||
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
}),
|
||||
],
|
||||
queryClientConfig: {
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* @link https://trpc.io/docs/data-transformers
|
||||
*/
|
||||
transformer: SuperJSON,
|
||||
/**
|
||||
* @link https://react-query.tanstack.com/reference/QueryClient
|
||||
*/
|
||||
// queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
|
||||
};
|
||||
},
|
||||
ssr: false,
|
||||
|
||||
/**
|
||||
* Set headers or status code when doing SSR
|
||||
*/
|
||||
responseMeta(opts) {
|
||||
const ctx = opts.ctx as SSRContext;
|
||||
|
||||
if (ctx.status) {
|
||||
// If HTTP status set, propagate that
|
||||
return {
|
||||
status: ctx.status,
|
||||
};
|
||||
}
|
||||
|
||||
const error = opts.clientErrors[0];
|
||||
if (error) {
|
||||
// Propagate http first error from API calls
|
||||
return {
|
||||
status: error.data?.httpStatus ?? 500,
|
||||
};
|
||||
}
|
||||
|
||||
// for app caching with SSR see https://trpc.io/docs/caching
|
||||
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* @link https://trpc.io/docs/ssr
|
||||
*/
|
||||
ssr: true,
|
||||
});
|
||||
|
||||
export type TRPCClient = typeof trpcClient;
|
||||
|
||||
export const trpcPureClient = createTRPCProxyClient<AppRouter>({
|
||||
links: [
|
||||
// adds pretty logs to your console in development and logs errors in production
|
||||
loggerLink({
|
||||
enabled: (opts) =>
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
(opts.direction === 'down' && opts.result instanceof Error),
|
||||
}),
|
||||
httpBatchLink({
|
||||
url,
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
}),
|
||||
],
|
||||
transformer: SuperJSON,
|
||||
});
|
||||
|
||||
export type TRPCPureClient = typeof trpcPureClient;
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
import { userMiddleware } from '../../middleware';
|
||||
import { t } from '../../server';
|
||||
import { convertCalculateResults } from './convert';
|
||||
import { getRequestData } from './request';
|
||||
import { CalculateInputSchema, CalculateOutputSchema } from './types';
|
||||
import { validate } from './validation';
|
||||
import { calculate } from '@/api/core/query';
|
||||
import type { ResponseCalculate } from '@/api/core/types/calculate';
|
||||
import initializeApollo from '@/apollo/client';
|
||||
import { STALE_TIME } from '@/constants/request';
|
||||
import type { QueryFunctionContext } from '@tanstack/react-query';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
const calculateRouter = t.router({
|
||||
calculate: t.procedure
|
||||
.use(userMiddleware)
|
||||
.input(CalculateInputSchema)
|
||||
.output(CalculateOutputSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const apolloClient = initializeApollo();
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const validationResult = await validate({
|
||||
context: {
|
||||
apolloClient,
|
||||
queryClient,
|
||||
user: ctx.user,
|
||||
},
|
||||
input,
|
||||
});
|
||||
|
||||
if (validationResult.success === false) {
|
||||
return {
|
||||
error: validationResult.error,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
const payload = await getRequestData({
|
||||
context: {
|
||||
apolloClient,
|
||||
queryClient,
|
||||
user: ctx.user,
|
||||
},
|
||||
input,
|
||||
user: ctx.user,
|
||||
});
|
||||
|
||||
try {
|
||||
const calculateResult = await queryClient.fetchQuery(
|
||||
['calculate'],
|
||||
(context: QueryFunctionContext) => calculate(payload, context),
|
||||
{
|
||||
staleTime: STALE_TIME,
|
||||
}
|
||||
);
|
||||
|
||||
if (calculateResult.errors?.length > 0) {
|
||||
return {
|
||||
error: calculateResult.errors[0],
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
const result = convertCalculateResults({
|
||||
calculateInput: input,
|
||||
requestCalculate: payload,
|
||||
responseCalculate: calculateResult,
|
||||
});
|
||||
|
||||
return {
|
||||
data: result,
|
||||
success: true,
|
||||
};
|
||||
} catch (error) {
|
||||
const { errors } = error as Pick<ResponseCalculate, 'errors'>;
|
||||
|
||||
return {
|
||||
error: errors[0],
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
export default calculateRouter;
|
||||
@ -1,12 +0,0 @@
|
||||
import { t } from '../server';
|
||||
import calculateRouter from './calculate';
|
||||
import quoteRouter from './quote';
|
||||
|
||||
const appRouter = t.router({
|
||||
calculate: calculateRouter,
|
||||
quote: quoteRouter,
|
||||
});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
|
||||
export default appRouter;
|
||||
@ -1,7 +0,0 @@
|
||||
import type { Context } from './context';
|
||||
import { initTRPC } from '@trpc/server';
|
||||
import SuperJSON from 'superjson';
|
||||
|
||||
export const t = initTRPC.context<Context>().create({
|
||||
transformer: SuperJSON,
|
||||
});
|
||||
@ -1,5 +0,0 @@
|
||||
import type { trpcClient, trpcPureClient } from './client';
|
||||
|
||||
export type TRPCClient = typeof trpcClient;
|
||||
|
||||
export type TRPCPureClient = typeof trpcPureClient;
|
||||
Loading…
x
Reference in New Issue
Block a user