Point your agent ay my profile. Vibe like a pro. A big fan of coding, digital freedom and pizza

Belfast, Ireland
Joined March 2010
DTOs + class-validator keep input honest. export class CreateUserDto { @IsEmail() email: string @MinLength(8) password: string } ValidationPipe on the controller or globally. transform: true turns plain objects into class instances. #NestJS #WebDev
29
Modules group related code. @Module({ imports: [DatabaseModule], controllers: [UsersController], providers: [UsersService], exports: [UsersService], }) export class UsersModule {} imports = other modules you need. exports = what you share. The root AppModule wires everything. #NestJS #WebDev
1
1
85
Providers are the injectable units. @Injectable() export class UsersService { findAll() { return [] } } Register in the module providers array. Nest creates and injects them. Tokens can be classes, strings, or symbols. #NestJS #WebDev
41
Controllers are the HTTP entry point. @Controller('users') export class UsersController { @Get(':id') findOne(@Param('id') id: string) { return { id } } } Routes live on methods. @Param, @Query, @Body extract data. One controller per resource keeps it scannable. #NestJS #WebDev
60
The Next.js 16 stuff teams still skip: - cacheComponents + 'use cache' - cacheLife / cacheTag / updateTag - Suspense holes for PPR - Server Actions instead of form route handlers - await params and searchParams - proxy.ts instead of middleware.ts - forbidden() / unauthorized() - next/font + Image priority on the LCP - after() for non-blocking work - generateMetadata Ship one of these this week. Stop wrapping a fetch in useEffect. #NextJS #WebDev
1
1
78
Parallel routes are slots, not nested pages you hope will line up. app/@modal/(.)photo/[id]/page.tsx app/@feed/page.tsx app/layout.tsx // receives { modal, feed, children } default.tsx is required or a hard refresh 404s the slot. Intercepting routes (.) keep the list mounted while the photo opens as a modal. #NextJS #WebDev
1
47
generateStaticParams is the static set, not a cache hint. export async function generateStaticParams() { const posts = await db.post.listSlugs() return posts.map((p) => ({ slug: p.slug })) } Build knows those URLs. Unknown slugs can still render dynamically. Empty array means "do not prerender this segment." #NextJS #WebDev
1
56
connection() opts a subtree into request time. On purpose. import { connection } from 'next/server' export async function Personalized() { await connection() const session = await cookies() return

{session.get('uid')?.value}

} Calling cookies() or headers() already does this. connection() is the explicit version when the dynamic bit is not a request API. Do not sprinkle it on the layout. #NextJS #WebDev
1
39
after() is the work that should not block the response. import { after } from 'next/server' export async function POST(request: Request) { const payload = await request.json() after(() => logEvent(payload)) return Response.json({ ok: true }) } Analytics, audit rows, email enqueue. The client already has the answer. Do not await the side effect on the critical path. #NextJS #WebDev
1
54
Link prefetches the route. prefetch={false} when the list is huge. import Link from 'next/link' {title} Default prefetch on viewport is why the dashboard feels instant and why a 200-row table hammers the server. Turn it off on low-intent links. #NextJS #WebDev
1
2
59
proxy.ts replaced middleware.ts. // proxy.ts export function proxy(request: NextRequest) { const token = request.cookies.get('session') if (!token) return NextResponse.redirect(new URL('/login', request.url)) return NextResponse.next() } Auth gate and rewrites live here. Do not fetch your database in the proxy. Check a cookie, then do the work in the server component. #NextJS #WebDev
2
47
forbidden() and unauthorized() are the status, not a redirect to /login disguised as a 200. import { forbidden, unauthorized } from 'next/navigation' if (!session) unauthorized() if (!session.isAdmin) forbidden() Pair with unauthorized.tsx and forbidden.tsx. A redirect() to the login page is navigation. These are HTTP. #NextJS #WebDev
2
60
loading.tsx and error.tsx are the boundaries you keep rewriting. app/dashboard/loading.tsx app/dashboard/error.tsx error.tsx must be a Client Component. It receives error and reset. Do not catch render errors in a try/catch around JSX. The segment file is the boundary. #NextJS #WebDev
1
74
params and searchParams are promises. Await them. export default async function Page({ params, searchParams, }: { params: Promise<{ slug: string }> searchParams: Promise<{ q?: string }> }) { const { slug } = await params const { q } = await searchParams return
} Sync access is the Next 15 warning that became a break. Await at the edge of the page. #NextJS #WebDev
1
72
generateMetadata is the head tag. export async function generateMetadata({ params }: Props): Promise { const { slug } = await params const post = await getPost(slug) return { title: post.title, description: post.excerpt } } Do not hand-roll in a client component. Open Graph and canonical live here too. #NextJS #WebDev
1
52
next/font kills the layout shift. import { Geist } from 'next/font/google' const geist = Geist({ subsets: ['latin'] }) export default function RootLayout({ children }: { children: React.ReactNode }) { return {children} } Self-hosted at build. No Google CSS request on the critical path. Variable fonts go in the same call. #NextJS #WebDev
2
109
Partial Prerendering is the shell, not a second mode. export default async function Page() { const user = await getCurrentUser() return ( <>

Welcome, {user.name}

}> user.id} /> ) } Static shell first. Dynamic holes stream. One route, both. Wrap the hole or the whole page waits. #NextJS #WebDev
1
59
cacheTag plus updateTag is read-your-writes. async function getUser(id: string) { 'use cache' cacheTag(`user-${id}`) return db.user.find(id) } 'use server' export async function save(id: string, input: Profile) { await db.user.update(id, input) updateTag(`user-${id}`) } revalidateTag waits. updateTag expires and reads fresh in the same action. #NextJS #WebDev
1
55
'use cache' is how you mark data cached in Next.js 16. async function Projects({ userId }: { userId: string }) { 'use cache' cacheLife('hours') return db.projects.list(userId) } Enable cacheComponents. Arguments are the cache key. cookies() and headers() do not belong inside the scope. Pass the id in. #NextJS #WebDev
2
3
103
Server Components are the default. 'use client' is the opt-in. // app/price.tsx — no directive, runs on the server export async function Price({ sku }: { sku: string }) { const row = await db.product.find(sku) return

{row.amount}

} Fetch in the component. Do not lift every leaf into a client bundle so a button can be clickable. #NextJS #WebDev
1
2
73