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

fix: async/await in getLatest procedure, reduce dev delay and add fuzz #1950

Merged
merged 2 commits into from
Jul 22, 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
26 changes: 17 additions & 9 deletions cli/template/extras/src/server/api/routers/post/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ import { z } from "zod";

import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";

let post = {
id: 1,
name: "Hello World",
};
// Mocked DB
interface Post {
id: number;
name: string;
}
const posts: Post[] = [
{
id: 1,
name: "Hello World",
},
];

export const postRouter = createTRPCRouter({
hello: publicProcedure
Expand All @@ -19,14 +26,15 @@ export const postRouter = createTRPCRouter({
create: publicProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));

post = { id: post.id + 1, name: input.name };
const post: Post = {
id: posts.length + 1,
name: input.name,
};
posts.push(post);
return post;
}),

getLatest: publicProcedure.query(() => {
return post;
return posts.at(-1) ?? null;
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ export const postRouter = createTRPCRouter({
create: protectedProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ ctx, input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));

await ctx.db.insert(posts).values({
name: input.name,
createdById: ctx.session.user.id,
});
}),

getLatest: publicProcedure.query(({ ctx }) => {
const post = ctx.db.query.posts.findFirst({
getLatest: publicProcedure.query(async ({ ctx }) => {
const post = await ctx.db.query.posts.findFirst({
orderBy: (posts, { desc }) => [desc(posts.createdAt)],
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ export const postRouter = createTRPCRouter({
create: protectedProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ ctx, input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));

return ctx.db.post.create({
data: {
name: input.name,
Expand All @@ -29,8 +26,8 @@ export const postRouter = createTRPCRouter({
});
}),

getLatest: protectedProcedure.query(({ ctx }) => {
const post = ctx.db.post.findFirst({
getLatest: protectedProcedure.query(async ({ ctx }) => {
const post = await ctx.db.post.findFirst({
orderBy: { createdAt: "desc" },
where: { createdBy: { id: ctx.session.user.id } },
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ export const postRouter = createTRPCRouter({
create: protectedProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));

post = { id: post.id + 1, name: input.name };
return post;
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@ export const postRouter = createTRPCRouter({
create: publicProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ ctx, input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));

await ctx.db.insert(posts).values({
name: input.name,
});
}),

getLatest: publicProcedure.query(({ ctx }) => {
const post = ctx.db.query.posts.findFirst({
getLatest: publicProcedure.query(async ({ ctx }) => {
const post = await ctx.db.query.posts.findFirst({
orderBy: (posts, { desc }) => [desc(posts.createdAt)],
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ export const postRouter = createTRPCRouter({
create: publicProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(async ({ ctx, input }) => {
// simulate a slow db call
await new Promise((resolve) => setTimeout(resolve, 1000));

return ctx.db.post.create({
data: {
name: input.name,
},
});
}),

getLatest: publicProcedure.query(({ ctx }) => {
const post = ctx.db.post.findFirst({
getLatest: publicProcedure.query(async ({ ctx }) => {
const post = await ctx.db.post.findFirst({
orderBy: { createdAt: "desc" },
});

Expand Down
25 changes: 24 additions & 1 deletion cli/template/extras/src/server/api/trpc-app/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,34 @@ export const createCallerFactory = t.createCallerFactory;
*/
export const createTRPCRouter = t.router;

/**
* Middleware for timing procedure execution and adding an articifial delay in development.
*
* You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
* network latency that would occur in production but not in local development.
*/
const timingMiddleware = t.middleware(async ({ next, path }) => {
const start = Date.now();

if (t._config.isDev) {
// artificial delay in dev
const waitMs = Math.floor(Math.random() * 400) + 100;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}

const result = await next();

const end = Date.now();
console.log(`[TRPC] ${path} took ${end - start}ms to execute`);

return result;
});

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;
export const publicProcedure = t.procedure.use(timingMiddleware);
47 changes: 36 additions & 11 deletions cli/template/extras/src/server/api/trpc-app/with-auth-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,37 @@ export const createCallerFactory = t.createCallerFactory;
*/
export const createTRPCRouter = t.router;

/**
* Middleware for timing procedure execution and adding an articifial delay in development.
*
* You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
* network latency that would occur in production but not in local development.
*/
const timingMiddleware = t.middleware(async ({ next, path }) => {
const start = Date.now();

if (t._config.isDev) {
// artificial delay in dev
const waitMs = Math.floor(Math.random() * 400) + 100;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}

const result = await next();

const end = Date.now();
console.log(`[TRPC] ${path} took ${end - start}ms to execute`);

return result;
});

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;
export const publicProcedure = t.procedure.use(timingMiddleware);

/**
* Protected (authenticated) procedure
Expand All @@ -95,14 +118,16 @@ export const publicProcedure = t.procedure;
*
* @see https://trpc.io/docs/procedures
*/
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
export const protectedProcedure = t.procedure
.use(timingMiddleware)
.use(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
});
});
});
47 changes: 36 additions & 11 deletions cli/template/extras/src/server/api/trpc-app/with-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,37 @@ export const createCallerFactory = t.createCallerFactory;
*/
export const createTRPCRouter = t.router;

/**
* Middleware for timing procedure execution and adding an articifial delay in development.
*
* You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
* network latency that would occur in production but not in local development.
*/
const timingMiddleware = t.middleware(async ({ next, path }) => {
const start = Date.now();

if (t._config.isDev) {
// artificial delay in dev
const waitMs = Math.floor(Math.random() * 400) + 100;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}

const result = await next();

const end = Date.now();
console.log(`[TRPC] ${path} took ${end - start}ms to execute`);

return result;
});

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;
export const publicProcedure = t.procedure.use(timingMiddleware);

/**
* Protected (authenticated) procedure
Expand All @@ -92,14 +115,16 @@ export const publicProcedure = t.procedure;
*
* @see https://trpc.io/docs/procedures
*/
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
export const protectedProcedure = t.procedure
.use(timingMiddleware)
.use(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
});
});
});
25 changes: 24 additions & 1 deletion cli/template/extras/src/server/api/trpc-app/with-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,34 @@ export const createCallerFactory = t.createCallerFactory;
*/
export const createTRPCRouter = t.router;

/**
* Middleware for timing procedure execution and adding an articifial delay in development.
*
* You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
* network latency that would occur in production but not in local development.
*/
const timingMiddleware = t.middleware(async ({ next, path }) => {
const start = Date.now();

if (t._config.isDev) {
// artificial delay in dev
const waitMs = Math.floor(Math.random() * 400) + 100;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}

const result = await next();

const end = Date.now();
console.log(`[TRPC] ${path} took ${end - start}ms to execute`);

return result;
});

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;
export const publicProcedure = t.procedure.use(timingMiddleware);
25 changes: 24 additions & 1 deletion cli/template/extras/src/server/api/trpc-pages/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,34 @@ export const createCallerFactory = t.createCallerFactory;
*/
export const createTRPCRouter = t.router;

/**
* Middleware for timing procedure execution and adding an articifial delay in development.
*
* You can remove this if you don't like it, but it can help catch unwanted waterfalls by simulating
* network latency that would occur in production but not in local development.
*/
const timingMiddleware = t.middleware(async ({ next, path }) => {
const start = Date.now();

if (t._config.isDev) {
// artificial delay in dev
const waitMs = Math.floor(Math.random() * 400) + 100;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}

const result = await next();

const end = Date.now();
console.log(`[TRPC] ${path} took ${end - start}ms to execute`);

return result;
});

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;
export const publicProcedure = t.procedure.use(timingMiddleware);
Loading
Loading