feat: scaffold monorepo

This commit is contained in:
Yangshun Tay
2022-09-29 16:23:34 +08:00
parent 27a82e8c0f
commit 523d91f920
62 changed files with 19346 additions and 712 deletions

View File

@ -0,0 +1,15 @@
// Wrapper for unstable_getServerSession https://next-auth.js.org/configuration/nextjs
import type { GetServerSidePropsContext } from 'next';
// eslint-disable-next-line camelcase
import { unstable_getServerSession } from 'next-auth';
import { authOptions as nextAuthOptions } from '~/pages/api/auth/[...nextauth]';
// Next API route example - /pages/api/restricted.ts
export const getServerAuthSession = async (ctx: {
req: GetServerSidePropsContext['req'];
res: GetServerSidePropsContext['res'];
}) => {
return await unstable_getServerSession(ctx.req, ctx.res, nextAuthOptions);
};

View File

@ -0,0 +1,20 @@
// Src/server/db/client.ts
import { PrismaClient } from '@prisma/client';
import { env } from '~/env/server.mjs';
declare global {
// eslint-disable-next-line no-var, init-declarations
var prisma: PrismaClient | undefined;
}
export const prisma =
global.prisma ||
new PrismaClient({
log:
env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
if (env.NODE_ENV !== 'production') {
global.prisma = prisma;
}

View File

@ -0,0 +1,61 @@
// Src/server/router/context.ts
import type { Session } from 'next-auth';
import * as trpc from '@trpc/server';
import type * as trpcNext from '@trpc/server/adapters/next';
import { getServerAuthSession } from '~/server/common/get-server-auth-session';
import { prisma } from '~/server/db/client';
type CreateContextOptions = {
session: Session | null;
};
/** Use this helper for:
* - testing, where we dont have to Mock Next.js' req/res
* - trpc's `createSSGHelpers` where we don't have req/res
**/
export const createContextInner = async (opts: CreateContextOptions) => {
return {
prisma,
session: opts.session,
};
};
/**
* This is the actual context you'll use in your router
* @link https://trpc.io/docs/context
**/
export const createContext = async (
opts: trpcNext.CreateNextContextOptions,
) => {
const { req, res } = opts;
// Get the session from the server using the unstable_getServerSession wrapper function
const session = await getServerAuthSession({ req, res });
return await createContextInner({
session,
});
};
type Context = trpc.inferAsyncReturnType<typeof createContext>;
export const createRouter = () => trpc.router<Context>();
/**
* Creates a tRPC router that asserts all queries and mutations are from an authorized user. Will throw an unauthorized error if a user is not signed in.
**/
export function createProtectedRouter() {
return createRouter().middleware(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new trpc.TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: {
...ctx,
// Infers that `session` is non-nullable to downstream resolvers
session: { ...ctx.session, user: ctx.session.user },
},
});
});
}

View File

@ -0,0 +1,23 @@
import { z } from 'zod';
import { createRouter } from './context';
export const exampleRouter = createRouter()
.query('hello', {
input: z
.object({
text: z.string().nullish(),
})
.nullish(),
resolve({ input }) {
return {
greeting: `Hello ${input?.text ?? 'world'}`,
};
},
})
.query('getAll', {
async resolve({ ctx }) {
const items = await ctx.prisma.example.findMany();
return items;
},
});

View File

@ -0,0 +1,14 @@
// Src/server/router/index.ts
import superjson from 'superjson';
import { createRouter } from './context';
import { exampleRouter } from './example';
import { protectedExampleRouter } from './protected-example-router';
export const appRouter = createRouter()
.transformer(superjson)
.merge('example.', exampleRouter)
.merge('auth.', protectedExampleRouter);
// Export type definition of API
export type AppRouter = typeof appRouter;

View File

@ -0,0 +1,14 @@
import { createProtectedRouter } from './context';
// Example router with queries that can only be hit if the user requesting is signed in
export const protectedExampleRouter = createProtectedRouter()
.query('getSession', {
resolve({ ctx }) {
return ctx.session;
},
})
.query('getSecretMessage', {
resolve({ ctx: _ctx }) {
return 'He who asks a question is a fool for five minutes; he who does not ask a question remains a fool forever.';
},
});