Repository: t3-oss/create-t3-app Branch: main Commit: 4709861f7e67 Files: 520 Total size: 1.6 MB Directory structure: gitextract_17kc1cit/ ├── .changeset/ │ ├── README.md │ ├── beige-clouds-behave.md │ ├── config.json │ ├── curly-zoos-add.md │ ├── quick-snakes-give.md │ └── tender-humans-hide.md ├── .codesandbox/ │ └── tasks.json ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ ├── actions/ │ │ └── setup/ │ │ └── action.yml │ ├── changeset-version.js │ ├── labeler.yml │ ├── pull_request_template.md │ ├── scripts/ │ │ └── generate-matrix.js │ ├── version-script-beta.js │ ├── version-script-next.js │ └── workflows/ │ ├── ci.yml │ ├── e2e.yml │ ├── pr-labeler.yml │ ├── prerelease-comment.yml │ ├── prerelease.yml │ ├── release.yml │ └── translations.yml ├── .gitignore ├── .gitpod.yml ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── cli/ │ ├── .yarnrc.yml │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.js │ ├── package.json │ ├── prettier.config.mjs │ ├── src/ │ │ ├── cli/ │ │ │ └── index.ts │ │ ├── consts.ts │ │ ├── helpers/ │ │ │ ├── createProject.ts │ │ │ ├── format.ts │ │ │ ├── git.ts │ │ │ ├── installDependencies.ts │ │ │ ├── installPackages.ts │ │ │ ├── logNextSteps.ts │ │ │ ├── scaffoldProject.ts │ │ │ ├── selectBoilerplate.ts │ │ │ └── setImportAlias.ts │ │ ├── index.ts │ │ ├── installers/ │ │ │ ├── betterAuth.ts │ │ │ ├── biome.ts │ │ │ ├── dbContainer.ts │ │ │ ├── dependencyVersionMap.ts │ │ │ ├── drizzle.ts │ │ │ ├── envVars.ts │ │ │ ├── eslint.ts │ │ │ ├── index.ts │ │ │ ├── nextAuth.ts │ │ │ ├── prisma.ts │ │ │ ├── tailwind.ts │ │ │ └── trpc.ts │ │ └── utils/ │ │ ├── addPackageDependency.ts │ │ ├── addPackageScript.ts │ │ ├── getT3Version.ts │ │ ├── getUserPkgManager.ts │ │ ├── isTTYError.ts │ │ ├── logger.ts │ │ ├── parseNameAndPath.ts │ │ ├── removeTrailingSlash.ts │ │ ├── renderTitle.ts │ │ ├── renderVersionWarning.ts │ │ ├── validateAppName.ts │ │ └── validateImportAlias.ts │ ├── template/ │ │ ├── base/ │ │ │ ├── README.md │ │ │ ├── _gitignore │ │ │ ├── next.config.js │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── env.js │ │ │ │ └── styles/ │ │ │ │ └── globals.css │ │ │ └── tsconfig.json │ │ └── extras/ │ │ ├── config/ │ │ │ ├── _eslint.base.js │ │ │ ├── _eslint.drizzle.js │ │ │ ├── _prettier.config.js │ │ │ ├── _tailwind.prettier.config.js │ │ │ ├── biome.jsonc │ │ │ ├── drizzle-config-mysql.ts │ │ │ ├── drizzle-config-postgres.ts │ │ │ ├── drizzle-config-sqlite.ts │ │ │ ├── next-config-appdir.js │ │ │ └── postcss.config.js │ │ ├── pnpm/ │ │ │ └── _npmrc │ │ ├── prisma/ │ │ │ └── schema/ │ │ │ ├── base-planetscale.prisma │ │ │ ├── base.prisma │ │ │ ├── with-auth-planetscale.prisma │ │ │ ├── with-auth.prisma │ │ │ ├── with-better-auth-planetscale.prisma │ │ │ └── with-better-auth.prisma │ │ ├── src/ │ │ │ ├── app/ │ │ │ │ ├── _components/ │ │ │ │ │ ├── post-tw.tsx │ │ │ │ │ └── post.tsx │ │ │ │ ├── api/ │ │ │ │ │ ├── auth/ │ │ │ │ │ │ ├── [...all]/ │ │ │ │ │ │ │ └── route.ts │ │ │ │ │ │ └── [...nextauth]/ │ │ │ │ │ │ └── route.ts │ │ │ │ │ └── trpc/ │ │ │ │ │ └── [trpc]/ │ │ │ │ │ └── route.ts │ │ │ │ ├── layout/ │ │ │ │ │ ├── base.tsx │ │ │ │ │ ├── with-trpc-tw.tsx │ │ │ │ │ ├── with-trpc.tsx │ │ │ │ │ └── with-tw.tsx │ │ │ │ └── page/ │ │ │ │ ├── base.tsx │ │ │ │ ├── with-auth-trpc-tw.tsx │ │ │ │ ├── with-auth-trpc.tsx │ │ │ │ ├── with-better-auth-trpc-tw.tsx │ │ │ │ ├── with-better-auth-trpc.tsx │ │ │ │ ├── with-better-auth-tw.tsx │ │ │ │ ├── with-better-auth.tsx │ │ │ │ ├── with-trpc-tw.tsx │ │ │ │ ├── with-trpc.tsx │ │ │ │ └── with-tw.tsx │ │ │ ├── env/ │ │ │ │ ├── with-auth-db-planetscale.js │ │ │ │ ├── with-auth-db.js │ │ │ │ ├── with-auth.js │ │ │ │ ├── with-better-auth-db-planetscale.js │ │ │ │ ├── with-better-auth-db.js │ │ │ │ ├── with-better-auth.js │ │ │ │ ├── with-db-planetscale.js │ │ │ │ └── with-db.js │ │ │ ├── index.module.css │ │ │ ├── pages/ │ │ │ │ ├── _app/ │ │ │ │ │ ├── base.tsx │ │ │ │ │ ├── with-auth-trpc-tw.tsx │ │ │ │ │ ├── with-auth-trpc.tsx │ │ │ │ │ ├── with-auth-tw.tsx │ │ │ │ │ ├── with-auth.tsx │ │ │ │ │ ├── with-better-auth-trpc-tw.tsx │ │ │ │ │ ├── with-better-auth-trpc.tsx │ │ │ │ │ ├── with-trpc-tw.tsx │ │ │ │ │ ├── with-trpc.tsx │ │ │ │ │ └── with-tw.tsx │ │ │ │ ├── api/ │ │ │ │ │ ├── auth/ │ │ │ │ │ │ └── [...all].ts │ │ │ │ │ └── trpc/ │ │ │ │ │ └── [trpc].ts │ │ │ │ └── index/ │ │ │ │ ├── base.tsx │ │ │ │ ├── with-auth-trpc-tw.tsx │ │ │ │ ├── with-auth-trpc.tsx │ │ │ │ ├── with-better-auth-trpc-tw.tsx │ │ │ │ ├── with-better-auth-trpc.tsx │ │ │ │ ├── with-better-auth-tw.tsx │ │ │ │ ├── with-better-auth.tsx │ │ │ │ ├── with-trpc-tw.tsx │ │ │ │ ├── with-trpc.tsx │ │ │ │ └── with-tw.tsx │ │ │ ├── server/ │ │ │ │ ├── api/ │ │ │ │ │ ├── root.ts │ │ │ │ │ ├── routers/ │ │ │ │ │ │ └── post/ │ │ │ │ │ │ ├── base.ts │ │ │ │ │ │ ├── with-auth-drizzle.ts │ │ │ │ │ │ ├── with-auth-prisma.ts │ │ │ │ │ │ ├── with-auth.ts │ │ │ │ │ │ ├── with-drizzle.ts │ │ │ │ │ │ └── with-prisma.ts │ │ │ │ │ ├── trpc-app/ │ │ │ │ │ │ ├── base.ts │ │ │ │ │ │ ├── with-auth-db.ts │ │ │ │ │ │ ├── with-auth.ts │ │ │ │ │ │ ├── with-better-auth-db.ts │ │ │ │ │ │ ├── with-better-auth.ts │ │ │ │ │ │ └── with-db.ts │ │ │ │ │ └── trpc-pages/ │ │ │ │ │ ├── base.ts │ │ │ │ │ ├── with-auth-db.ts │ │ │ │ │ ├── with-auth.ts │ │ │ │ │ ├── with-better-auth-db.ts │ │ │ │ │ ├── with-better-auth.ts │ │ │ │ │ └── with-db.ts │ │ │ │ ├── auth/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── base.ts │ │ │ │ │ │ ├── with-drizzle.ts │ │ │ │ │ │ └── with-prisma.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── better-auth/ │ │ │ │ │ ├── client.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── base.ts │ │ │ │ │ │ ├── with-drizzle.ts │ │ │ │ │ │ └── with-prisma.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── server.ts │ │ │ │ └── db/ │ │ │ │ ├── db-prisma-planetscale.ts │ │ │ │ ├── db-prisma.ts │ │ │ │ ├── index-drizzle/ │ │ │ │ │ ├── with-mysql.ts │ │ │ │ │ ├── with-planetscale.ts │ │ │ │ │ ├── with-postgres.ts │ │ │ │ │ └── with-sqlite.ts │ │ │ │ └── schema-drizzle/ │ │ │ │ ├── base-mysql.ts │ │ │ │ ├── base-planetscale.ts │ │ │ │ ├── base-postgres.ts │ │ │ │ ├── base-sqlite.ts │ │ │ │ ├── with-auth-mysql.ts │ │ │ │ ├── with-auth-planetscale.ts │ │ │ │ ├── with-auth-postgres.ts │ │ │ │ ├── with-auth-sqlite.ts │ │ │ │ ├── with-better-auth-mysql.ts │ │ │ │ ├── with-better-auth-planetscale.ts │ │ │ │ ├── with-better-auth-postgres.ts │ │ │ │ └── with-better-auth-sqlite.ts │ │ │ ├── styles/ │ │ │ │ └── globals.css │ │ │ ├── trpc/ │ │ │ │ ├── query-client.ts │ │ │ │ ├── react.tsx │ │ │ │ └── server.ts │ │ │ └── utils/ │ │ │ └── api.ts │ │ └── start-database/ │ │ ├── mysql.sh │ │ └── postgres.sh │ ├── tsconfig.eslint.json │ ├── tsconfig.json │ └── tsup.config.ts ├── package.json ├── pnpm-workspace.yaml ├── prettier.config.mjs ├── reset.d.ts ├── tsconfig.json ├── turbo.json └── www/ ├── .env.example ├── .gitignore ├── README.md ├── TRANSLATIONS.md ├── astro.config.mjs ├── package.json ├── postcss.config.cjs ├── prettier.config.mjs ├── public/ │ ├── makeScrollableCodeFocusable.js │ └── robots.txt ├── src/ │ ├── .vscode/ │ │ └── settings.json │ ├── components/ │ │ ├── accessibility/ │ │ │ └── jumpToContent.astro │ │ ├── branding/ │ │ │ └── Asset.astro │ │ ├── docs/ │ │ │ ├── avatarList.astro │ │ │ ├── breadCrumbs.tsx │ │ │ ├── callout.tsx │ │ │ ├── companyList.tsx │ │ │ ├── exampleOptionForm.astro │ │ │ ├── folderStructureDiagramApp.astro │ │ │ ├── folderStructureDiagramPages.astro │ │ │ ├── folderStructureForm.astro │ │ │ ├── indexPage.astro │ │ │ ├── introductionTab.tsx │ │ │ ├── openSourceAppList.tsx │ │ │ ├── outdatedDocsBanner.astro │ │ │ ├── pageContent.astro │ │ │ ├── pagination.astro │ │ │ └── tabs.astro │ │ ├── footer/ │ │ │ └── footer.astro │ │ ├── headCommon.astro │ │ ├── headSeo.astro │ │ ├── icons/ │ │ │ ├── leftArrow.astro │ │ │ └── rightArrow.astro │ │ ├── landingPage/ │ │ │ ├── ClipboardSelect.tsx │ │ │ ├── about.astro │ │ │ ├── banner.astro │ │ │ ├── button.astro │ │ │ ├── cli.tsx │ │ │ ├── community/ │ │ │ │ ├── community.astro │ │ │ │ └── communityCard.astro │ │ │ ├── pageSection.astro │ │ │ ├── stack/ │ │ │ │ ├── card.astro │ │ │ │ └── stack.astro │ │ │ └── tweets/ │ │ │ ├── featuredTweets.ts │ │ │ ├── tweetCard.astro │ │ │ ├── tweetSlider.astro │ │ │ └── tweets.astro │ │ ├── navigation/ │ │ │ ├── LanguageSelect.tsx │ │ │ ├── OnThisPageLinks.tsx │ │ │ ├── Search.tsx │ │ │ ├── githubIcon.astro │ │ │ ├── leftSidebar.astro │ │ │ ├── moreMenu.astro │ │ │ ├── navbar.astro │ │ │ ├── rightSidebar.astro │ │ │ ├── sidebarToggle.astro │ │ │ ├── tableOfContents.astro │ │ │ └── themeToggleButton.astro │ │ └── openGraph.tsx │ ├── config.ts │ ├── env.d.ts │ ├── languages.ts │ ├── layouts/ │ │ ├── docs.astro │ │ └── landingPage.astro │ ├── pages/ │ │ ├── 404.astro │ │ ├── ar/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ └── vercel.md │ │ │ ├── faq.md │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.md │ │ │ │ ├── first-steps.md │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── branding.astro │ │ ├── en/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.mdx │ │ │ │ └── vercel.md │ │ │ ├── examples.mdx │ │ │ ├── faq.mdx │ │ │ ├── folder-structure-app.mdx │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.mdx │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── _next-auth-app-router.mdx │ │ │ │ ├── _next-auth-pages.mdx │ │ │ │ ├── drizzle.mdx │ │ │ │ ├── env-variables.mdx │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.mdx │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── es/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.md │ │ │ │ └── vercel.md │ │ │ ├── faq.md │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.md │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── fr/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.mdx │ │ │ │ └── vercel.md │ │ │ ├── examples.mdx │ │ │ ├── faq.mdx │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.mdx │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.mdx │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── index.astro │ │ ├── ja/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.mdx │ │ │ │ └── vercel.md │ │ │ ├── faq.mdx │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.mdx │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── no/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.md │ │ │ │ └── vercel.md │ │ │ ├── faq.md │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.md │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── og.ts │ │ ├── pl/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.md │ │ │ │ └── vercel.md │ │ │ ├── examples.mdx │ │ │ ├── faq.md │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.md │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── pt/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.mdx │ │ │ │ └── vercel.md │ │ │ ├── faq.mdx │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.mdx │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── ru/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.md │ │ │ │ └── vercel.md │ │ │ ├── faq.md │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── env-variables.mdx │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.md │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ ├── themeTest.astro │ │ ├── uk/ │ │ │ ├── deployment/ │ │ │ │ ├── docker.md │ │ │ │ ├── index.astro │ │ │ │ ├── netlify.md │ │ │ │ └── vercel.md │ │ │ ├── examples.mdx │ │ │ ├── faq.mdx │ │ │ ├── folder-structure-pages.mdx │ │ │ ├── installation.mdx │ │ │ ├── introduction.md │ │ │ ├── other-recs.md │ │ │ ├── t3-collection.mdx │ │ │ ├── usage/ │ │ │ │ ├── drizzle.mdx │ │ │ │ ├── env-variables.mdx │ │ │ │ ├── first-steps.md │ │ │ │ ├── index.astro │ │ │ │ ├── next-auth.mdx │ │ │ │ ├── next-js.md │ │ │ │ ├── prisma.md │ │ │ │ ├── tailwind.md │ │ │ │ ├── trpc.md │ │ │ │ └── typescript.md │ │ │ └── why.md │ │ └── zh-hans/ │ │ ├── deployment/ │ │ │ ├── docker.md │ │ │ ├── index.astro │ │ │ ├── netlify.mdx │ │ │ └── vercel.md │ │ ├── faq.mdx │ │ ├── folder-structure-pages.mdx │ │ ├── installation.mdx │ │ ├── introduction.md │ │ ├── other-recs.md │ │ ├── t3-collection.mdx │ │ ├── usage/ │ │ │ ├── drizzle.mdx │ │ │ ├── env-variables.mdx │ │ │ ├── first-steps.md │ │ │ ├── index.astro │ │ │ ├── next-auth.mdx │ │ │ ├── next-js.md │ │ │ ├── prisma.md │ │ │ ├── tailwind.md │ │ │ ├── trpc.md │ │ │ └── typescript.md │ │ └── why.md │ ├── styles/ │ │ ├── accessibility.css │ │ ├── algolia/ │ │ │ ├── button.css │ │ │ ├── modal.css │ │ │ └── style.css │ │ ├── global.css │ │ └── swiper.css │ └── utils/ │ ├── fetchGithub.ts │ ├── ogFont.ts │ ├── pagination.ts │ └── siteUrl.ts ├── tailwind.config.ts ├── tsconfig.json └── vercel.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .changeset/README.md ================================================ # Changesets Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets) We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) ================================================ FILE: .changeset/beige-clouds-behave.md ================================================ --- "create-t3-app": patch --- fix #1903 #2157 #2163 ================================================ FILE: .changeset/config.json ================================================ { "$schema": "https://unpkg.com/@changesets/config@2.1.1/schema.json", "changelog": [ "@changesets/changelog-github", { "repo": "t3-oss/create-t3-app" } ], "commit": false, "fixed": [], "linked": [], "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": ["@ct3a/www"], "changedFilePatterns": ["src/**", "template/**"] } ================================================ FILE: .changeset/curly-zoos-add.md ================================================ --- "create-t3-app": minor --- Fixes Biome formatter during the initial installation process ================================================ FILE: .changeset/quick-snakes-give.md ================================================ --- "create-t3-app": patch --- fix(security): bump react and next ([BLOG](https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components#update-instructions)) ================================================ FILE: .changeset/tender-humans-hide.md ================================================ --- "create-t3-app": patch --- fix(security): bump react ([CVE-2025-55182](https://github.com/facebook/react/security/advisories/GHSA-fv66-9v8q-g76r)) ================================================ FILE: .codesandbox/tasks.json ================================================ { // These tasks will run in order when initializing your CodeSandbox project. "setupTasks": [ { "name": "Install Dependencies", "command": "pnpm install" } ], // These tasks can be run from CodeSandbox. Running one will open a log in the app. "tasks": { "typecheck": { "name": "typecheck", "command": "pnpm typecheck", "runAtStart": false }, "build:cli": { "name": "build:cli", "command": "pnpm build:cli", "runAtStart": false }, "build:www": { "name": "build:www", "command": "pnpm build:www", "runAtStart": false }, "build": { "name": "build", "command": "pnpm build", "runAtStart": false }, "start:cli": { "name": "start:cli", "command": "pnpm start:cli", "runAtStart": false }, "start:www": { "name": "start:www", "command": "pnpm start:www", "runAtStart": false }, "dev:cli": { "name": "dev:cli", "command": "pnpm dev:cli", "runAtStart": false }, "dev:www": { "name": "dev:www", "command": "pnpm dev:www", "runAtStart": true }, "clean": { "name": "clean", "command": "pnpm clean", "runAtStart": false }, "lint": { "name": "lint", "command": "pnpm lint", "runAtStart": false }, "format": { "name": "format", "command": "pnpm format", "runAtStart": false }, "format:check": { "name": "format:check", "command": "pnpm format:check", "runAtStart": false } } } ================================================ FILE: .gitattributes ================================================ # mdx *.mdx linguist-detectable=false ================================================ FILE: .github/CODEOWNERS ================================================ # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners * @juliusmarminge @nexxeln @t3dotgg @c-ehrlich ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: 🐞 Bug Report description: Create a bug report to help us improve title: "bug: " labels: ["🐞❔ unconfirmed bug"] body: - type: textarea attributes: label: Provide environment information description: | Run this command in your project root and paste the results: ```bash npx envinfo --system --binaries ``` If the issue is regarding a scaffolded app, please include the version used to scaffold that app which you can find in the `package.json` under `ct3aMetadata.initVersion`. validations: required: true - type: textarea attributes: label: Describe the bug description: A clear and concise description of the bug, as well as what you expected to happen when encountering it. validations: required: true - type: input attributes: label: Reproduction repo description: If applicable, please provide a link to a reproduction repo or a Stackblitz / CodeSandbox project. Your issue may be closed if this is not provided and we are unable to reproduce the issue. If your bug is a documentaion issue, link the appropriate page. validations: required: true - type: textarea attributes: label: To reproduce description: Describe how to reproduce your bug. Steps, code snippets, reproduction repos etc. validations: required: true - type: textarea attributes: label: Additional information description: Add any other information related to the bug here, screenshots if applicable. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Ask a question url: https://t3.gg/discord about: Ask questions and discuss with other community members ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: 🧑‍💻 Feature Request description: Suggest an idea for this project title: "feat: " labels: ["🌟 enhancement"] body: - type: textarea attributes: label: Is your feature request related to a problem? Please describe. description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] validations: required: true - type: textarea attributes: label: Describe the solution you'd like to see description: A clear and concise description of what you want to happen. validations: required: true - type: textarea attributes: label: Describe alternate solutions description: A clear and concise description of any alternative solutions or features you've considered. validations: required: true - type: textarea attributes: label: Additional information description: Add any other information related to the feature here. If your feature request is related to any issues or discussions, link them here. ================================================ FILE: .github/actions/setup/action.yml ================================================ name: "Setup and install" description: "Common setup steps for Actions" runs: using: composite steps: - uses: pnpm/action-setup@v3 - uses: actions/setup-node@v4 with: node-version: 20 cache: "pnpm" - shell: bash run: pnpm install ================================================ FILE: .github/changeset-version.js ================================================ // ORIGINALLY FROM CLOUDFLARE WRANGLER: // https://github.com/cloudflare/wrangler2/blob/main/.github/changeset-version.js import { exec } from "child_process"; // This script is used by the `release.yml` workflow to update the version of the packages being released. // The standard step is only to run `changeset version` but this does not update the package-lock.json file. // So we also run `npm install`, which does this update. // This is a workaround until this is handled automatically by `changeset version`. // See https://github.com/changesets/changesets/issues/421. exec("npx changeset version"); exec("npm install"); ================================================ FILE: .github/labeler.yml ================================================ "📌 area: cli": - any: ["cli/**/*"] "📌 area: t3-app": - any: ["cli/template/**/*"] "📚 documentation": - any: ["www/**/*"] - any: ["**/*.md"] "📌 area: ci": - any: [".github/**/*"] ================================================ FILE: .github/pull_request_template.md ================================================ Closes # ## ✅ Checklist - [ ] I have followed every step in the [contributing guide](https://github.com/t3-oss/create-t3-app/blob/main/CONTRIBUTING.md) (updated 2022-10-06). - [ ] The PR title follows the convention we established [conventional-commit](https://www.conventionalcommits.org/en/v1.0.0/) - [ ] I performed a functional test on my final commit --- ## Changelog _[Short description of what has changed]_ --- ## Screenshots _[Screenshots]_ 💯 ================================================ FILE: .github/scripts/generate-matrix.js ================================================ // Define all possible values const options = { trpc: ['true', 'false'], tailwind: ['true', 'false'], nextAuth: ['true', 'false'], betterAuth: ['true', 'false'], prisma: ['true', 'false'], drizzle: ['true', 'false'], appRouter: ['true', 'false'], dbType: ['planetscale', 'sqlite', 'mysql', 'postgres'] }; // Generate all combinations function generateCombinations(opts) { const keys = Object.keys(opts); const combinations = []; function recurse(index, current) { if (index === keys.length) { combinations.push({...current}); return; } const key = keys[index]; for (const value of opts[key]) { current[key] = value; recurse(index + 1, current); } } recurse(0, {}); return combinations; } // Filter valid combinations based on current validation logic function isValid(combo) { const { prisma, drizzle, nextAuth, betterAuth, dbType } = combo; // Not both auth true if (nextAuth === 'true' && betterAuth === 'true') return false; // Not both db true if (prisma === 'true' && drizzle === 'true') return false; // If no db selected, only allow sqlite if (prisma === 'false' && drizzle === 'false' && dbType !== 'sqlite') return false; return true; } const allCombos = generateCombinations(options); const validCombos = allCombos.filter(isValid); console.log(`matrix=${JSON.stringify({include: validCombos})}`); ================================================ FILE: .github/version-script-beta.js ================================================ // ORIGINALLY FROM CLOUDFLARE WRANGLER: // https://github.com/cloudflare/wrangler2/blob/main/.github/version-script.js import fs from "fs"; import { exec } from "child_process"; const pkgJsonPath = "cli/package.json"; try { const pkg = JSON.parse(fs.readFileSync(pkgJsonPath)); exec("git rev-parse --short HEAD", (err, stdout) => { if (err) { console.log(err); process.exit(1); } const [major, minor, patch] = pkg.version.split(".").map(Number); pkg.version = `${major}.${minor}.${patch + 1}-beta.${stdout.trim()}`; fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, "\t") + "\n"); }); } catch (error) { console.error(error); process.exit(1); } ================================================ FILE: .github/version-script-next.js ================================================ // ORIGINALLY FROM CLOUDFLARE WRANGLER: // https://github.com/cloudflare/wrangler2/blob/main/.github/version-script.js import fs from "fs"; import { exec } from "child_process"; const pkgJsonPath = "cli/package.json"; try { const pkg = JSON.parse(fs.readFileSync(pkgJsonPath)); exec("git rev-parse --short HEAD", (err, stdout) => { if (err) { console.log(err); process.exit(1); } const [major, minor, patch] = pkg.version.split(".").map(Number); pkg.version = `${major}.${minor}.${patch + 1}-next.${stdout.trim()}`; fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, "\t") + "\n"); }); } catch (error) { console.error(error); process.exit(1); } ================================================ FILE: .github/workflows/ci.yml ================================================ # this workflow will run on every pr to make sure the project is following the guidelines # after labeler, run other actions with strict permissions name: CI on: pull_request: branches: ["*"] merge_group: env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} jobs: lint: runs-on: ubuntu-latest name: Run ESLint steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - run: pnpm turbo run lint - run: pnpm manypkg check check-changeset: runs-on: ubuntu-latest name: Check Changeset steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - name: Check and Validate Changes in /cli run: | git fetch origin main:main changes=$(git diff --name-only main...${{ github.sha }} | grep '^cli/' || true) if [[ -n "$changes" ]]; then echo "Changes detected in /cli: $changes" pnpm changeset status --since origin/main exit_status=$? if [[ $exit_status -eq 0 ]]; then echo "Changeset validation succeeded." else echo "Changeset validation failed." fi else echo "No changes detected in /cli" fi prettier: runs-on: ubuntu-latest name: Run Prettier steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - run: pnpm format:check tsc: runs-on: ubuntu-latest name: Run Typechecker steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - run: pnpm typecheck build-www: runs-on: ubuntu-latest name: Build and Check Astro steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - run: pnpm run --filter=www check - run: pnpm turbo --filter=www build # env: # PUBLIC_GITHUB_TOKEN: ${{ secrets.PUBLIC_GITHUB_TOKEN }} ================================================ FILE: .github/workflows/e2e.yml ================================================ # this workflow will run on every pr to make sure the project is following the guidelines # after labeler, run other actions with strict permissions name: E2E Tests on: pull_request: branches: - "*" paths: - "cli/**/*" merge_group: env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ secrets.TURBO_TEAM }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: generate-matrix: runs-on: ubuntu-latest outputs: matrix: ${{ steps.generate.outputs.matrix }} steps: - uses: actions/checkout@v4 - name: Generate valid matrix combinations id: generate run: node .github/scripts/generate-matrix.js >> $GITHUB_OUTPUT build-t3-app: needs: generate-matrix runs-on: ubuntu-latest # if: | # contains(github.event.pull_request.labels.*.name, '📌 area: cli') || # contains(github.event.pull_request.labels.*.name, '📌 area: t3-app') strategy: matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} name: "Build and Start T3 App ${{ matrix.trpc }}-${{ matrix.tailwind }}-${{ matrix.nextAuth }}-${{ matrix.betterAuth }}-${{ matrix.prisma }}-${{ matrix.drizzle}}-${{ matrix.appRouter }}-${{ matrix.dbType }}" steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - run: pnpm turbo --filter=create-t3-app build # has to be scaffolded outside the CLI project so that no lint/tsconfig are leaking # through. this way it ensures that it is the app's configs that are being used # FIXME: this is a bit hacky, would rather have --packages=trpc,tailwind,... but not sure how to setup the matrix for that - run: cd cli && pnpm start ../../ci-${{ matrix.trpc }}-${{ matrix.tailwind }}-${{ matrix.nextAuth }}-${{ matrix.betterAuth }}-${{ matrix.prisma }}-${{ matrix.drizzle}}-${{ matrix.appRouter }}-${{ matrix.dbType }} --noGit --CI --trpc=${{ matrix.trpc }} --tailwind=${{ matrix.tailwind }} --nextAuth=${{ matrix.nextAuth }} --betterAuth=${{ matrix.betterAuth }} --prisma=${{ matrix.prisma }} --drizzle=${{ matrix.drizzle }} --appRouter=${{ matrix.appRouter }} --dbProvider=${{ matrix.dbType }} # can't use default mysql string cause t3-env blocks that - run: cd ../ci-${{ matrix.trpc }}-${{ matrix.tailwind }}-${{ matrix.nextAuth }}-${{ matrix.betterAuth }}-${{ matrix.prisma }}-${{ matrix.drizzle}}-${{ matrix.appRouter }}-${{ matrix.dbType }} && pnpm build env: AUTH_SECRET: foo AUTH_DISCORD_ID: bar AUTH_DISCORD_SECRET: baz SKIP_ENV_VALIDATION: true check-well-formatted: runs-on: ubuntu-latest strategy: matrix: eslint: ["true", "false"] biome: ["true", "false"] name: "Build and Start T3 App" steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check valid matrix id: matrix-valid run: | echo "continue=${{ (matrix.eslint == 'false' || matrix.biome == 'false') && (matrix.biome == 'true' || matrix.eslint == 'true') }}" >> $GITHUB_OUTPUT - uses: ./.github/actions/setup if: ${{ steps.matrix-valid.outputs.continue == 'true' }} - run: pnpm turbo --filter=create-t3-app build if: ${{ steps.matrix-valid.outputs.continue == 'true' }} - run: cd cli && pnpm start ../../ci-format-${{ matrix.eslint }}-${{ matrix.biome }} --noGit --CI --trpc --tailwind --nextAuth --drizzle --appRouter --dbProvider=postgres --eslint=${{ matrix.eslint }} --biome=${{ matrix.biome }} if: ${{ steps.matrix-valid.outputs.continue == 'true' }} - run: cd ../ci-format-${{ matrix.eslint }}-${{ matrix.biome }} && pnpm build if: ${{ steps.matrix-valid.outputs.continue == 'true' }} env: AUTH_SECRET: foo AUTH_DISCORD_ID: bar AUTH_DISCORD_SECRET: baz SKIP_ENV_VALIDATION: true # Run biome check - run: cd ../ci-format-${{ matrix.eslint }}-${{ matrix.biome }} && pnpm check if: ${{ steps.matrix-valid.outputs.continue == 'true' && matrix.biome == 'true' }} # Check linting and formatting with eslint and prettier - run: cd ../ci-format-${{ matrix.eslint }}-${{ matrix.biome }} && pnpm lint && pnpm format:check if: ${{ steps.matrix-valid.outputs.continue == 'true' && matrix.eslint == 'true' }} env: AUTH_SECRET: foo AUTH_DISCORD_ID: bar AUTH_DISCORD_SECRET: baz SKIP_ENV_VALIDATION: true build-t3-app-with-bun: runs-on: ubuntu-latest name: "Build and Start T3 App with Bun" steps: - uses: actions/checkout@v3 with: fetch-depth: 0 # First install everything and build the CLI with Node - uses: ./.github/actions/setup - run: pnpm turbo --filter=create-t3-app build # Then, run the CLI and build the generated app with Bun # Let's just build a full app with Bun, we don't need the matrix here - uses: oven-sh/setup-bun@v1 with: bun-version: latest - run: cd cli && bun run dist/index.js ../../ci-bun --default - name: We should have a Bun lockfile run: | if [ ! -f "../ci-bun/bun.lock" ]; then echo "Bun lockfile not found" exit 1 fi # FIXME: This doesn't actually run the build script using bun, since Next.js doesn't support it yet. # But you should still be able to use `bun` as a package manager for any Next.js app. # If/When Next.js supports it, we should be able to run `bun --bun run build` here to override any Node binaries. # See: https://bun.sh/docs/cli/bunx#shebangs - run: cd ../ci-bun && bun run build # - run: cd ../ci-bun && bun --bun run build env: AUTH_SECRET: foo AUTH_DISCORD_ID: bar AUTH_DISCORD_SECRET: baz DATABASE_URL: mysql://root:root@localhost:3306/test # can't use url from example env cause we block that in t3-env ================================================ FILE: .github/workflows/pr-labeler.yml ================================================ # this workflow will run on every pr to make sure the project is following the guidelines # run labeler with elevated permissions before other actions name: PR Checks on: pull_request_target: branches: ["*"] types: ["opened", "edited", "synchronize"] jobs: lint-pr-title: runs-on: ubuntu-latest name: Validate PR Title steps: - uses: amannn/action-semantic-pull-request@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} labeler: name: Label PR permissions: contents: read pull-requests: write runs-on: ubuntu-latest steps: - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true ================================================ FILE: .github/workflows/prerelease-comment.yml ================================================ name: Write Beta Release comment on: workflow_run: workflows: ["Release - Beta"] types: - completed jobs: comment: if: | github.repository_owner == 't3-oss' && ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest name: Write comment to the PR steps: - name: "Comment on PR" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.payload.workflow_run.id, }); for (const artifact of allArtifacts.data.artifacts) { // Extract the PR number and package version from the artifact name const match = /^npm-package-create-t3-app@(.*?)-pr-(\d+)/.exec(artifact.name); if (match) { require("fs").appendFileSync( process.env.GITHUB_ENV, `\nBETA_PACKAGE_VERSION=${match[1]}` + `\nWORKFLOW_RUN_PR=${match[2]}` + `\nWORKFLOW_RUN_ID=${context.payload.workflow_run.id}` ); break; } } - name: "Comment on PR with Link" uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ env.WORKFLOW_RUN_PR }} message: | A new create-t3-app prerelease is available for testing. You can install this latest build in your project with: ```sh pnpm create t3-app@${{ env.BETA_PACKAGE_VERSION }} ``` - name: "Remove the autorelease label once published" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ env.WORKFLOW_RUN_PR }}, name: '🚀 autorelease', }); ================================================ FILE: .github/workflows/prerelease.yml ================================================ # Originally inspired by Cloudflare Wrangler # https://github.com/cloudflare/wrangler2/blob/main/.github/workflows/prereleases.yml name: Release - Beta on: pull_request: types: [labeled] branches: - main jobs: prerelease: if: | github.repository_owner == 't3-oss' && contains(github.event.pull_request.labels.*.name, '🚀 autorelease') name: Build & Publish a beta release to NPM runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - name: Modify package.json version run: node .github/version-script-beta.js - name: Authenticate to NPM run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_ACCESS_TOKEN }}" > cli/.npmrc - name: Publish Beta to NPM run: pnpm pub:beta env: NPM_PUBLISH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} - name: get-npm-version id: package-version uses: martinbeentjes/npm-get-version-action@main with: path: cli - name: Upload packaged artifact uses: actions/upload-artifact@v4 with: name: npm-package-create-t3-app@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name path: cli/dist/index.js ================================================ FILE: .github/workflows/release.yml ================================================ # Originally inspired by Cloudflare Wrangler # https://github.com/cloudflare/wrangler2/blob/main/.github/workflows/release.yml name: Release on: push: branches: - main jobs: release: if: ${{ github.repository_owner == 't3-oss' }} permissions: contents: write # to create release (changesets/action) id-token: write # OpenID Connect token needed for provenance pull-requests: write # to create pull request (changesets/action) name: Create a PR for release workflow runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup - name: Check for errors run: pnpm check - name: Build the package run: pnpm build:cli - name: Create Version PR or Publish to NPM id: changesets uses: changesets/action@v1 with: commit: "chore(release): version packages" title: "chore(release): version packages" version: node .github/changeset-version.js publish: npx changeset publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_CONFIG_PROVENANCE: true NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} NODE_ENV: "production" ================================================ FILE: .github/workflows/translations.yml ================================================ name: "Notify codeowners about changes in docs" on: pull_request_target: types: [opened, edited, synchronize] branches: - "*" paths: - "www/src/pages/**" - "!www/src/pages/en/**" jobs: notify-codeowners: if: | github.repository_owner == 't3-oss' runs-on: ubuntu-latest steps: - name: Parse changed files and write to env uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const changedFiles = (await github.rest.pulls.listFiles({ owner: "t3-oss", repo: "create-t3-app", pull_number: context.payload.number, })).data.map(file => file.filename); const changedLanguages = [ ...new Set( changedFiles .filter((file) => file.startsWith("www/src/pages/")) .map((file) => file.split("/")[3]) .filter((file) => file !== "en"), ), ]; const CODEOWNERS = { ar: ["a4addel", "Sboonny"], fr: ["Defranos", "joachimjusth", "vdeva"], ja: ["t6adev", "uehaj"], no: ["estubmo", "josephayman"], pl: ["matibox", "Infiplaya", "PiotrekPKP"], pt: ["minsk-dev", "Sn0wye", "victoriaquasar", "MattFerreira18", "gilhrpenner"], ru: ["AmadeusTwi", "ronanru", "JohnBakhmat"], uk: ["pqoqubbw"], "zh-hans": ["fernandoxu", "escwxyz"], }; console.log("Changed languages:", changedLanguages) const formattedString = changedLanguages .map( (lang) => `${lang.toUpperCase()}: ${(CODEOWNERS[lang] || []).map((user) => `@${user}`).join(", ")}`, ) .join(" | "); // cause we can't linebreak in env vars console.log("Formatted string:", formattedString) require("fs").appendFileSync( process.env.GITHUB_ENV, `\nCODEOWNERS=${formattedString}`, ); - name: Write comment on the PR tagging codeowners uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ env.WORKFLOW_RUN_PR }} message: | Hey t3-oss/translators! This PR contains changes to your language. Please review the changes ❤️. ${{ env.CODEOWNERS }} ================================================ FILE: .gitignore ================================================ ### STANDARD GIT IGNORE FILE ### # DEPENDENCIES node_modules/ /.pnp .pnp.js package-lock.json yarn.lock # TESTING /coverage *.lcov .nyc_output # BUILD build/ public/build/ dist/ generated/ .astro # ENV FILES .env .env.local .env.development.local .env.test.local .env.production.local # LOGS logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # MISC .idea .cache/ .next/ next-env.d.ts .nuxt/ tmp/ temp/ .docusaurus *.tsbuildinfo # MAC ._* .DS_Store Thumbs.db .turbo .vercel # Emacs backup files \#* .\#* # TYPESCRIPT *.tsbuildinfo ================================================ FILE: .gitpod.yml ================================================ tasks: - init: pnpm install vscode: extensions: - astro-build.astro-vscode - bradlc.vscode-tailwindcss ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "astro-build.astro-vscode", "bradlc.vscode-tailwindcss", "unifiedjs.vscode-mdx", "yoavbls.pretty-ts-errors" ] } ================================================ FILE: .vscode/settings.json ================================================ { "workbench.colorCustomizations": { "titleBar.activeBackground": "#1f0d49", "titleBar.activeForeground": "#e2e8f0", "titleBar.inactiveBackground": "#181239", "titleBar.inactiveForeground": "#b1b6bd" }, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.rulers": [100], "editor.tabSize": 2, "editor.quickSuggestions": { "strings": true }, "eslint.validate": [ "javascript", "javascriptreact", "astro", "typescript", "typescriptreact" ], "eslint.workingDirectories": ["cli", "upgrade", "www"], "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], "typescript.tsdk": "node_modules/typescript/lib", "prettier.documentSelectors": [ "**/*.{cjs,mjs,ts,tsx,astro,md,mdx,json,yaml,yml}" ], "mdx.experimentalLanguageServer": true, "[astro]": { "editor.defaultFormatter": "astro-build.astro-vscode" }, "[prisma]": { "editor.defaultFormatter": "Prisma.prisma" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } } ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution Guidelines When contributing to `create-t3-app`, whether on GitHub or in other community spaces: - Be respectful, civil, and open-minded. - Before opening a new pull request, try searching through the [issue tracker](https://github.com/t3-oss/create-t3-app/issues) for known issues or fixes. - If you want to make code changes based on your personal opinion(s), make sure you open an issue first describing the changes you want to make, and open a pull request only when your suggestions get approved by maintainers. ## How to Contribute ### Prerequisites In order to not waste your time implementing a change that has already been declined, or is generally not needed, start by [opening an issue](https://github.com/t3-oss/create-t3-app/issues/new/choose) describing the problem you would like to solve. ### Contributing via Codesandbox You can contribute to this documentation on codesandbox which will automatically run all the setup command for you. [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/github/t3-oss/create-t3-app). ### Setup your environment locally _Some commands will assume you have the Github CLI installed, if you haven't, consider [installing it](https://github.com/cli/cli#installation), but you can always use the Web UI if you prefer that instead._ In order to contribute to this project, you will need to fork the repository: ```bash gh repo fork t3-oss/create-t3-app ``` then, clone it to your local machine: ```bash gh repo clone /create-t3-app ``` This project uses [pnpm](https://pnpm.io) as its package manager. Install it if you haven't already: ```bash npm install -g pnpm ``` Then, install the project's dependencies: ```bash pnpm install ``` ### Implement your changes This project is a [Turborepo](https://turborepo.org/) monorepo. The code for the CLI is in the `cli` directory, and the docs is in the `www` directory. Now you're all setup and can start implementing your changes. Here are some useful scripts for when you are developing: | Command | Description | | ---------------- | ------------------------------------------------------- | | `pnpm dev:cli` | Builds and starts the CLI in watch-mode | | `pnpm dev:www` | Starts the development server for the docs with HMR | | `pnpm build:cli` | Builds the CLI | | `pnpm build:www` | Builds the docs | | `pnpm build` | Builds CLI and docs | | `pnpm format` | Formats the code | | `pnpm lint` | Lints the code | | `pnpm lint:fix` | Lints the code and fixes any errors | | `pnpm check` | Checks your code for typeerrors, formatting and linting | When making commits, make sure to follow the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) guidelines, i.e. prepending the message with `feat:`, `fix:`, `chore:`, `docs:`, etc... You can use `git status` to double check which files have not yet been staged for commit: ```bash git add && git commit -m "feat/fix/chore/docs: commit message" ``` ### When you're done Check that your code follows the project's style guidelines by running: ```bash pnpm check ``` Please also make a manual, functional test of your changes. If your change should appear in the changelog, i.e. it changes some behavior of either the CLI or the outputted application, it must be captured by `changeset` which is done by running ```bash pnpm changeset ``` and filling out the form with the appropriate information. Then, add the generated changeset to git: ```bash git add .changeset/*.md && git commit -m "chore: add changeset" ``` When all that's done, it's time to file a pull request to upstream: ```bash gh pr create --web ``` and fill out the title and body appropriately. Again, make sure to follow the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) guidelines for your title. ## Translations For more information on how to help with translation, please see the [contributing guidelines for our docs](https://github.com/t3-oss/create-t3-app/blob/main/www/TRANSLATIONS.md). ## Credits This documented was inspired by the contributing guidelines for [cloudflare/wrangler2](https://github.com/cloudflare/wrangler2/blob/main/CONTRIBUTING.md). ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 Shoubhit Dash Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: cli/.yarnrc.yml ================================================ packageExtensions: chalk@5.0.1: dependencies: "#ansi-styles": npm:ansi-styles@6.1.0 "#supports-color": npm:supports-color@9.2.2 ================================================ FILE: cli/CHANGELOG.md ================================================ # Changelog ## 7.38.1 ### Patch Changes - [#2010](https://github.com/t3-oss/create-t3-app/pull/2010) [`12fccae15d08bc11168fe6b462d978a6787b5c7c`](https://github.com/t3-oss/create-t3-app/commit/12fccae15d08bc11168fe6b462d978a6787b5c7c) Thanks [@swyckoff](https://github.com/swyckoff)! - Added an import for crypto in the cli envVars.ts to support older node versions. ## 7.38.0 ### Minor Changes - [#2000](https://github.com/t3-oss/create-t3-app/pull/2000) [`41de302b5a76966741d1dc270ba5da27064f82e8`](https://github.com/t3-oss/create-t3-app/commit/41de302b5a76966741d1dc270ba5da27064f82e8) Thanks [@ronanru](https://github.com/ronanru)! - update to next.js 15 and next-auth v5 - [#2002](https://github.com/t3-oss/create-t3-app/pull/2002) [`54b66484692b76d325f59dc233eb27e0aee59af2`](https://github.com/t3-oss/create-t3-app/commit/54b66484692b76d325f59dc233eb27e0aee59af2) Thanks [@devotoare](https://github.com/devotoare)! - Change drizzle with PostgresQL to use identity columns ### Patch Changes - [#1969](https://github.com/t3-oss/create-t3-app/pull/1969) [`e08dbe307b4e37c7cc7e0cbf6243c008771ef4f8`](https://github.com/t3-oss/create-t3-app/commit/e08dbe307b4e37c7cc7e0cbf6243c008771ef4f8) Thanks [@Guria](https://github.com/Guria)! - fix issue with prefetch protected procedure in auth templates - [#1997](https://github.com/t3-oss/create-t3-app/pull/1997) [`fe85a233c62af607e8a39bf5ca1b2358d43c6587`](https://github.com/t3-oss/create-t3-app/commit/fe85a233c62af607e8a39bf5ca1b2358d43c6587) Thanks [@programming-with-ia](https://github.com/programming-with-ia)! - Add more scripts in `package.json` - [#1975](https://github.com/t3-oss/create-t3-app/pull/1975) [`78cb06f99f15f4f4f90b57e3545f1dc0b222eaf3`](https://github.com/t3-oss/create-t3-app/commit/78cb06f99f15f4f4f90b57e3545f1dc0b222eaf3) Thanks [@0pilatos0](https://github.com/0pilatos0)! - Resolved issue where database startup script did not check for docker daemon being up and running ## 7.37.0 ### Minor Changes - [#1936](https://github.com/t3-oss/create-t3-app/pull/1936) [`2d1878e53767e35ab4b22a37622fbf0d5fdb4da7`](https://github.com/t3-oss/create-t3-app/commit/2d1878e53767e35ab4b22a37622fbf0d5fdb4da7) Thanks [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)! - upgrade to typescript eslint v8 - [#1936](https://github.com/t3-oss/create-t3-app/pull/1936) [`2d1878e53767e35ab4b22a37622fbf0d5fdb4da7`](https://github.com/t3-oss/create-t3-app/commit/2d1878e53767e35ab4b22a37622fbf0d5fdb4da7) Thanks [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)! - upgrade to drizzle-orm@0.33 and corresponding drizzle-kit version ### Patch Changes - [#1954](https://github.com/t3-oss/create-t3-app/pull/1954) [`f9f96d069af87d4372dc917e3625409859e4e615`](https://github.com/t3-oss/create-t3-app/commit/f9f96d069af87d4372dc917e3625409859e4e615) Thanks [@ronanru](https://github.com/ronanru)! - fix typo ## 7.36.2 ### Patch Changes - [#1945](https://github.com/t3-oss/create-t3-app/pull/1945) [`9eea9fb5f6d26d42944a660bfcee5f7fb33438ad`](https://github.com/t3-oss/create-t3-app/commit/9eea9fb5f6d26d42944a660bfcee5f7fb33438ad) Thanks [@datasalaryman](https://github.com/datasalaryman)! - resolve client error during getLatest trpc call - [#1948](https://github.com/t3-oss/create-t3-app/pull/1948) [`3a1b94d29a3addc783e2d6645c2e88a5b7273174`](https://github.com/t3-oss/create-t3-app/commit/3a1b94d29a3addc783e2d6645c2e88a5b7273174) Thanks [@keyding](https://github.com/keyding)! - Update seed.ts in prisma in documentation - [#1940](https://github.com/t3-oss/create-t3-app/pull/1940) [`46a4e2d144da8b184bf39444b19e0b1bd00bab2b`](https://github.com/t3-oss/create-t3-app/commit/46a4e2d144da8b184bf39444b19e0b1bd00bab2b) Thanks [@KrishanGoyal1](https://github.com/KrishanGoyal1)! - changed default password error message for `start-database` script ## 7.36.1 ### Patch Changes - [#1937](https://github.com/t3-oss/create-t3-app/pull/1937) [`5a0cbc53afb8597d6da2bc6d184ea1427e741cec`](https://github.com/t3-oss/create-t3-app/commit/5a0cbc53afb8597d6da2bc6d184ea1427e741cec) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: dependency bumps ## 7.36.0 ### Minor Changes - [#1932](https://github.com/t3-oss/create-t3-app/pull/1932) [`42a485a29370fbfe4f03e5f756d97c0fee0b09e8`](https://github.com/t3-oss/create-t3-app/commit/42a485a29370fbfe4f03e5f756d97c0fee0b09e8) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: add trpc rsc prefetching ### Patch Changes - [#1934](https://github.com/t3-oss/create-t3-app/pull/1934) [`1cfbb117c892905ea956a597aa6d48e9984b1190`](https://github.com/t3-oss/create-t3-app/commit/1cfbb117c892905ea956a597aa6d48e9984b1190) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - remove duplicate `
` tags - [#1931](https://github.com/t3-oss/create-t3-app/pull/1931) [`a1a4b8707b95b82e755733b2853ac595bb9d4829`](https://github.com/t3-oss/create-t3-app/commit/a1a4b8707b95b82e755733b2853ac595bb9d4829) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: unify drizlze schema and fix sqlite timestamps ## 7.35.0 ### Minor Changes - [#1905](https://github.com/t3-oss/create-t3-app/pull/1905) [`b22e2e10d005689faa7b1db771a217b59c88635f`](https://github.com/t3-oss/create-t3-app/commit/b22e2e10d005689faa7b1db771a217b59c88635f) Thanks [@jessedelira](https://github.com/jessedelira)! - Add .idea to .gitignore ### Patch Changes - [#1909](https://github.com/t3-oss/create-t3-app/pull/1909) [`f69fa29733d3bd0d6e5043d1200e250a938b6335`](https://github.com/t3-oss/create-t3-app/commit/f69fa29733d3bd0d6e5043d1200e250a938b6335) Thanks [@TheDevilOnLine](https://github.com/TheDevilOnLine)! - fix: Added $defaultFn to the user table's id field when using Drizzle to fix #1906 ## 7.34.0 ### Minor Changes - [#1893](https://github.com/t3-oss/create-t3-app/pull/1893) [`1925236bcfcafc6c490319ab5f07b2a698e88ef5`](https://github.com/t3-oss/create-t3-app/commit/1925236bcfcafc6c490319ab5f07b2a698e88ef5) Thanks [@brandon93s](https://github.com/brandon93s)! - Add `db:generate` and `db:migrate` scripts to drizzle and prisma installers ### Patch Changes - [#1891](https://github.com/t3-oss/create-t3-app/pull/1891) [`4236f0dfa969ff0d8b7ce2a60b5595c43ced9043`](https://github.com/t3-oss/create-t3-app/commit/4236f0dfa969ff0d8b7ce2a60b5595c43ced9043) Thanks [@thecmdrunner](https://github.com/thecmdrunner)! - Apply drizzle rules for `ctx.db` object in tRPC context ## 7.33.1 ### Patch Changes - [#1884](https://github.com/t3-oss/create-t3-app/pull/1884) [`f148deb`](https://github.com/t3-oss/create-t3-app/commit/f148debf74e16b61dae89c684fbc9ac1d97ac2c8) Thanks [@Irere123](https://github.com/Irere123)! - fix: remove deprecated push command in drizzle package.json scripts (… ) ## 7.33.0 ### Minor Changes - [#1821](https://github.com/t3-oss/create-t3-app/pull/1821) [`4de1927`](https://github.com/t3-oss/create-t3-app/commit/4de1927489aa2d0cf61313ea1543f85268c0e645) Thanks [@kakeWasTaken](https://github.com/kakeWasTaken)! - change default font from Inter to Geist Sans ### Patch Changes - [#1881](https://github.com/t3-oss/create-t3-app/pull/1881) [`9e5e7d0`](https://github.com/t3-oss/create-t3-app/commit/9e5e7d0c4342542fec18fdc97552b494fe31f5a3) Thanks [@gcjbr](https://github.com/gcjbr)! - chore: update `drizzle-kit` to `0.21.0` ## 7.32.1 ### Patch Changes - [#1867](https://github.com/t3-oss/create-t3-app/pull/1867) [`304a67f`](https://github.com/t3-oss/create-t3-app/commit/304a67f5bde05116b143c86588546af02e86cfda) Thanks [@xelacast](https://github.com/xelacast)! - Parsed --dbprovider flag correctly and added related error message - [#1860](https://github.com/t3-oss/create-t3-app/pull/1860) [`b5ced34`](https://github.com/t3-oss/create-t3-app/commit/b5ced343f8a7fbc889a113ec05bef6c5e550cd55) Thanks [@victor-falcon](https://github.com/victor-falcon)! - Load PORT in start-database script - [#1871](https://github.com/t3-oss/create-t3-app/pull/1871) [`f29a69f`](https://github.com/t3-oss/create-t3-app/commit/f29a69fcc300db539fe0cfa9f52858551e89b869) Thanks [@xelacast](https://github.com/xelacast)! - Fixed project creation of . bug in ./start-database.sh - [#1858](https://github.com/t3-oss/create-t3-app/pull/1858) [`29348b6`](https://github.com/t3-oss/create-t3-app/commit/29348b6b4f11d7f19915be6639e3c5aaa27b8907) Thanks [@lcabraja](https://github.com/lcabraja)! - Added drizzle's db.sqlite location to .gitignore ## 7.32.0 ### Minor Changes - [#1842](https://github.com/t3-oss/create-t3-app/pull/1842) [`088a773`](https://github.com/t3-oss/create-t3-app/commit/088a773103a03e0ec34cbe22329ead33cd80c5ff) Thanks [@nick22985](https://github.com/nick22985)! - Infers tRPC input & output types to the client for app router - [#1841](https://github.com/t3-oss/create-t3-app/pull/1841) [`41bd524`](https://github.com/t3-oss/create-t3-app/commit/41bd5240276f1029633cde588b0ca08301c4e78c) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - mark app router as stable ### Patch Changes - [#1838](https://github.com/t3-oss/create-t3-app/pull/1838) [`ff886b3`](https://github.com/t3-oss/create-t3-app/commit/ff886b3fed4b193b8fc85882a71c7e8085457cf0) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - bump zod and t3-env ## 7.31.0 ### Minor Changes - [#1830](https://github.com/t3-oss/create-t3-app/pull/1830) [`3257d0a`](https://github.com/t3-oss/create-t3-app/commit/3257d0a2fe73847b501bdf36f09053638b4a41a4) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: replace better-sqlite3 with libsql ### Patch Changes - [`ab5fa95`](https://github.com/t3-oss/create-t3-app/commit/ab5fa95afce2a0feb8567aed6bb02beeef1401e5) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: pin zod version - [#1835](https://github.com/t3-oss/create-t3-app/pull/1835) [`f66e30a`](https://github.com/t3-oss/create-t3-app/commit/f66e30a0c84eb4f74182d79b23e3c5e900788e10) Thanks [@Inshiku-Han](https://github.com/Inshiku-Han)! - Only install @trpc/next when router style is page router ## 7.30.2 ### Patch Changes - [#1817](https://github.com/t3-oss/create-t3-app/pull/1817) [`d49300a`](https://github.com/t3-oss/create-t3-app/commit/d49300a4230835f6a771ed1a6b759c61c8902d7e) Thanks [@kxzazz](https://github.com/kxzazz)! - fix: `start-database.sh` is marked optional - [#1823](https://github.com/t3-oss/create-t3-app/pull/1823) [`f5c5db9`](https://github.com/t3-oss/create-t3-app/commit/f5c5db99dbd86010369b26e55dd65973ccd0db05) Thanks [@t3dotgg](https://github.com/t3dotgg)! - chore: update next to 14.2.1 ## 7.30.1 ### Patch Changes - [#1802](https://github.com/t3-oss/create-t3-app/pull/1802) [`41380d1`](https://github.com/t3-oss/create-t3-app/commit/41380d1cbf29418c3d60a2dcefd3eaf3f7e7bc28) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - fix: non planetscale env.js refers to YOUR_MYSQL_URL_HERE - [#1808](https://github.com/t3-oss/create-t3-app/pull/1808) [`fedd7b3`](https://github.com/t3-oss/create-t3-app/commit/fedd7b3926540fdf3137f327cac917237a438943) Thanks [@thecmdrunner](https://github.com/thecmdrunner)! - Apply drizzle-orm lint rules for `db` keyword only ## 7.30.0 ### Minor Changes - [#1741](https://github.com/t3-oss/create-t3-app/pull/1741) [`5de586d`](https://github.com/t3-oss/create-t3-app/commit/5de586da1933064c9eef84ddae47d795e71afc53) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: use trpc 11 ## 7.29.0 ### Minor Changes - [#1785](https://github.com/t3-oss/create-t3-app/pull/1785) [`57dc6ab`](https://github.com/t3-oss/create-t3-app/commit/57dc6ab0625d7cd56bf8f6b5c4d3b844421ffe48) Thanks [@thecmdrunner](https://github.com/thecmdrunner)! - Include ESLlint rules for `drizzle-orm`. - [#1794](https://github.com/t3-oss/create-t3-app/pull/1794) [`3cca996`](https://github.com/t3-oss/create-t3-app/commit/3cca9965739d29cc010e586406d40fff471a9bed) Thanks [@thecmdrunner](https://github.com/thecmdrunner)! - Use portable shebangs in scripts for nix systems ## 7.28.0 ### Minor Changes - [#1777](https://github.com/t3-oss/create-t3-app/pull/1777) [`640a0e0`](https://github.com/t3-oss/create-t3-app/commit/640a0e086d1515f4f88315d3295b44296fe7bbd0) Thanks [@limegorilla](https://github.com/limegorilla)! - Warning about planetscale's serverless driver now only shown if using mySQL ### Patch Changes - [#1778](https://github.com/t3-oss/create-t3-app/pull/1778) [`01badfb`](https://github.com/t3-oss/create-t3-app/commit/01badfb38caec5d0d211d7f93c6e288d55f2a751) Thanks [@DerTimonius](https://github.com/DerTimonius)! - fix: add missing default font in pagesDir - [#1766](https://github.com/t3-oss/create-t3-app/pull/1766) [`d7695df`](https://github.com/t3-oss/create-t3-app/commit/d7695df89a77ea5add1d703b63b9aeda5b5ef0c5) Thanks [@fuegoio](https://github.com/fuegoio)! - fix: cache database connection during development to prevent `Too many connections` due to HMR ## 7.27.0 ### Minor Changes - [#1773](https://github.com/t3-oss/create-t3-app/pull/1773) [`8f29eb9`](https://github.com/t3-oss/create-t3-app/commit/8f29eb990ccbc36c018ce596d2de0205b3c7edac) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat(prisma): support correct typings when using `.extends()` ### Patch Changes - [#1774](https://github.com/t3-oss/create-t3-app/pull/1774) [`0418663`](https://github.com/t3-oss/create-t3-app/commit/0418663ec6cc370ed1e067f1ef68f0d5ca41af53) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: update drizzle-orm - [#1763](https://github.com/t3-oss/create-t3-app/pull/1763) [`836fccf`](https://github.com/t3-oss/create-t3-app/commit/836fccf22dc224e283a764649c16090e4ba42b18) Thanks [@skullface](https://github.com/skullface)! - fix: correct casing of “PlanetScale” - [#1764](https://github.com/t3-oss/create-t3-app/pull/1764) [`79e361c`](https://github.com/t3-oss/create-t3-app/commit/79e361cfd8ad0740a353c7712cad8d38cbc8ee7b) Thanks [@vlxm](https://github.com/vlxm)! - fix: fix password substitution in start-database scripts - [#1769](https://github.com/t3-oss/create-t3-app/pull/1769) [`c944c49`](https://github.com/t3-oss/create-t3-app/commit/c944c490522a3fb921d564ab55eecd8c535142c3) Thanks [@jhamill34](https://github.com/jhamill34)! - fix: generated comments in env file - [#1772](https://github.com/t3-oss/create-t3-app/pull/1772) [`c3c4710`](https://github.com/t3-oss/create-t3-app/commit/c3c4710b2e7e6245c0afae45e1a5119d91cb9578) Thanks [@mladenovic-13](https://github.com/mladenovic-13)! - fix: use singleton pattern for client-side QueryClient to support `useSuspenseQuery` when there is no parent ``-element - [#1748](https://github.com/t3-oss/create-t3-app/pull/1748) [`17c2a5e`](https://github.com/t3-oss/create-t3-app/commit/17c2a5eb52a7faff7ea9e1d1a6270c2216235ff2) Thanks [@aaronmallen](https://github.com/aaronmallen)! - add missing `pg` dev dependency when using postgres drizzle option, required for `drizzle-studio` ## 7.26.0 ### Minor Changes - [#1622](https://github.com/t3-oss/create-t3-app/pull/1622) [`06b1bca`](https://github.com/t3-oss/create-t3-app/commit/06b1bcafe8751a0fc3cfb7992dfb12cb88bf0234) Thanks [@ronanru](https://github.com/ronanru)! - add more db options ## 7.25.2 ### Patch Changes - [#1723](https://github.com/t3-oss/create-t3-app/pull/1723) [`c116281`](https://github.com/t3-oss/create-t3-app/commit/c116281f3626ae5f05191037789d7338a8fa2e5c) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: don't pass cookies to CC - [#1728](https://github.com/t3-oss/create-t3-app/pull/1728) [`06ec5b3`](https://github.com/t3-oss/create-t3-app/commit/06ec5b37784c305d18a775b64d34cecb5de45bf2) Thanks [@Nsttt](https://github.com/Nsttt)! - fix missuse of binary expressions ## 7.25.1 ### Patch Changes - [#1713](https://github.com/t3-oss/create-t3-app/pull/1713) [`e34b082`](https://github.com/t3-oss/create-t3-app/commit/e34b08245cc3c1288f90b289577e7c18b37acb1c) Thanks [@daikiejp](https://github.com/daikiejp)! - remove unused @types/inquirer - [#1703](https://github.com/t3-oss/create-t3-app/pull/1703) [`b6ccded`](https://github.com/t3-oss/create-t3-app/commit/b6ccdedd11b22b64e7bb6f1c55997362fa2915a4) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - update eslint setup for next 14.0.4 - [#1714](https://github.com/t3-oss/create-t3-app/pull/1714) [`ba352ef`](https://github.com/t3-oss/create-t3-app/commit/ba352ef73823eb6413cf251bd7111db6fcb3a585) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: update drizzle dep to remove invalid expo peerDep - [#1707](https://github.com/t3-oss/create-t3-app/pull/1707) [`26efe48`](https://github.com/t3-oss/create-t3-app/commit/26efe482bdcb309796a2bf207c50ebc844233106) Thanks [@esize](https://github.com/esize)! - chore: update drizzle-orm and drizzle-kit - [#1708](https://github.com/t3-oss/create-t3-app/pull/1708) [`8f379b4`](https://github.com/t3-oss/create-t3-app/commit/8f379b4df75e56b98973aabcc2097b62be80d0c7) Thanks [@KATT](https://github.com/KATT)! - chore: drop `t.middleware()` ## 7.25.0 ### Minor Changes - [#1702](https://github.com/t3-oss/create-t3-app/pull/1702) [`62fa8dc`](https://github.com/t3-oss/create-t3-app/commit/62fa8dcad2e0d3d6bad04bb6167d7580a754049c) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - update drizzle-kit and change connectionString to uri ### Patch Changes - [#1690](https://github.com/t3-oss/create-t3-app/pull/1690) [`6efb99a`](https://github.com/t3-oss/create-t3-app/commit/6efb99a94f29d28243fa7c625b099995e84e4db2) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - fix typo in env.mjs ## 7.24.2 ### Patch Changes - [#1687](https://github.com/t3-oss/create-t3-app/pull/1687) [`6461090`](https://github.com/t3-oss/create-t3-app/commit/6461090a029dd2094065493e513a289a1b414673) Thanks [@sinasab](https://github.com/sinasab)! - chore: reuse existing AppRouter type #1687 ## 7.24.1 ### Patch Changes - [#1673](https://github.com/t3-oss/create-t3-app/pull/1673) [`b80188e`](https://github.com/t3-oss/create-t3-app/commit/b80188eaecc64e564909ed7ee566790f5e56e59f) Thanks [@ahkhanjani](https://github.com/ahkhanjani)! - fix: Use the correct types for `prettier-plugin-tailwindcss` ## 7.24.0 ### Minor Changes - [#1670](https://github.com/t3-oss/create-t3-app/pull/1670) [`32e0747`](https://github.com/t3-oss/create-t3-app/commit/32e0747a027be16cc36ad5bead062eea696e4012) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: trpc server caller without http ### Patch Changes - [#1661](https://github.com/t3-oss/create-t3-app/pull/1661) [`ef8f99f`](https://github.com/t3-oss/create-t3-app/commit/ef8f99f65443e48284bc54699eeddc9d10c43a8c) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - update sharp - [#1667](https://github.com/t3-oss/create-t3-app/pull/1667) [`43cc10b`](https://github.com/t3-oss/create-t3-app/commit/43cc10b0e4ddd6c1b3eb175309df3aebe0571285) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: bump some deps - [#1666](https://github.com/t3-oss/create-t3-app/pull/1666) [`3317a41`](https://github.com/t3-oss/create-t3-app/commit/3317a41546728509543d8fc44a8fe715b04f09e8) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - refactor: use `type: module` for more modern setup - [#1664](https://github.com/t3-oss/create-t3-app/pull/1664) [`66213b0`](https://github.com/t3-oss/create-t3-app/commit/66213b04cf03fd7d4d0602bbb34a3de2ab703daa) Thanks [@piousdeer](https://github.com/piousdeer)! - chore: replace a `2` with `"error"` in eslint config ## 7.23.2 ### Patch Changes - [#1651](https://github.com/t3-oss/create-t3-app/pull/1651) [`4834ec3`](https://github.com/t3-oss/create-t3-app/commit/4834ec3cd16c8dba4be1bcd2a9b162c37a0bc7d2) Thanks [@mattpocock](https://github.com/mattpocock)! - tidy up and modernize tsconfig ## 7.23.1 ### Patch Changes - [#1644](https://github.com/t3-oss/create-t3-app/pull/1644) [`e6649f1`](https://github.com/t3-oss/create-t3-app/commit/e6649f18963086b2d016815e60cc9915711a85d8) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: only forward required cookie header ## 7.23.0 ### Minor Changes - [#1628](https://github.com/t3-oss/create-t3-app/pull/1628) [`57a82d2`](https://github.com/t3-oss/create-t3-app/commit/57a82d2f4c1a34068280a1f01c1f2618af260650) Thanks [@CarlosZiegler](https://github.com/CarlosZiegler)! - Update Next to use last version (14.0.0) ## 7.22.0 ### Minor Changes - [#1567](https://github.com/t3-oss/create-t3-app/pull/1567) [`e71bedd`](https://github.com/t3-oss/create-t3-app/commit/e71bedde0d6944fa7719a5f97cd27a1503156faa) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: add app router option - [#1595](https://github.com/t3-oss/create-t3-app/pull/1595) [`d7824e0`](https://github.com/t3-oss/create-t3-app/commit/d7824e0e2523703662b731945229e2fae88708ae) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - show callout when scaffolding with app router ## 7.21.0 ### Minor Changes - [#1584](https://github.com/t3-oss/create-t3-app/pull/1584) [`9614d84`](https://github.com/t3-oss/create-t3-app/commit/9614d8424d810278facf012fb99d1da51d2238e6) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - bump deps and add `emptyStringAsUndefined` option for `t3-env` ## 7.20.2 ### Patch Changes - [#1564](https://github.com/t3-oss/create-t3-app/pull/1564) [`58ee08e`](https://github.com/t3-oss/create-t3-app/commit/58ee08e850d985b5ef1b7e5bf0cc164cfaff3f3f) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: block drizzle apps when the example database url hasn't been changed yet ## 7.20.1 ### Patch Changes - [#1561](https://github.com/t3-oss/create-t3-app/pull/1561) [`b8507dc`](https://github.com/t3-oss/create-t3-app/commit/b8507dc64ee91b8423de2ab6145cb3e7d8072528) Thanks [@Nsttt](https://github.com/Nsttt)! - Add packageManager field in package.json at app creation ## 7.20.0 ### Minor Changes - [#1461](https://github.com/t3-oss/create-t3-app/pull/1461) [`8034db0`](https://github.com/t3-oss/create-t3-app/commit/8034db042b9ed12f5ba01c1f779c5ee77d78afc4) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: add drizzle This release adds a new option to use [`drizzle-orm`](https://orm.drizzle.team/docs/overview) as an alternative to Prisma. To make the different ORM options as similar as possible, some minor changes has also been made to the Prisma installer: - a new script `db:push` has been added and is included in both ORM options. - the prisma client has been renamed to `db` in the trpc context - you now access your database client like ```ts examples: publicProcedure.query((opts) => { // prisma opts.ctx.db.example.findMany() // drizzle opts.ctx.db.query.example.findMany() }), ``` You cannot choose the two options in the same app. ### Patch Changes - [#1461](https://github.com/t3-oss/create-t3-app/pull/1461) [`8034db0`](https://github.com/t3-oss/create-t3-app/commit/8034db042b9ed12f5ba01c1f779c5ee77d78afc4) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - refactor: swap inquirer for clack ## 7.19.0 ### Minor Changes - [#1375](https://github.com/t3-oss/create-t3-app/pull/1375) [`d781f08`](https://github.com/t3-oss/create-t3-app/commit/d781f0861ba05aee6b3d460fc53e126868409fb5) Thanks [@Zeko369](https://github.com/Zeko369)! - feat: detect and support usage of Bun as package manager ## 7.18.0 ### Minor Changes - [#1532](https://github.com/t3-oss/create-t3-app/pull/1532) [`941a0fc`](https://github.com/t3-oss/create-t3-app/commit/941a0fc79bcfb24c894525c93aab87c2860b2ad4) Thanks [@spicybackend](https://github.com/spicybackend)! - chore(deps): upgrade Prisma to v5.1.1, bump patch versions of other dependencies - [#1507](https://github.com/t3-oss/create-t3-app/pull/1507) [`fe5755c`](https://github.com/t3-oss/create-t3-app/commit/fe5755c2d575ce27e8e9c86e6fc79e7903af0390) Thanks [@LevinUncu](https://github.com/LevinUncu)! - If the project is not in a Git repo, the next steps will show git init instead of git commit -m "initial commit". ## 7.17.0 ### Minor Changes - [#1523](https://github.com/t3-oss/create-t3-app/pull/1523) [`c441a9e`](https://github.com/t3-oss/create-t3-app/commit/c441a9ecfaeb61866a76379712b297fc6c2c36b3) Thanks [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)! - reduced stylistic linting ## 7.16.0 ### Minor Changes - [#1476](https://github.com/t3-oss/create-t3-app/pull/1476) [`f3fd50f`](https://github.com/t3-oss/create-t3-app/commit/f3fd50fbd5dfd705507b1a2b53e8e6fd39b2dadb) Thanks [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)! - Upgraded typescript-eslint to v6, with reworked ESLint configurations - [#1508](https://github.com/t3-oss/create-t3-app/pull/1508) [`e9c862e`](https://github.com/t3-oss/create-t3-app/commit/e9c862e7e114214139f05572d62af45d87d3fc1c) Thanks [@Sn0wye](https://github.com/Sn0wye)! - chore(deps): bump prisma to v5.0.0 ## 7.15.0 ### Minor Changes - [#1484](https://github.com/t3-oss/create-t3-app/pull/1484) [`23a9d60`](https://github.com/t3-oss/create-t3-app/commit/23a9d60cd013c40d76ab96d0340425e1e3d8efa2) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - reminder to git commit after creating an application with the CLI ### Patch Changes - [#1493](https://github.com/t3-oss/create-t3-app/pull/1493) [`a230d9f`](https://github.com/t3-oss/create-t3-app/commit/a230d9f7bb3a78669f2003ae9084c868f06639b7) Thanks [@dcottr](https://github.com/dcottr)! - Remove trailing newlines in generated app that aren't allowed by the generated prettier config ## 7.14.1 ### Patch Changes - [#1479](https://github.com/t3-oss/create-t3-app/pull/1479) [`bb7324c`](https://github.com/t3-oss/create-t3-app/commit/bb7324c9503995949c61c0a5abe736c38f07b815) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - use custom error class for IsTTYError ## 7.14.0 ### Minor Changes - [#1466](https://github.com/t3-oss/create-t3-app/pull/1466) [`e8b68d9`](https://github.com/t3-oss/create-t3-app/commit/e8b68d92af093361bf73eed7eacd7ed6848500c3) Thanks [@brunoeduardodev](https://github.com/brunoeduardodev)! - Ignore trailing slashes when prompting the app name. ## 7.13.2 ### Patch Changes - [#1469](https://github.com/t3-oss/create-t3-app/pull/1469) [`547f504`](https://github.com/t3-oss/create-t3-app/commit/547f504b91c3da9b6419ea7f9f43d03e6c243839) Thanks [@ericshively](https://github.com/ericshively)! - Replace React.FC with basic function syntax ## 7.13.1 ### Patch Changes - [#1436](https://github.com/t3-oss/create-t3-app/pull/1436) [`b0facee`](https://github.com/t3-oss/create-t3-app/commit/b0facee2736e3e0afc909c3d80b38499174fd84c) Thanks [@harrismcc](https://github.com/harrismcc)! - When using NextAuth but not Prisma, the session user's id is now set to `token.sub` instead of `user.id` because `user` is undefined when not using database sessions. - [#1433](https://github.com/t3-oss/create-t3-app/pull/1433) [`7fbe991`](https://github.com/t3-oss/create-t3-app/commit/7fbe991175f4da02184e0e43a5439515d1ef7439) Thanks [@FinnDore](https://github.com/FinnDore)! - fix: SKIP_ENV_VALIDATION not skipping env validation ## 7.13.0 ### Minor Changes - [#1397](https://github.com/t3-oss/create-t3-app/pull/1397) [`be9a834`](https://github.com/t3-oss/create-t3-app/commit/be9a83483339f8bb799be7a3d69cf67e64882daf) Thanks [@brieyla1](https://github.com/brieyla1)! - feat: Added JsonProtocol as the Default Prisma Engine for Serverless speedups modified: - cli/template/extras/prisma/schema/base.prisma - cli/template/extras/prisma/schema/with-auth.prisma Description: This pull request proposes the enabling of the JsonProtocol by default. The rationale for this change is based on the performance improvements provided by the JsonProtocol without compromising stability, even though it is still a preview feature. Empirical evidence gathered from large-scale projects, with 100+ Prisma calls, demonstrates the significant time reductions enabled by this optimization, particularly in serverless environments such as Vercel. Another point worth adding is that most projects using nextJS create-t3-app will be deployed on serverless platforms such as vercel, where the real impact is. ## 7.12.3 ### Patch Changes - [#1404](https://github.com/t3-oss/create-t3-app/pull/1404) [`48dd89a`](https://github.com/t3-oss/create-t3-app/commit/48dd89adb33ff9625dba82f6e149cf111f02e488) Thanks [@michalbundyra](https://github.com/michalbundyra)! - update nextjs dependency to ^13.4.1 ## 7.12.2 ### Patch Changes - [#1401](https://github.com/t3-oss/create-t3-app/pull/1401) [`b1a35dd`](https://github.com/t3-oss/create-t3-app/commit/b1a35dd66c5c9abab13daebfee93c08b67317272) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - temporarily pin next to fix vercel build error ## 7.12.1 ### Patch Changes - [#1377](https://github.com/t3-oss/create-t3-app/pull/1377) [`00018ff`](https://github.com/t3-oss/create-t3-app/commit/00018ff0cbe5904f0bb37b8cb0a64814ca5f9410) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - move env validation logic out to a separate package - [#1385](https://github.com/t3-oss/create-t3-app/pull/1385) [`ac12abb`](https://github.com/t3-oss/create-t3-app/commit/ac12abb92a77d251b2c4bc72caceb422ab4ca706) Thanks [@iAverages](https://github.com/iAverages)! - Fixed issue with creating git repository with git versions below 2.28 Fixed bug with check to use --inital-branch set in git v2.28 ## 7.12.0 ### Minor Changes - [#1370](https://github.com/t3-oss/create-t3-app/pull/1370) [`18bc6a7`](https://github.com/t3-oss/create-t3-app/commit/18bc6a73a77b0b48191792ddfd0c296b58cc8221) Thanks [@chungweileong94](https://github.com/chungweileong94)! - Enforce `NEXT_PUBLIC_` env key on client ## 7.11.0 ### Minor Changes - [#1358](https://github.com/t3-oss/create-t3-app/pull/1358) [`1a8a97e`](https://github.com/t3-oss/create-t3-app/commit/1a8a97e42b8991596fc70e50dd6870c987a59075) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - improve boolean coercion of `SKIP_ENV_VALIDATION` environment variable If defined, "0" and "false" are now considered falsey values, all other values are considered truthy. ### Patch Changes - [#1352](https://github.com/t3-oss/create-t3-app/pull/1352) [`c48b38f`](https://github.com/t3-oss/create-t3-app/commit/c48b38f522b81097e2f0e883ede053dbac620c12) Thanks [@Tungetyt](https://github.com/Tungetyt)! - refactor: make destructuring consistent ## 7.10.3 ### Patch Changes - [#1330](https://github.com/t3-oss/create-t3-app/pull/1330) [`85997e0`](https://github.com/t3-oss/create-t3-app/commit/85997e0cc26a2ae6fbcd08458734bd6335ba467a) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: create a new object for the session callback ## 7.10.2 ### Patch Changes - [#1329](https://github.com/t3-oss/create-t3-app/pull/1329) [`5429989`](https://github.com/t3-oss/create-t3-app/commit/54299895517d3c406637efec8f0512cf1ba2da5a) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - temporarily pin next-auth version - [#1321](https://github.com/t3-oss/create-t3-app/pull/1321) [`7fcb41c`](https://github.com/t3-oss/create-t3-app/commit/7fcb41c95d24567040b72409a576d8c694f910a1) Thanks [@JingLi1998](https://github.com/JingLi1998)! - Update the type for global prisma instance ## 7.10.1 ### Patch Changes - [#1312](https://github.com/t3-oss/create-t3-app/pull/1312) [`2fb3a51`](https://github.com/t3-oss/create-t3-app/commit/2fb3a51b8b91cf2eb992a3af49ff52588f1e9c95) Thanks [@FinnDore](https://github.com/FinnDore)! - feat: bumped tailwind from 3.2.7 -> 3.3.0 ## 7.10.0 ### Minor Changes - [#1295](https://github.com/t3-oss/create-t3-app/pull/1295) [`d0a8143`](https://github.com/t3-oss/create-t3-app/commit/d0a8143587c935819790521533e7cfa0862d300c) Thanks [@iAverages](https://github.com/iAverages)! - Replace Git Bash check with Mintty check ### Patch Changes - [#1304](https://github.com/t3-oss/create-t3-app/pull/1304) [`2cb41e6`](https://github.com/t3-oss/create-t3-app/commit/2cb41e676f01d4e8bfb8c7fc41bf0d5c4a1c0689) Thanks [@PiotrekPKP](https://github.com/PiotrekPKP)! - fix: replace trpc links ## 7.9.0 ### Minor Changes - [#1284](https://github.com/t3-oss/create-t3-app/pull/1284) [`a2af1a2`](https://github.com/t3-oss/create-t3-app/commit/a2af1a2b7d4fd3005f8efe0e297b871d6b1747ff) Thanks [@michalbundyra](https://github.com/michalbundyra)! - chore: bump typescript - 5.0.2 ## 7.8.0 ### Minor Changes - [#1273](https://github.com/t3-oss/create-t3-app/pull/1273) [`10a5e0b`](https://github.com/t3-oss/create-t3-app/commit/10a5e0b2fe91b887526960515c6422791a77c724) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: add errorformatter for zod errors ### Patch Changes - [#1263](https://github.com/t3-oss/create-t3-app/pull/1263) [`8dc6e33`](https://github.com/t3-oss/create-t3-app/commit/8dc6e33ea149851de97f84e8d8388accdd5547d9) Thanks [@mkreuzmayr](https://github.com/mkreuzmayr)! - search for tsconfig relative to eslintrc - [#1279](https://github.com/t3-oss/create-t3-app/pull/1279) [`ae5cd40`](https://github.com/t3-oss/create-t3-app/commit/ae5cd405c9d5bb672e3971c496d437a7a05b3f8f) Thanks [@arielconti10](https://github.com/arielconti10)! - fix: remove //@ts-check comment in next.config ## 7.7.0 ### Minor Changes - [#1268](https://github.com/t3-oss/create-t3-app/pull/1268) [`db394ed`](https://github.com/t3-oss/create-t3-app/commit/db394edf396288dd754f5743ae2fe26a39c70ef5) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - ignore unused vars that begin with underscore ## 7.6.0 ### Minor Changes - [#1255](https://github.com/t3-oss/create-t3-app/pull/1255) [`601cbb2`](https://github.com/t3-oss/create-t3-app/commit/601cbb23075cd33170c5b3ea2b1b0dbb0f2d5716) Thanks [@justasam](https://github.com/justasam)! - Add import alias validation ## 7.5.9 ### Patch Changes - [#1252](https://github.com/t3-oss/create-t3-app/pull/1252) [`d8be181`](https://github.com/t3-oss/create-t3-app/commit/d8be181fd71dafba8c4c1358fea38b70bca8171a) Thanks [@ggrochow](https://github.com/ggrochow)! - Rename \_eslint.cjs before intializing git repo to ensure .eslint.cjs is added by default ## 7.5.8 ### Patch Changes - [#1249](https://github.com/t3-oss/create-t3-app/pull/1249) [`2432d76`](https://github.com/t3-oss/create-t3-app/commit/2432d76acb337e215743e712d12969b141b22879) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: bump nextjs ## 7.5.7 ### Patch Changes - [#1232](https://github.com/t3-oss/create-t3-app/pull/1232) [`9c82f86`](https://github.com/t3-oss/create-t3-app/commit/9c82f862f5f5227c48e355a8fc31f83d88b0baeb) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: bind configs to variables before exporting to make sure typeerrors are reported ## 7.5.6 ### Patch Changes - [#1222](https://github.com/t3-oss/create-t3-app/pull/1222) [`7baa339`](https://github.com/t3-oss/create-t3-app/commit/7baa339b36b40760443083e0a378d8fe04dddb40) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: detect and handle git bash environment ## 7.5.5 ### Patch Changes - [#1218](https://github.com/t3-oss/create-t3-app/pull/1218) [`4f8d41a`](https://github.com/t3-oss/create-t3-app/commit/4f8d41ae2f41bef197cab13a332034e0ddffc955) Thanks [@bai](https://github.com/bai)! - feat: change eslint config to use `.cjs` format and typecheck it - [#1217](https://github.com/t3-oss/create-t3-app/pull/1217) [`55c87af`](https://github.com/t3-oss/create-t3-app/commit/55c87af37f64fbf778a5091b9cca75f9c9546468) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: add autofix to eslint type rule ## 7.5.4 ### Patch Changes - [#1191](https://github.com/t3-oss/create-t3-app/pull/1191) [`aabb9a4`](https://github.com/t3-oss/create-t3-app/commit/aabb9a41aa8acce4b3b02e2341b83cd78b802924) Thanks [@bai](https://github.com/bai)! - Use ~/\* import alias ## 7.5.3 ### Patch Changes - [#1213](https://github.com/t3-oss/create-t3-app/pull/1213) [`ab3c7cb`](https://github.com/t3-oss/create-t3-app/commit/ab3c7cb4d4e78ab646a645bc28b4fe3496ba7831) Thanks [@michalbundyra](https://github.com/michalbundyra)! - fix: update comment regarding schema definition of env vars ## 7.5.2 ### Patch Changes - [#1189](https://github.com/t3-oss/create-t3-app/pull/1189) [`bf12635`](https://github.com/t3-oss/create-t3-app/commit/bf1263576866116a904f3d1fa741c5496a535cb9) Thanks [@p13lgst](https://github.com/p13lgst)! - fix: remove ts-ignores on env.mjs ## 7.5.1 ### Patch Changes - [#1187](https://github.com/t3-oss/create-t3-app/pull/1187) [`e111fb5`](https://github.com/t3-oss/create-t3-app/commit/e111fb5cb816de9b2d87bdf4ffcf84f446fd2023) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - remove old env files ## 7.5.0 ### Minor Changes - [#1092](https://github.com/t3-oss/create-t3-app/pull/1092) [`aa44574`](https://github.com/t3-oss/create-t3-app/commit/aa44574680bf380cf4cf7f255fb547e5d9ced7d7) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - single env file ### Patch Changes - [#1171](https://github.com/t3-oss/create-t3-app/pull/1171) [`efe8b7f`](https://github.com/t3-oss/create-t3-app/commit/efe8b7f54cf0fe5448dc52118745668ae4913547) Thanks [@esotuvaka](https://github.com/esotuvaka)! - fix: swap `@ts-check` comments to `checkJs: true` in scaffolded app's tsconfig ## 7.4.1 ### Patch Changes - [#1155](https://github.com/t3-oss/create-t3-app/pull/1155) [`90955a5`](https://github.com/t3-oss/create-t3-app/commit/90955a50390eee8a23e1c1b35e10c860e4341fed) Thanks [@christopher-kapic](https://github.com/christopher-kapic)! - Change unstable_getServerSession to match the updated getServerSession from next-auth (introduced in next-auth 4.19.0) ## 7.4.0 ### Minor Changes - [#1136](https://github.com/t3-oss/create-t3-app/pull/1136) [`715f6e8`](https://github.com/t3-oss/create-t3-app/commit/715f6e8e795685c054145149acff427a1b46172f) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: colocate auth config/dts/session-getter into a single file - [#1134](https://github.com/t3-oss/create-t3-app/pull/1134) [`4cb2e4e`](https://github.com/t3-oss/create-t3-app/commit/4cb2e4e5020f93636889499bf71029ce7fcb5fce) Thanks [@PiotrekPKP](https://github.com/PiotrekPKP)! - feat: added package installation progress ### Patch Changes - [#1131](https://github.com/t3-oss/create-t3-app/pull/1131) [`ab0790e`](https://github.com/t3-oss/create-t3-app/commit/ab0790e95194291cab65dee458982a2b637a4d15) Thanks [@bastibuck](https://github.com/bastibuck)! - fix: fix types of serverEnv and clientEnv - [#1140](https://github.com/t3-oss/create-t3-app/pull/1140) [`fc445bf`](https://github.com/t3-oss/create-t3-app/commit/fc445bfaa72e2de2880fb286cee71ff2b26a72a6) Thanks [@Zamiell](https://github.com/Zamiell)! - chore: add descriptive comments and format .env prettier ## 7.3.2 ### Patch Changes - [#1097](https://github.com/t3-oss/create-t3-app/pull/1097) [`2ed36cd`](https://github.com/t3-oss/create-t3-app/commit/2ed36cd624cba2032ba70c465cd47c49a962bb0d) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: bump nextjs and add a note about appDir+i18n being incompat ## 7.3.1 ### Patch Changes - [#1084](https://github.com/t3-oss/create-t3-app/pull/1084) [`7490a34`](https://github.com/t3-oss/create-t3-app/commit/7490a34a3f46d8e912f996843d27ae5933f26164) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - fix: undo env changes ## 7.3.0 ### Minor Changes - [#1071](https://github.com/t3-oss/create-t3-app/pull/1071) [`34d6c5b`](https://github.com/t3-oss/create-t3-app/commit/34d6c5b0431ea7baa922a27768d0c82b05c12bdc) Thanks [@ JacobADevore](https://github.com/JacobADevore)! - feat: automatically destructure env objects ## 7.2.0 ### Minor Changes - [#1021](https://github.com/t3-oss/create-t3-app/pull/1021) [`78dfdc9`](https://github.com/t3-oss/create-t3-app/commit/78dfdc9b480ea19ac90270827653400e31300e6e) Thanks [@iduuck](https://github.com/iduuck)! - construct `serverEnv`, like we do with `clientEnv` #1000 ### Patch Changes - [#1060](https://github.com/t3-oss/create-t3-app/pull/1060) [`a8cc106`](https://github.com/t3-oss/create-t3-app/commit/a8cc106c3ef507e275f38c09a5d6a7a14336c254) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: cleanup context type in `initTRPC` ## 7.1.0 ### Minor Changes - [#1036](https://github.com/t3-oss/create-t3-app/pull/1036) [`c5ca65e`](https://github.com/t3-oss/create-t3-app/commit/c5ca65e03e2a9d1c3c7f6844eb678402b0fa8993) Thanks [@JoshuaKGoldberg](https://github.com/JoshuaKGoldberg)! - use typescript-eslint's recommended-required-type-checking ## 7.0.2 ### Patch Changes - [#1056](https://github.com/t3-oss/create-t3-app/pull/1056) [`c6704fb`](https://github.com/t3-oss/create-t3-app/commit/c6704fbeef1473e77ad62543770f1804645389a8) Thanks [@prashantrahul141](https://github.com/prashantrahul141)! - fix: update `package.json->name` before installing dependencies. ## 7.0.1 ### Patch Changes - [#1043](https://github.com/t3-oss/create-t3-app/pull/1043) [`6fe7e1e`](https://github.com/t3-oss/create-t3-app/commit/6fe7e1e1025f46ea8fe6cc82bee7a68e96864078) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix(cli): exit with error code when aborting installation fix(cli): don't log when scaffolding in current directory and it's empty ## 7.0.0 ### Major Changes - [#934](https://github.com/t3-oss/create-t3-app/pull/934) [`c20ec70`](https://github.com/t3-oss/create-t3-app/commit/c20ec705a6d3d3005da24d072cc9be04370ab822) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat!: implement new folder structure create-t3-app has decided to switch out the folder structure of the generated t3-app to, what is to us, a more minimal, logical and easier to understand structure. ## 6.12.0 ### Minor Changes - [#1031](https://github.com/t3-oss/create-t3-app/pull/1031) [`61ba991`](https://github.com/t3-oss/create-t3-app/commit/61ba991ea92f5eb3e971d34769df0ba470310990) Thanks [@complexengine](https://github.com/complexengine)! - The CLI now stages the files on project initialization. ## 6.11.6 ### Patch Changes - [#1024](https://github.com/t3-oss/create-t3-app/pull/1024) [`fcf59b0`](https://github.com/t3-oss/create-t3-app/commit/fcf59b077d80fdf0728ab7a4ea12a4b3b5521da3) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: allow dot name in name prompt ## 6.11.5 ### Patch Changes - [#1010](https://github.com/t3-oss/create-t3-app/pull/1010) [`9eddbab`](https://github.com/t3-oss/create-t3-app/commit/9eddbabb9e045b2a8a668ec1389218020360b305) Thanks [@maxmellen](https://github.com/maxmellen)! - chore(create-t3-app): add @types/prettier package to tailwind installer and bump prettier versions ## 6.11.4 ### Patch Changes - [#981](https://github.com/t3-oss/create-t3-app/pull/981) [`ac32b6d`](https://github.com/t3-oss/create-t3-app/commit/ac32b6d09df6d7b3620a41ace8cd2612bd909cc8) Thanks [@balazsorban44](https://github.com/balazsorban44)! - fix(cli): prompt TS first ## 6.11.3 ### Patch Changes - [#939](https://github.com/t3-oss/create-t3-app/pull/939) [`77cfb4b`](https://github.com/t3-oss/create-t3-app/commit/77cfb4bd156ca3d97cbb70cf51ec8e12a632bd64) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - use next-auth 4.18.3 and undo hotfix - [#932](https://github.com/t3-oss/create-t3-app/pull/932) [`c04a35a`](https://github.com/t3-oss/create-t3-app/commit/c04a35a2b215c8537cf7f0d9ff9760c831720514) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - fix next-auth error ## 6.11.2 ### Patch Changes - [#917](https://github.com/t3-oss/create-t3-app/pull/917) [`eb55d19`](https://github.com/t3-oss/create-t3-app/commit/eb55d19ae0204f6e61fb65b82057b93ed485e880) Thanks [@sharkby7e](https://github.com/sharkby7e)! - feat: add blank target to base template ## 6.11.1 ### Patch Changes - [#914](https://github.com/t3-oss/create-t3-app/pull/914) [`1037d14`](https://github.com/t3-oss/create-t3-app/commit/1037d1472764080a5641d657cac75adec2bbd3f1) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - temporarily pin next-auth ## 6.11.0 ### Minor Changes - [#901](https://github.com/t3-oss/create-t3-app/pull/901) [`9b6ae5c`](https://github.com/t3-oss/create-t3-app/commit/9b6ae5cc1c8fe85becd367d36b61828b06a0a6a9) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - include env files in all apps ### Patch Changes - [#895](https://github.com/t3-oss/create-t3-app/pull/895) [`91362db`](https://github.com/t3-oss/create-t3-app/commit/91362db87a50fe7f09807aa9950acc5c60ba80dc) Thanks [@Mvmo](https://github.com/Mvmo)! - When initializing a new git repo the git config value 'init.defaultBranch' will be used as the branch name - [#897](https://github.com/t3-oss/create-t3-app/pull/897) [`272b954`](https://github.com/t3-oss/create-t3-app/commit/272b9547f42f70df3c24dc9d438a7cd88a244773) Thanks [@AyanavaKarmakar](https://github.com/AyanavaKarmakar)! - fix(cli): fix wrong prompt when setting up project name ## 6.10.3 ### Patch Changes - [#869](https://github.com/t3-oss/create-t3-app/pull/869) [`cd93b98`](https://github.com/t3-oss/create-t3-app/commit/cd93b98d00e559721da0674b81978a00870e68ff) Thanks [@meech-ward](https://github.com/meech-ward)! - docs: add notes about env vars - [#878](https://github.com/t3-oss/create-t3-app/pull/878) [`9abaf80`](https://github.com/t3-oss/create-t3-app/commit/9abaf80d20620e3399c3a1ae51e06a41e2990e28) Thanks [@AyanavaKarmakar](https://github.com/AyanavaKarmakar)! - feat: update scaffolded README.md - [#845](https://github.com/t3-oss/create-t3-app/pull/845) [`c387187`](https://github.com/t3-oss/create-t3-app/commit/c38718768661944807daee92b21aae2789649ecb) Thanks [@GabrielPedroza](https://github.com/gabrielpedroza)! - fix: typo in env-schema comments ## 6.10.2 ### Patch Changes - [#834](https://github.com/t3-oss/create-t3-app/pull/834) [`3ace6c8`](https://github.com/t3-oss/create-t3-app/commit/3ace6c8149b01d68c1503c6b3087f2a8e5ebc865) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - use trpc 10.0.0 ## 6.10.1 ### Patch Changes - [#784](https://github.com/t3-oss/create-t3-app/pull/784) [`f10ce0e`](https://github.com/t3-oss/create-t3-app/commit/f10ce0e9badd3ad6ed45b8dca272bdfc5fbfbff6) Thanks [@AyanavaKarmakar](https://github.com/AyanavaKarmakar)! - fix(t3-app): fix broken links - [#786](https://github.com/t3-oss/create-t3-app/pull/786) [`4d70f04`](https://github.com/t3-oss/create-t3-app/commit/4d70f048955cd75aa67f0ba91fd5cec5d0311d08) Thanks [@skuridin](https://github.com/skuridin)! - Add next-env.d.ts to .gitignore - [#800](https://github.com/t3-oss/create-t3-app/pull/800) [`d5d5a2f`](https://github.com/t3-oss/create-t3-app/commit/d5d5a2f7503cf9d7c3bfab4609aeb6a0188d6af1) Thanks [@HatulaPro](https://github.com/HatulaPro)! - fix: removing redundant optional chaining in templates ## 6.10.0 ### Minor Changes - [#711](https://github.com/t3-oss/create-t3-app/pull/711) [`da760c7`](https://github.com/t3-oss/create-t3-app/commit/da760c7d4b96fb783ca1621aed933d63f0d51d4e) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - create new index pages ### Patch Changes - [#754](https://github.com/t3-oss/create-t3-app/pull/754) [`b6cdcb3`](https://github.com/t3-oss/create-t3-app/commit/b6cdcb3f21feeec6153601d420177b0ada490f2e) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - fix: rename example env file ## 6.9.0 ### Minor Changes - [#727](https://github.com/t3-oss/create-t3-app/pull/727) [`57cb2b5`](https://github.com/t3-oss/create-t3-app/commit/57cb2b5881c31182acc6918cf86330dda26d0586) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: add warning message when not using latest ## 6.8.1 ### Patch Changes - [#717](https://github.com/t3-oss/create-t3-app/pull/717) [`7f8e706`](https://github.com/t3-oss/create-t3-app/commit/7f8e70643c3de0ba8c457833a54c4700bd974816) Thanks [@robotkutya](https://github.com/robotkutya)! - feat: update NEXTAUTH_SECRET validator and set a default - [#719](https://github.com/t3-oss/create-t3-app/pull/719) [`b359802`](https://github.com/t3-oss/create-t3-app/commit/b359802f128297f0048ae587453031d05e49b566) Thanks [@AyanavaKarmakar](https://github.com/AyanavaKarmakar)! - fix: update NEXTAUTH_SECRET validator to be optional in dev ## 6.8.0 ### Minor Changes - [#673](https://github.com/t3-oss/create-t3-app/pull/673) [`44a107b`](https://github.com/t3-oss/create-t3-app/commit/44a107b8d5345023bafc8a773322e5ee39ba8580) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - remove node 18 and next 13 checks ### Patch Changes - [#684](https://github.com/t3-oss/create-t3-app/pull/684) [`a707c4b`](https://github.com/t3-oss/create-t3-app/commit/a707c4ba9ffedb8991fc14976e352ff83f4502ac) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: version bumps ## 6.7.0 ### Minor Changes - [#667](https://github.com/t3-oss/create-t3-app/pull/667) [`fde5b76`](https://github.com/t3-oss/create-t3-app/commit/fde5b7628860b305c95f70f5cc42ae3c37ad59a9) Thanks [@Pegasust](https://github.com/Pegasust)! - Add documentation on how to generate NEXTAUTH_SECRET in .env file for clarity - [#675](https://github.com/t3-oss/create-t3-app/pull/675) [`5d20745`](https://github.com/t3-oss/create-t3-app/commit/5d207453497c327b5c54cd3a55c824d59b80effe) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - only try to get secret message if there is a user ### Patch Changes - [#676](https://github.com/t3-oss/create-t3-app/pull/676) [`a497e47`](https://github.com/t3-oss/create-t3-app/commit/a497e4765b17e686482ef5b5701766b2299ea0b2) Thanks [@nexxeln](https://github.com/nexxeln)! - update next to 13.0.1 - [#654](https://github.com/t3-oss/create-t3-app/pull/654) [`3dfc27d`](https://github.com/t3-oss/create-t3-app/commit/3dfc27d7edc8dfedee6ed06e9b381cbd791ae0e7) Thanks [@nexxeln](https://github.com/nexxeln)! - Make experimental flags booleans and not require the name to be "double passed" ## 6.6.0 ### Minor Changes - [#657](https://github.com/t3-oss/create-t3-app/pull/657) [`f1673eb`](https://github.com/t3-oss/create-t3-app/commit/f1673ebbf58033746b9a020b7f0b72d976988f84) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: update dependency Next.js to 13 ## 6.5.2 ### Patch Changes - [#649](https://github.com/t3-oss/create-t3-app/pull/649) [`6a21032`](https://github.com/t3-oss/create-t3-app/commit/6a210329136d089d61d41acaf99519223003f606) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - chore: version bumps ## 6.5.1 ### Patch Changes - [#642](https://github.com/t3-oss/create-t3-app/pull/642) [`9639419`](https://github.com/t3-oss/create-t3-app/commit/9639419f1f24f8c3f5418680d9d8a214e16c8cd6) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - get next-auth to work with node 18 ## 6.5.0 ### Minor Changes - [#623](https://github.com/t3-oss/create-t3-app/pull/623) [`cf99211`](https://github.com/t3-oss/create-t3-app/commit/cf992110e7d7e741e56561a728ee0f2dee6fc614) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - consistent cli text color ### Patch Changes - [#619](https://github.com/t3-oss/create-t3-app/pull/619) [`1122720`](https://github.com/t3-oss/create-t3-app/commit/112272083e6130209bfbac0cd350e985dcd9f19f) Thanks [@nexxeln](https://github.com/nexxeln)! - Added `createdAt` and `updatedAt` fields to the Example model in the Prisma schema. - [#621](https://github.com/t3-oss/create-t3-app/pull/621) [`1cc6735`](https://github.com/t3-oss/create-t3-app/commit/1cc67350c626db3c67f012a84d1a73a7a0f03a91) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - fix git spinner ## 6.4.0 ### Minor Changes - [#613](https://github.com/t3-oss/create-t3-app/pull/613) [`135ee5b`](https://github.com/t3-oss/create-t3-app/commit/135ee5b469c575e3fa0d7729a9a545811cc9cf74) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - add inference helper for v10 ### Patch Changes - [#611](https://github.com/t3-oss/create-t3-app/pull/611) [`78a0da3`](https://github.com/t3-oss/create-t3-app/commit/78a0da3d08b9e4468fecbae29cccde7ec43801ab) Thanks [@nexxeln](https://github.com/nexxeln)! - fix(cli): make `installDependencies` step async to make loader spin ## 6.3.0 ### Minor Changes - [#606](https://github.com/t3-oss/create-t3-app/pull/606) [`7f73518`](https://github.com/t3-oss/create-t3-app/commit/7f73518b488b0391f19c8d4d31f583e2be71195a) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - rename index.ts to \_app.ts ## 6.2.1 ### Patch Changes - [#602](https://github.com/t3-oss/create-t3-app/pull/602) [`9f86e39`](https://github.com/t3-oss/create-t3-app/commit/9f86e391fded7bb196ac40b83ac633d996bf035f) Thanks [@nexxeln](https://github.com/nexxeln)! - feat(template): add prettier.Config type to prettier config and resolve prettier-plugin-tailwindcss import ## 6.2.0 ### Minor Changes - [#581](https://github.com/t3-oss/create-t3-app/pull/581) [`dd0e172`](https://github.com/t3-oss/create-t3-app/commit/dd0e172579d0436f5bdb5d29b721dd55362f620e) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - export helper procedures for trpc instead of the `t`-object ## 6.1.2 ### Patch Changes - [#593](https://github.com/t3-oss/create-t3-app/pull/593) [`c8e2753`](https://github.com/t3-oss/create-t3-app/commit/c8e2753b916d15f3ce1103e7fa89dbd046150474) Thanks [@nexxeln](https://github.com/nexxeln)! - chore(template): remove the mention of @next in template readme ## 6.1.1 ### Patch Changes - [#591](https://github.com/t3-oss/create-t3-app/pull/591) [`c90a695`](https://github.com/t3-oss/create-t3-app/commit/c90a6957de7a644e1669762c7a0d19bfbd300715) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - bump next-auth dependency to ^4.12 ## 6.1.0 ### Minor Changes - [#582](https://github.com/t3-oss/create-t3-app/pull/582) [`e562ee8`](https://github.com/t3-oss/create-t3-app/commit/e562ee89401307d92177573a9ead1b45373b63c7) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - move prettier from base to tailwind installer - [#587](https://github.com/t3-oss/create-t3-app/pull/587) [`8c0267f`](https://github.com/t3-oss/create-t3-app/commit/8c0267f98bb7c71e0d90644a9f6af97f1599c723) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - Add .env-example to scaffolded app ## 6.0.1 ### Patch Changes - [#578](https://github.com/t3-oss/create-t3-app/pull/578) [`befc7f4`](https://github.com/t3-oss/create-t3-app/commit/befc7f4f74faeb1538cb6cad6191a1849bf34987) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - upgrade trpc to beta.15 and typescript to 4.8.4 ## 6.0.0 ### Major Changes - [#575](https://github.com/t3-oss/create-t3-app/pull/575) [`75fcb49`](https://github.com/t3-oss/create-t3-app/commit/75fcb49f338fbf783a1b157332a803f7c51048c6) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - upgrades trpc to v10 and improves cli performance by making it synchronous ### Minor Changes - [#575](https://github.com/t3-oss/create-t3-app/pull/575) [`75fcb49`](https://github.com/t3-oss/create-t3-app/commit/75fcb49f338fbf783a1b157332a803f7c51048c6) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - enable vercel_url to pass default env-schema ## 5.15.0 ### Minor Changes - [#533](https://github.com/t3-oss/create-t3-app/pull/533) [`a3a168a`](https://github.com/t3-oss/create-t3-app/commit/a3a168a5a2c54a51e841b2fce5786ec94d72b0fb) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - Tailor .env to only include relevant keys ## 5.14.1 ### Patch Changes - [#510](https://github.com/t3-oss/create-t3-app/pull/510) [`39d766d`](https://github.com/t3-oss/create-t3-app/commit/39d766d3b384c3420c6c058c27be775e961a9319) Thanks [@EkkoKo](https://github.com/EkkoKo)! - fix: resolve target area of `Documentation` links in TechnologyCard ## 5.14.0 ### Minor Changes - [#517](https://github.com/t3-oss/create-t3-app/pull/517) [`2b434bb`](https://github.com/t3-oss/create-t3-app/commit/2b434bb181dc346f299591ddf5465d3175df5c07) Thanks [@gabrielelpidio](https://github.com/gabrielelpidio)! - Add prettier and prettier-plugin-tailwindcss ## 5.13.1 ### Patch Changes - [#506](https://github.com/t3-oss/create-t3-app/pull/506) [`f371ef8`](https://github.com/t3-oss/create-t3-app/commit/f371ef8f0b64205cbbfb5dc6f3416da01a673a71) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - temp fix for next-auth peer deps issue ## 5.13.0 ### Minor Changes - [#485](https://github.com/t3-oss/create-t3-app/pull/485) [`ecc7642`](https://github.com/t3-oss/create-t3-app/commit/ecc764264eff0cf5062fee4f7e4bd9f75975ef41) Thanks [@nexxeln](https://github.com/nexxeln)! - bump next to 12.3.1 and fix AppType ## 5.12.0 ### Minor Changes - [#446](https://github.com/t3-oss/create-t3-app/pull/446) [`6f63ec6`](https://github.com/t3-oss/create-t3-app/commit/6f63ec65201a077e534ec89c4ef18bfa07a30331) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - feat: better cli text when scaffolding in '.' ## 5.11.0 ### Minor Changes - [#430](https://github.com/t3-oss/create-t3-app/pull/430) [`306777a`](https://github.com/t3-oss/create-t3-app/commit/306777a5513c365dfb3525c1484459236734a289) Thanks [@benmarg](https://github.com/benmarg)! - feat: direct developer to add schema from .env to schema.mjs for zod validation ### Patch Changes - [#416](https://github.com/t3-oss/create-t3-app/pull/416) [`14b6d4a`](https://github.com/t3-oss/create-t3-app/commit/14b6d4a0764f57ece45749724c5b12a85544733e) Thanks [@xpressivecode](https://github.com/xpressivecode)! - fix(template): missing ctx when using ssr with trpc ## 5.10.3 ### Patch Changes - [#419](https://github.com/t3-oss/create-t3-app/pull/419) [`554de8f`](https://github.com/t3-oss/create-t3-app/commit/554de8f2838a8d539d81bcb520f121a7e14e3c84) Thanks [@sor4chi](https://github.com/sor4chi)! - Remove unnecessary singlequote in tailwind classname ## 5.10.2 ### Patch Changes - [#387](https://github.com/t3-oss/create-t3-app/pull/387) [`0f8f59c`](https://github.com/t3-oss/create-t3-app/commit/0f8f59c30155ddf4f6be90f3713ff5d5379f0cbb) Thanks [@Vallerem](https://github.com/Vallerem)! - feat: add basic i18n configuration in next.config ## 5.10.1 ### Patch Changes - [#396](https://github.com/t3-oss/create-t3-app/pull/396) [`30f3db6`](https://github.com/t3-oss/create-t3-app/commit/30f3db64c3a81475488c712fa16682076ba3f18b) Thanks [@rhenriquez28](https://github.com/rhenriquez28)! - fixing ssr by forwarding client's headers ## 5.10.0 ### Minor Changes - [#388](https://github.com/t3-oss/create-t3-app/pull/388) [`86d053c`](https://github.com/t3-oss/create-t3-app/commit/86d053c440ed3c5d5101faf8d73574b6d24bf99e) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - feat: non-tw index.tsx with styling * [#381](https://github.com/t3-oss/create-t3-app/pull/381) [`773b27e`](https://github.com/t3-oss/create-t3-app/commit/773b27e1399970992e232975830d327100316e74) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - feat: include all cards on all index.tsx files - [#383](https://github.com/t3-oss/create-t3-app/pull/383) [`e05bc3d`](https://github.com/t3-oss/create-t3-app/commit/e05bc3da9867fcf8d8262b4e8dd976565650cf8f) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - add safeguards for overwriting git repos * [#375](https://github.com/t3-oss/create-t3-app/pull/375) [`296c79b`](https://github.com/t3-oss/create-t3-app/commit/296c79b9617142d727a12e9a5c4250f68f437d9d) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - feat: add option to continue and overwrite on existing directory for reference see: https://github.com/t3-oss/create-t3-app/issues/230 ### Patch Changes - [#394](https://github.com/t3-oss/create-t3-app/pull/394) [`52e9db7`](https://github.com/t3-oss/create-t3-app/commit/52e9db7be8eacbb1e0bb2196647f646f8af617ea) Thanks [@nadilas](https://github.com/nadilas)! - Extending the warning message for invalid client environment variables by including the reason ## 5.9.0 ### Minor Changes - [#366](https://github.com/t3-oss/create-t3-app/pull/366) [`1f43f83`](https://github.com/t3-oss/create-t3-app/commit/1f43f83261c0e6f91b6af0fb39f579845889b724) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - Thanks [@hnqg](https://github.com/hnqg) for the initial PR. add wrapper function for unstable_getServerSession ## 5.8.5 ### Patch Changes - [#357](https://github.com/t3-oss/create-t3-app/pull/357) [`b032074`](https://github.com/t3-oss/create-t3-app/commit/b03207434484d7ec3411b2d7c062a22cf425badc) Thanks [@virtuoushub](https://github.com/virtuoushub)! - chore: Sort package.json when in noInstallMode * [#365](https://github.com/t3-oss/create-t3-app/pull/365) [`b3ce004`](https://github.com/t3-oss/create-t3-app/commit/b3ce004affb9474e53e845e8507f676e3a771de0) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - revert prisma cleint teardown from #353 ## 5.8.4 ### Patch Changes - [#353](https://github.com/t3-oss/create-t3-app/pull/353) [`7a20de9`](https://github.com/t3-oss/create-t3-app/commit/7a20de9f19dfd9ae64143da112a4e3e1bfe30f52) Thanks [@Gejsi](https://github.com/Gejsi)! - Added loggers during development ## 5.8.3 ### Patch Changes - [#345](https://github.com/t3-oss/create-t3-app/pull/345) [`8ee2ea0`](https://github.com/t3-oss/create-t3-app/commit/8ee2ea0c6ca4e49f2bb9b0f5c988471947c8a7ff) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - Include `cjs` and `mjs` files in tsconfig. Thanks @timfee for reporting! ## 5.8.2 ### Patch Changes - [#330](https://github.com/t3-oss/create-t3-app/pull/330) [`5ba03fd`](https://github.com/t3-oss/create-t3-app/commit/5ba03fdf801265668c7bb89b757840ef1f8410f8) Thanks [@nexxeln](https://github.com/nexxeln)! - use auth instead of question while merging the router ## 5.8.1 ### Patch Changes - [#335](https://github.com/t3-oss/create-t3-app/pull/335) [`5745ad7`](https://github.com/t3-oss/create-t3-app/commit/5745ad796ed5647a7103bfb91e16f981152bcbe5) Thanks [@julianread](https://github.com/julianread)! - Fix invalid typeof window comparison to undefined * [#334](https://github.com/t3-oss/create-t3-app/pull/334) [`a107f14`](https://github.com/t3-oss/create-t3-app/commit/a107f14c5981a233d118252a57066bdf1938b96c) Thanks [@c-ehrlich](https://github.com/c-ehrlich)! - fix: build fails on trpc, trpc+next ## 5.8.0 ### Minor Changes - [#324](https://github.com/t3-oss/create-t3-app/pull/324) [`fb5498c`](https://github.com/t3-oss/create-t3-app/commit/fb5498ceda6d3c528c2fe4e9a5ba4adf3dd36b10) Thanks [@juliusmarminge](https://github.com/juliusmarminge)! - feat: wrap createContext with helper function to make it independent of req/res ## 5.7.0 ### Minor Changes - 7a76c95: feat(app): add lint config for typescript ## [5.6.0](https://github.com/t3-oss/create-t3-app/compare/v5.5.0...v5.6.0) (2022-08-11) ### Features - Include ct3a version as metadata in generated app ([#305](https://github.com/t3-oss/create-t3-app/issues/305)) ([0be4a97](https://github.com/t3-oss/create-t3-app/commit/0be4a97fdf1c7bfab78d7be2051c2b21e665c491)) ### Bug Fixes - change ! reversion to equality in env validation ([#293](https://github.com/t3-oss/create-t3-app/issues/293)) ([7822c43](https://github.com/t3-oss/create-t3-app/commit/7822c433df18011f0cc6748e2c499c0e4283386c)) - parse path returned from process.cwd() before using it ([#303](https://github.com/t3-oss/create-t3-app/issues/303)) ([b55f196](https://github.com/t3-oss/create-t3-app/commit/b55f196d0146c5c3969de4c25e7732046aad6284)) ## [5.5.0](https://github.com/t3-oss/create-t3-app/compare/v5.4.0...v5.5.0) (2022-08-07) ### Features - make session.user.id non-nullable ([#282](https://github.com/t3-oss/create-t3-app/issues/282)) ([6a81f03](https://github.com/t3-oss/create-t3-app/commit/6a81f03bfafdf77c1c82b7972a97e3e54d77935c)) ## [5.4.0](https://github.com/t3-oss/create-t3-app/compare/v5.3.0...v5.4.0) (2022-08-02) ### Features - add deployment strategy to generated README ([#258](https://github.com/t3-oss/create-t3-app/issues/258)) ([c7d4dce](https://github.com/t3-oss/create-t3-app/commit/c7d4dce182b1c306dc2a41d42483994c336843f4)) - set appName to directory on 'npx create-t3-app .' ([#273](https://github.com/t3-oss/create-t3-app/issues/273)) ([2179f2d](https://github.com/t3-oss/create-t3-app/commit/2179f2d38f74de0e3fda5661f5125d049eb4d238)) ### Bug Fixes - height on small screens ([#256](https://github.com/t3-oss/create-t3-app/issues/256)) ([fe86915](https://github.com/t3-oss/create-t3-app/commit/fe86915ced72dc979e5fe68bd56b038d9c714928)) - missing next-auth types ([#255](https://github.com/t3-oss/create-t3-app/issues/255)) ([ed42629](https://github.com/t3-oss/create-t3-app/commit/ed42629be5fb9be72abd40bbe784f2bda99eec99)), closes [#254](https://github.com/t3-oss/create-t3-app/issues/254) - **prisma:** make db changes for next-auth on mysql obvious ([#275](https://github.com/t3-oss/create-t3-app/issues/275)) ([e9507c8](https://github.com/t3-oss/create-t3-app/commit/e9507c887e135f3e44357c618689f5cdb2f13c43)) ## [5.3.0](https://github.com/t3-oss/create-t3-app/compare/v5.2.1...v5.3.0) (2022-07-25) ### Features - add typesafe client side env variables ([#209](https://github.com/t3-oss/create-t3-app/issues/209)) ([d4cf879](https://github.com/t3-oss/create-t3-app/commit/d4cf879df811ded012a5f7cb3d91ff0bb95d87ac)) - improved logo typography ([#238](https://github.com/t3-oss/create-t3-app/issues/238)) ([48c6720](https://github.com/t3-oss/create-t3-app/commit/48c67207da48c901822e214a00285c99bb785fd8)) - updating tailwind and postcss config's to use .cjs ([#242](https://github.com/t3-oss/create-t3-app/issues/242)) ([5b97367](https://github.com/t3-oss/create-t3-app/commit/5b973670cae729c9e93b6c3c6f0b3e5b48dda904)) ### Bug Fixes - added JSDoc type to clientEnv in all env-schema ([#240](https://github.com/t3-oss/create-t3-app/issues/240)) ([9cb5ebb](https://github.com/t3-oss/create-t3-app/commit/9cb5ebbb2a0147ce15726c7e1a6c6be4065bc2dc)) - clarify some comments and rename some files in env ([#245](https://github.com/t3-oss/create-t3-app/issues/245)) ([2048783](https://github.com/t3-oss/create-t3-app/commit/2048783bd26f7f0522357a3ab1e74e6f560a221c)) - remove semicolon in \_app.tsx with next-auth to avoid early return ([1be7713](https://github.com/t3-oss/create-t3-app/commit/1be771393628631b818b46d3f3bf1ca41ad99dce)) - title being offset when using yarn/pnpm ([c881f00](https://github.com/t3-oss/create-t3-app/commit/c881f00430f606040a448eecc41e779ab0c728a7)) ### [5.2.1](https://github.com/t3-oss/create-t3-app/compare/v5.2.0...v5.2.1) (2022-07-19) ### Bug Fixes - update url in auth prisma schema ([97c9e27](https://github.com/t3-oss/create-t3-app/commit/97c9e279bf7465a47f29add10c8a6d4c9b7339a5)) ## [5.2.0](https://github.com/t3-oss/create-t3-app/compare/v5.1.1...v5.2.0) (2022-07-19) ### Features - add db.sqlite-journal to gitignore ([#221](https://github.com/t3-oss/create-t3-app/issues/221)) ([0604d21](https://github.com/t3-oss/create-t3-app/commit/0604d217f0dfe3d165013395c1a801e6bc32a17c)) ### Bug Fixes - handle pkg with version in noInstall mode ([#220](https://github.com/t3-oss/create-t3-app/issues/220)) ([5737beb](https://github.com/t3-oss/create-t3-app/commit/5737bebaab3ff6f955bf825274e25419a91642d6)) - missing DATABASE_URL ([#222](https://github.com/t3-oss/create-t3-app/issues/222)) ([b6164d8](https://github.com/t3-oss/create-t3-app/commit/b6164d8944330564e9911e47f36d82620986c8c5)) - typo - rename 'varibles' to 'variables' ([#223](https://github.com/t3-oss/create-t3-app/issues/223)) ([fc6519f](https://github.com/t3-oss/create-t3-app/commit/fc6519f66d067e4e348c7f4c00f6287dc5c40c3d)) - typo in readme ([9390400](https://github.com/t3-oss/create-t3-app/commit/9390400b8720240c650e5dbde7ca0718b7835757)) ### [5.1.1](https://github.com/t3-oss/create-t3-app/compare/v5.1.0...v5.1.1) (2022-07-18) ### Bug Fixes - add missing trpc import ([37b6283](https://github.com/t3-oss/create-t3-app/commit/37b6283bb56e44092fc9ef2d65c84e5298e51d22)) ## [5.1.0](https://github.com/t3-oss/create-t3-app/compare/v5.0.0...v5.1.0) (2022-07-18) ### Features - add trpc inference helpers ([1fcec81](https://github.com/t3-oss/create-t3-app/commit/1fcec81a74372ce5cfaf6503d63f16d5e2b12e3c)) - migrate env + next config to esm ([#205](https://github.com/t3-oss/create-t3-app/issues/205)) ([59f8ad4](https://github.com/t3-oss/create-t3-app/commit/59f8ad4f2104aa1f11508b55da75c333dce03625)) ### Bug Fixes - **#218:** lock react-query to v3 ([15ea5f1](https://github.com/t3-oss/create-t3-app/commit/15ea5f1dc8b86430a720f2eaeb6ccdfaab629892)), closes [#218](https://github.com/t3-oss/create-t3-app/issues/218) - move gitignore rename call ([#215](https://github.com/t3-oss/create-t3-app/issues/215)) ([7111033](https://github.com/t3-oss/create-t3-app/commit/7111033d75c858041f06d69e95c51e40515457fc)) ## 5.0.0 (2022-07-16) ### Features - add check for yarn 3 ([#173](https://github.com/t3-oss/create-t3-app/issues/173)) ([cd846e1](https://github.com/t3-oss/create-t3-app/commit/cd846e16123a6bb11549276030224ec4ff1223df)) - add ci for pull requests ([0e7cdc8](https://github.com/t3-oss/create-t3-app/commit/0e7cdc8bd307e17e95bd1874af2d0eb43211530e)) - add commitlint ([#126](https://github.com/t3-oss/create-t3-app/issues/126)) ([3672aa3](https://github.com/t3-oss/create-t3-app/commit/3672aa3f4482ef592022cd94f1300b81e3fa7429)) - add CONTRIBUTING.md for newcomers ([99f8220](https://github.com/t3-oss/create-t3-app/commit/99f8220f83a6363e5aab6c0ec33260eb059c9643)) - add db.sqlite to gitignore ([#201](https://github.com/t3-oss/create-t3-app/issues/201)) ([90371c7](https://github.com/t3-oss/create-t3-app/commit/90371c7f88258b4b3e9a2689c09f67a9e439b189)) - add environment variables validation ([#147](https://github.com/t3-oss/create-t3-app/issues/147)) ([0f0b56d](https://github.com/t3-oss/create-t3-app/commit/0f0b56d2604c1ba9dccd8d2a5213f0d2a640cd68)) - add git prompt ([#130](https://github.com/t3-oss/create-t3-app/issues/130)) ([e5be271](https://github.com/t3-oss/create-t3-app/commit/e5be271612db741e3a692cd780ef84455faa115c)) - add inferQueryResponse helper for tRPC ([748800f](https://github.com/t3-oss/create-t3-app/commit/748800f060031da58ce2b45bd65e4fd83266ce5c)) - add main as initial branch ([7b94005](https://github.com/t3-oss/create-t3-app/commit/7b94005fd515d140176e639a487ab5035a290125)) - add next-auth config to env-example [#108](https://github.com/t3-oss/create-t3-app/issues/108) ([c3ab997](https://github.com/t3-oss/create-t3-app/commit/c3ab997f149ee3635d2d33a67650e95e04877d5b)) - add noInstall flag and prompt ([#133](https://github.com/t3-oss/create-t3-app/issues/133)) ([7ff0fca](https://github.com/t3-oss/create-t3-app/commit/7ff0fcacc3b94a4fd15d0db25e2efc09131d73c9)) - add standard-version ([#125](https://github.com/t3-oss/create-t3-app/issues/125)) ([0fc6b47](https://github.com/t3-oss/create-t3-app/commit/0fc6b479c99c76ab830a81d011ba0e4ef90ce849)) - add tRPC inference helper docs commment on utils ([3e784cb](https://github.com/t3-oss/create-t3-app/commit/3e784cbf3f9b4f6eeb4441a881ebbd028f311957)) - added windows build support ([29acbf8](https://github.com/t3-oss/create-t3-app/commit/29acbf816c69f980454568946fba1afea6be114c)) - better ui for templates ([e2e8c7e](https://github.com/t3-oss/create-t3-app/commit/e2e8c7eeeab576d730cd1c7e397497460c1787e0)) - better validation and add path support ([#135](https://github.com/t3-oss/create-t3-app/issues/135)) ([e50cb8a](https://github.com/t3-oss/create-t3-app/commit/e50cb8a3e13126de628d098e00650a52753681f3)) - check if dir is empty ([71bbdba](https://github.com/t3-oss/create-t3-app/commit/71bbdbae564d6e7df4b08645db59a8364b891b99)) - make the ui symmetric ([19df71e](https://github.com/t3-oss/create-t3-app/commit/19df71ec53fd64e60b27ecd055cd9f0c6acc4a63)) ### Bug Fixes - add newline at the end of the file ([64eee1c](https://github.com/t3-oss/create-t3-app/commit/64eee1c8058aa5d2ec29fc1903ee63819fa8fc9d)) - add scripts for CI ([539f3fd](https://github.com/t3-oss/create-t3-app/commit/539f3fdc44e1d96e5429f21afbb74f71bf4d963d)) - add semantic elements and update tw classes on tw pages ([#151](https://github.com/t3-oss/create-t3-app/issues/151)) ([61d6819](https://github.com/t3-oss/create-t3-app/commit/61d6819f14ac5b65c779081c91ca894f724f7b16)) - bumped package.json for beta-release ([3fe35f1](https://github.com/t3-oss/create-t3-app/commit/3fe35f14313da611cb16e26141e06d1afb5efb78)) - change comment in .env to discord ([d992713](https://github.com/t3-oss/create-t3-app/commit/d992713cad08c08267a0f018fa8582b569a554db)) - **cli:** use correct spelling, fix short choices ([7776aa9](https://github.com/t3-oss/create-t3-app/commit/7776aa9fc03293f27d9e27808cd03810f96ccc33)) - conditionally run envVars installer ([#196](https://github.com/t3-oss/create-t3-app/issues/196)) ([f5f4f62](https://github.com/t3-oss/create-t3-app/commit/f5f4f6275c1a967f6de2e7c6d40d9a7731a3830b)) - docs wrong format ([3d2dbb0](https://github.com/t3-oss/create-t3-app/commit/3d2dbb06cadb42d24f134ce09edc5a371d79aff9)) - dont prompt for typesafe env-vars ([2faee56](https://github.com/t3-oss/create-t3-app/commit/2faee56720c39d770ebe05d15e3fb2aaacaf704e)) - eslint warning (import/no-anonymous-default-export) ([a1cce55](https://github.com/t3-oss/create-t3-app/commit/a1cce5546a8fd6efb79c65a2f2b52d4c597dafd0)) - fixed some typos ([#131](https://github.com/t3-oss/create-t3-app/issues/131)) ([a86466e](https://github.com/t3-oss/create-t3-app/commit/a86466eb0bc1ef51a1c8ba1b96938dc1bd7dfbbb)) - format contributing.md ([4111476](https://github.com/t3-oss/create-t3-app/commit/41114766582ca33fb2a3fd0b566e609a6dd3e861)) - handle git projects correctly ([3ed897f](https://github.com/t3-oss/create-t3-app/commit/3ed897ff49c06f6d70d49aee31d5c3741fced96e)) - incompatible git version ([#127](https://github.com/t3-oss/create-t3-app/issues/127)) ([34f44f8](https://github.com/t3-oss/create-t3-app/commit/34f44f817df52b317afea5b8d55c87eea487833f)) - invalid pnpm action version ([2a1f6e3](https://github.com/t3-oss/create-t3-app/commit/2a1f6e32aedb8f86ee7ca403e481be8e474b76b2)) - language prompt short answer should both be ts ([#142](https://github.com/t3-oss/create-t3-app/issues/142)) ([5e4f80d](https://github.com/t3-oss/create-t3-app/commit/5e4f80d7aa930f16add3bb2273a23f5829548f49)) - **markup:** multiple h1 replaced to h2 ([6bf9040](https://github.com/t3-oss/create-t3-app/commit/6bf90407b9a9774348a77927d37c47f3ded5552a)) - **prompts:** show correct package manager in prompts ([#153](https://github.com/t3-oss/create-t3-app/issues/153)) ([fe66ae9](https://github.com/t3-oss/create-t3-app/commit/fe66ae9f60cbe6a2e429be406a1cd548c51e7bbd)) - properly display the welcome title when installing with Yarn ([#160](https://github.com/t3-oss/create-t3-app/issues/160)) ([a5b9d53](https://github.com/t3-oss/create-t3-app/commit/a5b9d534e6395e494dcfab8419e71bf201b7326b)) - provide cwd inside git init exec call ([9679492](https://github.com/t3-oss/create-t3-app/commit/9679492ebd9175973c99d647cf11a4f1b979a505)) - reapply style fixes on tw pages ([#189](https://github.com/t3-oss/create-t3-app/issues/189)) ([af1067b](https://github.com/t3-oss/create-t3-app/commit/af1067b196205b0dd70a44571c62012d9667987e)), closes [#178](https://github.com/t3-oss/create-t3-app/issues/178) - remove changeset from repo ([#134](https://github.com/t3-oss/create-t3-app/issues/134)) ([d58b380](https://github.com/t3-oss/create-t3-app/commit/d58b380ed4310021a771e05037a6054176c14b91)) - remove duplicated types ([#138](https://github.com/t3-oss/create-t3-app/issues/138)) ([e4c8e33](https://github.com/t3-oss/create-t3-app/commit/e4c8e336fff94bd125cac06f4bfa4d5eb365dd54)) - remove unncesary import on auth router ([abdc61f](https://github.com/t3-oss/create-t3-app/commit/abdc61ffc18dd4f1e82f3fc02cd87a2144d0cc03)) - run prettier ([9961645](https://github.com/t3-oss/create-t3-app/commit/99616456d54abde5b458d57add1132dcb2b68256)) - standardize log messages ([83a13b9](https://github.com/t3-oss/create-t3-app/commit/83a13b9963f83317a62e3eeaed14dd5e2cb45344)) - **template:** converting to use tech props and remove tw from template ([#183](https://github.com/t3-oss/create-t3-app/issues/183)) ([b2d542a](https://github.com/t3-oss/create-t3-app/commit/b2d542a44eed0aa699fe91e52f25f6ac68a0021c)) - **templates:** h1 restored ([e51e03d](https://github.com/t3-oss/create-t3-app/commit/e51e03d93a899674bef839ddb215a36e62397e26)) - type entrire app and not just the props ([908630e](https://github.com/t3-oss/create-t3-app/commit/908630e5cc14789b88a0ddf6dd097fb14eb9068a)) - typo in "Javascript" ([#136](https://github.com/t3-oss/create-t3-app/issues/136)) ([6873a7a](https://github.com/t3-oss/create-t3-app/commit/6873a7a5ff17c6efda1b78fe73597ec43821552c)) - update templates ([4418f1d](https://github.com/t3-oss/create-t3-app/commit/4418f1db06a29050293f0d4dd16a1347d34c6cf9)) - use components for the tech within the splash screen ([#178](https://github.com/t3-oss/create-t3-app/issues/178)) ([c436382](https://github.com/t3-oss/create-t3-app/commit/c4363821664fe95b4301d46b3e2d34fbeb14952d)) - with-tw removed unused styles and added motion saftey ([#148](https://github.com/t3-oss/create-t3-app/issues/148)) ([598ed0e](https://github.com/t3-oss/create-t3-app/commit/598ed0eda73e5218658ff7d574e28ecc25a4e2cb)) ## 4.4.1 ### Patch Changes - changed ui for template ## 4.4.0 ### Minor Changes - improve ui examples of template app ## 4.3.5 ### Patch Changes - add warning for old git versions ## 4.3.4 ### Patch Changes - improve template styling ## 4.3.3 ### Patch Changes - Add description for packages in the template ## 4.3.2 ### Patch Changes - Redesign pages ## 4.3.0 ### Minor Changes - added Next-Auth protected routes when using tRPC ## 4.2.12 ### Patch Changes - upgrade next to 12.2.0 ## 4.2.11 ### Patch Changes - modified the title to print a colorful ascii art ## 4.2.10 ### Patch Changes - add warning about next-auth when using Node 18 ## 4.2.9 ### Patch Changes - fix resolved path to package root ## 4.2.8 ### Patch Changes - diable version flag ## 4.2.2 ### Patch Changes - fix esm issues ## 4.2.1 ### Patch Changes - trying to fix esm error ## 4.2.0 ### Minor Changes - a548f22: added cli args, esm support ## 4.1.1 ### Patch Changes - Updated package to full ESM support ## 4.1.0 ### Minor Changes - Create-t3-app now accepts args! When calling create-t3-app, you can specify the app name and directory directly from the cli. Additonal options are visable by passing -h or --help into the command line ## 4.0.8 ### Patch Changes - added loading spinners ## 4.0.7 ### Patch Changes - added tsup as build tool ## 4.0.6 ### Patch Changes - Added dev tooling. ================================================ FILE: cli/README.md ================================================

Logo for T3

create-t3-app

Interactive CLI to start a full-stack, typesafe Next.js app.

Get started with the T3 Stack by running npm create t3-app@latest

[![PRs-Welcome][contribute-image]][contribute-url] [![Discord](https://img.shields.io/discord/966627436387266600?color=%235865F2&label=Discord&logo=discord&logoColor=%23fff)](https://t3.gg/discord) [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url]

Video thumbnail of Theo with an indecipherable expression on his face

Watch Theo's overview on Youtube here

## Table of contents - The T3 Stack - T3 Axioms - Getting Started - Community - Contributors

The T3 Stack

The _"T3 Stack"_ is a web development stack made by [Theo](https://twitter.com/t3dotgg) focused on **simplicity**, **modularity**, and **full-stack typesafety**. It consists of: - [Next.js](https://nextjs.org) - [tRPC](https://trpc.io) - [Tailwind CSS](https://tailwindcss.com) - [TypeScript](https://typescriptlang.org) - [Prisma](https://prisma.io) - [Drizzle](https://orm.drizzle.team) - [NextAuth.js](https://next-auth.js.org) ### So... what is `create-t3-app`? A template? Kind of? `create-t3-app` is a CLI built by seasoned T3 Stack devs to streamline the setup of a modular T3 Stack app. This means each piece is optional, and the "template" is generated based on your specific needs. After countless projects and many years on this tech, we have lots of opinions and insights. We’ve done our best to encode them into this CLI. This is **NOT** an all-inclusive template. We **expect** you to bring your own libraries that solve the needs of **YOUR** application. While we don’t want to prescribe solutions to more specific problems like state management and deployment, we [do have some recommendations listed here](https://create.t3.gg/en/other-recs).

T3 Axioms

We'll be frank - this is an _opinionated project_. We share a handful of core beliefs around building and we treat them as the basis for our decisions. ### 1. Solve Problems It's easy to fall into the trap of "adding everything" - we explicitly _don't_ want to do that. Everything added to `create-t3-app` should solve a _specific_ problem that exists within the core technologies included. This means we **won't** add things like state libraries (`zustand`, `redux`) but we **will** add things like NextAuth.js and integrate Prisma and tRPC for you. ### 2. Bleed Responsibly We love our bleeding edge tech. The amount of speed and, honestly, _fun_ that comes out of new shit is really cool. We think it's important to **bleed responsibly**, using riskier tech in the less risky parts. This means we **wouldn't** ⛔️ bet on risky new database tech (SQL is great!). But we **happily** ✅ bet on tRPC since it's just functions that are trivial to move off. ### 3. Typesafety Isn't Optional The stated goal of `create-t3-app` is to provide the quickest way to start a new full-stack, typesafe web application. We take typesafety seriously in these parts as it improves our productivity and helps us ship fewer bugs. Any decision that compromises the typesafe nature of `create-t3-app` is a decision that should be made in a different project.

Getting Started

To scaffold an app using `create-t3-app`, run any of the following four commands and answer the command prompt questions: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` For more advanced usage, check out the [CLI docs](https://create.t3.gg/en/installation).

Community

For help, discussion about best practices, or any other conversation that would benefit create-t3-app: [Join the T3 Discord Server](https://t3.gg/discord)

Contributors

We 💖 contributors! Feel free to contribute to this project but **please read the [Contributing Guidelines](CONTRIBUTING.md) before opening an issue or PR** so you understand the branching strategy and local development environment. We also welcome you to join our [Discord](https://discord.gg/tEAQjDseSX) community for either support or contributing guidance.

A table of avatars from the project's contributors

Made with contrib.rocks

Powered by vercel

[downloads-image]: https://img.shields.io/npm/dm/create-t3-app?color=364fc7&logoColor=364fc7 [npm-url]: https://www.npmjs.com/package/create-t3-app [npm-image]: https://img.shields.io/npm/v/create-t3-app?color=0b7285&logoColor=0b7285 [contribute-url]: https://github.com/t3-oss/create-t3-app/blob/main/CONTRIBUTING.md [contribute-image]: https://img.shields.io/badge/PRs-welcome-blue.svg ================================================ FILE: cli/eslint.config.js ================================================ // @ts-check import tseslint from "typescript-eslint"; // import isaacscript from "eslint-plugin-isaacscript"; import importPlugin from "eslint-plugin-import-x"; export default tseslint.config( { ignores: ["dist", "*.config.*"] }, { files: ["**/*.{ts,tsx}"], plugins: { // isaacscript, // REENABLE "import-x": importPlugin, }, extends: [ ...tseslint.configs.recommended, ], rules: { "@typescript-eslint/no-unused-vars": [ "error", { argsIgnorePattern: "^_", destructuredArrayIgnorePattern: "^_" }, ], "@typescript-eslint/consistent-type-imports": [ "error", { prefer: "type-imports", fixStyle: "inline-type-imports" }, ], "import-x/consistent-type-specifier-style": ["error", "prefer-inline"], // // For educational purposes we format our comments/jsdoc nicely // "isaacscript/complete-sentences-jsdoc": "warn", // "isaacscript/format-jsdoc-comments": "warn", // These lint rules don't make sense for us but are enabled in the preset configs "@typescript-eslint/no-confusing-void-expression": "off", "@typescript-eslint/restrict-template-expressions": "off", // This rule doesn't seem to be working properly "@typescript-eslint/prefer-nullish-coalescing": "off", }, }, { files: ["src/**/*.{ts,tsx}"], extends: [ ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked, ], rules: { "@typescript-eslint/no-explicit-any": "error", }, }, { // Template files don't have reliable type information files: ["./template/**/*.{ts,tsx}"], extends: [tseslint.configs.disableTypeChecked], }, { linterOptions: { reportUnusedDisableDirectives: true, }, languageOptions: { sourceType: "module", parserOptions: { project: [ "./tsconfig.eslint.json", ], // projectService: { // defaultProject: "tsconfig.eslint.json", // }, tsconfigRootDir: import.meta.dirname, }, }, }, ); ================================================ FILE: cli/package.json ================================================ { "name": "create-t3-app", "version": "7.40.0", "description": "Create web application with the t3 stack", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/t3-oss/create-t3-app.git", "directory": "cli" }, "keywords": [ "create-t3-app", "init.tips", "next.js", "t3-stack", "tailwind", "tRPC", "typescript" ], "type": "module", "exports": "./dist/index.js", "bin": { "create-t3-app": "./dist/index.js" }, "files": [ "dist", "template", "README.md", "LICENSE", "CHANGELOG.md", ".yarnrc.yml", "package.json" ], "engines": { "node": ">=18.17.0" }, "scripts": { "typecheck": "tsc", "build": "tsup", "dev": "tsup --watch", "clean": "rm -rf dist .turbo node_modules", "start": "node dist/index.js", "lint": "eslint", "lint:fix": "pnpm lint --fix", "format": "prettier '**/*.{cjs,mjs,ts,tsx,md,json}' --ignore-path ../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --write", "format:check": "prettier '**/*.{cjs,mjs,ts,tsx,md,json}' --ignore-path ../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --check", "release": "changeset version", "pub:beta": "pnpm build && npm publish --tag beta", "pub:next": "pnpm build && npm publish --tag next", "pub:release": "pnpm build && npm publish" }, "dependencies": { "@clack/core": "^0.3.4", "@clack/prompts": "^0.6.3", "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "chalk": "5.2.0", "commander": "^10.0.1", "execa": "^7.2.0", "fs-extra": "^11.2.0", "gradient-string": "^2.0.2", "ora": "6.3.1", "sort-package-json": "^2.10.0" }, "devDependencies": { "@auth/drizzle-adapter": "^1.1.0", "@auth/prisma-adapter": "^1.6.0", "@libsql/client": "^0.14.0", "@planetscale/database": "^1.19.0", "@prisma/adapter-planetscale": "^6.6.0", "@prisma/client": "^6.6.0", "@t3-oss/env-nextjs": "^0.12.0", "@tailwindcss/postcss": "^4.0.15", "@tanstack/react-query": "^5.69.0", "@trpc/client": "11.0.0", "@trpc/next": "11.0.0", "@trpc/react-query": "11.0.0", "@trpc/server": "11.0.0", "@types/fs-extra": "^11.0.4", "@types/gradient-string": "^1.1.6", "@types/node": "^24.10.1", "better-auth": "^1.3", "drizzle-kit": "^0.30.5", "drizzle-orm": "^0.41.0", "mysql2": "^3.11.0", "next": "^15.5.9", "next-auth": "^4.24.7", "postgres": "^3.4.4", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", "prisma": "^6.6.0", "react": "^19.2.3", "react-dom": "^19.2.3", "superjson": "^2.2.1", "tailwindcss": "^4.0.15", "tsup": "^6.7.0", "type-fest": "^3.13.1", "typescript": "^5.8.2", "zod": "^3.24.2" } } ================================================ FILE: cli/prettier.config.mjs ================================================ import baseConfig from "../prettier.config.mjs"; /** * @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions & * import("@ianvs/prettier-plugin-sort-imports").PluginConfig} */ const config = { ...baseConfig, arrowParens: "always", plugins: [ "@ianvs/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss", ], tailwindConfig: "./template/extras/config/tailwind.config.ts", trailingComma: "es5", importOrder: ["", "", "^~/", "^[.][.]/", "^[.]/"], importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], importOrderTypeScriptVersion: "4.4.0", }; export default config; ================================================ FILE: cli/src/cli/index.ts ================================================ import * as p from "@clack/prompts"; import chalk from "chalk"; import { Command } from "commander"; import { CREATE_T3_APP, DEFAULT_APP_NAME } from "~/consts.js"; import { databaseProviders, type AvailablePackages, type DatabaseProvider, } from "~/installers/index.js"; import { getVersion } from "~/utils/getT3Version.js"; import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; import { IsTTYError } from "~/utils/isTTYError.js"; import { logger } from "~/utils/logger.js"; import { validateAppName } from "~/utils/validateAppName.js"; import { validateImportAlias } from "~/utils/validateImportAlias.js"; interface CliFlags { noGit: boolean; noInstall: boolean; default: boolean; importAlias: string; /** @internal Used in CI. */ CI: boolean; /** @internal Used in CI. */ tailwind: boolean; /** @internal Used in CI. */ trpc: boolean; /** @internal Used in CI. */ prisma: boolean; /** @internal Used in CI. */ drizzle: boolean; /** @internal Used in CI. */ nextAuth: boolean; /** @internal Used in CI. */ betterAuth: boolean; /** @internal Used in CI. */ appRouter: boolean; /** @internal Used in CI. */ dbProvider: DatabaseProvider; /** @internal Used in CI */ eslint: boolean; /** @internal Used in CI */ biome: boolean; } interface CliResults { appName: string; packages: AvailablePackages[]; flags: CliFlags; databaseProvider: DatabaseProvider; } const defaultOptions: CliResults = { appName: DEFAULT_APP_NAME, packages: ["nextAuth", "prisma", "tailwind", "trpc", "eslint"], flags: { noGit: false, noInstall: false, default: false, CI: false, tailwind: false, trpc: false, prisma: false, drizzle: false, nextAuth: false, betterAuth: false, importAlias: "~/", appRouter: false, dbProvider: "sqlite", eslint: false, biome: false, }, databaseProvider: "sqlite", }; export const runCli = async (): Promise => { const cliResults = defaultOptions; const program = new Command() .name(CREATE_T3_APP) .description("A CLI for creating web applications with the t3 stack") .argument( "[dir]", "The name of the application, as well as the name of the directory to create" ) .option( "--noGit", "Explicitly tell the CLI to not initialize a new git repo in the project", false ) .option( "--noInstall", "Explicitly tell the CLI to not run the package manager's install command", false ) .option( "-y, --default", "Bypass the CLI and use all default options to bootstrap a new t3-app", false ) /** START CI-FLAGS */ /** * @experimental Used for CI E2E tests. If any of the following option-flags are provided, we * skip prompting. */ .option("--CI", "Boolean value if we're running in CI", false) /** @experimental - Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "--tailwind [boolean]", "Experimental: Boolean value if we should install Tailwind CSS. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** @experimental Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "--nextAuth [boolean]", "Experimental: Boolean value if we should install NextAuth.js. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** @experimental Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "--betterAuth [boolean]", "Experimental: Boolean value if we should install BetterAuth. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** @experimental - Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "--prisma [boolean]", "Experimental: Boolean value if we should install Prisma. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** @experimental - Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "--drizzle [boolean]", "Experimental: Boolean value if we should install Drizzle. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** @experimental - Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "--trpc [boolean]", "Experimental: Boolean value if we should install tRPC. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** @experimental - Used for CI E2E tests. Used in conjunction with `--CI` to skip prompting. */ .option( "-i, --import-alias [alias]", "Explicitly tell the CLI to use a custom import alias", defaultOptions.flags.importAlias ) .option( "--dbProvider [provider]", `Choose a database provider to use. Possible values: ${databaseProviders.join( ", " )}`, defaultOptions.flags.dbProvider ) .option( "--appRouter [boolean]", "Explicitly tell the CLI to use the new Next.js app router", (value) => !!value && value !== "false" ) .option( "--eslint [boolean]", "Experimental: Boolean value if we should install eslint and prettier. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) .option( "--biome [boolean]", "Experimental: Boolean value if we should install biome. Must be used in conjunction with `--CI`.", (value) => !!value && value !== "false" ) /** END CI-FLAGS */ .version(getVersion(), "-v, --version", "Display the version number") .addHelpText( "afterAll", `\n The t3 stack was inspired by ${chalk .hex("#E8DCFF") .bold( "@t3dotgg" )} and has been used to build awesome fullstack applications like ${chalk .hex("#E24A8D") .underline("https://ping.gg")} \n` ) .parse(process.argv); // FIXME: TEMPORARY WARNING WHEN USING YARN 3. SEE ISSUE #57 if (process.env.npm_config_user_agent?.startsWith("yarn/3")) { logger.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported, and likely to result in a crash. Please run create-t3-app with another package manager such as pnpm, npm, or Yarn Classic. See: https://github.com/t3-oss/create-t3-app/issues/57`); } // Needs to be separated outside the if statement to correctly infer the type as string | undefined const cliProvidedName = program.args[0]; if (cliProvidedName) { cliResults.appName = cliProvidedName; } cliResults.flags = program.opts(); /** @internal Used for CI E2E tests. */ if (cliResults.flags.CI) { cliResults.packages = []; if (cliResults.flags.trpc) cliResults.packages.push("trpc"); if (cliResults.flags.tailwind) cliResults.packages.push("tailwind"); if (cliResults.flags.prisma) cliResults.packages.push("prisma"); if (cliResults.flags.drizzle) cliResults.packages.push("drizzle"); if (cliResults.flags.nextAuth) cliResults.packages.push("nextAuth"); if (cliResults.flags.betterAuth) cliResults.packages.push("betterAuth"); if (cliResults.flags.eslint) cliResults.packages.push("eslint"); if (cliResults.flags.biome) cliResults.packages.push("biome"); if (cliResults.flags.prisma && cliResults.flags.drizzle) { // We test a matrix of all possible combination of packages in CI. Checking for impossible // combinations here and exiting gracefully is easier than changing the CI matrix to exclude // invalid combinations. We are using an "OK" exit code so CI continues with the next combination. logger.warn("Incompatible combination Prisma + Drizzle. Exiting."); process.exit(0); } if (cliResults.flags.biome && cliResults.flags.eslint) { logger.warn("Incompatible combination Biome + ESLint. Exiting."); process.exit(0); } if (cliResults.flags.nextAuth && cliResults.flags.betterAuth) { logger.warn("Incompatible combination NextAuth + BetterAuth. Exiting."); process.exit(0); } if (databaseProviders.includes(cliResults.flags.dbProvider) === false) { logger.warn( `Incompatible database provided. Use: ${databaseProviders.join(", ")}. Exiting.` ); process.exit(0); } cliResults.databaseProvider = cliResults.packages.includes("drizzle") || cliResults.packages.includes("prisma") ? cliResults.flags.dbProvider : "sqlite"; return cliResults; } if (cliResults.flags.default) { return cliResults; } // Explained below why this is in a try/catch block try { if (process.env.TERM_PROGRAM?.toLowerCase().includes("mintty")) { logger.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal. Alternatively, you can provide the arguments from the CLI directly: https://create.t3.gg/en/installation#experimental-usage to skip the prompts.`); throw new IsTTYError("Non-interactive environment"); } // if --CI flag is set, we are running in CI mode and should not prompt the user const pkgManager = getUserPkgManager(); const project = await p.group( { ...(!cliProvidedName && { name: () => p.text({ message: "What will your project be called?", defaultValue: cliProvidedName, validate: validateAppName, }), }), language: () => { return p.select({ message: "Will you be using TypeScript or JavaScript?", options: [ { value: "typescript", label: "TypeScript" }, { value: "javascript", label: "JavaScript" }, ], initialValue: "typescript", }); }, _: ({ results }) => results.language === "javascript" ? p.note(chalk.redBright("Wrong answer, using TypeScript instead")) : undefined, styling: () => { return p.confirm({ message: "Will you be using Tailwind CSS for styling?", }); }, trpc: () => { return p.confirm({ message: "Would you like to use tRPC?", }); }, authentication: () => { return p.select({ message: "What authentication provider would you like to use?", options: [ { value: "none", label: "None" }, { value: "next-auth", label: "NextAuth.js" }, { value: "better-auth", label: "BetterAuth" }, // Maybe later // { value: "clerk", label: "Clerk" }, ], initialValue: "none", }); }, database: () => { return p.select({ message: "What database ORM would you like to use?", options: [ { value: "none", label: "None" }, { value: "prisma", label: "Prisma" }, { value: "drizzle", label: "Drizzle" }, ], initialValue: "none", }); }, appRouter: () => { return p.confirm({ message: "Would you like to use Next.js App Router?", initialValue: true, }); }, databaseProvider: ({ results }) => { if (results.database === "none") return; return p.select({ message: "What database provider would you like to use?", options: [ { value: "sqlite", label: "SQLite (LibSQL)" }, { value: "mysql", label: "MySQL" }, { value: "postgres", label: "PostgreSQL" }, { value: "planetscale", label: "PlanetScale" }, ], initialValue: "sqlite", }); }, linter: () => { return p.select({ message: "Would you like to use ESLint and Prettier or Biome for linting and formatting?", options: [ { value: "eslint", label: "ESLint/Prettier" }, { value: "biome", label: "Biome" }, ], initialValue: "eslint", }); }, ...(!cliResults.flags.noGit && { git: () => { return p.confirm({ message: "Should we initialize a Git repository and stage the changes?", initialValue: !defaultOptions.flags.noGit, }); }, }), ...(!cliResults.flags.noInstall && { install: () => { return p.confirm({ message: `Should we run '${pkgManager}` + (pkgManager === "yarn" ? `'?` : ` install' for you?`), initialValue: !defaultOptions.flags.noInstall, }); }, }), importAlias: () => { return p.text({ message: "What import alias would you like to use?", defaultValue: defaultOptions.flags.importAlias, placeholder: defaultOptions.flags.importAlias, validate: validateImportAlias, }); }, }, { onCancel() { process.exit(1); }, } ); const packages: AvailablePackages[] = []; if (project.styling) packages.push("tailwind"); if (project.trpc) packages.push("trpc"); if (project.authentication === "next-auth") packages.push("nextAuth"); if (project.authentication === "better-auth") packages.push("betterAuth"); if (project.database === "prisma") packages.push("prisma"); if (project.database === "drizzle") packages.push("drizzle"); if (project.linter === "eslint") packages.push("eslint"); if (project.linter === "biome") packages.push("biome"); return { appName: project.name ?? cliResults.appName, packages, databaseProvider: (project.databaseProvider as DatabaseProvider) || "sqlite", flags: { ...cliResults.flags, appRouter: project.appRouter ?? cliResults.flags.appRouter, noGit: !project.git || cliResults.flags.noGit, noInstall: !project.install || cliResults.flags.noInstall, importAlias: project.importAlias ?? cliResults.flags.importAlias, }, }; } catch (err) { // If the user is not calling create-t3-app from an interactive terminal, inquirer will throw an IsTTYError // If this happens, we catch the error, tell the user what has happened, and then continue to run the program with a default t3 app if (err instanceof IsTTYError) { logger.warn(` ${CREATE_T3_APP} needs an interactive terminal to provide options`); const shouldContinue = await p.confirm({ message: `Continue scaffolding a default T3 app?`, initialValue: true, }); if (!shouldContinue) { logger.info("Exiting..."); process.exit(0); } logger.info(`Bootstrapping a default T3 app in ./${cliResults.appName}`); } else { throw err; } } return cliResults; }; ================================================ FILE: cli/src/consts.ts ================================================ import path from "path"; import { fileURLToPath } from "url"; // With the move to TSUP as a build tool, this keeps path routes in other files (installers, loaders, etc) in check more easily. // Path is in relation to a single index.js file inside ./dist const __filename = fileURLToPath(import.meta.url); const distPath = path.dirname(__filename); export const PKG_ROOT = path.join(distPath, "../"); //export const PKG_ROOT = path.dirname(require.main.filename); export const TITLE_TEXT = ` ___ ___ ___ __ _____ ___ _____ ____ __ ___ ___ / __| _ \\ __| / \\_ _| __| |_ _|__ / / \\ | _ \\ _ \\ | (__| / _| / /\\ \\| | | _| | | |_ \\ / /\\ \\| _/ _/ \\___|_|_\\___|_/‾‾\\_\\_| |___| |_| |___/ /_/‾‾\\_\\_| |_| `; export const DEFAULT_APP_NAME = "my-t3-app"; export const CREATE_T3_APP = "create-t3-app"; ================================================ FILE: cli/src/helpers/createProject.ts ================================================ import fs from "fs"; import path from "path"; import { PKG_ROOT } from "~/consts.js"; import { installPackages } from "~/helpers/installPackages.js"; import { scaffoldProject } from "~/helpers/scaffoldProject.js"; import { selectAppFile, selectIndexFile, selectLayoutFile, selectPageFile, } from "~/helpers/selectBoilerplate.js"; import { type DatabaseProvider, type PkgInstallerMap, } from "~/installers/index.js"; import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; interface CreateProjectOptions { projectName: string; packages: PkgInstallerMap; scopedAppName: string; noInstall: boolean; importAlias: string; appRouter: boolean; databaseProvider: DatabaseProvider; } export const createProject = async ({ projectName, scopedAppName, packages, noInstall, appRouter, databaseProvider, }: CreateProjectOptions) => { const pkgManager = getUserPkgManager(); const projectDir = path.resolve(process.cwd(), projectName); // Bootstraps the base Next.js application await scaffoldProject({ projectName, projectDir, pkgManager, scopedAppName, noInstall, appRouter, databaseProvider, }); // Install the selected packages installPackages({ projectName, scopedAppName, projectDir, pkgManager, packages, noInstall, appRouter, databaseProvider, }); // Select necessary _app,index / layout,page files if (appRouter) { // Replace next.config fs.copyFileSync( path.join(PKG_ROOT, "template/extras/config/next-config-appdir.js"), path.join(projectDir, "next.config.js") ); selectLayoutFile({ projectDir, packages }); selectPageFile({ projectDir, packages }); } else { selectAppFile({ projectDir, packages }); selectIndexFile({ projectDir, packages }); } // If no tailwind, select use css modules if (!packages.tailwind.inUse) { const indexModuleCss = path.join( PKG_ROOT, "template/extras/src/index.module.css" ); const indexModuleCssDest = path.join( projectDir, "src", appRouter ? "app" : "pages", "index.module.css" ); fs.copyFileSync(indexModuleCss, indexModuleCssDest); } return projectDir; }; ================================================ FILE: cli/src/helpers/format.ts ================================================ import chalk from "chalk"; import { execa } from "execa"; import ora from "ora"; import { type PackageManager } from "~/utils/getUserPkgManager.js"; import { logger } from "~/utils/logger.js"; // Runs format and lint command to ensure created repository is tidy upon creation export const formatProject = async ({ pkgManager, projectDir, eslint, biome, }: { pkgManager: PackageManager; projectDir: string; eslint: boolean; biome: boolean; }) => { logger.info(`Formatting project with ${eslint ? "prettier" : "biome"}...`); const spinner = ora("Running format command\n").start(); if (eslint) { await execa(pkgManager, ["run", "format:write"], { cwd: projectDir, }); } else if (biome) { await execa(pkgManager, ["run", "check:unsafe"], { cwd: projectDir, }); } spinner.succeed(`${chalk.green("Successfully formatted project")}`); }; ================================================ FILE: cli/src/helpers/git.ts ================================================ import { execSync } from "child_process"; import path from "path"; import * as p from "@clack/prompts"; import chalk from "chalk"; import { execa } from "execa"; import fs from "fs-extra"; import ora from "ora"; import { logger } from "~/utils/logger.js"; const isGitInstalled = (dir: string): boolean => { try { execSync("git --version", { cwd: dir }); return true; } catch { return false; } }; /** @returns Whether or not the provided directory has a `.git` subdirectory in it. */ export const isRootGitRepo = (dir: string): boolean => { return fs.existsSync(path.join(dir, ".git")); }; /** @returns Whether or not this directory or a parent directory has a `.git` directory. */ export const isInsideGitRepo = async (dir: string): Promise => { try { // If this command succeeds, we're inside a git repo await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dir, stdout: "ignore", }); return true; } catch { // Else, it will throw a git-error and we return false return false; } }; const getGitVersion = () => { const stdout = execSync("git --version").toString().trim(); const gitVersionTag = stdout.split(" ")[2]; const major = gitVersionTag?.split(".")[0]; const minor = gitVersionTag?.split(".")[1]; return { major: Number(major), minor: Number(minor) }; }; /** @returns The git config value of "init.defaultBranch". If it is not set, returns "main". */ const getDefaultBranch = () => { const stdout = execSync("git config --global init.defaultBranch || echo main") .toString() .trim(); return stdout; }; // This initializes the Git-repository for the project export const initializeGit = async (projectDir: string) => { logger.info("Initializing Git..."); if (!isGitInstalled(projectDir)) { logger.warn("Git is not installed. Skipping Git initialization."); return; } const spinner = ora("Creating a new git repo...\n").start(); const isRoot = isRootGitRepo(projectDir); const isInside = await isInsideGitRepo(projectDir); const dirName = path.parse(projectDir).name; // skip full path for logging if (isInside && isRoot) { // Dir is a root git repo spinner.stop(); const overwriteGit = await p.confirm({ message: `${chalk.redBright.bold( "Warning:" )} Git is already initialized in "${dirName}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`, initialValue: false, }); if (!overwriteGit) { spinner.info("Skipping Git initialization."); return; } // Deleting the .git folder fs.removeSync(path.join(projectDir, ".git")); } else if (isInside && !isRoot) { // Dir is inside a git worktree spinner.stop(); const initializeChildGitRepo = await p.confirm({ message: `${chalk.redBright.bold( "Warning:" )} "${dirName}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`, initialValue: false, }); if (!initializeChildGitRepo) { spinner.info("Skipping Git initialization."); return; } } // We're good to go, initializing the git repo try { const branchName = getDefaultBranch(); // --initial-branch flag was added in git v2.28.0 const { major, minor } = getGitVersion(); if (major < 2 || (major == 2 && minor < 28)) { await execa("git", ["init"], { cwd: projectDir }); // symbolic-ref is used here due to refs/heads/master not existing // It is only created after the first commit // https://superuser.com/a/1419674 await execa("git", ["symbolic-ref", "HEAD", `refs/heads/${branchName}`], { cwd: projectDir, }); } else { await execa("git", ["init", `--initial-branch=${branchName}`], { cwd: projectDir, }); } await execa("git", ["add", "."], { cwd: projectDir }); spinner.succeed( `${chalk.green("Successfully initialized and staged")} ${chalk.green.bold( "git" )}\n` ); } catch { // Safeguard, should be unreachable spinner.fail( `${chalk.bold.red( "Failed:" )} could not initialize git. Update git to the latest version!\n` ); } }; ================================================ FILE: cli/src/helpers/installDependencies.ts ================================================ import chalk from "chalk"; import { execa, type StdioOption } from "execa"; import ora, { type Ora } from "ora"; import { getUserPkgManager, type PackageManager, } from "~/utils/getUserPkgManager.js"; import { logger } from "~/utils/logger.js"; const execWithSpinner = async ( projectDir: string, pkgManager: PackageManager, options: { args?: string[]; stdout?: StdioOption; onDataHandle?: (spinner: Ora) => (data: Buffer) => void; } ) => { const { onDataHandle, args = ["install"], stdout = "pipe" } = options; const spinner = ora(`Running ${pkgManager} install...`).start(); const subprocess = execa(pkgManager, args, { cwd: projectDir, stdout }); await new Promise((res, rej) => { if (onDataHandle) { subprocess.stdout?.on("data", onDataHandle(spinner)); } void subprocess.on("error", (e) => rej(e)); void subprocess.on("close", () => res()); }); return spinner; }; const runInstallCommand = async ( pkgManager: PackageManager, projectDir: string ): Promise => { switch (pkgManager) { // When using npm, inherit the stderr stream so that the progress bar is shown case "npm": await execa(pkgManager, ["install"], { cwd: projectDir, stderr: "inherit", }); return null; // When using yarn or pnpm, use the stdout stream and ora spinner to show the progress case "pnpm": return execWithSpinner(projectDir, pkgManager, { onDataHandle: (spinner) => (data) => { const text = data.toString(); if (text.includes("Progress")) { spinner.text = text.includes("|") ? (text.split(" | ")[1] ?? "") : text; } }, }); case "yarn": return execWithSpinner(projectDir, pkgManager, { onDataHandle: (spinner) => (data) => { spinner.text = data.toString(); }, }); // When using bun, the stdout stream is ignored and the spinner is shown case "bun": return execWithSpinner(projectDir, pkgManager, { stdout: "ignore" }); } }; export const installDependencies = async ({ projectDir, }: { projectDir: string; }) => { logger.info("Installing dependencies..."); const pkgManager = getUserPkgManager(); const installSpinner = await runInstallCommand(pkgManager, projectDir); // If the spinner was used to show the progress, use succeed method on it // If not, use the succeed on a new spinner (installSpinner ?? ora()).succeed( chalk.green("Successfully installed dependencies!\n") ); }; ================================================ FILE: cli/src/helpers/installPackages.ts ================================================ import chalk from "chalk"; import ora from "ora"; import { type InstallerOptions, type PkgInstallerMap, } from "~/installers/index.js"; import { logger } from "~/utils/logger.js"; type InstallPackagesOptions = InstallerOptions & { packages: PkgInstallerMap; }; // This runs the installer for all the packages that the user has selected export const installPackages = (options: InstallPackagesOptions) => { const { packages } = options; logger.info("Adding boilerplate..."); for (const [name, pkgOpts] of Object.entries(packages)) { if (pkgOpts.inUse) { const spinner = ora(`Boilerplating ${name}...`).start(); pkgOpts.installer(options); spinner.succeed( chalk.green( `Successfully setup boilerplate for ${chalk.green.bold(name)}` ) ); } } logger.info(""); }; ================================================ FILE: cli/src/helpers/logNextSteps.ts ================================================ import { DEFAULT_APP_NAME } from "~/consts.js"; import { type InstallerOptions } from "~/installers/index.js"; import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; import { logger } from "~/utils/logger.js"; import { isInsideGitRepo, isRootGitRepo } from "./git.js"; // This logs the next steps that the user should take in order to advance the project export const logNextSteps = async ({ projectName = DEFAULT_APP_NAME, packages, noInstall, projectDir, databaseProvider, }: Pick< InstallerOptions, | "projectName" | "packages" | "noInstall" | "projectDir" | "appRouter" | "databaseProvider" >) => { const pkgManager = getUserPkgManager(); logger.info("Next steps:"); if (projectName !== ".") { logger.info(` cd ${projectName}`); } if (noInstall) { // To reflect yarn's default behavior of installing packages when no additional args provided if (pkgManager === "yarn") { logger.info(` ${pkgManager}`); } else { logger.info(` ${pkgManager} install`); } } if (["postgres", "mysql"].includes(databaseProvider)) { logger.info(" Start up a database, if needed using './start-database.sh'"); } if (packages?.prisma.inUse || packages?.drizzle.inUse) { if (["npm", "bun"].includes(pkgManager)) { logger.info(` ${pkgManager} run db:push`); } else { logger.info(` ${pkgManager} db:push`); } } if (packages?.nextAuth.inUse) { logger.info( ` Fill in your .env with necessary values. See https://create.t3.gg/en/usage/first-steps for more info.` ); } if (["npm", "bun"].includes(pkgManager)) { logger.info(` ${pkgManager} run dev`); } else { logger.info(` ${pkgManager} dev`); } if (!(await isInsideGitRepo(projectDir)) && !isRootGitRepo(projectDir)) { logger.info(` git init`); } logger.info(` git commit -m "initial commit"`); }; ================================================ FILE: cli/src/helpers/scaffoldProject.ts ================================================ import path from "path"; import * as p from "@clack/prompts"; import chalk from "chalk"; import fs from "fs-extra"; import ora from "ora"; import { PKG_ROOT } from "~/consts.js"; import { type InstallerOptions } from "~/installers/index.js"; import { logger } from "~/utils/logger.js"; // This bootstraps the base Next.js application export const scaffoldProject = async ({ projectName, projectDir, pkgManager, noInstall, }: InstallerOptions) => { const srcDir = path.join(PKG_ROOT, "template/base"); if (!noInstall) { logger.info(`\nUsing: ${chalk.cyan.bold(pkgManager)}\n`); } else { logger.info(""); } const spinner = ora(`Scaffolding in: ${projectDir}...\n`).start(); if (fs.existsSync(projectDir)) { if (fs.readdirSync(projectDir).length === 0) { if (projectName !== ".") spinner.info( `${chalk.cyan.bold(projectName)} exists but is empty, continuing...\n` ); } else { spinner.stopAndPersist(); const overwriteDir = await p.select({ message: `${chalk.redBright.bold("Warning:")} ${chalk.cyan.bold( projectName )} already exists and isn't empty. How would you like to proceed?`, options: [ { label: "Abort installation (recommended)", value: "abort", }, { label: "Clear the directory and continue installation", value: "clear", }, { label: "Continue installation and overwrite conflicting files", value: "overwrite", }, ], initialValue: "abort", }); if (p.isCancel(overwriteDir) || overwriteDir === "abort") { spinner.fail("Aborting installation..."); process.exit(1); } const confirmOverwriteDir = await p.confirm({ message: `Are you sure you want to ${ overwriteDir === "clear" ? "clear the directory" : "overwrite conflicting files" }?`, initialValue: false, }); if (p.isCancel(confirmOverwriteDir) || !confirmOverwriteDir) { spinner.fail("Aborting installation..."); process.exit(1); } if (overwriteDir === "clear") { spinner.info( `Emptying ${chalk.cyan.bold(projectName)} and creating t3 app..\n` ); fs.emptyDirSync(projectDir); } } } spinner.start(); fs.copySync(srcDir, projectDir); fs.renameSync( path.join(projectDir, "_gitignore"), path.join(projectDir, ".gitignore") ); const scaffoldedName = projectName === "." ? "App" : chalk.cyan.bold(projectName); spinner.succeed( `${scaffoldedName} ${chalk.green("scaffolded successfully!")}\n` ); }; ================================================ FILE: cli/src/helpers/selectBoilerplate.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type InstallerOptions } from "~/installers/index.js"; type SelectBoilerplateProps = Required< Pick >; // This generates the _app.tsx file that is used to render the app export const selectAppFile = ({ projectDir, packages, }: SelectBoilerplateProps) => { const appFileDir = path.join(PKG_ROOT, "template/extras/src/pages/_app"); const usingTw = packages.tailwind.inUse; const usingTRPC = packages.trpc.inUse; const usingAuth = packages?.nextAuth.inUse ?? packages?.betterAuth.inUse; const usingBetterAuth = packages?.betterAuth.inUse; let appFile = "base.tsx"; if (usingTRPC && usingTw && usingBetterAuth) { appFile = "with-better-auth-trpc-tw.tsx"; } else if (usingTRPC && !usingTw && usingBetterAuth) { appFile = "with-better-auth-trpc.tsx"; } else if (usingTRPC && usingTw && usingAuth) { appFile = "with-auth-trpc-tw.tsx"; } else if (usingTRPC && !usingTw && usingAuth) { appFile = "with-auth-trpc.tsx"; } else if (usingTRPC && usingTw) { appFile = "with-trpc-tw.tsx"; } else if (usingTRPC && !usingTw) { appFile = "with-trpc.tsx"; } else if (!usingTRPC && usingTw) { appFile = "with-tw.tsx"; } else if (usingAuth && usingTw) { appFile = "with-auth-tw.tsx"; } else if (usingAuth && !usingTw) { appFile = "with-auth.tsx"; } const appSrc = path.join(appFileDir, appFile); const appDest = path.join(projectDir, "src/pages/_app.tsx"); fs.copySync(appSrc, appDest); }; // Similar to _app, but for app router export const selectLayoutFile = ({ projectDir, packages, }: SelectBoilerplateProps) => { const layoutFileDir = path.join(PKG_ROOT, "template/extras/src/app/layout"); const usingTw = packages.tailwind.inUse; const usingTRPC = packages.trpc.inUse; let layoutFile = "base.tsx"; if (usingTRPC && usingTw) { layoutFile = "with-trpc-tw.tsx"; } else if (usingTRPC && !usingTw) { layoutFile = "with-trpc.tsx"; } else if (!usingTRPC && usingTw) { layoutFile = "with-tw.tsx"; } const appSrc = path.join(layoutFileDir, layoutFile); const appDest = path.join(projectDir, "src/app/layout.tsx"); fs.copySync(appSrc, appDest); }; // This selects the proper index.tsx to be used that showcases the chosen tech export const selectIndexFile = ({ projectDir, packages, }: SelectBoilerplateProps) => { const indexFileDir = path.join(PKG_ROOT, "template/extras/src/pages/index"); const usingTRPC = packages.trpc.inUse; const usingTw = packages.tailwind.inUse; const usingBetterAuth = packages?.betterAuth.inUse; const usingNextAuth = packages?.nextAuth.inUse; const usingAuth = usingNextAuth || usingBetterAuth; let indexFile = "base.tsx"; if (usingTRPC && usingTw && usingBetterAuth) { indexFile = "with-better-auth-trpc-tw.tsx"; } else if (usingTRPC && !usingTw && usingBetterAuth) { indexFile = "with-better-auth-trpc.tsx"; } else if (!usingTRPC && usingTw && usingBetterAuth) { indexFile = "with-better-auth-tw.tsx"; } else if (!usingTRPC && !usingTw && usingBetterAuth) { indexFile = "with-better-auth.tsx"; } else if (usingTRPC && usingTw && usingAuth) { indexFile = "with-auth-trpc-tw.tsx"; } else if (usingTRPC && !usingTw && usingAuth) { indexFile = "with-auth-trpc.tsx"; } else if (usingTRPC && usingTw) { indexFile = "with-trpc-tw.tsx"; } else if (usingTRPC && !usingTw) { indexFile = "with-trpc.tsx"; } else if (!usingTRPC && usingTw) { indexFile = "with-tw.tsx"; } const indexSrc = path.join(indexFileDir, indexFile); const indexDest = path.join(projectDir, "src/pages/index.tsx"); fs.copySync(indexSrc, indexDest); }; // Similar to index, but for app router export const selectPageFile = ({ projectDir, packages, }: SelectBoilerplateProps) => { const indexFileDir = path.join(PKG_ROOT, "template/extras/src/app/page"); const usingTRPC = packages.trpc.inUse; const usingTw = packages.tailwind.inUse; const usingAuth = packages?.nextAuth.inUse; const usingBetterAuth = packages?.betterAuth.inUse; let indexFile = "base.tsx"; if (usingTRPC && usingTw && usingBetterAuth) { indexFile = "with-better-auth-trpc-tw.tsx"; } else if (usingTRPC && !usingTw && usingBetterAuth) { indexFile = "with-better-auth-trpc.tsx"; } else if (!usingTRPC && usingTw && usingBetterAuth) { indexFile = "with-better-auth-tw.tsx"; } else if (!usingTRPC && !usingTw && usingBetterAuth) { indexFile = "with-better-auth.tsx"; } else if (usingTRPC && usingTw && usingAuth) { indexFile = "with-auth-trpc-tw.tsx"; } else if (usingTRPC && !usingTw && usingAuth) { indexFile = "with-auth-trpc.tsx"; } else if (usingTRPC && usingTw) { indexFile = "with-trpc-tw.tsx"; } else if (usingTRPC && !usingTw) { indexFile = "with-trpc.tsx"; } else if (!usingTRPC && usingTw) { indexFile = "with-tw.tsx"; } const indexSrc = path.join(indexFileDir, indexFile); const indexDest = path.join(projectDir, "src/app/page.tsx"); fs.copySync(indexSrc, indexDest); }; ================================================ FILE: cli/src/helpers/setImportAlias.ts ================================================ import fs from "fs"; import path from "path"; function replaceTextInFiles( directoryPath: string, search: string, replacement: string ): void { const files = fs.readdirSync(directoryPath); files.forEach((file) => { const filePath = path.join(directoryPath, file); if (fs.statSync(filePath).isDirectory()) { replaceTextInFiles(filePath, search, replacement); } else { const data = fs.readFileSync(filePath, "utf8"); const updatedData = data.replace(new RegExp(search, "g"), replacement); fs.writeFileSync(filePath, updatedData, "utf8"); } }); } export const setImportAlias = (projectDir: string, importAlias: string) => { const normalizedImportAlias = importAlias .replace(/\*/g, "") // remove any wildcards (~/* -> ~/) .replace(/[^/]$/, "$&/"); // ensure trailing slash (@ -> ~/) // update import alias in any files if not using the default replaceTextInFiles(projectDir, `~/`, normalizedImportAlias); }; ================================================ FILE: cli/src/index.ts ================================================ #!/usr/bin/env node import path from "path"; import { execa } from "execa"; import fs from "fs-extra"; import { type PackageJson } from "type-fest"; import { runCli } from "~/cli/index.js"; import { createProject } from "~/helpers/createProject.js"; import { initializeGit } from "~/helpers/git.js"; import { logNextSteps } from "~/helpers/logNextSteps.js"; import { setImportAlias } from "~/helpers/setImportAlias.js"; import { buildPkgInstallerMap } from "~/installers/index.js"; import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; import { logger } from "~/utils/logger.js"; import { parseNameAndPath } from "~/utils/parseNameAndPath.js"; import { renderTitle } from "~/utils/renderTitle.js"; import { formatProject } from "./helpers/format.js"; import { installDependencies } from "./helpers/installDependencies.js"; import { getVersion } from "./utils/getT3Version.js"; import { getNpmVersion, renderVersionWarning, } from "./utils/renderVersionWarning.js"; type CT3APackageJSON = PackageJson & { ct3aMetadata?: { initVersion: string; }; }; const main = async () => { const npmVersion = await getNpmVersion(); const pkgManager = getUserPkgManager(); renderTitle(); if (npmVersion) { renderVersionWarning(npmVersion); } const { appName, packages, flags: { noGit, noInstall, importAlias, appRouter }, databaseProvider, } = await runCli(); const usePackages = buildPkgInstallerMap(packages, databaseProvider); // e.g. dir/@mono/app returns ["@mono/app", "dir/app"] const [scopedAppName, appDir] = parseNameAndPath(appName); const projectDir = await createProject({ projectName: appDir, scopedAppName, packages: usePackages, databaseProvider, importAlias, noInstall, appRouter, }); // Write name to package.json const pkgJson = fs.readJSONSync( path.join(projectDir, "package.json") ) as CT3APackageJSON; pkgJson.name = scopedAppName; pkgJson.ct3aMetadata = { initVersion: getVersion() }; // ? Bun doesn't support this field (yet) if (pkgManager !== "bun") { const { stdout } = await execa(pkgManager, ["-v"], { cwd: projectDir, }); pkgJson.packageManager = `${pkgManager}@${stdout.trim()}`; } fs.writeJSONSync(path.join(projectDir, "package.json"), pkgJson, { spaces: 2, }); // update import alias in any generated files if not using the default if (importAlias !== "~/") { setImportAlias(projectDir, importAlias); } if (!noInstall) { await installDependencies({ projectDir }); if (usePackages.prisma.inUse) { logger.info("Generating Prisma client..."); await execa("npx", ["prisma", "generate"], { cwd: projectDir }); logger.info("Successfully generated Prisma client!"); } await formatProject({ pkgManager, projectDir, eslint: packages.includes("eslint"), biome: packages.includes("biome"), }); } if (!noGit) { await initializeGit(projectDir); } await logNextSteps({ projectName: appDir, packages: usePackages, appRouter, noInstall, projectDir, databaseProvider, }); process.exit(0); }; main().catch((err) => { logger.error("Aborting installation..."); if (err instanceof Error) { logger.error(err); } else { logger.error( "An unknown error has occurred. Please open an issue on github with the below:" ); console.log(err); } process.exit(1); }); ================================================ FILE: cli/src/installers/betterAuth.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type AvailableDependencies } from "~/installers/dependencyVersionMap.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; export const betterAuthInstaller: Installer = ({ projectDir, packages, databaseProvider, appRouter, }) => { const usingPrisma = packages?.prisma.inUse; const usingDrizzle = packages?.drizzle.inUse; const deps: AvailableDependencies[] = ["better-auth"]; if (usingPrisma) deps.push("@auth/prisma-adapter"); if (usingDrizzle) deps.push("@auth/drizzle-adapter"); addPackageDependency({ projectDir, dependencies: deps, devMode: false, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); const isAppRouter = appRouter ?? true; // Default to app router if not specified const apiHandlerFile = isAppRouter ? "src/app/api/auth/[...all]/route.ts" : "src/pages/api/auth/[...all].ts"; const apiHandlerSrc = path.join(extrasDir, apiHandlerFile); const apiHandlerDest = path.join(projectDir, apiHandlerFile); const authConfigSrc = path.join( extrasDir, "src/server/better-auth/config", usingPrisma ? "with-prisma.ts" : usingDrizzle ? "with-drizzle.ts" : "base.ts" ); const authConfigDest = path.join( projectDir, "src/server/better-auth/config.ts" ); const authIndexSrc = path.join(extrasDir, "src/server/better-auth/index.ts"); const authIndexDest = path.join( projectDir, "src/server/better-auth/index.ts" ); // Better Auth client and server helpers const betterAuthClientSrc = path.join( extrasDir, "src/server/better-auth/client.ts" ); const betterAuthClientDest = path.join( projectDir, "src/server/better-auth/client.ts" ); const betterAuthServerSrc = path.join( extrasDir, "src/server/better-auth/server.ts" ); const betterAuthServerDest = path.join( projectDir, "src/server/better-auth/server.ts" ); fs.copySync(apiHandlerSrc, apiHandlerDest); fs.copySync(authConfigSrc, authConfigDest); fs.copySync(authIndexSrc, authIndexDest); fs.copySync(betterAuthClientSrc, betterAuthClientDest); fs.copySync(betterAuthServerSrc, betterAuthServerDest); // Update Better Auth adapter provider according to selected DB try { if (fs.pathExistsSync(authConfigDest)) { const content = fs.readFileSync(authConfigDest, "utf8"); // Map CLI database provider to adapter provider strings const providerForDrizzle = (db: string) => { switch (db) { case "postgres": return "pg"; case "mysql": case "planetscale": return "mysql"; case "sqlite": return "sqlite"; default: return "pg"; } }; const providerForPrisma = (db: string) => { switch (db) { case "postgres": return "postgresql"; case "mysql": case "planetscale": return "mysql"; case "sqlite": return "sqlite"; default: return "postgresql"; } }; const providerValue = usingPrisma ? providerForPrisma(databaseProvider) : usingDrizzle ? providerForDrizzle(databaseProvider) : undefined; if (providerValue) { const updated = content.replace( /(provider:\s*")[^"]+("\s*,?)/, `$1${providerValue}$2` ); fs.writeFileSync(authConfigDest, updated, "utf8"); } } } catch { // Non-fatal: leave default provider from template } }; ================================================ FILE: cli/src/installers/biome.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; import { addPackageScript } from "~/utils/addPackageScript.js"; export const biomeInstaller: Installer = ({ projectDir }) => { addPackageDependency({ projectDir, dependencies: ["@biomejs/biome"], devMode: true, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); const biomeConfigSrc = path.join(extrasDir, "config/biome.jsonc"); const biomeConfigDest = path.join(projectDir, "biome.jsonc"); fs.copySync(biomeConfigSrc, biomeConfigDest); addPackageScript({ projectDir, scripts: { "check:unsafe": "biome check --write --unsafe .", "check:write": "biome check --write .", check: "biome check .", }, }); }; ================================================ FILE: cli/src/installers/dbContainer.ts ================================================ import fs from "fs"; import path from "path"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { parseNameAndPath } from "~/utils/parseNameAndPath.js"; // Sanitizes a project name to ensure it adheres to Docker container naming conventions. const sanitizeName = (name: string): string => { return name .replace(/[^a-zA-Z0-9_.-]/g, "_") // Replace invalid characters with underscores .toLowerCase(); // Convert to lowercase for consistency }; export const dbContainerInstaller: Installer = ({ projectDir, databaseProvider, projectName, }) => { const scriptSrc = path.join( PKG_ROOT, `template/extras/start-database/${databaseProvider}.sh` ); const scriptText = fs.readFileSync(scriptSrc, "utf-8"); const scriptDest = path.join(projectDir, "start-database.sh"); // for configuration with postgresql and mysql when project is created with '.' project name const [projectNameParsed] = projectName === "." ? parseNameAndPath(projectDir) : [projectName]; // Sanitize the project name for Docker container usage const sanitizedProjectName = sanitizeName(projectNameParsed); fs.writeFileSync( scriptDest, scriptText.replaceAll("project1", sanitizedProjectName) ); fs.chmodSync(scriptDest, "755"); }; ================================================ FILE: cli/src/installers/dependencyVersionMap.ts ================================================ /* * This maps the necessary packages to a version. * This improves performance significantly over fetching it from the npm registry. */ export const dependencyVersionMap = { // NextAuth.js "next-auth": "5.0.0-beta.25", "@auth/prisma-adapter": "^2.7.2", "@auth/drizzle-adapter": "^1.7.2", // Better-Auth "better-auth": "^1.3", // Prisma prisma: "^6.6.0", "@prisma/client": "^6.6.0", "@prisma/adapter-planetscale": "^6.6.0", // Drizzle "drizzle-kit": "^0.30.5", "drizzle-orm": "^0.41.0", mysql2: "^3.11.0", "@planetscale/database": "^1.19.0", postgres: "^3.4.4", "@libsql/client": "^0.14.0", // TailwindCSS tailwindcss: "^4.0.15", postcss: "^8.5.3", "@tailwindcss/postcss": "^4.0.15", // tRPC "@trpc/client": "^11.0.0", "@trpc/server": "^11.0.0", "@trpc/react-query": "^11.0.0", "@trpc/next": "^11.0.0", "@tanstack/react-query": "^5.69.0", superjson: "^2.2.1", "server-only": "^0.0.1", // biome "@biomejs/biome": "^2.2.5", // eslint / prettier prettier: "^3.5.3", "@eslint/eslintrc": "^3.3.1", "prettier-plugin-tailwindcss": "^0.6.11", eslint: "^9.23.0", "eslint-config-next": "^15.2.3", "eslint-plugin-drizzle": "^0.2.3", "typescript-eslint": "^8.27.0", } as const; export type AvailableDependencies = keyof typeof dependencyVersionMap; ================================================ FILE: cli/src/installers/drizzle.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; import { addPackageScript } from "~/utils/addPackageScript.js"; export const drizzleInstaller: Installer = ({ projectDir, packages, scopedAppName, databaseProvider, }) => { addPackageDependency({ projectDir, dependencies: ["drizzle-kit"], devMode: true, }); addPackageDependency({ projectDir, dependencies: [ "drizzle-orm", ( { planetscale: "@planetscale/database", mysql: "mysql2", postgres: "postgres", sqlite: "@libsql/client", } as const )[databaseProvider], ], devMode: false, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); const configFile = path.join( extrasDir, `config/drizzle-config-${ databaseProvider === "planetscale" ? "mysql" : databaseProvider }.ts` ); const configDest = path.join(projectDir, "drizzle.config.ts"); const schemaBaseName = packages?.betterAuth.inUse ? "with-better-auth" : packages?.nextAuth.inUse ? "with-auth" : "base"; const schemaSrc = path.join( extrasDir, "src/server/db/schema-drizzle", `${schemaBaseName}-${databaseProvider}.ts` ); const schemaDest = path.join(projectDir, "src/server/db/schema.ts"); // Replace placeholder table prefix with project name let schemaContent = fs.readFileSync(schemaSrc, "utf-8"); schemaContent = schemaContent.replace( "project1_${name}", `${scopedAppName}_\${name}` ); let configContent = fs.readFileSync(configFile, "utf-8"); configContent = configContent.replace("project1_*", `${scopedAppName}_*`); const clientSrc = path.join( extrasDir, `src/server/db/index-drizzle/with-${databaseProvider}.ts` ); const clientDest = path.join(projectDir, "src/server/db/index.ts"); addPackageScript({ projectDir, scripts: { "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", }, }); fs.copySync(configFile, configDest); fs.mkdirSync(path.dirname(schemaDest), { recursive: true }); fs.writeFileSync(schemaDest, schemaContent); fs.writeFileSync(configDest, configContent); fs.copySync(clientSrc, clientDest); }; ================================================ FILE: cli/src/installers/envVars.ts ================================================ import crypto from "node:crypto"; import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type DatabaseProvider, type Installer } from "~/installers/index.js"; export const envVariablesInstaller: Installer = ({ projectDir, packages, databaseProvider, scopedAppName, }) => { const usingNextAuth = packages?.nextAuth.inUse; const usingBetterAuth = packages?.betterAuth.inUse; const usingPrisma = packages?.prisma.inUse; const usingDrizzle = packages?.drizzle.inUse; const usingDb = usingPrisma === true || usingDrizzle === true; const usingPlanetScale = databaseProvider === "planetscale"; const envContent = getEnvContent( !!usingNextAuth, !!usingBetterAuth, !!usingPrisma, !!usingDrizzle, databaseProvider, scopedAppName ); let envFile = ""; if (usingDb) { if (usingPlanetScale) { if (usingBetterAuth) envFile = "with-better-auth-db-planetscale.js"; else if (usingNextAuth) envFile = "with-auth-db-planetscale.js"; else envFile = "with-db-planetscale.js"; } else { if (usingBetterAuth) envFile = "with-better-auth-db.js"; else if (usingNextAuth) envFile = "with-auth-db.js"; else envFile = "with-db.js"; } } else { if (usingBetterAuth) envFile = "with-better-auth.js"; else if (usingNextAuth) envFile = "with-auth.js"; } if (envFile !== "") { const envSchemaSrc = path.join( PKG_ROOT, "template/extras/src/env", envFile ); const envSchemaDest = path.join(projectDir, "src/env.js"); fs.copyFileSync(envSchemaSrc, envSchemaDest); } const envDest = path.join(projectDir, ".env"); const envExampleDest = path.join(projectDir, ".env.example"); const _exampleEnvContent = exampleEnvContent + envContent; // Generate an auth secret and put in .env, not .env.example const secret = Buffer.from( crypto.getRandomValues(new Uint8Array(32)) ).toString("base64"); const _envContent = envContent .replace( 'AUTH_SECRET=""', `AUTH_SECRET="${secret}" # Generated by create-t3-app.` ) .replace( 'BETTER_AUTH_SECRET=""', `BETTER_AUTH_SECRET="${secret}" # Generated by create-t3-app.` ); fs.writeFileSync(envDest, _envContent, "utf-8"); fs.writeFileSync(envExampleDest, _exampleEnvContent, "utf-8"); }; const getEnvContent = ( usingNextAuth: boolean, usingBetterAuth: boolean, usingPrisma: boolean, usingDrizzle: boolean, databaseProvider: DatabaseProvider, scopedAppName: string ) => { let content = ` # When adding additional environment variables, the schema in "/src/env.js" # should be updated accordingly. ` .trim() .concat("\n"); if (usingNextAuth) content += ` # Next Auth # You can generate a new secret on the command line with: # npx auth secret # https://next-auth.js.org/configuration/options#secret AUTH_SECRET="" # Next Auth Discord Provider AUTH_DISCORD_ID="" AUTH_DISCORD_SECRET="" `; if (usingBetterAuth) content += ` # Better Auth # Secret used by Better Auth BETTER_AUTH_SECRET="" # Better Auth GitHub OAuth BETTER_AUTH_GITHUB_CLIENT_ID="" BETTER_AUTH_GITHUB_CLIENT_SECRET="" `; if (usingPrisma) content += ` # Prisma # https://www.prisma.io/docs/reference/database-reference/connection-urls#env `; if (usingDrizzle) content += "\n# Drizzle\n"; if (usingPrisma || usingDrizzle) { if (databaseProvider === "planetscale") { if (usingDrizzle) { content += `# Get the Database URL from the "prisma" dropdown selector in PlanetScale. # Change the query params at the end of the URL to "?ssl={"rejectUnauthorized":true}" DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?ssl={"rejectUnauthorized":true}'`; } else { content += `# Get the Database URL from the "prisma" dropdown selector in PlanetScale. DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?sslaccept=strict'`; } } else if (databaseProvider === "mysql") { content += `DATABASE_URL="mysql://root:password@localhost:3306/${scopedAppName}"`; } else if (databaseProvider === "postgres") { content += `DATABASE_URL="postgresql://postgres:password@localhost:5432/${scopedAppName}"`; } else if (databaseProvider === "sqlite") { content += 'DATABASE_URL="file:./db.sqlite"'; } content += "\n"; } if (!usingNextAuth && !usingBetterAuth && !usingPrisma && !usingDrizzle) content += ` # Example: # SERVERVAR="foo" # NEXT_PUBLIC_CLIENTVAR="bar" `; return content; }; const exampleEnvContent = ` # Since the ".env" file is gitignored, you can use the ".env.example" file to # build a new ".env" file when you clone the repo. Keep this file up-to-date # when you add new variables to \`.env\`. # This file will be committed to version control, so make sure not to have any # secrets in it. If you are cloning this repo, create a copy of this file named # ".env" and populate it with your secrets. ` .trim() .concat("\n\n"); ================================================ FILE: cli/src/installers/eslint.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; import { addPackageScript } from "~/utils/addPackageScript.js"; import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; import { type AvailableDependencies } from "./dependencyVersionMap.js"; // Also installs prettier export const dynamicEslintInstaller: Installer = ({ projectDir, packages }) => { const devPackages: AvailableDependencies[] = [ "prettier", "eslint", "eslint-config-next", "typescript-eslint", "@eslint/eslintrc", ]; if (packages?.tailwind.inUse) { devPackages.push("prettier-plugin-tailwindcss"); } if (packages?.drizzle.inUse) { devPackages.push("eslint-plugin-drizzle"); } addPackageDependency({ projectDir, dependencies: devPackages, devMode: true, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); // Prettier let prettierSrc: string; if (packages?.tailwind.inUse) { prettierSrc = path.join(extrasDir, "config/_tailwind.prettier.config.js"); } else { prettierSrc = path.join(extrasDir, "config/_prettier.config.js"); } const prettierDest = path.join(projectDir, "prettier.config.js"); fs.copySync(prettierSrc, prettierDest); // pnpm const pkgManager = getUserPkgManager(); if (pkgManager === "pnpm") { const pnpmSrc = path.join(extrasDir, "pnpm/_npmrc"); fs.copySync(pnpmSrc, path.join(projectDir, ".npmrc")); } addPackageScript({ projectDir, scripts: { lint: "next lint", "lint:fix": "next lint --fix", check: "next lint && tsc --noEmit", "format:write": 'prettier --write "**/*.{ts,tsx,js,jsx,mdx}" --cache', "format:check": 'prettier --check "**/*.{ts,tsx,js,jsx,mdx}" --cache', }, }); // eslint const usingDrizzle = !!packages?.drizzle?.inUse; const eslintConfigSrc = path.join( extrasDir, usingDrizzle ? "config/_eslint.drizzle.js" : "config/_eslint.base.js" ); const eslintConfigDest = path.join(projectDir, "eslint.config.js"); fs.copySync(eslintConfigSrc, eslintConfigDest); }; ================================================ FILE: cli/src/installers/index.ts ================================================ import { envVariablesInstaller } from "~/installers/envVars.js"; import { nextAuthInstaller } from "~/installers/nextAuth.js"; import { prismaInstaller } from "~/installers/prisma.js"; import { tailwindInstaller } from "~/installers/tailwind.js"; import { trpcInstaller } from "~/installers/trpc.js"; import { type PackageManager } from "~/utils/getUserPkgManager.js"; import { betterAuthInstaller } from "./betterAuth.js"; import { biomeInstaller } from "./biome.js"; import { dbContainerInstaller } from "./dbContainer.js"; import { drizzleInstaller } from "./drizzle.js"; import { dynamicEslintInstaller } from "./eslint.js"; // Turning this into a const allows the list to be iterated over for programmatically creating prompt options // Should increase extensibility in the future export const availablePackages = [ "nextAuth", "betterAuth", "prisma", "drizzle", "tailwind", "trpc", "envVariables", "eslint", "biome", "dbContainer", ] as const; export type AvailablePackages = (typeof availablePackages)[number]; export const databaseProviders = [ "mysql", "postgres", "sqlite", "planetscale", ] as const; export type DatabaseProvider = (typeof databaseProviders)[number]; export interface InstallerOptions { projectDir: string; pkgManager: PackageManager; noInstall: boolean; packages?: PkgInstallerMap; appRouter?: boolean; projectName: string; scopedAppName: string; databaseProvider: DatabaseProvider; } export type Installer = (opts: InstallerOptions) => void; export type PkgInstallerMap = Record< AvailablePackages, { inUse: boolean; installer: Installer; } >; export const buildPkgInstallerMap = ( packages: AvailablePackages[], databaseProvider: DatabaseProvider ): PkgInstallerMap => ({ nextAuth: { inUse: packages.includes("nextAuth"), installer: nextAuthInstaller, }, betterAuth: { inUse: packages.includes("betterAuth"), installer: betterAuthInstaller, }, prisma: { inUse: packages.includes("prisma"), installer: prismaInstaller, }, drizzle: { inUse: packages.includes("drizzle"), installer: drizzleInstaller, }, tailwind: { inUse: packages.includes("tailwind"), installer: tailwindInstaller, }, trpc: { inUse: packages.includes("trpc"), installer: trpcInstaller, }, dbContainer: { inUse: ["mysql", "postgres"].includes(databaseProvider), installer: dbContainerInstaller, }, envVariables: { inUse: true, installer: envVariablesInstaller, }, eslint: { inUse: packages.includes("eslint"), installer: dynamicEslintInstaller, }, biome: { inUse: packages.includes("biome"), installer: biomeInstaller, }, }); ================================================ FILE: cli/src/installers/nextAuth.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type AvailableDependencies } from "~/installers/dependencyVersionMap.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; export const nextAuthInstaller: Installer = ({ projectDir, packages }) => { const usingPrisma = packages?.prisma.inUse; const usingDrizzle = packages?.drizzle.inUse; const deps: AvailableDependencies[] = ["next-auth"]; if (usingPrisma) deps.push("@auth/prisma-adapter"); if (usingDrizzle) deps.push("@auth/drizzle-adapter"); addPackageDependency({ projectDir, dependencies: deps, devMode: false, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); const apiHandlerFile = "src/app/api/auth/[...nextauth]/route.ts"; const apiHandlerSrc = path.join(extrasDir, apiHandlerFile); const apiHandlerDest = path.join(projectDir, apiHandlerFile); const authConfigSrc = path.join( extrasDir, "src/server/auth/config", usingPrisma ? "with-prisma.ts" : usingDrizzle ? "with-drizzle.ts" : "base.ts" ); const authConfigDest = path.join(projectDir, "src/server/auth/config.ts"); const authIndexSrc = path.join(extrasDir, "src/server/auth/index.ts"); const authIndexDest = path.join(projectDir, "src/server/auth/index.ts"); fs.copySync(apiHandlerSrc, apiHandlerDest); fs.copySync(authConfigSrc, authConfigDest); fs.copySync(authIndexSrc, authIndexDest); }; ================================================ FILE: cli/src/installers/prisma.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; import { addPackageScript } from "~/utils/addPackageScript.js"; export const prismaInstaller: Installer = ({ projectDir, packages, databaseProvider, }) => { addPackageDependency({ projectDir, dependencies: ["prisma"], devMode: true, }); addPackageDependency({ projectDir, dependencies: ["@prisma/client"], devMode: false, }); if (databaseProvider === "planetscale") addPackageDependency({ projectDir, dependencies: ["@prisma/adapter-planetscale", "@planetscale/database"], devMode: false, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); const schemaBaseName = packages?.betterAuth.inUse ? "with-better-auth" : packages?.nextAuth.inUse ? "with-auth" : "base"; const schemaSrc = path.join( extrasDir, "prisma/schema", `${schemaBaseName}${ databaseProvider === "planetscale" ? "-planetscale" : "" }.prisma` ); let schemaText = fs.readFileSync(schemaSrc, "utf-8"); if (databaseProvider !== "sqlite") { schemaText = schemaText.replace( 'provider = "sqlite"', `provider = "${ { mysql: "mysql", postgres: "postgresql", planetscale: "mysql", }[databaseProvider] }"` ); if (["mysql", "planetscale"].includes(databaseProvider)) { schemaText = schemaText.replace("// @db.Text", "@db.Text"); } } const schemaDest = path.join(projectDir, "prisma/schema.prisma"); fs.mkdirSync(path.dirname(schemaDest), { recursive: true }); fs.writeFileSync(schemaDest, schemaText); const clientSrc = path.join( extrasDir, databaseProvider === "planetscale" ? "src/server/db/db-prisma-planetscale.ts" : "src/server/db/db-prisma.ts" ); const clientDest = path.join(projectDir, "src/server/db.ts"); addPackageScript({ projectDir, scripts: { postinstall: "prisma generate", "db:push": "prisma db push", "db:studio": "prisma studio", "db:generate": "prisma migrate dev", "db:migrate": "prisma migrate deploy", }, }); fs.copySync(clientSrc, clientDest); }; ================================================ FILE: cli/src/installers/tailwind.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; export const tailwindInstaller: Installer = ({ projectDir }) => { addPackageDependency({ projectDir, dependencies: ["tailwindcss", "postcss", "@tailwindcss/postcss"], devMode: true, }); const extrasDir = path.join(PKG_ROOT, "template/extras"); const postcssCfgSrc = path.join(extrasDir, "config/postcss.config.js"); const postcssCfgDest = path.join(projectDir, "postcss.config.js"); const cssSrc = path.join(extrasDir, "src/styles/globals.css"); const cssDest = path.join(projectDir, "src/styles/globals.css"); fs.copySync(postcssCfgSrc, postcssCfgDest); fs.copySync(cssSrc, cssDest); }; ================================================ FILE: cli/src/installers/trpc.ts ================================================ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "~/consts.js"; import { type Installer } from "~/installers/index.js"; import { addPackageDependency } from "~/utils/addPackageDependency.js"; export const trpcInstaller: Installer = ({ projectDir, packages, appRouter, }) => { addPackageDependency({ projectDir, dependencies: [ "@tanstack/react-query", "superjson", "@trpc/server", "@trpc/client", "@trpc/react-query", ], devMode: false, }); const usingAuth = packages?.nextAuth.inUse; const usingBetterAuth = packages?.betterAuth.inUse; const usingPrisma = packages?.prisma.inUse; const usingDrizzle = packages?.drizzle.inUse; const usingDb = usingPrisma === true || usingDrizzle === true; const extrasDir = path.join(PKG_ROOT, "template/extras"); const apiHandlerFile = "src/pages/api/trpc/[trpc].ts"; const routeHandlerFile = "src/app/api/trpc/[trpc]/route.ts"; const srcToUse = appRouter ? routeHandlerFile : apiHandlerFile; const apiHandlerSrc = path.join(extrasDir, srcToUse); const apiHandlerDest = path.join(projectDir, srcToUse); const trpcFile = (() => { if (usingBetterAuth && usingDb) return "with-better-auth-db.ts"; if (usingBetterAuth) return "with-better-auth.ts"; if (usingAuth && usingDb) return "with-auth-db.ts"; if (usingAuth) return "with-auth.ts"; if (usingDb) return "with-db.ts"; return "base.ts"; })(); const trpcSrc = path.join( extrasDir, "src/server/api", appRouter ? "trpc-app" : "trpc-pages", trpcFile ); const trpcDest = path.join(projectDir, "src/server/api/trpc.ts"); const rootRouterSrc = path.join(extrasDir, "src/server/api/root.ts"); const rootRouterDest = path.join(projectDir, "src/server/api/root.ts"); const exampleRouterFile = (usingAuth || usingBetterAuth) && usingPrisma ? "with-auth-prisma.ts" : (usingAuth || usingBetterAuth) && usingDrizzle ? "with-auth-drizzle.ts" : usingAuth || usingBetterAuth ? "with-auth.ts" : usingPrisma ? "with-prisma.ts" : usingDrizzle ? "with-drizzle.ts" : "base.ts"; const exampleRouterSrc = path.join( extrasDir, "src/server/api/routers/post", exampleRouterFile ); const exampleRouterDest = path.join( projectDir, "src/server/api/routers/post.ts" ); const copySrcDest: [string, string][] = [ [apiHandlerSrc, apiHandlerDest], [trpcSrc, trpcDest], [rootRouterSrc, rootRouterDest], [exampleRouterSrc, exampleRouterDest], ]; if (appRouter) { addPackageDependency({ dependencies: ["server-only"], devMode: false, projectDir, }); const trpcDir = path.join(extrasDir, "src/trpc"); copySrcDest.push( [ path.join(trpcDir, "server.ts"), path.join(projectDir, "src/trpc/server.ts"), ], [ path.join(trpcDir, "react.tsx"), path.join(projectDir, "src/trpc/react.tsx"), ], [ path.join( extrasDir, "src/app/_components", packages?.tailwind.inUse ? "post-tw.tsx" : "post.tsx" ), path.join(projectDir, "src/app/_components/post.tsx"), ], [ path.join(extrasDir, "src/trpc/query-client.ts"), path.join(projectDir, "src/trpc/query-client.ts"), ] ); } else { addPackageDependency({ dependencies: ["@trpc/next"], devMode: false, projectDir, }); const utilsSrc = path.join(extrasDir, "src/utils/api.ts"); const utilsDest = path.join(projectDir, "src/utils/api.ts"); copySrcDest.push([utilsSrc, utilsDest]); } copySrcDest.forEach(([src, dest]) => { fs.copySync(src, dest); }); }; ================================================ FILE: cli/src/utils/addPackageDependency.ts ================================================ import path from "path"; import fs from "fs-extra"; import sortPackageJson from "sort-package-json"; import { type PackageJson } from "type-fest"; import { dependencyVersionMap, type AvailableDependencies, } from "~/installers/dependencyVersionMap.js"; export const addPackageDependency = (opts: { dependencies: AvailableDependencies[]; devMode: boolean; projectDir: string; }) => { const { dependencies, devMode, projectDir } = opts; const pkgJson = fs.readJSONSync( path.join(projectDir, "package.json") ) as PackageJson; dependencies.forEach((pkgName) => { const version = dependencyVersionMap[pkgName]; if (devMode && pkgJson.devDependencies) { pkgJson.devDependencies[pkgName] = version; } else if (pkgJson.dependencies) { pkgJson.dependencies[pkgName] = version; } }); const sortedPkgJson = sortPackageJson(pkgJson); fs.writeJSONSync(path.join(projectDir, "package.json"), sortedPkgJson, { spaces: 2, }); }; ================================================ FILE: cli/src/utils/addPackageScript.ts ================================================ import path from "path"; import fs from "fs-extra"; import sortPackageJson from "sort-package-json"; import { type PackageJson } from "type-fest"; export const addPackageScript = (opts: { scripts: Record; projectDir: string; }) => { const { scripts, projectDir } = opts; const packageJsonPath = path.join(projectDir, "package.json"); const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson; packageJsonContent.scripts = { ...packageJsonContent.scripts, ...scripts, }; const sortedPkgJson = sortPackageJson(packageJsonContent); fs.writeJSONSync(packageJsonPath, sortedPkgJson, { spaces: 2, }); }; ================================================ FILE: cli/src/utils/getT3Version.ts ================================================ import path from "path"; import fs from "fs-extra"; import { type PackageJson } from "type-fest"; import { PKG_ROOT } from "~/consts.js"; export const getVersion = () => { const packageJsonPath = path.join(PKG_ROOT, "package.json"); const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson; return packageJsonContent.version ?? "1.0.0"; }; ================================================ FILE: cli/src/utils/getUserPkgManager.ts ================================================ export type PackageManager = "npm" | "pnpm" | "yarn" | "bun"; export const getUserPkgManager: () => PackageManager = () => { // This environment variable is set by npm and yarn but pnpm seems less consistent const userAgent = process.env.npm_config_user_agent; if (userAgent) { if (userAgent.startsWith("yarn")) { return "yarn"; } else if (userAgent.startsWith("pnpm")) { return "pnpm"; } else if (userAgent.startsWith("bun")) { return "bun"; } else { return "npm"; } } else { // If no user agent is set, assume npm return "npm"; } }; ================================================ FILE: cli/src/utils/isTTYError.ts ================================================ export class IsTTYError extends Error { constructor(msg: string) { super(msg); } } ================================================ FILE: cli/src/utils/logger.ts ================================================ import chalk from "chalk"; export const logger = { error(...args: unknown[]) { console.log(chalk.red(...args)); }, warn(...args: unknown[]) { console.log(chalk.yellow(...args)); }, info(...args: unknown[]) { console.log(chalk.cyan(...args)); }, success(...args: unknown[]) { console.log(chalk.green(...args)); }, }; ================================================ FILE: cli/src/utils/parseNameAndPath.ts ================================================ import pathModule from "path"; import { removeTrailingSlash } from "./removeTrailingSlash.js"; /** * Parses the appName and its path from the user input. * * Returns a tuple of of `[appName, path]`, where `appName` is the name put in the "package.json" * file and `path` is the path to the directory where the app will be created. * * If `appName` is ".", the name of the directory will be used instead. Handles the case where the * input includes a scoped package name in which case that is being parsed as the name, but not * included as the path. * * For example: * * - dir/@mono/app => ["@mono/app", "dir/app"] * - dir/app => ["app", "dir/app"] */ export const parseNameAndPath = (rawInput: string) => { const input = removeTrailingSlash(rawInput); const paths = input.split("/"); let appName = paths[paths.length - 1]!; // If the user ran `npx create-t3-app .` or similar, the appName should be the current directory if (appName === ".") { const parsedCwd = pathModule.resolve(process.cwd()); appName = pathModule.basename(parsedCwd); } // If the first part is a @, it's a scoped package const indexOfDelimiter = paths.findIndex((p) => p.startsWith("@")); if (paths.findIndex((p) => p.startsWith("@")) !== -1) { appName = paths.slice(indexOfDelimiter).join("/"); } const path = paths.filter((p) => !p.startsWith("@")).join("/"); return [appName, path] as const; }; ================================================ FILE: cli/src/utils/removeTrailingSlash.ts ================================================ export const removeTrailingSlash = (input: string) => { if (input.length > 1 && input.endsWith("/")) { input = input.slice(0, -1); } return input; }; ================================================ FILE: cli/src/utils/renderTitle.ts ================================================ import gradient from "gradient-string"; import { TITLE_TEXT } from "~/consts.js"; import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; // colors brought in from vscode poimandres theme const poimandresTheme = { blue: "#add7ff", cyan: "#89ddff", green: "#5de4c7", magenta: "#fae4fc", red: "#d0679d", yellow: "#fffac2", }; export const renderTitle = () => { const t3Gradient = gradient(Object.values(poimandresTheme)); // resolves weird behavior where the ascii is offset const pkgManager = getUserPkgManager(); if (pkgManager === "yarn" || pkgManager === "pnpm") { console.log(""); } console.log(t3Gradient.multiline(TITLE_TEXT)); }; ================================================ FILE: cli/src/utils/renderVersionWarning.ts ================================================ import { execSync } from "child_process"; import https from "https"; import { getVersion } from "./getT3Version.js"; import { logger } from "./logger.js"; export const renderVersionWarning = (npmVersion: string) => { const currentVersion = getVersion(); // console.log("current", currentVersion); // console.log("npm", npmVersion); if (currentVersion.includes("beta")) { logger.warn(" You are using a beta version of create-t3-app."); logger.warn(" Please report any bugs you encounter."); } else if (currentVersion.includes("next")) { logger.warn( " You are running create-t3-app with the @next tag which is no longer maintained." ); logger.warn(" Please run the CLI with @latest instead."); } else if (currentVersion !== npmVersion) { logger.warn(" You are using an outdated version of create-t3-app."); logger.warn( " Your version:", currentVersion + ".", "Latest version in the npm registry:", npmVersion ); logger.warn(" Please run the CLI with @latest to get the latest updates."); } console.log(""); }; /** * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the LICENSE file in the root * directory of this source tree. * https://github.com/facebook/create-react-app/blob/main/packages/create-react-app/LICENSE */ interface DistTagsBody { latest: string; } function checkForLatestVersion(): Promise { return new Promise((resolve, reject) => { https .get( "https://registry.npmjs.org/-/package/create-t3-app/dist-tags", (res) => { if (res.statusCode === 200) { let body = ""; res.on("data", (data) => (body += data)); res.on("end", () => { resolve((JSON.parse(body) as DistTagsBody).latest); }); } else { // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(); } } ) .on("error", () => { // logger.error("Unable to check for latest version."); // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(); }); }); } export const getNpmVersion = () => // `fetch` to the registry is faster than `npm view` so we try that first checkForLatestVersion().catch(() => { try { return execSync("npm view create-t3-app version").toString().trim(); } catch { return null; } }); ================================================ FILE: cli/src/utils/validateAppName.ts ================================================ import { removeTrailingSlash } from "./removeTrailingSlash.js"; const validationRegExp = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/; //Validate a string against allowed package.json names export const validateAppName = (rawInput: string) => { const input = removeTrailingSlash(rawInput); const paths = input.split("/"); // If the first part is a @, it's a scoped package const indexOfDelimiter = paths.findIndex((p) => p.startsWith("@")); let appName = paths[paths.length - 1]; if (paths.findIndex((p) => p.startsWith("@")) !== -1) { appName = paths.slice(indexOfDelimiter).join("/"); } if (input === "." || validationRegExp.test(appName ?? "")) { return; } else { return "App name must consist of only lowercase alphanumeric characters, '-', and '_'"; } }; ================================================ FILE: cli/src/utils/validateImportAlias.ts ================================================ export const validateImportAlias = (input: string) => { if (input.startsWith(".") || input.startsWith("/")) { return "Import alias can't start with '.' or '/'"; } return; }; ================================================ FILE: cli/template/base/README.md ================================================ # Create T3 App This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`. ## What's next? How do I make an app with this? We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary. If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help. - [Next.js](https://nextjs.org) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Drizzle](https://orm.drizzle.team) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## Learn More To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources: - [Documentation](https://create.t3.gg/) - [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome! ## How do I deploy this? Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information. ================================================ FILE: cli/template/base/_gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # database /prisma/db.sqlite /prisma/db.sqlite-journal db.sqlite /generated/ # next.js /.next/ /out/ next-env.d.ts # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* # local env files # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables .env .env*.local # vercel .vercel # typescript *.tsbuildinfo # idea files .idea ================================================ FILE: cli/template/base/next.config.js ================================================ /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful * for Docker builds. */ import "./src/env.js"; /** @type {import("next").NextConfig} */ const config = { reactStrictMode: true, /** * If you are using `appDir` then you must comment the below `i18n` config out. * * @see https://github.com/vercel/next.js/issues/41980 */ i18n: { locales: ["en"], defaultLocale: "en", }, }; export default config; ================================================ FILE: cli/template/base/package.json ================================================ { "name": "template", "version": "0.1.0", "type": "module", "private": true, "scripts": { "dev": "next dev --turbo", "build": "next build", "start": "next start", "preview": "next build && next start", "typecheck": "tsc --noEmit" }, "dependencies": { "@t3-oss/env-nextjs": "^0.12.0", "next": "^15.5.9", "react": "^19.2.3", "react-dom": "^19.2.3", "zod": "^3.24.2" }, "devDependencies": { "@types/node": "^24.10.1", "@types/react": "~19.1.0", "@types/react-dom": "~19.1.0", "typescript": "^5.8.2" } } ================================================ FILE: cli/template/base/src/env.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/base/src/styles/globals.css ================================================ html, body { padding: 0; margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; } a { color: inherit; text-decoration: none; } * { box-sizing: border-box; } ================================================ FILE: cli/template/base/tsconfig.json ================================================ { "compilerOptions": { /* Base Options: */ "esModuleInterop": true, "skipLibCheck": true, "target": "es2022", "allowJs": true, "resolveJsonModule": true, "moduleDetection": "force", "isolatedModules": true, "verbatimModuleSyntax": true, /* Strictness */ "strict": true, "noUncheckedIndexedAccess": true, "checkJs": true, /* Bundled projects */ "lib": ["dom", "dom.iterable", "ES2022"], "noEmit": true, "module": "ESNext", "moduleResolution": "Bundler", "jsx": "preserve", "plugins": [{ "name": "next" }], "incremental": true, /* Path Aliases */ "baseUrl": ".", "paths": { "~/*": ["./src/*"] } }, "include": [ "next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.js", ".next/types/**/*.ts" ], "exclude": ["node_modules", "generated"] } ================================================ FILE: cli/template/extras/config/_eslint.base.js ================================================ import { FlatCompat } from "@eslint/eslintrc"; import tseslint from 'typescript-eslint'; const compat = new FlatCompat({ baseDirectory: import.meta.dirname, }); export default tseslint.config( { ignores: ['.next'] }, ...compat.extends("next/core-web-vitals"), { files: ['**/*.ts', '**/*.tsx'], extends: [ ...tseslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked ], rules: { "@typescript-eslint/array-type": "off", "@typescript-eslint/consistent-type-definitions": "off", "@typescript-eslint/consistent-type-imports": [ "warn", { prefer: "type-imports", fixStyle: "inline-type-imports" }, ], "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], "@typescript-eslint/require-await": "off", "@typescript-eslint/no-misused-promises": [ "error", { checksVoidReturn: { attributes: false } }, ], }, }, { linterOptions: { reportUnusedDisableDirectives: true }, languageOptions: { parserOptions: { projectService: true } } } ) ================================================ FILE: cli/template/extras/config/_eslint.drizzle.js ================================================ import { FlatCompat } from "@eslint/eslintrc"; import tseslint from 'typescript-eslint'; // @ts-ignore -- no types for this plugin import drizzle from "eslint-plugin-drizzle"; const compat = new FlatCompat({ baseDirectory: import.meta.dirname, }); export default tseslint.config( { ignores: ['.next'] }, ...compat.extends("next/core-web-vitals"), { files: ['**/*.ts', '**/*.tsx'], plugins: { drizzle, }, extends: [ ...tseslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked ], rules: { "@typescript-eslint/array-type": "off", "@typescript-eslint/consistent-type-definitions": "off", "@typescript-eslint/consistent-type-imports": [ "warn", { prefer: "type-imports", fixStyle: "inline-type-imports" }, ], "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], "@typescript-eslint/require-await": "off", "@typescript-eslint/no-misused-promises": [ "error", { checksVoidReturn: { attributes: false } }, ], "drizzle/enforce-delete-with-where": [ "error", { drizzleObjectName: ["db", "ctx.db"] }, ], "drizzle/enforce-update-with-where": [ "error", { drizzleObjectName: ["db", "ctx.db"] }, ], }, }, { linterOptions: { reportUnusedDisableDirectives: true }, languageOptions: { parserOptions: { projectService: true } } } ) ================================================ FILE: cli/template/extras/config/_prettier.config.js ================================================ /** @type {import('prettier').Config} */ export default {}; ================================================ FILE: cli/template/extras/config/_tailwind.prettier.config.js ================================================ /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ export default { plugins: ["prettier-plugin-tailwindcss"], }; ================================================ FILE: cli/template/extras/config/biome.jsonc ================================================ { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", "root": true, "vcs": { "enabled": true, "useIgnoreFile": true, "clientKind": "git", }, "assist": { "enabled": true, "actions": { "recommended": true, "source": { "recommended": true, "organizeImports": "on", "useSortedAttributes": "on", }, }, }, "formatter": { "enabled": true, }, "linter": { "enabled": true, "rules": { "recommended": true, "nursery": { "useSortedClasses": { "level": "warn", "fix": "safe", "options": { "functions": ["clsx", "cva", "cn"], }, }, }, }, }, "html": { "formatter": { "enabled": true, }, }, "javascript": { "assist": { "enabled": true, }, "formatter": { "enabled": true, }, "linter": { "enabled": true, }, }, "css": { "assist": { "enabled": true, }, "formatter": { "enabled": true, }, "linter": { "enabled": true, }, "parser": { "cssModules": true, "tailwindDirectives": true, }, }, } ================================================ FILE: cli/template/extras/config/drizzle-config-mysql.ts ================================================ import { type Config } from "drizzle-kit"; import { env } from "~/env"; export default { schema: "./src/server/db/schema.ts", dialect: "mysql", dbCredentials: { url: env.DATABASE_URL, }, tablesFilter: ["project1_*"], } satisfies Config; ================================================ FILE: cli/template/extras/config/drizzle-config-postgres.ts ================================================ import { type Config } from "drizzle-kit"; import { env } from "~/env"; export default { schema: "./src/server/db/schema.ts", dialect: "postgresql", dbCredentials: { url: env.DATABASE_URL, }, tablesFilter: ["project1_*"], } satisfies Config; ================================================ FILE: cli/template/extras/config/drizzle-config-sqlite.ts ================================================ import { type Config } from "drizzle-kit"; import { env } from "~/env"; export default { schema: "./src/server/db/schema.ts", dialect: "sqlite", dbCredentials: { url: env.DATABASE_URL, }, tablesFilter: ["project1_*"], } satisfies Config; ================================================ FILE: cli/template/extras/config/next-config-appdir.js ================================================ /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful * for Docker builds. */ import "./src/env.js"; /** @type {import("next").NextConfig} */ const config = {}; export default config; ================================================ FILE: cli/template/extras/config/postcss.config.js ================================================ export default { plugins: { "@tailwindcss/postcss": {}, }, }; ================================================ FILE: cli/template/extras/pnpm/_npmrc ================================================ public-hoist-pattern[]=*eslint* public-hoist-pattern[]=*prettier* ================================================ FILE: cli/template/extras/prisma/schema/base-planetscale.prisma ================================================ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" previewFeatures = ["driverAdapters"] output = "../generated/prisma" } datasource db { provider = "mysql" url = env("DATABASE_URL") // If you have enabled foreign key constraints for your database, remove this line. relationMode = "prisma" } model Post { id Int @id @default(autoincrement()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([name]) } ================================================ FILE: cli/template/extras/prisma/schema/base.prisma ================================================ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" output = "../generated/prisma" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Post { id Int @id @default(autoincrement()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([name]) } ================================================ FILE: cli/template/extras/prisma/schema/with-auth-planetscale.prisma ================================================ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" output = "../generated/prisma" previewFeatures = ["driverAdapters"] } datasource db { provider = "mysql" url = env("DATABASE_URL") // If you have enabled foreign key constraints for your database, remove this line. relationMode = "prisma" } model Post { id Int @id @default(autoincrement()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User @relation(fields: [createdById], references: [id]) createdById String @@index([name]) @@index([createdById]) } // Necessary for Next auth model Account { id String @id @default(cuid()) userId String type String provider String providerAccountId String refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@index([userId]) } model Session { id String @id @default(cuid()) sessionToken String @unique userId String expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) } model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? image String? accounts Account[] sessions Session[] posts Post[] } model VerificationToken { identifier String token String @unique expires DateTime @@unique([identifier, token]) } ================================================ FILE: cli/template/extras/prisma/schema/with-auth.prisma ================================================ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" output = "../generated/prisma" } datasource db { provider = "sqlite" // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below // Further reading: // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string url = env("DATABASE_URL") } model Post { id Int @id @default(autoincrement()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User @relation(fields: [createdById], references: [id]) createdById String @@index([name]) } // Necessary for Next auth model Account { id String @id @default(cuid()) userId String type String provider String providerAccountId String refresh_token String? // @db.Text access_token String? // @db.Text expires_at Int? token_type String? scope String? id_token String? // @db.Text session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) refresh_token_expires_in Int? @@unique([provider, providerAccountId]) } model Session { id String @id @default(cuid()) sessionToken String @unique userId String expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? image String? accounts Account[] sessions Session[] posts Post[] } model VerificationToken { identifier String token String @unique expires DateTime @@unique([identifier, token]) } ================================================ FILE: cli/template/extras/prisma/schema/with-better-auth-planetscale.prisma ================================================ // Prisma schema for Better Auth // learn more: https://better-auth.com/docs/concepts/database generator client { provider = "prisma-client-js" output = "../generated/prisma" } // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below // Further reading: // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string datasource db { provider = "mysql" relationMode = "prisma" url = env("DATABASE_URL") } model Post { id String @id @default(cuid()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User @relation(fields: [createdById], references: [id]) createdById String @@index([name]) } model User { id String @id name String @db.Text email String emailVerified Boolean @default(false) image String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt sessions Session[] accounts Account[] posts Post[] @@unique([email]) @@map("user") } model Session { id String @id expiresAt DateTime token String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ipAddress String? @db.Text userAgent String? @db.Text userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([token]) @@map("session") } model Account { id String @id accountId String @db.Text providerId String @db.Text userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) accessToken String? @db.Text refreshToken String? @db.Text idToken String? @db.Text accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? @db.Text password String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("account") } model Verification { id String @id identifier String @db.Text value String @db.Text expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("verification") } ================================================ FILE: cli/template/extras/prisma/schema/with-better-auth.prisma ================================================ // Prisma schema for Better Auth // learn more: https://better-auth.com/docs/concepts/database generator client { provider = "prisma-client-js" output = "../generated/prisma" } // NOTE: When using mysql or sqlserver, uncomment the //@db.Text annotations in model Account below // Further reading: // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Post { id String @id @default(cuid()) name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User @relation(fields: [createdById], references: [id]) createdById String @@index([name]) } model User { id String @id name String //@db.Text email String emailVerified Boolean @default(false) image String? //@db.Text createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt sessions Session[] accounts Account[] posts Post[] @@unique([email]) @@map("user") } model Session { id String @id expiresAt DateTime token String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ipAddress String? //@db.Text userAgent String? //@db.Text userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([token]) @@map("session") } model Account { id String @id accountId String //@db.Text providerId String //@db.Text userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) accessToken String? //@db.Text refreshToken String? //@db.Text idToken String? //@db.Text accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? //@db.Text password String? //@db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("account") } model Verification { id String @id identifier String //@db.Text value String //@db.Text expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt @@map("verification") } ================================================ FILE: cli/template/extras/src/app/_components/post-tw.tsx ================================================ "use client"; import { useState } from "react"; import { api } from "~/trpc/react"; export function LatestPost() { const [latestPost] = api.post.getLatest.useSuspenseQuery(); const utils = api.useUtils(); const [name, setName] = useState(""); const createPost = api.post.create.useMutation({ onSuccess: async () => { await utils.post.invalidate(); setName(""); }, }); return (
{latestPost ? (

Your most recent post: {latestPost.name}

) : (

You have no posts yet.

)}
{ e.preventDefault(); createPost.mutate({ name }); }} className="flex flex-col gap-2" > setName(e.target.value)} className="w-full rounded-full bg-white/10 px-4 py-2 text-white" />
); } ================================================ FILE: cli/template/extras/src/app/_components/post.tsx ================================================ "use client"; import { useState } from "react"; import { api } from "~/trpc/react"; import styles from "../index.module.css"; export function LatestPost() { const [latestPost] = api.post.getLatest.useSuspenseQuery(); const utils = api.useUtils(); const [name, setName] = useState(""); const createPost = api.post.create.useMutation({ onSuccess: async () => { await utils.post.invalidate(); setName(""); }, }); return (
{latestPost ? (

Your most recent post: {latestPost.name}

) : (

You have no posts yet.

)}
{ e.preventDefault(); createPost.mutate({ name }); }} className={styles.form} > setName(e.target.value)} className={styles.input} />
); } ================================================ FILE: cli/template/extras/src/app/api/auth/[...all]/route.ts ================================================ import { toNextJsHandler } from "better-auth/next-js"; import { auth } from "~/server/better-auth"; export const { GET, POST } = toNextJsHandler(auth.handler); ================================================ FILE: cli/template/extras/src/app/api/auth/[...nextauth]/route.ts ================================================ import { handlers } from "~/server/auth"; export const { GET, POST } = handlers; ================================================ FILE: cli/template/extras/src/app/api/trpc/[trpc]/route.ts ================================================ import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { type NextRequest } from "next/server"; import { env } from "~/env"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; /** * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when * handling a HTTP request (e.g. when you make requests from Client Components). */ const createContext = async (req: NextRequest) => { return createTRPCContext({ headers: req.headers, }); }; const handler = (req: NextRequest) => fetchRequestHandler({ endpoint: "/api/trpc", req, router: appRouter, createContext: () => createContext(req), onError: env.NODE_ENV === "development" ? ({ path, error }) => { console.error( `❌ tRPC failed on ${path ?? ""}: ${error.message}` ); } : undefined, }); export { handler as GET, handler as POST }; ================================================ FILE: cli/template/extras/src/app/layout/base.tsx ================================================ import "~/styles/globals.css"; import { type Metadata } from "next"; import { Geist } from "next/font/google"; export const metadata: Metadata = { title: "Create T3 App", description: "Generated by create-t3-app", icons: [{ rel: "icon", url: "/favicon.ico" }], }; const geist = Geist({ subsets: ["latin"], }); export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( {children} ); } ================================================ FILE: cli/template/extras/src/app/layout/with-trpc-tw.tsx ================================================ import "~/styles/globals.css"; import { type Metadata } from "next"; import { Geist } from "next/font/google"; import { TRPCReactProvider } from "~/trpc/react"; export const metadata: Metadata = { title: "Create T3 App", description: "Generated by create-t3-app", icons: [{ rel: "icon", url: "/favicon.ico" }], }; const geist = Geist({ subsets: ["latin"], variable: "--font-geist-sans", }); export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( {children} ); } ================================================ FILE: cli/template/extras/src/app/layout/with-trpc.tsx ================================================ import "~/styles/globals.css"; import { type Metadata } from "next"; import { Geist } from "next/font/google"; import { TRPCReactProvider } from "~/trpc/react"; export const metadata: Metadata = { title: "Create T3 App", description: "Generated by create-t3-app", icons: [{ rel: "icon", url: "/favicon.ico" }], }; const geist = Geist({ subsets: ["latin"], }); export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( {children} ); } ================================================ FILE: cli/template/extras/src/app/layout/with-tw.tsx ================================================ import "~/styles/globals.css"; import { type Metadata } from "next"; import { Geist } from "next/font/google"; export const metadata: Metadata = { title: "Create T3 App", description: "Generated by create-t3-app", icons: [{ rel: "icon", url: "/favicon.ico" }], }; const geist = Geist({ subsets: ["latin"], variable: "--font-geist-sans", }); export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( {children} ); } ================================================ FILE: cli/template/extras/src/app/page/base.tsx ================================================ import Link from "next/link"; import styles from "./index.module.css"; export default function Home() { return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.
); } ================================================ FILE: cli/template/extras/src/app/page/with-auth-trpc-tw.tsx ================================================ import Link from "next/link"; import { LatestPost } from "~/app/_components/post"; import { auth } from "~/server/auth"; import { api, HydrateClient } from "~/trpc/server"; export default async function Home() { const hello = await api.post.hello({ text: "from tRPC" }); const session = await auth(); if (session?.user) { void api.post.getLatest.prefetch(); } return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello ? hello.greeting : "Loading tRPC query..."}

{session && Logged in as {session.user?.name}}

{session ? "Sign out" : "Sign in"}
{session?.user && }
); } ================================================ FILE: cli/template/extras/src/app/page/with-auth-trpc.tsx ================================================ import Link from "next/link"; import { LatestPost } from "~/app/_components/post"; import { auth } from "~/server/auth"; import { api, HydrateClient } from "~/trpc/server"; import styles from "./index.module.css"; export default async function Home() { const hello = await api.post.hello({ text: "from tRPC" }); const session = await auth(); if (session?.user) { void api.post.getLatest.prefetch(); } return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello ? hello.greeting : "Loading tRPC query..."}

{session && Logged in as {session.user?.name}}

{session ? "Sign out" : "Sign in"}
{session?.user && }
); } ================================================ FILE: cli/template/extras/src/app/page/with-better-auth-trpc-tw.tsx ================================================ import { headers } from "next/headers"; import Link from "next/link"; import { redirect } from "next/navigation"; import { LatestPost } from "~/app/_components/post"; import { auth } from "~/server/better-auth"; import { getSession } from "~/server/better-auth/server"; import { api, HydrateClient } from "~/trpc/server"; export default async function Home() { const hello = await api.post.hello({ text: "from tRPC" }); const session = await getSession(); if (session) { void api.post.getLatest.prefetch(); } return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello ? hello.greeting : "Loading tRPC query..."}

{session && Logged in as {session.user?.name}}

{!session ? (
) : (
)}
{session?.user && }
); } ================================================ FILE: cli/template/extras/src/app/page/with-better-auth-trpc.tsx ================================================ import { headers } from "next/headers"; import Link from "next/link"; import { redirect } from "next/navigation"; import { LatestPost } from "~/app/_components/post"; import { auth } from "~/server/better-auth"; import { getSession } from "~/server/better-auth/server"; import { api, HydrateClient } from "~/trpc/server"; import styles from "./index.module.css"; export default async function Home() { const hello = await api.post.hello({ text: "from tRPC" }); const session = await getSession(); if (session?.user) { void api.post.getLatest.prefetch(); } return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello ? hello.greeting : "Loading tRPC query..."}

{session && Logged in as {session.user?.name}}

{!session ? (
) : (
)}
{session?.user && }
); } ================================================ FILE: cli/template/extras/src/app/page/with-better-auth-tw.tsx ================================================ import { headers } from "next/headers"; import Link from "next/link"; import { redirect } from "next/navigation"; import { auth } from "~/server/better-auth"; import { getSession } from "~/server/better-auth/server"; export default async function Home() { const session = await getSession(); return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{session && Logged in as {session.user?.name}}

{!session ? (
) : (
)}
); } ================================================ FILE: cli/template/extras/src/app/page/with-better-auth.tsx ================================================ import { headers } from "next/headers"; import Link from "next/link"; import { redirect } from "next/navigation"; import { auth } from "~/server/better-auth"; import { getSession } from "~/server/better-auth/server"; import styles from "./index.module.css"; export default async function Home() { const session = await getSession(); return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{session && Logged in as {session.user?.name}}

{!session ? (
) : (
)}
); } ================================================ FILE: cli/template/extras/src/app/page/with-trpc-tw.tsx ================================================ import Link from "next/link"; import { LatestPost } from "~/app/_components/post"; import { api, HydrateClient } from "~/trpc/server"; export default async function Home() { const hello = await api.post.hello({ text: "from tRPC" }); void api.post.getLatest.prefetch(); return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello ? hello.greeting : "Loading tRPC query..."}

); } ================================================ FILE: cli/template/extras/src/app/page/with-trpc.tsx ================================================ import Link from "next/link"; import { LatestPost } from "~/app/_components/post"; import { api, HydrateClient } from "~/trpc/server"; import styles from "./index.module.css"; export default async function Home() { const hello = await api.post.hello({ text: "from tRPC" }); void api.post.getLatest.prefetch(); return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello ? hello.greeting : "Loading tRPC query..."}

); } ================================================ FILE: cli/template/extras/src/app/page/with-tw.tsx ================================================ import Link from "next/link"; export default function HomePage() { return (

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.
); } ================================================ FILE: cli/template/extras/src/env/with-auth-db-planetscale.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string() : z.string().optional(), AUTH_DISCORD_ID: z.string(), AUTH_DISCORD_SECRET: z.string(), DATABASE_URL: z .string() .url() .refine( (str) => !str.includes("YOUR_MYSQL_URL_HERE"), "You forgot to change the default URL" ), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { AUTH_SECRET: process.env.AUTH_SECRET, AUTH_DISCORD_ID: process.env.AUTH_DISCORD_ID, AUTH_DISCORD_SECRET: process.env.AUTH_DISCORD_SECRET, DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-auth-db.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string() : z.string().optional(), AUTH_DISCORD_ID: z.string(), AUTH_DISCORD_SECRET: z.string(), DATABASE_URL: z.string().url(), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { AUTH_SECRET: process.env.AUTH_SECRET, AUTH_DISCORD_ID: process.env.AUTH_DISCORD_ID, AUTH_DISCORD_SECRET: process.env.AUTH_DISCORD_SECRET, DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-auth.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string() : z.string().optional(), AUTH_DISCORD_ID: z.string(), AUTH_DISCORD_SECRET: z.string(), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { AUTH_SECRET: process.env.AUTH_SECRET, AUTH_DISCORD_ID: process.env.AUTH_DISCORD_ID, AUTH_DISCORD_SECRET: process.env.AUTH_DISCORD_SECRET, NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-better-auth-db-planetscale.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { BETTER_AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string() : z.string().optional(), BETTER_AUTH_GITHUB_CLIENT_ID: z.string(), BETTER_AUTH_GITHUB_CLIENT_SECRET: z.string(), DATABASE_URL: z .string() .url() .refine( (str) => !str.includes("YOUR_MYSQL_URL_HERE"), "You forgot to change the default URL" ), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET, BETTER_AUTH_GITHUB_CLIENT_ID: process.env.BETTER_AUTH_GITHUB_CLIENT_ID, BETTER_AUTH_GITHUB_CLIENT_SECRET: process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET, DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-better-auth-db.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { BETTER_AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string() : z.string().optional(), BETTER_AUTH_GITHUB_CLIENT_ID: z.string(), BETTER_AUTH_GITHUB_CLIENT_SECRET: z.string(), DATABASE_URL: z.string().url(), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET, BETTER_AUTH_GITHUB_CLIENT_ID: process.env.BETTER_AUTH_GITHUB_CLIENT_ID, BETTER_AUTH_GITHUB_CLIENT_SECRET: process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET, DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-better-auth.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { BETTER_AUTH_SECRET: process.env.NODE_ENV === "production" ? z.string() : z.string().optional(), BETTER_AUTH_GITHUB_CLIENT_ID: z.string(), BETTER_AUTH_GITHUB_CLIENT_SECRET: z.string(), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET, BETTER_AUTH_GITHUB_CLIENT_ID: process.env.BETTER_AUTH_GITHUB_CLIENT_ID, BETTER_AUTH_GITHUB_CLIENT_SECRET: process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET, NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-db-planetscale.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { DATABASE_URL: z .string() .url() .refine( (str) => !str.includes("YOUR_MYSQL_URL_HERE"), "You forgot to change the default URL" ), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/env/with-db.js ================================================ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. */ server: { DATABASE_URL: z.string().url(), NODE_ENV: z .enum(["development", "test", "production"]) .default("development"), }, /** * Specify your client-side environment variables schema here. This way you can ensure the app * isn't built with invalid env vars. To expose them to the client, prefix them with * `NEXT_PUBLIC_`. */ client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. * middlewares) or client-side so we need to destruct manually. */ runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially * useful for Docker builds. */ skipValidation: !!process.env.SKIP_ENV_VALIDATION, /** * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, }); ================================================ FILE: cli/template/extras/src/index.module.css ================================================ .main { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; background-image: linear-gradient(to bottom, #2e026d, #15162c); } .container { width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3rem; padding: 4rem 1rem; } @media (min-width: 640px) { .container { max-width: 640px; } } @media (min-width: 768px) { .container { max-width: 768px; } } @media (min-width: 1024px) { .container { max-width: 1024px; } } @media (min-width: 1280px) { .container { max-width: 1280px; } } @media (min-width: 1536px) { .container { max-width: 1536px; } } .title { font-size: 3rem; line-height: 1; font-weight: 800; letter-spacing: -0.025em; margin: 0; color: white; } @media (min-width: 640px) { .title { font-size: 5rem; } } .pinkSpan { color: hsl(280 100% 70%); } .cardRow { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)); gap: 1rem; } @media (min-width: 640px) { .cardRow { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (min-width: 768px) { .cardRow { gap: 2rem; } } .card { max-width: 20rem; display: flex; flex-direction: column; gap: 1rem; padding: 1rem; border-radius: 0.75rem; color: white; background-color: rgb(255 255 255 / 0.1); } .card:hover { background-color: rgb(255 255 255 / 0.2); transition: background-color 150ms cubic-bezier(0.5, 0, 0.2, 1); } .cardTitle { font-size: 1.5rem; line-height: 2rem; font-weight: 700; margin: 0; } .cardText { font-size: 1.125rem; line-height: 1.75rem; } .showcaseContainer { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } .showcaseText { color: white; text-align: center; font-size: 1.5rem; line-height: 2rem; } .authContainer { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1rem; } .loginButton { border-radius: 9999px; background-color: rgb(255 255 255 / 0.1); padding: 0.75rem 2.5rem; font-weight: 600; color: white; text-decoration-line: none; transition: background-color 150ms cubic-bezier(0.5, 0, 0.2, 1); } .loginButton:hover { background-color: rgb(255 255 255 / 0.2); } .form { display: flex; flex-direction: column; gap: 0.5rem; } .input { width: 100%; border-radius: 9999px; padding: 0.5rem 1rem; color: black; } .submitButton { all: unset; border-radius: 9999px; background-color: rgb(255 255 255 / 0.1); padding: 0.75rem 2.5rem; font-weight: 600; color: white; text-align: center; transition: background-color 150ms cubic-bezier(0.5, 0, 0.2, 1); } .submitButton:hover { background-color: rgb(255 255 255 / 0.2); } ================================================ FILE: cli/template/extras/src/pages/_app/base.tsx ================================================ import { type AppType } from "next/dist/shared/lib/utils"; import { Geist } from "next/font/google"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType = ({ Component, pageProps }) => { return (
); }; export default MyApp; ================================================ FILE: cli/template/extras/src/pages/_app/with-auth-trpc-tw.tsx ================================================ import { type Session } from "next-auth"; import { SessionProvider } from "next-auth/react"; import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import { api } from "~/utils/api"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType<{ session: Session | null }> = ({ Component, pageProps: { session, ...pageProps }, }) => { return (
); }; export default api.withTRPC(MyApp); ================================================ FILE: cli/template/extras/src/pages/_app/with-auth-trpc.tsx ================================================ import { type Session } from "next-auth"; import { SessionProvider } from "next-auth/react"; import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import { api } from "~/utils/api"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType<{ session: Session | null }> = ({ Component, pageProps: { session, ...pageProps }, }) => { return (
); }; export default api.withTRPC(MyApp); ================================================ FILE: cli/template/extras/src/pages/_app/with-auth-tw.tsx ================================================ import { type Session } from "next-auth"; import { SessionProvider } from "next-auth/react"; import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType<{ session: Session | null }> = ({ Component, pageProps: { session, ...pageProps }, }) => { return (
); }; export default MyApp; ================================================ FILE: cli/template/extras/src/pages/_app/with-auth.tsx ================================================ import { type Session } from "next-auth"; import { SessionProvider } from "next-auth/react"; import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType<{ session: Session | null }> = ({ Component, pageProps: { session, ...pageProps }, }) => { return (
); }; export default MyApp; ================================================ FILE: cli/template/extras/src/pages/_app/with-better-auth-trpc-tw.tsx ================================================ import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import { api } from "~/utils/api"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType = ({ Component, pageProps }) => { return (
); }; export default api.withTRPC(MyApp); ================================================ FILE: cli/template/extras/src/pages/_app/with-better-auth-trpc.tsx ================================================ import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import { api } from "~/utils/api"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType = ({ Component, pageProps }) => { return (
); }; export default api.withTRPC(MyApp); ================================================ FILE: cli/template/extras/src/pages/_app/with-trpc-tw.tsx ================================================ import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import { api } from "~/utils/api"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType = ({ Component, pageProps }) => { return (
); }; export default api.withTRPC(MyApp); ================================================ FILE: cli/template/extras/src/pages/_app/with-trpc.tsx ================================================ import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import { api } from "~/utils/api"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType = ({ Component, pageProps }) => { return (
); }; export default api.withTRPC(MyApp); ================================================ FILE: cli/template/extras/src/pages/_app/with-tw.tsx ================================================ import { type AppType } from "next/app"; import { Geist } from "next/font/google"; import "~/styles/globals.css"; const geist = Geist({ subsets: ["latin"], }); const MyApp: AppType = ({ Component, pageProps }) => { return (
); }; export default MyApp; ================================================ FILE: cli/template/extras/src/pages/api/auth/[...all].ts ================================================ import { toNodeHandler } from "better-auth/node"; import { auth } from "~/server/better-auth"; // Disallow body parsing, we will parse it manually export const config = { api: { bodyParser: false } }; export default toNodeHandler(auth.handler); ================================================ FILE: cli/template/extras/src/pages/api/trpc/[trpc].ts ================================================ import { createNextApiHandler } from "@trpc/server/adapters/next"; import { env } from "~/env"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; // export API handler export default createNextApiHandler({ router: appRouter, createContext: createTRPCContext, onError: env.NODE_ENV === "development" ? ({ path, error }) => { console.error( `❌ tRPC failed on ${path ?? ""}: ${error.message}` ); } : undefined, }); ================================================ FILE: cli/template/extras/src/pages/index/base.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import styles from "./index.module.css"; export default function Home() { return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.
); } ================================================ FILE: cli/template/extras/src/pages/index/with-auth-trpc-tw.tsx ================================================ import { signIn, signOut, useSession } from "next-auth/react"; import Head from "next/head"; import Link from "next/link"; import { api } from "~/utils/api"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello.data ? hello.data.greeting : "Loading tRPC query..."}

); } function AuthShowcase() { const { data: sessionData } = useSession(); const { data: secretMessage } = api.post.getSecretMessage.useQuery( undefined, // no input { enabled: sessionData?.user !== undefined } ); return (

{sessionData && Logged in as {sessionData.user?.name}} {secretMessage && - {secretMessage}}

); } ================================================ FILE: cli/template/extras/src/pages/index/with-auth-trpc.tsx ================================================ import { signIn, signOut, useSession } from "next-auth/react"; import Head from "next/head"; import Link from "next/link"; import { api } from "~/utils/api"; import styles from "./index.module.css"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello.data ? hello.data.greeting : "Loading tRPC query..."}

); } function AuthShowcase() { const { data: sessionData } = useSession(); const { data: secretMessage } = api.post.getSecretMessage.useQuery( undefined, // no input { enabled: sessionData?.user !== undefined } ); return (

{sessionData && Logged in as {sessionData.user?.name}} {secretMessage && - {secretMessage}}

); } ================================================ FILE: cli/template/extras/src/pages/index/with-better-auth-trpc-tw.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import { authClient } from "~/server/better-auth/client"; import { api } from "~/utils/api"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello.data ? hello.data.greeting : "Loading tRPC query..."}

); } function AuthShowcase() { const { data: sessionData, isPending } = authClient.useSession(); const { data: secretMessage } = api.post.getSecretMessage.useQuery( undefined, // no input { enabled: sessionData?.user !== undefined } ); if (isPending) { return

Loading...

; } return (

{sessionData && Logged in as {sessionData.user?.name}} {secretMessage && - {secretMessage}}

{sessionData ? ( ) : ( )}
); } ================================================ FILE: cli/template/extras/src/pages/index/with-better-auth-trpc.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import { authClient } from "~/server/better-auth/client"; import { api } from "~/utils/api"; import styles from "./index.module.css"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello.data ? hello.data.greeting : "Loading tRPC query..."}

); } function AuthShowcase() { const { data: sessionData, isPending } = authClient.useSession(); const { data: secretMessage } = api.post.getSecretMessage.useQuery( undefined, // no input { enabled: sessionData?.user !== undefined } ); if (isPending) { return

Loading...

; } return (

{sessionData && Logged in as {sessionData.user?.name}} {secretMessage && - {secretMessage}}

{sessionData ? ( ) : ( )}
); } ================================================ FILE: cli/template/extras/src/pages/index/with-better-auth-tw.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import { authClient } from "~/server/better-auth/client"; export default function Home() { return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.
); } function AuthShowcase() { const { data: sessionData, isPending } = authClient.useSession(); if (isPending) { return

Loading...

; } return (

{sessionData && Logged in as {sessionData.user?.name}}

{sessionData ? ( ) : ( )}
); } ================================================ FILE: cli/template/extras/src/pages/index/with-better-auth.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import { authClient } from "~/server/better-auth/client"; import styles from "./index.module.css"; export default function Home() { return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.
); } function AuthShowcase() { const { data: sessionData, isPending } = authClient.useSession(); if (isPending) { return

Loading...

; } return (

{sessionData && Logged in as {sessionData.user?.name}}

{sessionData ? ( ) : ( )}
); } ================================================ FILE: cli/template/extras/src/pages/index/with-trpc-tw.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import { api } from "~/utils/api"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello.data ? hello.data.greeting : "Loading tRPC query..."}

); } ================================================ FILE: cli/template/extras/src/pages/index/with-trpc.tsx ================================================ import Head from "next/head"; import Link from "next/link"; import { api } from "~/utils/api"; import styles from "./index.module.css"; export default function Home() { const hello = api.post.hello.useQuery({ text: "from tRPC" }); return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.

{hello.data ? hello.data.greeting : "Loading tRPC query..."}

); } ================================================ FILE: cli/template/extras/src/pages/index/with-tw.tsx ================================================ import Head from "next/head"; import Link from "next/link"; export default function Home() { return ( <> Create T3 App

Create T3 App

First Steps →

Just the basics - Everything you need to know to set up your database and authentication.

Documentation →

Learn more about Create T3 App, the libraries it uses, and how to deploy it.
); } ================================================ FILE: cli/template/extras/src/server/api/root.ts ================================================ import { postRouter } from "~/server/api/routers/post"; import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc"; /** * This is the primary router for your server. * * All routers added in /api/routers should be manually added here. */ export const appRouter = createTRPCRouter({ post: postRouter, }); // export type definition of API export type AppRouter = typeof appRouter; /** * Create a server-side caller for the tRPC API. * @example * const trpc = createCaller(createContext); * const res = await trpc.post.all(); * ^? Post[] */ export const createCaller = createCallerFactory(appRouter); ================================================ FILE: cli/template/extras/src/server/api/routers/post/base.ts ================================================ import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; // Mocked DB interface Post { id: number; name: string; } const posts: Post[] = [ { id: 1, name: "Hello World", }, ]; export const postRouter = createTRPCRouter({ hello: publicProcedure .input(z.object({ text: z.string() })) .query(({ input }) => { return { greeting: `Hello ${input.text}`, }; }), create: publicProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ input }) => { const post: Post = { id: posts.length + 1, name: input.name, }; posts.push(post); return post; }), getLatest: publicProcedure.query(() => { return posts.at(-1) ?? null; }), }); ================================================ FILE: cli/template/extras/src/server/api/routers/post/with-auth-drizzle.ts ================================================ import { z } from "zod"; import { createTRPCRouter, protectedProcedure, publicProcedure, } from "~/server/api/trpc"; import { posts } from "~/server/db/schema"; export const postRouter = createTRPCRouter({ hello: publicProcedure .input(z.object({ text: z.string() })) .query(({ input }) => { return { greeting: `Hello ${input.text}`, }; }), create: protectedProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ ctx, input }) => { await ctx.db.insert(posts).values({ name: input.name, createdById: ctx.session.user.id, }); }), getLatest: protectedProcedure.query(async ({ ctx }) => { const post = await ctx.db.query.posts.findFirst({ orderBy: (posts, { desc }) => [desc(posts.createdAt)], }); return post ?? null; }), getSecretMessage: protectedProcedure.query(() => { return "you can now see this secret message!"; }), }); ================================================ FILE: cli/template/extras/src/server/api/routers/post/with-auth-prisma.ts ================================================ import { z } from "zod"; import { createTRPCRouter, protectedProcedure, publicProcedure, } from "~/server/api/trpc"; export const postRouter = createTRPCRouter({ hello: publicProcedure .input(z.object({ text: z.string() })) .query(({ input }) => { return { greeting: `Hello ${input.text}`, }; }), create: protectedProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ ctx, input }) => { return ctx.db.post.create({ data: { name: input.name, createdBy: { connect: { id: ctx.session.user.id } }, }, }); }), getLatest: protectedProcedure.query(async ({ ctx }) => { const post = await ctx.db.post.findFirst({ orderBy: { createdAt: "desc" }, where: { createdBy: { id: ctx.session.user.id } }, }); return post ?? null; }), getSecretMessage: protectedProcedure.query(() => { return "you can now see this secret message!"; }), }); ================================================ FILE: cli/template/extras/src/server/api/routers/post/with-auth.ts ================================================ import { z } from "zod"; import { createTRPCRouter, protectedProcedure, publicProcedure, } from "~/server/api/trpc"; let post = { id: 1, name: "Hello World", }; export const postRouter = createTRPCRouter({ hello: publicProcedure .input(z.object({ text: z.string() })) .query(({ input }) => { return { greeting: `Hello ${input.text}`, }; }), create: protectedProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ input }) => { post = { id: post.id + 1, name: input.name }; return post; }), getLatest: protectedProcedure.query(() => { return post; }), getSecretMessage: protectedProcedure.query(() => { return "you can now see this secret message!"; }), }); ================================================ FILE: cli/template/extras/src/server/api/routers/post/with-drizzle.ts ================================================ import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; import { posts } from "~/server/db/schema"; export const postRouter = createTRPCRouter({ hello: publicProcedure .input(z.object({ text: z.string() })) .query(({ input }) => { return { greeting: `Hello ${input.text}`, }; }), create: publicProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ ctx, input }) => { await ctx.db.insert(posts).values({ name: input.name, }); }), getLatest: publicProcedure.query(async ({ ctx }) => { const post = await ctx.db.query.posts.findFirst({ orderBy: (posts, { desc }) => [desc(posts.createdAt)], }); return post ?? null; }), }); ================================================ FILE: cli/template/extras/src/server/api/routers/post/with-prisma.ts ================================================ import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; export const postRouter = createTRPCRouter({ hello: publicProcedure .input(z.object({ text: z.string() })) .query(({ input }) => { return { greeting: `Hello ${input.text}`, }; }), create: publicProcedure .input(z.object({ name: z.string().min(1) })) .mutation(async ({ ctx, input }) => { return ctx.db.post.create({ data: { name: input.name, }, }); }), getLatest: publicProcedure.query(async ({ ctx }) => { const post = await ctx.db.post.findFirst({ orderBy: { createdAt: "desc" }, }); return post ?? null; }), }); ================================================ FILE: cli/template/extras/src/server/api/trpc-app/base.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC } from "@trpc/server"; import superjson from "superjson"; import { ZodError } from "zod"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. * * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each * wrap this and provides the required context. * * @see https://trpc.io/docs/server/context */ export const createTRPCContext = async (opts: { headers: Headers }) => { return { ...opts, }; }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); ================================================ FILE: cli/template/extras/src/server/api/trpc-app/with-auth-db.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/auth"; import { db } from "~/server/db"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. * * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each * wrap this and provides the required context. * * @see https://trpc.io/docs/server/context */ export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth(); return { db, session, ...opts, }; }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-app/with-auth.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/auth"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. * * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each * wrap this and provides the required context. * * @see https://trpc.io/docs/server/context */ export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth(); return { session, ...opts, }; }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-app/with-better-auth-db.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/better-auth"; import { db } from "~/server/db"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. * * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each * wrap this and provides the required context. * * @see https://trpc.io/docs/server/context */ export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth.api.getSession({ headers: opts.headers, }); return { db, session, ...opts, }; }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-app/with-better-auth.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/better-auth"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. * * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each * wrap this and provides the required context. * * @see https://trpc.io/docs/server/context */ export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth.api.getSession({ headers: opts.headers, }); return { session, ...opts, }; }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-app/with-db.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC } from "@trpc/server"; import superjson from "superjson"; import { ZodError } from "zod"; import { db } from "~/server/db"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. * * This helper generates the "internals" for a tRPC context. The API handler and RSC clients each * wrap this and provides the required context. * * @see https://trpc.io/docs/server/context */ export const createTRPCContext = async (opts: { headers: Headers }) => { return { db, ...opts, }; }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); ================================================ FILE: cli/template/extras/src/server/api/trpc-pages/base.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC } from "@trpc/server"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import superjson from "superjson"; import { ZodError } from "zod"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. */ type CreateContextOptions = Record; /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export * it from here. * * Examples of things you may need it for: * - testing, so we don't have to mock Next.js' req/res * - tRPC's `createSSGHelpers`, where we don't have req/res * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ const createInnerTRPCContext = (_opts: CreateContextOptions) => { return {}; }; /** * This is the actual context you will use in your router. It will be used to process every request * that goes through your tRPC endpoint. * * @see https://trpc.io/docs/context */ export const createTRPCContext = (_opts: CreateNextContextOptions) => { return createInnerTRPCContext({}); }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); ================================================ FILE: cli/template/extras/src/server/api/trpc-pages/with-auth-db.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import { type Session } from "next-auth"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/auth"; import { db } from "~/server/db"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. */ interface CreateContextOptions { session: Session | null; } /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export * it from here. * * Examples of things you may need it for: * - testing, so we don't have to mock Next.js' req/res * - tRPC's `createSSGHelpers`, where we don't have req/res * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ const createInnerTRPCContext = (opts: CreateContextOptions) => { return { session: opts.session, db, }; }; /** * This is the actual context you will use in your router. It will be used to process every request * that goes through your tRPC endpoint. * * @see https://trpc.io/docs/context */ export const createTRPCContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; // Get the session from the server using the getServerSession wrapper function const session = await auth(req, res); return createInnerTRPCContext({ session, }); }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-pages/with-auth.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import { type Session } from "next-auth"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/auth"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. */ interface CreateContextOptions { session: Session | null; } /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export * it from here. * * Examples of things you may need it for: * - testing, so we don't have to mock Next.js' req/res * - tRPC's `createSSGHelpers`, where we don't have req/res * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ const createInnerTRPCContext = ({ session }: CreateContextOptions) => { return { session, }; }; /** * This is the actual context you will use in your router. It will be used to process every request * that goes through your tRPC endpoint. * * @see https://trpc.io/docs/context */ export const createTRPCContext = async ({ req, res, }: CreateNextContextOptions) => { // Get the session from the server using the getServerSession wrapper function const session = await auth(req, res); return createInnerTRPCContext({ session, }); }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-pages/with-better-auth-db.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/better-auth"; import { type Session } from "~/server/better-auth/config"; import { db } from "~/server/db"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. */ interface CreateContextOptions { session: Session | null; } /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export * it from here. * * Examples of things you may need it for: * - testing, so we don't have to mock Next.js' req/res * - tRPC's `createSSGHelpers`, where we don't have req/res * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ const createInnerTRPCContext = (opts: CreateContextOptions) => { return { session: opts.session, db, }; }; /** * This is the actual context you will use in your router. It will be used to process every request * that goes through your tRPC endpoint. * * @see https://trpc.io/docs/context */ export const createTRPCContext = async ({ req }: CreateNextContextOptions) => { // Convert IncomingHttpHeaders to Headers object const headers = new Headers(); for (const [key, value] of Object.entries(req.headers)) { if (Array.isArray(value)) { value.forEach((v) => headers.append(key, v)); } else if (value) { headers.append(key, value); } } const session = await auth.api.getSession({ headers, }); return createInnerTRPCContext({ session, }); }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-pages/with-better-auth.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC, TRPCError } from "@trpc/server"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; // import { } from "next-auth"; import superjson from "superjson"; import { ZodError } from "zod"; import { auth } from "~/server/better-auth"; import { type Session } from "~/server/better-auth/config"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. */ interface CreateContextOptions { session: Session | null; } /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export * it from here. * * Examples of things you may need it for: * - testing, so we don't have to mock Next.js' req/res * - tRPC's `createSSGHelpers`, where we don't have req/res * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ const createInnerTRPCContext = ({ session }: CreateContextOptions) => { return { session, }; }; /** * This is the actual context you will use in your router. It will be used to process every request * that goes through your tRPC endpoint. * * @see https://trpc.io/docs/context */ export const createTRPCContext = async ({ req }: CreateNextContextOptions) => { // Convert IncomingHttpHeaders to Headers object const headers = new Headers(); for (const [key, value] of Object.entries(req.headers)) { if (Array.isArray(value)) { value.forEach((v) => headers.append(key, v)); } else if (value) { headers.append(key, value); } } const session = await auth.api.getSession({ headers, }); return createInnerTRPCContext({ session, }); }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); /** * Protected (authenticated) procedure * * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies * the session is valid and guarantees `ctx.session.user` is not null. * * @see https://trpc.io/docs/procedures */ export const protectedProcedure = t.procedure .use(timingMiddleware) .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ================================================ FILE: cli/template/extras/src/server/api/trpc-pages/with-db.ts ================================================ /** * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS: * 1. You want to modify request context (see Part 1). * 2. You want to create a new middleware or type of procedure (see Part 3). * * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will * need to use are documented accordingly near the end. */ import { initTRPC } from "@trpc/server"; import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import superjson from "superjson"; import { ZodError } from "zod"; import { db } from "~/server/db"; /** * 1. CONTEXT * * This section defines the "contexts" that are available in the backend API. * * These allow you to access things when processing a request, like the database, the session, etc. */ type CreateContextOptions = Record; /** * This helper generates the "internals" for a tRPC context. If you need to use it, you can export * it from here. * * Examples of things you may need it for: * - testing, so we don't have to mock Next.js' req/res * - tRPC's `createSSGHelpers`, where we don't have req/res * * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts */ const createInnerTRPCContext = (_opts: CreateContextOptions) => { return { db, }; }; /** * This is the actual context you will use in your router. It will be used to process every request * that goes through your tRPC endpoint. * * @see https://trpc.io/docs/context */ export const createTRPCContext = (_opts: CreateNextContextOptions) => { return createInnerTRPCContext({}); }; /** * 2. INITIALIZATION * * This is where the tRPC API is initialized, connecting the context and transformer. We also parse * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation * errors on the backend. */ const t = initTRPC.context().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); /** * Create a server-side caller. * * @see https://trpc.io/docs/server/server-side-calls */ export const createCallerFactory = t.createCallerFactory; /** * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT) * * These are the pieces you use to build your tRPC API. You should import these a lot in the * "/src/server/api/routers" directory. */ /** * This is how you create new routers and sub-routers in your tRPC API. * * @see https://trpc.io/docs/router */ export const createTRPCRouter = t.router; /** * Middleware for timing procedure execution and adding an artificial 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.use(timingMiddleware); ================================================ FILE: cli/template/extras/src/server/auth/config/base.ts ================================================ import { type DefaultSession, type NextAuthConfig } from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; /** * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` * object and keep type safety. * * @see https://next-auth.js.org/getting-started/typescript#module-augmentation */ declare module "next-auth" { interface Session extends DefaultSession { user: { id: string; // ...other properties // role: UserRole; } & DefaultSession["user"]; } // interface User { // // ...other properties // // role: UserRole; // } } /** * Options for NextAuth.js used to configure adapters, providers, callbacks, etc. * * @see https://next-auth.js.org/configuration/options */ export const authConfig = { providers: [ DiscordProvider, /** * ...add more providers here. * * Most other providers require a bit more work than the Discord provider. For example, the * GitHub provider requires you to add the `refresh_token_expires_in` field to the Account * model. Refer to the NextAuth.js docs for the provider you want to use. Example: * * @see https://next-auth.js.org/providers/github */ ], callbacks: { session: ({ session, token }) => ({ ...session, user: { ...session.user, id: token.sub, }, }), }, } satisfies NextAuthConfig; ================================================ FILE: cli/template/extras/src/server/auth/config/with-drizzle.ts ================================================ import { DrizzleAdapter } from "@auth/drizzle-adapter"; import { type DefaultSession, type NextAuthConfig } from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; import { db } from "~/server/db"; import { accounts, sessions, users, verificationTokens, } from "~/server/db/schema"; /** * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` * object and keep type safety. * * @see https://next-auth.js.org/getting-started/typescript#module-augmentation */ declare module "next-auth" { interface Session extends DefaultSession { user: { id: string; // ...other properties // role: UserRole; } & DefaultSession["user"]; } // interface User { // // ...other properties // // role: UserRole; // } } /** * Options for NextAuth.js used to configure adapters, providers, callbacks, etc. * * @see https://next-auth.js.org/configuration/options */ export const authConfig = { providers: [ DiscordProvider, /** * ...add more providers here. * * Most other providers require a bit more work than the Discord provider. For example, the * GitHub provider requires you to add the `refresh_token_expires_in` field to the Account * model. Refer to the NextAuth.js docs for the provider you want to use. Example: * * @see https://next-auth.js.org/providers/github */ ], adapter: DrizzleAdapter(db, { usersTable: users, accountsTable: accounts, sessionsTable: sessions, verificationTokensTable: verificationTokens, }), callbacks: { session: ({ session, user }) => ({ ...session, user: { ...session.user, id: user.id, }, }), }, } satisfies NextAuthConfig; ================================================ FILE: cli/template/extras/src/server/auth/config/with-prisma.ts ================================================ import { PrismaAdapter } from "@auth/prisma-adapter"; import { type DefaultSession, type NextAuthConfig } from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; import { db } from "~/server/db"; /** * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` * object and keep type safety. * * @see https://next-auth.js.org/getting-started/typescript#module-augmentation */ declare module "next-auth" { interface Session extends DefaultSession { user: { id: string; // ...other properties // role: UserRole; } & DefaultSession["user"]; } // interface User { // // ...other properties // // role: UserRole; // } } /** * Options for NextAuth.js used to configure adapters, providers, callbacks, etc. * * @see https://next-auth.js.org/configuration/options */ export const authConfig = { providers: [ DiscordProvider, /** * ...add more providers here. * * Most other providers require a bit more work than the Discord provider. For example, the * GitHub provider requires you to add the `refresh_token_expires_in` field to the Account * model. Refer to the NextAuth.js docs for the provider you want to use. Example: * * @see https://next-auth.js.org/providers/github */ ], adapter: PrismaAdapter(db), callbacks: { session: ({ session, user }) => ({ ...session, user: { ...session.user, id: user.id, }, }), }, } satisfies NextAuthConfig; ================================================ FILE: cli/template/extras/src/server/auth/index.ts ================================================ import NextAuth from "next-auth"; import { cache } from "react"; import { authConfig } from "./config"; const { auth: uncachedAuth, handlers, signIn, signOut } = NextAuth(authConfig); const auth = cache(uncachedAuth); export { auth, handlers, signIn, signOut }; ================================================ FILE: cli/template/extras/src/server/better-auth/client.ts ================================================ import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient(); export type Session = typeof authClient.$Infer.Session; ================================================ FILE: cli/template/extras/src/server/better-auth/config/base.ts ================================================ import { betterAuth } from "better-auth"; import { nextCookies } from "better-auth/next-js"; export const auth = betterAuth({ emailAndPassword: { enabled: true, }, // Make sure nextCookies() is the last plugin in the array plugins: [nextCookies()], }); export type Session = typeof auth.$Infer.Session; ================================================ FILE: cli/template/extras/src/server/better-auth/config/with-drizzle.ts ================================================ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { nextCookies } from "better-auth/next-js"; import { env } from "~/env"; import { db } from "~/server/db"; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", // or "pg" or "mysql" }), emailAndPassword: { enabled: true, }, socialProviders: { github: { clientId: env.BETTER_AUTH_GITHUB_CLIENT_ID, clientSecret: env.BETTER_AUTH_GITHUB_CLIENT_SECRET, redirectURI: "http://localhost:3000/api/auth/callback/github", }, }, // Make sure nextCookies() is the last plugin in the array plugins: [nextCookies()], }); export type Session = typeof auth.$Infer.Session; ================================================ FILE: cli/template/extras/src/server/better-auth/config/with-prisma.ts ================================================ import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { nextCookies } from "better-auth/next-js"; import { env } from "~/env"; import { db } from "~/server/db"; export const auth = betterAuth({ database: prismaAdapter(db, { provider: "postgresql", // or "sqlite" or "mysql" }), emailAndPassword: { enabled: true, }, socialProviders: { github: { clientId: env.BETTER_AUTH_GITHUB_CLIENT_ID, clientSecret: env.BETTER_AUTH_GITHUB_CLIENT_SECRET, redirectURI: "http://localhost:3000/api/auth/callback/github", }, }, // Make sure nextCookies() is the last plugin in the array plugins: [nextCookies()], }); export type Session = typeof auth.$Infer.Session; ================================================ FILE: cli/template/extras/src/server/better-auth/index.ts ================================================ export { auth } from "./config"; ================================================ FILE: cli/template/extras/src/server/better-auth/server.ts ================================================ import { auth } from "."; import { headers } from "next/headers"; import { cache } from "react"; export const getSession = cache(async () => auth.api.getSession({ headers: await headers() }) ); ================================================ FILE: cli/template/extras/src/server/db/db-prisma-planetscale.ts ================================================ import { PrismaPlanetScale } from "@prisma/adapter-planetscale"; import { env } from "~/env"; import { PrismaClient } from "../../generated/prisma"; const createPrismaClient = () => new PrismaClient({ log: env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], adapter: new PrismaPlanetScale({ url: env.DATABASE_URL }), }); const globalForPrisma = globalThis as unknown as { prisma: ReturnType | undefined; }; export const db = globalForPrisma.prisma ?? createPrismaClient(); if (env.NODE_ENV !== "production") globalForPrisma.prisma = db; ================================================ FILE: cli/template/extras/src/server/db/db-prisma.ts ================================================ import { env } from "~/env"; import { PrismaClient } from "../../generated/prisma"; const createPrismaClient = () => new PrismaClient({ log: env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"], }); const globalForPrisma = globalThis as unknown as { prisma: ReturnType | undefined; }; export const db = globalForPrisma.prisma ?? createPrismaClient(); if (env.NODE_ENV !== "production") globalForPrisma.prisma = db; ================================================ FILE: cli/template/extras/src/server/db/index-drizzle/with-mysql.ts ================================================ import { drizzle } from "drizzle-orm/mysql2"; import { createPool, type Pool } from "mysql2/promise"; import { env } from "~/env"; import * as schema from "./schema"; /** * Cache the database connection in development. This avoids creating a new connection on every HMR * update. */ const globalForDb = globalThis as unknown as { conn: Pool | undefined; }; const conn = globalForDb.conn ?? createPool({ uri: env.DATABASE_URL }); if (env.NODE_ENV !== "production") globalForDb.conn = conn; export const db = drizzle(conn, { schema, mode: "default" }); ================================================ FILE: cli/template/extras/src/server/db/index-drizzle/with-planetscale.ts ================================================ import { Client } from "@planetscale/database"; import { drizzle } from "drizzle-orm/planetscale-serverless"; import { env } from "~/env"; import * as schema from "./schema"; export const db = drizzle(new Client({ url: env.DATABASE_URL }), { schema }); ================================================ FILE: cli/template/extras/src/server/db/index-drizzle/with-postgres.ts ================================================ import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import { env } from "~/env"; import * as schema from "./schema"; /** * Cache the database connection in development. This avoids creating a new connection on every HMR * update. */ const globalForDb = globalThis as unknown as { conn: postgres.Sql | undefined; }; const conn = globalForDb.conn ?? postgres(env.DATABASE_URL); if (env.NODE_ENV !== "production") globalForDb.conn = conn; export const db = drizzle(conn, { schema }); ================================================ FILE: cli/template/extras/src/server/db/index-drizzle/with-sqlite.ts ================================================ import { createClient, type Client } from "@libsql/client"; import { drizzle } from "drizzle-orm/libsql"; import { env } from "~/env"; import * as schema from "./schema"; /** * Cache the database connection in development. This avoids creating a new connection on every HMR * update. */ const globalForDb = globalThis as unknown as { client: Client | undefined; }; export const client = globalForDb.client ?? createClient({ url: env.DATABASE_URL }); if (env.NODE_ENV !== "production") globalForDb.client = client; export const db = drizzle(client, { schema }); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/base-mysql.ts ================================================ // Example model schema from the Drizzle docs // https://orm.drizzle.team/docs/sql-schema-declaration import { index, mysqlTableCreator } from "drizzle-orm/mysql-core"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = mysqlTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.bigint({ mode: "number" }).primaryKey().autoincrement(), name: d.varchar({ length: 256 }), createdAt: d .timestamp() .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp().onUpdateNow(), }), (t) => [index("name_idx").on(t.name)] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/base-planetscale.ts ================================================ // Example model schema from the Drizzle docs // https://orm.drizzle.team/docs/sql-schema-declaration import { index, mysqlTableCreator } from "drizzle-orm/mysql-core"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = mysqlTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.bigint({ mode: "number" }).primaryKey().autoincrement(), name: d.varchar({ length: 256 }), createdAt: d .timestamp() .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp().onUpdateNow(), }), (t) => [index("name_idx").on(t.name)] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/base-postgres.ts ================================================ // Example model schema from the Drizzle docs // https://orm.drizzle.team/docs/sql-schema-declaration import { index, pgTableCreator } from "drizzle-orm/pg-core"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = pgTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.integer().primaryKey().generatedByDefaultAsIdentity(), name: d.varchar({ length: 256 }), createdAt: d .timestamp({ withTimezone: true }) .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp({ withTimezone: true }).$onUpdate(() => new Date()), }), (t) => [index("name_idx").on(t.name)] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/base-sqlite.ts ================================================ // Example model schema from the Drizzle docs // https://orm.drizzle.team/docs/sql-schema-declaration import { sql } from "drizzle-orm"; import { index, sqliteTableCreator } from "drizzle-orm/sqlite-core"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = sqliteTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.integer({ mode: "number" }).primaryKey({ autoIncrement: true }), name: d.text({ length: 256 }), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), }), (t) => [index("name_idx").on(t.name)] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-auth-mysql.ts ================================================ import { relations, sql } from "drizzle-orm"; import { index, mysqlTableCreator, primaryKey } from "drizzle-orm/mysql-core"; import { type AdapterAccount } from "next-auth/adapters"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = mysqlTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.bigint({ mode: "number" }).primaryKey().autoincrement(), name: d.varchar({ length: 256 }), createdById: d .varchar({ length: 255 }) .notNull() .references(() => users.id), createdAt: d .timestamp() .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp().onUpdateNow(), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const users = createTable("user", (d) => ({ id: d .varchar({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: d.varchar({ length: 255 }), email: d.varchar({ length: 255 }).notNull(), emailVerified: d .timestamp({ mode: "date", fsp: 3, }) .default(sql`CURRENT_TIMESTAMP(3)`), image: d.varchar({ length: 255 }), })); export const usersRelations = relations(users, ({ many }) => ({ accounts: many(accounts), sessions: many(sessions), })); export const accounts = createTable( "account", (d) => ({ userId: d .varchar({ length: 255 }) .notNull() .references(() => users.id), type: d.varchar({ length: 255 }).$type().notNull(), provider: d.varchar({ length: 255 }).notNull(), providerAccountId: d.varchar({ length: 255 }).notNull(), refresh_token: d.text(), access_token: d.text(), expires_at: d.int(), token_type: d.varchar({ length: 255 }), scope: d.varchar({ length: 255 }), id_token: d.text(), session_state: d.varchar({ length: 255 }), }), (t) => [ primaryKey({ columns: [t.provider, t.providerAccountId], }), index("account_user_id_idx").on(t.userId), ] ); export const accountsRelations = relations(accounts, ({ one }) => ({ user: one(users, { fields: [accounts.userId], references: [users.id] }), })); export const sessions = createTable( "session", (d) => ({ sessionToken: d.varchar({ length: 255 }).notNull().primaryKey(), userId: d .varchar({ length: 255 }) .notNull() .references(() => users.id), expires: d.timestamp({ mode: "date" }).notNull(), }), (t) => [index("session_user_id_idx").on(t.userId)] ); export const sessionsRelations = relations(sessions, ({ one }) => ({ user: one(users, { fields: [sessions.userId], references: [users.id] }), })); export const verificationTokens = createTable( "verification_token", (d) => ({ identifier: d.varchar({ length: 255 }).notNull(), token: d.varchar({ length: 255 }).notNull(), expires: d.timestamp({ mode: "date" }).notNull(), }), (t) => [primaryKey({ columns: [t.identifier, t.token] })] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-auth-planetscale.ts ================================================ import { relations, sql } from "drizzle-orm"; import { index, mysqlTableCreator, primaryKey } from "drizzle-orm/mysql-core"; import { type AdapterAccount } from "next-auth/adapters"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = mysqlTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.bigint({ mode: "number" }).primaryKey().autoincrement(), name: d.varchar({ length: 256 }), createdById: d.varchar({ length: 255 }).notNull(), createdAt: d .timestamp() .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp().onUpdateNow(), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const users = createTable("user", (d) => ({ id: d .varchar({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: d.varchar({ length: 255 }), email: d.varchar({ length: 255 }).notNull(), emailVerified: d .timestamp({ mode: "date", fsp: 3, }) .default(sql`CURRENT_TIMESTAMP(3)`), image: d.varchar({ length: 255 }), })); export const usersRelations = relations(users, ({ many }) => ({ accounts: many(accounts), sessions: many(sessions), })); export const accounts = createTable( "account", (d) => ({ userId: d.varchar({ length: 255 }).notNull(), type: d.varchar({ length: 255 }).$type().notNull(), provider: d.varchar({ length: 255 }).notNull(), providerAccountId: d.varchar({ length: 255 }).notNull(), refresh_token: d.text(), access_token: d.text(), expires_at: d.int(), token_type: d.varchar({ length: 255 }), scope: d.varchar({ length: 255 }), id_token: d.text(), session_state: d.varchar({ length: 255 }), }), (account) => [ primaryKey({ columns: [account.provider, account.providerAccountId] }), index("accounts_user_id_idx").on(account.userId), ] ); export const accountsRelations = relations(accounts, ({ one }) => ({ user: one(users, { fields: [accounts.userId], references: [users.id] }), })); export const sessions = createTable( "session", (d) => ({ sessionToken: d.varchar({ length: 255 }).notNull().primaryKey(), userId: d.varchar({ length: 255 }).notNull(), expires: d.timestamp({ mode: "date" }).notNull(), }), (session) => [index("session_user_id_idx").on(session.userId)] ); export const sessionsRelations = relations(sessions, ({ one }) => ({ user: one(users, { fields: [sessions.userId], references: [users.id] }), })); export const verificationTokens = createTable( "verification_token", (d) => ({ identifier: d.varchar({ length: 255 }).notNull(), token: d.varchar({ length: 255 }).notNull(), expires: d.timestamp({ mode: "date" }).notNull(), }), (vt) => [primaryKey({ columns: [vt.identifier, vt.token] })] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-auth-postgres.ts ================================================ import { relations } from "drizzle-orm"; import { index, pgTableCreator, primaryKey } from "drizzle-orm/pg-core"; import { type AdapterAccount } from "next-auth/adapters"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = pgTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.integer().primaryKey().generatedByDefaultAsIdentity(), name: d.varchar({ length: 256 }), createdById: d .varchar({ length: 255 }) .notNull() .references(() => users.id), createdAt: d .timestamp({ withTimezone: true }) .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp({ withTimezone: true }).$onUpdate(() => new Date()), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const users = createTable("user", (d) => ({ id: d .varchar({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: d.varchar({ length: 255 }), email: d.varchar({ length: 255 }).notNull(), emailVerified: d .timestamp({ mode: "date", withTimezone: true, }) .$defaultFn(() => /* @__PURE__ */ new Date()), image: d.varchar({ length: 255 }), })); export const usersRelations = relations(users, ({ many }) => ({ accounts: many(accounts), })); export const accounts = createTable( "account", (d) => ({ userId: d .varchar({ length: 255 }) .notNull() .references(() => users.id), type: d.varchar({ length: 255 }).$type().notNull(), provider: d.varchar({ length: 255 }).notNull(), providerAccountId: d.varchar({ length: 255 }).notNull(), refresh_token: d.text(), access_token: d.text(), expires_at: d.integer(), token_type: d.varchar({ length: 255 }), scope: d.varchar({ length: 255 }), id_token: d.text(), session_state: d.varchar({ length: 255 }), }), (t) => [ primaryKey({ columns: [t.provider, t.providerAccountId] }), index("account_user_id_idx").on(t.userId), ] ); export const accountsRelations = relations(accounts, ({ one }) => ({ user: one(users, { fields: [accounts.userId], references: [users.id] }), })); export const sessions = createTable( "session", (d) => ({ sessionToken: d.varchar({ length: 255 }).notNull().primaryKey(), userId: d .varchar({ length: 255 }) .notNull() .references(() => users.id), expires: d.timestamp({ mode: "date", withTimezone: true }).notNull(), }), (t) => [index("t_user_id_idx").on(t.userId)] ); export const sessionsRelations = relations(sessions, ({ one }) => ({ user: one(users, { fields: [sessions.userId], references: [users.id] }), })); export const verificationTokens = createTable( "verification_token", (d) => ({ identifier: d.varchar({ length: 255 }).notNull(), token: d.varchar({ length: 255 }).notNull(), expires: d.timestamp({ mode: "date", withTimezone: true }).notNull(), }), (t) => [primaryKey({ columns: [t.identifier, t.token] })] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-auth-sqlite.ts ================================================ import { relations, sql } from "drizzle-orm"; import { index, primaryKey, sqliteTableCreator } from "drizzle-orm/sqlite-core"; import { type AdapterAccount } from "next-auth/adapters"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ export const createTable = sqliteTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.integer({ mode: "number" }).primaryKey({ autoIncrement: true }), name: d.text({ length: 256 }), createdById: d .text({ length: 255 }) .notNull() .references(() => users.id), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const users = createTable("user", (d) => ({ id: d .text({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: d.text({ length: 255 }), email: d.text({ length: 255 }).notNull(), emailVerified: d.integer({ mode: "timestamp" }).default(sql`(unixepoch())`), image: d.text({ length: 255 }), })); export const usersRelations = relations(users, ({ many }) => ({ accounts: many(accounts), })); export const accounts = createTable( "account", (d) => ({ userId: d .text({ length: 255 }) .notNull() .references(() => users.id), type: d.text({ length: 255 }).$type().notNull(), provider: d.text({ length: 255 }).notNull(), providerAccountId: d.text({ length: 255 }).notNull(), refresh_token: d.text(), access_token: d.text(), expires_at: d.integer(), token_type: d.text({ length: 255 }), scope: d.text({ length: 255 }), id_token: d.text(), session_state: d.text({ length: 255 }), }), (t) => [ primaryKey({ columns: [t.provider, t.providerAccountId], }), index("account_user_id_idx").on(t.userId), ] ); export const accountsRelations = relations(accounts, ({ one }) => ({ user: one(users, { fields: [accounts.userId], references: [users.id] }), })); export const sessions = createTable( "session", (d) => ({ sessionToken: d.text({ length: 255 }).notNull().primaryKey(), userId: d .text({ length: 255 }) .notNull() .references(() => users.id), expires: d.integer({ mode: "timestamp" }).notNull(), }), (t) => [index("session_userId_idx").on(t.userId)] ); export const sessionsRelations = relations(sessions, ({ one }) => ({ user: one(users, { fields: [sessions.userId], references: [users.id] }), })); export const verificationTokens = createTable( "verification_token", (d) => ({ identifier: d.text({ length: 255 }).notNull(), token: d.text({ length: 255 }).notNull(), expires: d.integer({ mode: "timestamp" }).notNull(), }), (t) => [primaryKey({ columns: [t.identifier, t.token] })] ); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-better-auth-mysql.ts ================================================ import { relations } from "drizzle-orm"; import { boolean, index, mysqlTable, mysqlTableCreator, text, timestamp, varchar, } from "drizzle-orm/mysql-core"; export const createTable = mysqlTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.bigint({ mode: "number" }).primaryKey().autoincrement(), name: d.varchar({ length: 256 }), createdById: d .varchar({ length: 255 }) .notNull() .references(() => user.id), createdAt: d .timestamp() .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp().onUpdateNow(), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const user = mysqlTable("user", { id: varchar("id", { length: 36 }).primaryKey(), name: text("name").notNull(), email: varchar("email", { length: 255 }).notNull().unique(), emailVerified: boolean("email_verified") .$defaultFn(() => false) .notNull(), image: text("image"), createdAt: timestamp("created_at") .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: timestamp("updated_at") .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), }); export const session = mysqlTable("session", { id: varchar("id", { length: 36 }).primaryKey(), expiresAt: timestamp("expires_at").notNull(), token: varchar("token", { length: 255 }).notNull().unique(), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: varchar("user_id", { length: 36 }) .notNull() .references(() => user.id, { onDelete: "cascade" }), }); export const account = mysqlTable("account", { id: varchar("id", { length: 36 }).primaryKey(), accountId: text("account_id").notNull(), providerId: text("provider_id").notNull(), userId: varchar("user_id", { length: 36 }) .notNull() .references(() => user.id, { onDelete: "cascade" }), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), accessTokenExpiresAt: timestamp("access_token_expires_at"), refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), scope: text("scope"), password: text("password"), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), }); export const verification = mysqlTable("verification", { id: varchar("id", { length: 36 }).primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: timestamp("expires_at").notNull(), createdAt: timestamp("created_at").$defaultFn( () => /* @__PURE__ */ new Date() ), updatedAt: timestamp("updated_at").$defaultFn( () => /* @__PURE__ */ new Date() ), }); export const usersRelations = relations(user, ({ many }) => ({ accounts: many(account), sessions: many(session), })); export const accountsRelations = relations(account, ({ one }) => ({ user: one(user, { fields: [account.userId], references: [user.id] }), })); export const sessionsRelations = relations(session, ({ one }) => ({ user: one(user, { fields: [session.userId], references: [user.id] }), })); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-better-auth-planetscale.ts ================================================ import { relations } from "drizzle-orm"; import { boolean, index, mysqlTable, mysqlTableCreator, text, timestamp, varchar, } from "drizzle-orm/mysql-core"; export const createTable = mysqlTableCreator((name) => `project1_${name}`); export const posts = createTable( "post", (d) => ({ id: d.bigint({ mode: "number" }).primaryKey().autoincrement(), name: d.varchar({ length: 256 }), createdById: d .varchar({ length: 255 }) .notNull() .references(() => user.id), createdAt: d .timestamp() .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: d.timestamp().onUpdateNow(), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const user = mysqlTable("user", { id: varchar("id", { length: 36 }).primaryKey(), name: text("name").notNull(), email: varchar("email", { length: 255 }).notNull().unique(), emailVerified: boolean("email_verified") .$defaultFn(() => false) .notNull(), image: text("image"), createdAt: timestamp("created_at") .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: timestamp("updated_at") .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), }); export const session = mysqlTable("session", { id: varchar("id", { length: 36 }).primaryKey(), expiresAt: timestamp("expires_at").notNull(), token: varchar("token", { length: 255 }).notNull().unique(), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: varchar("user_id", { length: 36 }) .notNull() .references(() => user.id, { onDelete: "cascade" }), }); export const account = mysqlTable("account", { id: varchar("id", { length: 36 }).primaryKey(), accountId: text("account_id").notNull(), providerId: text("provider_id").notNull(), userId: varchar("user_id", { length: 36 }) .notNull() .references(() => user.id, { onDelete: "cascade" }), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), accessTokenExpiresAt: timestamp("access_token_expires_at"), refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), scope: text("scope"), password: text("password"), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), }); export const verification = mysqlTable("verification", { id: varchar("id", { length: 36 }).primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: timestamp("expires_at").notNull(), createdAt: timestamp("created_at").$defaultFn( () => /* @__PURE__ */ new Date() ), updatedAt: timestamp("updated_at").$defaultFn( () => /* @__PURE__ */ new Date() ), }); export const usersRelations = relations(user, ({ many }) => ({ accounts: many(account), sessions: many(session), })); export const accountsRelations = relations(account, ({ one }) => ({ user: one(user, { fields: [account.userId], references: [user.id] }), })); export const sessionsRelations = relations(session, ({ one }) => ({ user: one(user, { fields: [session.userId], references: [user.id] }), })); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-better-auth-postgres.ts ================================================ import { relations } from "drizzle-orm"; import { boolean, index, pgTable, pgTableCreator, text, timestamp, } from "drizzle-orm/pg-core"; export const createTable = pgTableCreator((name) => `pg-drizzle_${name}`); export const posts = createTable( "post", (d) => ({ id: d.integer().primaryKey().generatedByDefaultAsIdentity(), name: d.varchar({ length: 256 }), createdById: d .varchar({ length: 255 }) .notNull() .references(() => user.id), createdAt: d .timestamp({ withTimezone: true }) .$defaultFn(() => new Date()) .notNull(), updatedAt: d.timestamp({ withTimezone: true }).$onUpdate(() => new Date()), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); export const user = pgTable("user", { id: text("id").primaryKey(), name: text("name").notNull(), email: text("email").notNull().unique(), emailVerified: boolean("email_verified") .$defaultFn(() => false) .notNull(), image: text("image"), createdAt: timestamp("created_at") .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), updatedAt: timestamp("updated_at") .$defaultFn(() => /* @__PURE__ */ new Date()) .notNull(), }); export const session = pgTable("session", { id: text("id").primaryKey(), expiresAt: timestamp("expires_at").notNull(), token: text("token").notNull().unique(), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), }); export const account = pgTable("account", { id: text("id").primaryKey(), accountId: text("account_id").notNull(), providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() .references(() => user.id, { onDelete: "cascade" }), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), accessTokenExpiresAt: timestamp("access_token_expires_at"), refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), scope: text("scope"), password: text("password"), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), }); export const verification = pgTable("verification", { id: text("id").primaryKey(), identifier: text("identifier").notNull(), value: text("value").notNull(), expiresAt: timestamp("expires_at").notNull(), createdAt: timestamp("created_at").$defaultFn( () => /* @__PURE__ */ new Date() ), updatedAt: timestamp("updated_at").$defaultFn( () => /* @__PURE__ */ new Date() ), }); export const userRelations = relations(user, ({ many }) => ({ account: many(account), session: many(session), })); export const accountRelations = relations(account, ({ one }) => ({ user: one(user, { fields: [account.userId], references: [user.id] }), })); export const sessionRelations = relations(session, ({ one }) => ({ user: one(user, { fields: [session.userId], references: [user.id] }), })); ================================================ FILE: cli/template/extras/src/server/db/schema-drizzle/with-better-auth-sqlite.ts ================================================ import { relations, sql } from "drizzle-orm"; import { index, sqliteTable } from "drizzle-orm/sqlite-core"; /** * Multi-project schema prefix helper */ // Posts example table export const posts = sqliteTable( "post", (d) => ({ id: d.integer({ mode: "number" }).primaryKey({ autoIncrement: true }), name: d.text({ length: 256 }), createdById: d .text({ length: 255 }) .notNull() .references(() => user.id), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), }), (t) => [ index("created_by_idx").on(t.createdById), index("name_idx").on(t.name), ] ); // Better Auth core tables export const user = sqliteTable("user", (d) => ({ id: d .text({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), name: d.text({ length: 255 }), email: d.text({ length: 255 }).notNull().unique(), emailVerified: d.integer({ mode: "boolean" }).default(false), image: d.text({ length: 255 }), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), })); export const userRelations = relations(user, ({ many }) => ({ account: many(account), session: many(session), })); export const account = sqliteTable( "account", (d) => ({ id: d .text({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), userId: d .text({ length: 255 }) .notNull() .references(() => user.id), accountId: d.text({ length: 255 }).notNull(), providerId: d.text({ length: 255 }).notNull(), accessToken: d.text(), refreshToken: d.text(), accessTokenExpiresAt: d.integer({ mode: "timestamp" }), refreshTokenExpiresAt: d.integer({ mode: "timestamp" }), scope: d.text({ length: 255 }), idToken: d.text(), password: d.text(), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), }), (t) => [index("account_user_id_idx").on(t.userId)] ); export const accountRelations = relations(account, ({ one }) => ({ user: one(user, { fields: [account.userId], references: [user.id] }), })); export const session = sqliteTable( "session", (d) => ({ id: d .text({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), userId: d .text({ length: 255 }) .notNull() .references(() => user.id), token: d.text({ length: 255 }).notNull().unique(), expiresAt: d.integer({ mode: "timestamp" }).notNull(), ipAddress: d.text({ length: 255 }), userAgent: d.text({ length: 255 }), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), }), (t) => [index("session_user_id_idx").on(t.userId)] ); export const sessionRelations = relations(session, ({ one }) => ({ user: one(user, { fields: [session.userId], references: [user.id] }), })); export const verification = sqliteTable( "verification", (d) => ({ id: d .text({ length: 255 }) .notNull() .primaryKey() .$defaultFn(() => crypto.randomUUID()), identifier: d.text({ length: 255 }).notNull(), value: d.text({ length: 255 }).notNull(), expiresAt: d.integer({ mode: "timestamp" }).notNull(), createdAt: d .integer({ mode: "timestamp" }) .default(sql`(unixepoch())`) .notNull(), updatedAt: d.integer({ mode: "timestamp" }).$onUpdate(() => new Date()), }), (t) => [index("verification_identifier_idx").on(t.identifier)] ); ================================================ FILE: cli/template/extras/src/styles/globals.css ================================================ @import "tailwindcss"; @theme { --font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } ================================================ FILE: cli/template/extras/src/trpc/query-client.ts ================================================ import { defaultShouldDehydrateQuery, QueryClient, } from "@tanstack/react-query"; import SuperJSON from "superjson"; export const createQueryClient = () => new QueryClient({ defaultOptions: { queries: { // With SSR, we usually want to set some default staleTime // above 0 to avoid refetching immediately on the client staleTime: 30 * 1000, }, dehydrate: { serializeData: SuperJSON.serialize, shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === "pending", }, hydrate: { deserializeData: SuperJSON.deserialize, }, }, }); ================================================ FILE: cli/template/extras/src/trpc/react.tsx ================================================ "use client"; import { QueryClientProvider, type QueryClient } from "@tanstack/react-query"; import { httpBatchStreamLink, loggerLink } from "@trpc/client"; import { createTRPCReact } from "@trpc/react-query"; import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server"; import { useState } from "react"; import SuperJSON from "superjson"; import { type AppRouter } from "~/server/api/root"; import { createQueryClient } from "./query-client"; let clientQueryClientSingleton: QueryClient | undefined = undefined; const getQueryClient = () => { if (typeof window === "undefined") { // Server: always make a new query client return createQueryClient(); } // Browser: use singleton pattern to keep the same query client clientQueryClientSingleton ??= createQueryClient(); return clientQueryClientSingleton; }; export const api = createTRPCReact(); /** * Inference helper for inputs. * * @example type HelloInput = RouterInputs['example']['hello'] */ export type RouterInputs = inferRouterInputs; /** * Inference helper for outputs. * * @example type HelloOutput = RouterOutputs['example']['hello'] */ export type RouterOutputs = inferRouterOutputs; export function TRPCReactProvider(props: { children: React.ReactNode }) { const queryClient = getQueryClient(); const [trpcClient] = useState(() => api.createClient({ links: [ loggerLink({ enabled: (op) => process.env.NODE_ENV === "development" || (op.direction === "down" && op.result instanceof Error), }), httpBatchStreamLink({ transformer: SuperJSON, url: getBaseUrl() + "/api/trpc", headers: () => { const headers = new Headers(); headers.set("x-trpc-source", "nextjs-react"); return headers; }, }), ], }) ); return ( {props.children} ); } function getBaseUrl() { if (typeof window !== "undefined") return window.location.origin; if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; return `http://localhost:${process.env.PORT ?? 3000}`; } ================================================ FILE: cli/template/extras/src/trpc/server.ts ================================================ import "server-only"; import { createHydrationHelpers } from "@trpc/react-query/rsc"; import { headers } from "next/headers"; import { cache } from "react"; import { createCaller, type AppRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import { createQueryClient } from "./query-client"; /** * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when * handling a tRPC call from a React Server Component. */ const createContext = cache(async () => { const heads = new Headers(await headers()); heads.set("x-trpc-source", "rsc"); return createTRPCContext({ headers: heads, }); }); const getQueryClient = cache(createQueryClient); const caller = createCaller(createContext); export const { trpc: api, HydrateClient } = createHydrationHelpers( caller, getQueryClient ); ================================================ FILE: cli/template/extras/src/utils/api.ts ================================================ /** * This is the client-side entrypoint for your tRPC API. It is used to create the `api` object which * contains the Next.js App-wrapper, as well as your type-safe React Query hooks. * * We also create a few inference helpers for input and output types. */ import { httpBatchLink, loggerLink } from "@trpc/client"; import { createTRPCNext } from "@trpc/next"; import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server"; import superjson from "superjson"; import { type AppRouter } from "~/server/api/root"; const getBaseUrl = () => { if (typeof window !== "undefined") return ""; // browser should use relative url if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost }; /** A set of type-safe react-query hooks for your tRPC API. */ export const api = createTRPCNext({ config() { return { /** * Links used to determine request flow from client to server. * * @see https://trpc.io/docs/links */ links: [ loggerLink({ enabled: (opts) => process.env.NODE_ENV === "development" || (opts.direction === "down" && opts.result instanceof Error), }), httpBatchLink({ /** * Transformer used for data de-serialization from the server. * * @see https://trpc.io/docs/data-transformers */ transformer: superjson, url: `${getBaseUrl()}/api/trpc`, }), ], }; }, /** * Whether tRPC should await queries when server rendering pages. * * @see https://trpc.io/docs/nextjs#ssr-boolean-default-false */ ssr: false, transformer: superjson, }); /** * Inference helper for inputs. * * @example type HelloInput = RouterInputs['example']['hello'] */ export type RouterInputs = inferRouterInputs; /** * Inference helper for outputs. * * @example type HelloOutput = RouterOutputs['example']['hello'] */ export type RouterOutputs = inferRouterOutputs; ================================================ FILE: cli/template/extras/start-database/mysql.sh ================================================ #!/usr/bin/env bash # Use this script to start a docker container for a local development database # TO RUN ON WINDOWS: # 1. Install WSL (Windows Subsystem for Linux) - https://learn.microsoft.com/en-us/windows/wsl/install # 2. Install Docker Desktop or Podman Deskop # - Docker Desktop for Windows - https://docs.docker.com/docker-for-windows/install/ # - Podman Desktop - https://podman.io/getting-started/installation # 3. Open WSL - `wsl` # 4. Run this script - `./start-database.sh` # On Linux and macOS you can run this script directly - `./start-database.sh` set -a source .env DB_PASSWORD=$(echo "$DATABASE_URL" | awk -F':' '{print $3}' | awk -F'@' '{print $1}') DB_PORT=$(echo "$DATABASE_URL" | awk -F':' '{print $4}' | awk -F'\/' '{print $1}') DB_NAME=$(echo "$DATABASE_URL" | awk -F'/' '{print $4}') DB_CONTAINER_NAME="$DB_NAME-mysql" if ! [ -x "$(command -v docker)" ] && ! [ -x "$(command -v podman)" ]; then echo -e "Docker or Podman is not installed. Please install docker or podman and try again.\nDocker install guide: https://docs.docker.com/engine/install/\nPodman install guide: https://podman.io/getting-started/installation" exit 1 fi # determine which docker command to use if [ -x "$(command -v docker)" ]; then DOCKER_CMD="docker" elif [ -x "$(command -v podman)" ]; then DOCKER_CMD="podman" fi if ! $DOCKER_CMD info > /dev/null 2>&1; then echo "$DOCKER_CMD daemon is not running. Please start $DOCKER_CMD and try again." exit 1 fi if command -v nc >/dev/null 2>&1; then if nc -z localhost "$DB_PORT" 2>/dev/null; then echo "Port $DB_PORT is already in use." exit 1 fi else echo "Warning: Unable to check if port $DB_PORT is already in use (netcat not installed)" read -p "Do you want to continue anyway? [y/N]: " -r REPLY if ! [[ $REPLY =~ ^[Yy]$ ]]; then echo "Aborting." exit 1 fi fi if [ "$($DOCKER_CMD ps -q -f name=$DB_CONTAINER_NAME)" ]; then echo "Database container '$DB_CONTAINER_NAME' already running" exit 0 fi if [ "$($DOCKER_CMD ps -q -a -f name=$DB_CONTAINER_NAME)" ]; then $DOCKER_CMD start "$DB_CONTAINER_NAME" echo "Existing database container '$DB_CONTAINER_NAME' started" exit 0 fi if [ "$DB_PASSWORD" == "password" ]; then echo "You are using the default database password" read -p "Should we generate a random password for you? [y/N]: " -r REPLY if ! [[ $REPLY =~ ^[Yy]$ ]]; then echo "Please change the default password in the .env file and try again" exit 1 fi # Generate a random URL-safe password DB_PASSWORD=$(openssl rand -base64 12 | tr '+/' '-_') if [[ "$(uname)" == "Darwin" ]]; then # macOS requires an empty string to be passed with the `i` flag sed -i '' "s#:password@#:$DB_PASSWORD@#" .env else sed -i "s#:password@#:$DB_PASSWORD@#" .env fi fi $DOCKER_CMD run -d \ --name $DB_CONTAINER_NAME \ -e MYSQL_ROOT_PASSWORD="$DB_PASSWORD" \ -e MYSQL_DATABASE="$DB_NAME" \ -p "$DB_PORT":3306 \ docker.io/mysql && echo "Database container '$DB_CONTAINER_NAME' was successfully created" ================================================ FILE: cli/template/extras/start-database/postgres.sh ================================================ #!/usr/bin/env bash # Use this script to start a docker container for a local development database # TO RUN ON WINDOWS: # 1. Install WSL (Windows Subsystem for Linux) - https://learn.microsoft.com/en-us/windows/wsl/install # 2. Install Docker Desktop or Podman Deskop # - Docker Desktop for Windows - https://docs.docker.com/docker-for-windows/install/ # - Podman Desktop - https://podman.io/getting-started/installation # 3. Open WSL - `wsl` # 4. Run this script - `./start-database.sh` # On Linux and macOS you can run this script directly - `./start-database.sh` # import env variables from .env set -a source .env DB_PASSWORD=$(echo "$DATABASE_URL" | awk -F':' '{print $3}' | awk -F'@' '{print $1}') DB_PORT=$(echo "$DATABASE_URL" | awk -F':' '{print $4}' | awk -F'\/' '{print $1}') DB_NAME=$(echo "$DATABASE_URL" | awk -F'/' '{print $4}') DB_CONTAINER_NAME="$DB_NAME-postgres" if ! [ -x "$(command -v docker)" ] && ! [ -x "$(command -v podman)" ]; then echo -e "Docker or Podman is not installed. Please install docker or podman and try again.\nDocker install guide: https://docs.docker.com/engine/install/\nPodman install guide: https://podman.io/getting-started/installation" exit 1 fi # determine which docker command to use if [ -x "$(command -v docker)" ]; then DOCKER_CMD="docker" elif [ -x "$(command -v podman)" ]; then DOCKER_CMD="podman" fi if ! $DOCKER_CMD info > /dev/null 2>&1; then echo "$DOCKER_CMD daemon is not running. Please start $DOCKER_CMD and try again." exit 1 fi if command -v nc >/dev/null 2>&1; then if nc -z localhost "$DB_PORT" 2>/dev/null; then echo "Port $DB_PORT is already in use." exit 1 fi else echo "Warning: Unable to check if port $DB_PORT is already in use (netcat not installed)" read -p "Do you want to continue anyway? [y/N]: " -r REPLY if ! [[ $REPLY =~ ^[Yy]$ ]]; then echo "Aborting." exit 1 fi fi if [ "$($DOCKER_CMD ps -q -f name=$DB_CONTAINER_NAME)" ]; then echo "Database container '$DB_CONTAINER_NAME' already running" exit 0 fi if [ "$($DOCKER_CMD ps -q -a -f name=$DB_CONTAINER_NAME)" ]; then $DOCKER_CMD start "$DB_CONTAINER_NAME" echo "Existing database container '$DB_CONTAINER_NAME' started" exit 0 fi if [ "$DB_PASSWORD" = "password" ]; then echo "You are using the default database password" read -p "Should we generate a random password for you? [y/N]: " -r REPLY if ! [[ $REPLY =~ ^[Yy]$ ]]; then echo "Please change the default password in the .env file and try again" exit 1 fi # Generate a random URL-safe password DB_PASSWORD=$(openssl rand -base64 12 | tr '+/' '-_') if [[ "$(uname)" == "Darwin" ]]; then # macOS requires an empty string to be passed with the `i` flag sed -i '' "s#:password@#:$DB_PASSWORD@#" .env else sed -i "s#:password@#:$DB_PASSWORD@#" .env fi fi $DOCKER_CMD run -d \ --name $DB_CONTAINER_NAME \ -e POSTGRES_USER="postgres" \ -e POSTGRES_PASSWORD="$DB_PASSWORD" \ -e POSTGRES_DB="$DB_NAME" \ -p "$DB_PORT":5432 \ docker.io/postgres && echo "Database container '$DB_CONTAINER_NAME' was successfully created" ================================================ FILE: cli/tsconfig.eslint.json ================================================ { "extends": "./tsconfig.json", "include": ["src", "template/**/*.ts", "template/**/*.tsx"] } ================================================ FILE: cli/tsconfig.json ================================================ { "extends": "../tsconfig.json", "compilerOptions": { "baseUrl": "./", "paths": { "~/*": ["./src/*"] }, "checkJs": true }, "include": ["src", "tsup.config.ts", "../reset.d.ts", "prettier.config.mjs"] } ================================================ FILE: cli/tsup.config.ts ================================================ import { defineConfig } from "tsup"; const isDev = process.env.npm_lifecycle_event === "dev"; export default defineConfig({ clean: true, entry: ["src/index.ts"], format: ["esm"], minify: !isDev, target: "esnext", outDir: "dist", onSuccess: isDev ? "node dist/index.js" : undefined, }); ================================================ FILE: package.json ================================================ { "name": "@ct3a/root", "version": "0.0.0", "description": "Quickest way to start a new web app with full stack typesafety", "author": "Shoubhit Dash (https://nexxel.dev)", "maintainers": [ "Julius Marminge (https://jumr.dev)" ], "type": "module", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/t3-oss/create-t3-app.git" }, "keywords": [ "create-t3-app", "init.tips", "next.js", "t3-stack", "tailwind", "tRPC", "typescript" ], "engines": { "node": ">=20.0.0" }, "packageManager": "pnpm@10.8.0", "scripts": { "typecheck": "turbo typecheck", "build:cli": "turbo --filter=create-t3-app build", "build:www": "turbo --filter=www build", "build": "turbo build", "start:cli": "turbo --filter=create-t3-app start", "start:www": "turbo --filter=www start", "dev:cli": "turbo --filter=create-t3-app dev", "dev:www": "turbo --filter=www dev", "clean": "turbo clean && rm -rf node_modules", "lint": "turbo lint && manypkg check", "lint:fix": "turbo lint:fix && manypkg fix", "format": "turbo format && prettier --write '*.{cjs,json}' --ignore-unknown --no-error-on-unmatched-pattern", "format:check": "turbo format:check && prettier --check '*.{cjs,json}' --ignore-unknown --no-error-on-unmatched-pattern", "check": "turbo lint typecheck format:check && manypkg check", "release": "changeset version", "pub:beta": "cd cli && pnpm pub:beta", "pub:release": "cd cli && pnpm pub:release" }, "dependencies": { "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.27.3", "@eslint/compat": "^1.2.7", "@eslint/eslintrc": "^3.3.1", "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@manypkg/cli": "^0.20.0", "@total-typescript/ts-reset": "^0.3.7", "@types/node": "^24.10.1", "eslint": "^9.23.0", "eslint-plugin-import-x": "^4.9.1", "eslint-plugin-isaacscript": "^4.0.0", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.2.0", "prettier": "^3.5.3", "turbo": "1.13.3-canary.3", "typescript": "^5.8.2", "typescript-eslint": "^8.27.0" } } ================================================ FILE: pnpm-workspace.yaml ================================================ packages: - cli - www onlyBuiltDependencies: - '@prisma/client' - '@prisma/engines' - esbuild - prisma - sharp ================================================ FILE: prettier.config.mjs ================================================ /** * @type {import('prettier').Config & import("@ianvs/prettier-plugin-sort-imports").PluginConfig} */ const config = { arrowParens: "always", printWidth: 80, singleQuote: false, jsxSingleQuote: false, semi: true, trailingComma: "all", tabWidth: 2, }; export default config; ================================================ FILE: reset.d.ts ================================================ import "@total-typescript/ts-reset"; ================================================ FILE: tsconfig.json ================================================ { "include": [".eslintrc.cjs", "prettier.config.mjs"], "compilerOptions": { /* LANGUAGE COMPILATION OPTIONS */ "target": "ES2020", "lib": ["DOM", "DOM.Iterable", "ES2021"], "module": "Node16", "moduleResolution": "nodenext", "resolveJsonModule": true, "allowJs": true, "checkJs": true, /* EMIT RULES */ "outDir": "./dist", "noEmit": true, // TSUP takes care of emitting js for us, in a MUCH faster way "declaration": true, "declarationMap": true, "sourceMap": true, "removeComments": true, /* TYPE CHECKING RULES */ "strict": true, // "noImplicitAny": true, // Included in "Strict" // "noImplicitThis": true, // Included in "Strict" // "strictBindCallApply": true, // Included in "Strict" // "strictFunctionTypes": true, // Included in "Strict" // "strictNullChecks": true, // Included in "Strict" // "strictPropertyInitialization": true, // Included in "Strict" "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": true, "useUnknownInCatchVariables": true, "noUncheckedIndexedAccess": true, // TLDR - Checking an indexed value (array[0]) now forces type as there is no confirmation that index exists // THE BELOW ARE EXTRA STRICT OPTIONS THAT SHOULD ONLY BY CONSIDERED IN VERY SAFE PROJECTS // "exactOptionalPropertyTypes": true, // TLDR - Setting to undefined is not the same as a property not being defined at all // "noPropertyAccessFromIndexSignature": true, // TLDR - Use dot notation for objects if youre sure it exists, use ['index'] notaion if unsure /* OTHER OPTIONS */ "allowSyntheticDefaultImports": true, "esModuleInterop": true, // "emitDecoratorMetadata": true, // "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "useDefineForClassFields": true } } ================================================ FILE: turbo.json ================================================ { "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "outputs": ["dist/**", ".vercel/output/**", ".next/**"], "dependsOn": ["^build"] }, "dev": { "cache": false, "persistent": true }, "start": { "dependsOn": ["^build"] }, "lint": { "cache": false }, "lint:fix": { "cache": false }, "format": { "cache": false }, "format:check": { "cache": false }, "clean": { "cache": false }, "typecheck": {} } } ================================================ FILE: www/.env.example ================================================ # For developers who would like the data to be pulled correctly, # generate these tokens from the provider and add them to a .gitignored .env file # You can run `pnpm dev` without them, and dummy data (or no data) will be used instead # Github - for grabbing star count in navbar, and commit history for docs pages # https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token PUBLIC_GITHUB_TOKEN= # Twitter (API v2)- for grabbing Twitter card data # https://developer.twitter.com/en/docs/authentication/overview TWITTER_BEARER_TOKEN= # For enabling debug mode in satori (for OG images) DEBUG_OG= ================================================ FILE: www/.gitignore ================================================ # build output dist/ # dependencies node_modules/ # logs npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* # environment variables .env .env.production !.env.example # macOS-specific files .DS_Store ================================================ FILE: www/README.md ================================================ # create-t3-app Documentation Site Based on the [Astro Starter Kit: Docs Site](https://github.com/withastro/astro/tree/latest/examples/docs). New to Astro? - Check out [their documentation](https://docs.astro.build). - Jump into their [Discord server](https://astro.build/chat). - Deploy a site to production with the guide, [Deploy an Astro Website](https://docs.astro.build/guides/deploy). ## Outline - [Features](#features) - [Run Site Locally](#run-site-locally) - [Command Cheat Sheet](#command-cheat-sheet) - [Customize This Theme](#customize-this-theme) - [Site Metadata](#site-metadata) - [CSS Styling](#css-styling) - [Page Metadata](#page-metadata) - [Sidebar Navigation](#sidebar-navigation) - [Multiple Languages Support](#multiple-languages-support) ## Features - ✅ **Full Markdown support** - ✅ **Responsive mobile-friendly design** - ✅ **Sidebar navigation** - ✅ **Search (powered by Algolia)** - ✅ **Multi-language i18n** - ✅ **Automatic table of contents** - ✅ **Automatic list of contributors** - ✅ (and, best of all) **dark mode** ## Run Site Locally ```bash git clone https://github.com/t3-oss/create-t3-app.git cd create-t3-app/www pnpm i pnpm dev ``` ## Command Cheat Sheet All commands are run from the root of the project, from a terminal. | Command | Action | | :--------------------- | :----------------------------------------------- | | `npm install` | Installs dependencies | | `npm run dev` | Starts local dev server at `localhost:3000` | | `npm run build` | Build your production site to `./dist/` | | `npm run preview` | Preview your build locally, before deploying | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | | `npm run astro --help` | Get help using the Astro CLI | ## Customize This Theme ### Site Metadata `src/config.ts` contains several data objects that describe metadata about your site like title, description, default language, and Open Graph details. You can customize these to match your project. ### CSS Styling The theme's look and feel is controlled by a few key variables that you can customize yourself. You'll find them in the `public/theme.css` CSS file. If you've never worked with CSS variables before, give [MDN's guide on CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) a quick read. This theme uses a "cool blue" accent color by default. To customize this for your project, change the `--theme-accent` variable to whatever color you'd like: ```diff /* public/theme.css */ :root { color-scheme: light; - --theme-accent: hsla(var(--color-blue), 1); + --theme-accent: hsla(var(--color-red), 1); /* or: hsla(#FF0000, 1); */ ``` ### Page Metadata Astro uses frontmatter in Markdown pages to choose layouts and pass properties to those layouts. If you are using the default layout, you can customize the page in many different ways to optimize SEO and other things. ```markdown --- title: Example title description: Really cool docs example that uses Astro layout: ../../layouts/MainLayout.astro --- # Page content... ``` This uses the `title` and `description` properties to set the document title, meta title, meta description, and Open Graph description. See `src/components/HeadSEO.astro` for more SEO related properties. ### Sidebar Navigation The sidebar navigation is controlled by the `SIDEBAR` variable in your `src/config.ts` file. You can customize the sidebar by modifying this object. A default, starter navigation has already been created for you. ```ts // src/config.ts export const SIDEBAR = { en: [ { text: "Section Header", header: true }, { text: "Introduction", link: "en/introduction" }, { text: "Page 2", link: "en/page-2" }, { text: "Page 3", link: "en/page-3" }, { text: "Another Section", header: true }, { text: "Page 4", link: "en/page-4" }, ], }; ``` Note the top-level `en` key: This is needed for multi-language support. You can change it to whatever language you'd like, or add new languages as you go. More details on this below. ### Multiple Languages Support The Astro docs template supports multiple languages out of the box. The default theme only shows `en` docs. But you can enable multi-language support features. To add a second language to your project, you'll want to extend the layout, `src/pages/[lang]/...`: ```diff 📂 src/pages ┣ 📂 en ┃ ┣ 📜 page-1.md ┃ ┣ 📜 page-2.md ┃ ┣ 📜 page-3.astro + ┣ 📂 es + ┃ ┣ 📜 page-1.md + ┃ ┣ 📜 page-2.md + ┃ ┣ 📜 page-3.astro ``` You'll also need to add the new language name to the `KNOWN_LANGUAGES` map in your `src/config.ts` file. This will enable your new language switcher in the site header. ```diff // src/config.ts export const KNOWN_LANGUAGES = { English: 'en', + Spanish: 'es', }; ``` Last step: you'll need to add a new entry to your sidebar, to create the table of contents for that language. While duplicating every page might not sound ideal to everyone, this extra control allows you to create entirely custom content for every language. > Make sure the sidebar `link` value points to the correct language! ```diff // src/config.ts export const SIDEBAR = { en: [ { text: 'Section Header', header: true, }, { text: 'Introduction', link: 'en/introduction' }, // ... ], + es: [ + { text: 'Encabezado de sección', header: true, }, + { text: 'Introducción', link: 'es/introduction' }, + // ... + ], }; // ... ``` If you plan to use Spanish as the default language, you just need to modify the redirect path in `src/pages/index.astro` (or you can remove the script and write a landing page in Spanish instead): ```diff ``` What if I don't plan to support multiple languages? Totally fine! Not all projects need (or can support) that. If that single language is not English, you can just replace `en` in directory layouts and configurations with the preferred language such as `es`. ================================================ FILE: www/TRANSLATIONS.md ================================================ # Contributing Guidelines for our docs Are you a native speaker of a language other than English? We'd love to have your help! ## How to contribute (Read the [contribution guidelines](../CONTRIBUTING.md) first for general information about contributing to this project.) ### Initial Translation If the docs are not available in your language yet and you would like to translate them, you can do so by following these steps: 1. Copy the `pages/en` directory to a new directory with the name of your language, e.g. `pages/de` for German. (If you're not sure what the language code is, you can find it [here](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).) 2. File a draft PR to let others know you're working on it. This is to avoid duplicate work, and also allows for community feedback along the way. 3. File by file, translate the content of the files. 4. Update the `lang` attribute in the frontmatter of the files translated to your language. If there are any files you haven't translated yet, that's fine. Just leave the `lang: en` attribute in the frontmatter. 5. If the language you are translating into is read right-to-left (for example, Arabic or Hebrew), also add `dir: rtl` to the frontmatter. 6. Add your language to the `KNOWN_LANGUAGE` object in [config.ts](./src/config.ts). You'll now get some type errors on the `SIDEBAR` and `SIDEBAR_HEADER_MAP` objects in the same file. Follow the instructions in the comments and translate the requested entries. 7. When you're done, mark the pull request as ready for review. ### Reviewing We aim to have 1-2 reviews on each PR before merging. This allows for some back and forth and ensures that the quality of the docs is high, and the tone is consistent. We'd highly appreciate it if you knew someone who speaks the language you're translating into to review your PR. This can help speed up the process of getting your contribution merged. ### Maintaining Naturally, the English docs will move faster than the translated ones. We've implemented a feature that alerts visitors if a translated version of a page is outdated. If you see that a page is outdated and you're able to update it, please do so! We also have code owners for each language. Being a code owner means that you get notified when there is a PR that includes changes to the files of your language, so that you can review it. Becoming a code owner is open to anyone who has contributed to a language, either by writing translations themselves or by reviewing those of others. If would like to become a code owner, either add your GitHub username to your language in [translations.yml](https://github.com/t3-oss/create-t3-app/blob/next/.github/workflows/translations.yml) or let us know in the `create-t3-translation` channel on [Discord](https://create.t3.gg/discord). ================================================ FILE: www/astro.config.mjs ================================================ import mdx from "@astrojs/mdx"; import react from "@astrojs/react"; import sitemap from "@astrojs/sitemap"; import vercel from "@astrojs/vercel"; import { defineConfig } from "astro/config"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; import rehypeExternalLinks from "rehype-external-links"; import rehypeSlug from "rehype-slug"; import remarkCodeTitles from "remark-code-titles"; /** @link https://astro.build/config */ export default defineConfig({ site: `https://create.t3.gg/`, output: "server", adapter: vercel(), markdown: { remarkPlugins: [remarkCodeTitles], rehypePlugins: [ [ rehypeExternalLinks, { target: "_blank", rel: ["noreferrer noopener"], content: { type: "text", value: "↗", }, }, ], rehypeSlug, [ rehypeAutolinkHeadings, { properties: { class: "heading-link heading-link--hidden---effects", "data-heading-link": true, }, behavior: "wrap", }, ], ], shikiConfig: { theme: "rose-pine", wrap: true, }, }, integrations: [react(), sitemap(), mdx()], vite: { optimizeDeps: { exclude: ["@resvg/resvg-js"], }, }, }); ================================================ FILE: www/package.json ================================================ { "name": "@ct3a/www", "version": "0.0.1", "private": true, "scripts": { "typecheck": "tsc --noEmit", "dev": "astro dev", "start": "astro dev", "check": "astro check", "clean": "rm -rf node_modules .turbo dist .eslintcache", "format": "prettier '**/*.{cjs,mjs,ts,tsx,md,json,astro}' --ignore-path ../.gitignore --ignore-unknown --write", "format:check": "prettier '**/*.{cjs,mjs,ts,tsx,md,json,astro}' --ignore-path ../.gitignore --ignore-unknown --check", "build": "astro build", "preview": "astro preview", "astro": "astro" }, "dependencies": { "@algolia/client-search": "^5.21.0", "@astrojs/check": "^0.9.4", "@astrojs/mdx": "^4.2.1", "@astrojs/sitemap": "^3.3.0", "@astrojs/vercel": "^8.1.3", "@docsearch/css": "^3.9.0", "@docsearch/react": "^3.9.0", "@fontsource-variable/inter": "^5.2.5", "@headlessui/react": "^2.2.0", "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@resvg/resvg-js": "^2.6.2", "@stackblitz/sdk": "^1.11.0", "@vercel/analytics": "^1.5.0", "clsx": "^2.1.1", "embla-carousel": "^8.5.2", "embla-carousel-autoplay": "^8.5.2", "satori": "^0.12.1", "sharp": "^0.33.5", "tailwind-scrollbar": "^3.0.0", "treeify": "^1.1.0", "unist-util-visit": "^5.0.0", "zod": "^3.24.2" }, "devDependencies": { "@astrojs/react": "^4.2.1", "@astrojs/tailwind": "^6.0.1", "@types/node": "^24.10.1", "@types/react": "~19.1.0", "@types/react-dom": "~19.1.0", "@types/react-typist": "^2.0.6", "@types/treeify": "^1.0.3", "astro": "^5.5.4", "prettier": "^3.5.3", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-tailwindcss": "^0.6.11", "react": "^19.2.3", "react-dom": "^19.2.3", "react-typist": "^2.0.5", "rehype-autolink-headings": "^7.1.0", "rehype-external-links": "^3.0.0", "rehype-slug": "^6.0.0", "remark-code-titles": "^0.1.2", "tailwindcss": "npm:tailwindcss@^3", "typescript": "^5.8.2" } } ================================================ FILE: www/postcss.config.cjs ================================================ const config = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }; module.exports = config; ================================================ FILE: www/prettier.config.mjs ================================================ import baseConfig from "../prettier.config.mjs"; /** * @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions & * import("@ianvs/prettier-plugin-sort-imports").PluginConfig} */ const config = { ...baseConfig, plugins: [ "@ianvs/prettier-plugin-sort-imports", "prettier-plugin-astro", "prettier-plugin-tailwindcss", // MUST come last ], overrides: [ { files: "*.astro", options: { parser: "astro", }, }, ], astroAllowShorthand: false, tailwindConfig: "./tailwind.config.ts", importOrder: ["", "", "^~/", "^[.][.]/", "^[.]/"], importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], importOrderTypeScriptVersion: "4.4.0", }; export default config; ================================================ FILE: www/public/makeScrollableCodeFocusable.js ================================================ Array.from(document.getElementsByTagName("pre")).forEach((element) => { element.setAttribute("tabindex", "0"); }); ================================================ FILE: www/public/robots.txt ================================================ # Algolia-Crawler-Verif: 013A1A14AB9EE32B User-agent: * Allow: / Sitemap: https://create.t3.gg/sitemap-index.xml ================================================ FILE: www/src/.vscode/settings.json ================================================ { "workbench.editor.wrapTabs": false, "editor.wordWrap": "on" } ================================================ FILE: www/src/components/accessibility/jumpToContent.astro ================================================ --- import Button from "../landingPage/button.astro"; --- ================================================ FILE: www/src/components/branding/Asset.astro ================================================ --- import Button from "../landingPage/button.astro"; export interface Props { title: string; bg: "white" | "black"; description: string; assets: { svg: string | null; png: string | null; }; attribution?: boolean; } const { title, bg, description, assets: { svg, png }, attribution = false, } = Astro.props; function getDownloadName(path: string) { const lastIndex = path.lastIndexOf("/"); return path.slice(lastIndex + 1, path.length - 4); } ---
{title} { attribution ? ( Designed by{" "} Zak ) : null }

{description}

{ svg ? ( ) : null } { png ? ( ) : null }
================================================ FILE: www/src/components/docs/avatarList.astro ================================================ --- import { fetchGithub, type Commit } from "../../utils/fetchGithub"; // fetch all commits for just this page's path export interface Props { path: string; } const { path } = Astro.props; const resolvedPath = `www/${path}`; const url = `https://api.github.com/repos/t3-oss/create-t3-app/commits?path=${resolvedPath}`; const commitsURL = `https://github.com/t3-oss/create-t3-app/commits/main/${resolvedPath}`; async function getCommits(url: string) { try { const data = await fetchGithub(url, { fetchType: "commits" }); return data; } catch (e) { console.warn(`[warn] /src/components/AvatarList.astro ${(e as Error)?.message ?? e}`); return [] as Commit[]; } } function removeDups(arr: Commit[]): Pick[] { const map = new Map>(); for (let item of arr) { const author = item.author; if (!author || !author.id || !author.login) continue; // Deduplicate based on author.id map.set(author.id.toString(), { login: author.login, id: author.id }); } return [...map.values()]; } const data = await getCommits(url); const unique = removeDups(data ?? []); const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors ---
Recent Contributors To This Page
    { recentContributors.map((item) => (
  • {`Contributor
  • )) }
{ additionalContributors > 0 && ( {`and ${additionalContributors} additional contributor${ additionalContributors > 1 ? "s" : "" }.`} ) } { unique.length === 0 && ( Contributors ) }
================================================ FILE: www/src/components/docs/breadCrumbs.tsx ================================================ import clsx from "clsx"; import { SIDEBAR, SIDEBAR_HEADER_MAP, type OuterHeaders } from "../../config"; import { getIsRtlFromLangCode, getLanguageFromURL } from "../../languages"; type SlugType = "" | "deployment" | "usage"; export default function BreadCrumbs() { const lang = getLanguageFromURL(window.location.href); const isRtl = getIsRtlFromLangCode(lang ?? "en"); const slugToEntryPath = (slug: SlugType): OuterHeaders => { switch (slug) { case "": return "Create T3 App"; case "usage": return "Usage"; case "deployment": return "Deployment"; } }; const pathname = window.location.pathname.endsWith("/") ? window.location.pathname.slice(0, -1) : window.location.pathname; const slug = pathname.slice(1).split("/").length > 2 ? pathname.slice(1).split("/")[1] : ""; const actualEntries = SIDEBAR[lang][ slugToEntryPath( slug === undefined || slug === "" ? "" : (slug as SlugType), ) ]; const getPathNameFromLink = (link: string) => { return [...(actualEntries ?? [])].find((entry) => entry.link === link) ?.text; }; const getHeaderName = (header: OuterHeaders) => { if (lang === "en") return header; return SIDEBAR_HEADER_MAP[lang][header]; }; const breadcrumbs = pathname .split("/") .slice(pathname.split("/").length > 3 ? -2 : -1) .map((crumb) => { const path = pathname .split("/") .slice(0, pathname.split("/").indexOf(crumb) + 1) .join("/"); return { href: `${window.location.protocol}//${window.location.host}${path}`, key: crumb, text: getPathNameFromLink(path.slice(path.indexOf(lang))) ?? getHeaderName( `${crumb[0]?.toUpperCase()}${crumb.slice(1)}` as OuterHeaders, ), }; }); return (
{breadcrumbs.map((crumb, index) => (
{crumb.text} {index < breadcrumbs.length - 1 && ( )}
))}
); } function BreadCrumbsArrow(props: { isRtl: boolean }) { return ( ); } ================================================ FILE: www/src/components/docs/callout.tsx ================================================ import clsx from "clsx"; export default function Callout({ type, children, }: { type: "info" | "tip" | "warning"; children: React.ReactNode; }) { const getEmoji = () => { switch (type) { case "warning": return "⚠️"; case "info": return "ℹ️"; case "tip": return "💡"; } }; return (
{getEmoji()} {children}
); } ================================================ FILE: www/src/components/docs/companyList.tsx ================================================ interface Company { name: string; link: string; linkName: string; } const companies: Company[] = [ { name: "Ping.gg", linkName: "ping.gg", link: "https://ping.gg", }, { name: "Social Crow", linkName: "socialcrow.co", link: "https://www.socialcrow.co", }, { name: "Nexiona", linkName: "nexiona.com", link: "https://nexiona.com", }, { name: "Layer3", linkName: "layer3.xyz", link: "https://layer3.xyz", }, { name: "EcoToken", linkName: "ecotokens.net", link: "https://ecotokens.net", }, { name: "Civitai", linkName: "civitai.com", link: "https://civitai.com", }, { name: "GreatFrontEnd", linkName: "greatfrontend.com", link: "https://www.greatfrontend.com", }, { name: "River", linkName: "getriver.io", link: "https://getriver.io", }, { name: "FlowGPT", linkName: "flowgpt.com", link: "https://flowgpt.com", }, { name: "Papertrail", linkName: "Papertrail", link: "https://papertrail.biblish.com", }, { name: "AIKeywording", linkName: "aikeywording.com/", link: "https://aikeywording.com/", }, { name: "HIX AI", linkName: "HIX AI", link: "https://hix.ai", }, ]; export default function CompanyList({ companyIntl = "Description", linkIntl = "Link", }: { companyIntl: string; linkIntl: string; }) { return ( {companies.map((company) => ( ))}
{companyIntl} {linkIntl}
{company.name} {company.linkName}
); } ================================================ FILE: www/src/components/docs/exampleOptionForm.astro ================================================ --- const options = { nextAuth: "NextAuth.js", prisma: "Prisma", tailwind: "Tailwind CSS", trpc: "tRPC", drizzle: "Drizzle", }; ---
{ Object.entries(options).map(([code, name]) => (
)) }
================================================ FILE: www/src/components/docs/folderStructureDiagramApp.astro ================================================
  
================================================ FILE: www/src/components/docs/folderStructureDiagramPages.astro ================================================
  
================================================ FILE: www/src/components/docs/folderStructureForm.astro ================================================ --- const options = { nextauth: "NextAuth.js", prisma: "Prisma", drizzle: "Drizzle", tailwind: "Tailwind CSS", trpc: "tRPC", }; ---
{ Object.entries(options).map(([code, name]) => (
)) }
================================================ FILE: www/src/components/docs/indexPage.astro ================================================ --- import { type Frontmatter } from "../../config"; export interface Props { sidebarEntries: { text: string; link: string }[]; frontmatter: Frontmatter; files: Record[]; } const { sidebarEntries, frontmatter, files } = Astro.props; ---

{frontmatter.description}

{ sidebarEntries.map(({ text, link }, index) => { const frontmatter = files.find((file) => file.url === `/${link}`) ?.frontmatter as Frontmatter; return (

#{index + 1} {text}

{frontmatter?.description && (

{frontmatter.description}

)}
); }) }
================================================ FILE: www/src/components/docs/introductionTab.tsx ================================================ import { useState } from "react"; export function IntroductionTab() { const [isAppRouterSelected, setIsAppRouterSelected] = useState(true); return ( <>
); } ================================================ FILE: www/src/components/docs/openSourceAppList.tsx ================================================ interface App { description: string; repoName: string; repo: string; linkName: string; linkExtra?: string; link: string; } const projects: App[] = [ { description: "OpenBio - Open Source Link in Bio", repoName: "vanxh/openbio", repo: "https://github.com/vanxh/openbio", linkName: "openbio.app", link: "https://openbio.app", }, { description: "Exaya - Organic transportation processes manager", repoName: "exaya", repo: "https://github.com/kralion/exaya", linkName: "exaya.app", link: "https://exaya.vercel.app", }, { description: "TheNinja-RPG - A free browser-based ninja game", repoName: "TheNinjaRPG", repo: "https://github.com/MathiasGruber/TheNinjaRPG", linkName: "theninja-rpg.com", link: "https://www.theninja-rpg.com", }, { description: "Create T3 Turbo - T3 Stack using Turborepo", repoName: "create-t3-turbo", repo: "https://github.com/t3-oss/create-t3-turbo", linkName: "create-t3-turbo.vercel.app", link: "https://create-t3-turbo.vercel.app/", }, { description: "T3 turborepo template with biomejs and both shadcn native and web ui", repoName: "rajatsandeepsen/t3-turbo-biome", repo: "https://github.com/rajatsandeepsen/t3-turbo-biome", linkName: "create-t3-turbo.vercel.app", link: "https://create-t3-turbo.vercel.app/", }, { description: "Zapdos - a QnA app for streamers", repoName: "pingdotgg/zapdos", repo: "https://github.com/pingdotgg/zapdos", linkName: "ask.ping.gg", link: "https://ask.ping.gg", }, { description: "Shoutify - Free, open-source, self-hosted social media management", repoName: "techsquidtv/shoutify", repo: "https://github.com/TechSquidTV/Shoutify", linkName: "shoutify.app", link: "https://github.com/TechSquidTV/Shoutify", linkExtra: "(coming soon)", }, { description: "Me3 - Describe yourself in 3 things and share with your friends.", repoName: "henriqgoncalvs/me3", repo: "https://github.com/henriqgoncalvs/me3", linkName: "me3-henriiqueg.vercel.app", link: "https://me3-henriiqueg.vercel.app/", }, { description: "Josh's personal site", repoName: "GentikSolm/imjosh-blog", repo: "https://github.com/GentikSolm/imjosh-blog", linkName: "imjosh.dev", link: "https://imjosh.dev", }, { description: "Cal.com - Scheduling infrastructure for absolutely everyone.", repoName: "calcom/cal.com", repo: "https://github.com/calcom/cal.com", linkName: "cal.com", link: "https://cal.com", }, { description: "My FAQ Page - An FAQ Page generator", repoName: "ronanru/myfaq.page", repo: "https://github.com/ronanru/myfaq.page", linkName: "MyFAQ.page", link: "https://myfaq.page", }, { description: "Tincy Pics - A tincy wincy image host", repoName: "mozzius/tincypics", repo: "https://github.com/mozzius/tincypics", linkName: "tincy.pics", link: "https://tincy.pics", }, { description: "Ayanava's Guestbook", repoName: "AyanavaKarmakar/Guestbook", repo: "https://github.com/AyanavaKarmakar/Guestbook", linkName: "guestbook.ayanavakarmakar.software", link: "https://guestbook.ayanavakarmakar.software/", }, { description: "Slug - URL Shortener", repoName: "pheralb/slug", repo: "https://github.com/pheralb/slug", linkName: "slug.vercel.app", link: "https://slug.vercel.app", }, { description: "AI TTS Donations - FOSS AI Text To Speech service for Streamers.", repoName: "mmattDonk/AI-TTS-Donations", repo: "https://github.com/mmattDonk/AI-TTS-Donations", linkName: "staging.solrock.mmattDonk.com", link: "https://staging.solrock.mmattDonk.com", }, { description: "The Doom", repoName: "moltivie/slug", repo: "https://github.com/Moltivie/the-t3-stack", linkName: "the-t3-stack.vercel.app", link: "https://the-t3-stack.vercel.app", }, { description: "Railtrack", repoName: "noahflk/railtrack", repo: "https://github.com/noahflk/railtrack", linkName: "railtrack.ch", link: "https://railtrack.ch", }, { description: "KARA Shop - Ecommerce website", repoName: "mehrabmp/kara-shop", repo: "https://github.com/mehrabmp/kara-shop", linkName: "karashop.vercel.app", link: "https://karashop.vercel.app/", }, { description: "Tauri T3 App - Tauri App using T3 Stack", repoName: "tauri-t3-app", repo: "https://github.com/AyanavaKarmakar/tauri-t3-app", linkName: "tauri-t3-app.docs", link: "https://github.com/AyanavaKarmakar/tauri-t3-app#readme", }, { description: "Azon - E-Commerce website", repoName: "andrewsolonets/Azon-Shop", repo: "https://github.com/andrewsolonets/Azon-Shop", linkName: "azon-shop.vercel.app", link: "https://azon-shop.vercel.app/", }, { description: "Analyzemyrepo.com - Useful insights for any GitHub repo", repoName: "CrowdDotDev/analyzemyrepo", repo: "https://github.com/CrowdDotDev/analyzemyrepo", linkName: "analyzemyrepo.com", link: "https://analyzemyrepo.com", }, { description: "Answer Overflow - Discord bot to index help channels into Google", repoName: "AnswerOverflow/AnswerOverflow", repo: "https://github.com/AnswerOverflow/AnswerOverflow", linkName: "answeroverflow.com", link: "https://www.answeroverflow.com/", }, { description: "CUA - Create an Universal App for web, native and desktop", repoName: "chen-rn/CUA", repo: "https://github.com/chen-rn/CUA", linkName: "cua-demo.vercel.app", link: "https://cua-demo.vercel.app/", }, { description: "Menufic - Digital menu generator for restaurants", repoName: "kaje94/menufic", repo: "https://github.com/kaje94/menufic", linkName: "menufic.com", link: "https://menufic.com", }, { description: "Twitter clone - A simple Twitter clone", repoName: "AlandSleman/t3-twitter-clone", repo: "https://github.com/AlandSleman/t3-twitter-clone", linkName: "twitter-clone.kurdmake.com", link: "https://twitter-clone.kurdmake.com", }, { description: "Prisma Editor - A powerful tool to visualize and edit Prisma Schema", repoName: "mohammed-bahumaish/prisma-editor", repo: "https://github.com/mohammed-bahumaish/prisma-editor", linkName: "prisma-editor.bahumaish.com", link: "https://prisma-editor.bahumaish.com", }, { description: "Judge devs - website, where developers share their projects", repoName: "judge-devs (gh)", repo: "https://github.com/serzhan181/judge-devs", linkName: "judge-devs.com", link: "https://judge-devs.vercel.app/", }, { description: "T3 Blog - A Reddit inspired forum website", repoName: "leojuriolli7/t3-blog", repo: "https://github.com/leojuriolli7/t3-blog", linkName: "t3-blog-pi.vercel.app", link: "https://t3-blog-pi.vercel.app", }, { description: "Checkinout - A checklist management project using T3 Stack", repoName: "burak-sevinc/t3-checkinout", repo: "https://github.com/burak-sevinc/t3-checkinout", linkName: "checkinout.vercel.app", link: "https://checkinout.vercel.app/", }, { description: "Sozluk Clone - A simple clone of Ekşi Sözlük", repoName: "doandroidsdreamof/sozluk-clone", repo: "https://github.com/doandroidsdreamof/sozluk-clone", linkName: "sozluk-clone.vercel.app", link: "https://sozluk-clone.vercel.app", }, { description: "Henrique's personal site", repoName: "henriqgoncalvs/website", repo: "https://github.com/henriqgoncalvs/website", linkName: "henriqgoncalvs.com", link: "https://henriqgoncalvs.com", }, { description: "Code Notes - Takes your notes without any b*llshit", repoName: "JungRama/code-notes", repo: "https://github.com/JungRama/code-notes", linkName: "code-notes-app.vercel.app", link: "https://code-notes-app.vercel.app", }, { description: "Rao.Pics - :electron: 帮助你远程访问 Eagle、Pixcall、Billfish 的素材资源。", repoName: "meetqy/rao-pics", repo: "https://github.com/meetqy/rao-pics", linkName: "rao.pics", link: "https://rao.pics", }, { description: "eBoto - One-Stop Online Voting Solution", repoName: "bricesuazo/eboto", repo: "https://github.com/bricesuazo/eboto", linkName: "eboto-mo.com", link: "https://eboto-mo.com/", }, { description: "ImprovDB - The ultimate repository for improv games & exercises", repoName: "aberonni/improvdb", repo: "https://github.com/aberonni/improvdb", linkName: "improvdb.com", link: "https://improvdb.com/", }, { description: "Simple Todo app - with custom auth", repoName: "Parthvsquare/t3-todo-mvc", repo: "https://github.com/Parthvsquare/t3-todo-mvc", linkName: "", link: "", }, { description: "Profilee - A Profile Link Builder", repoName: "mittalsam98/profilee", repo: "https://github.com/mittalsam98/profilee", linkName: "Profilee", link: "https://www.profilee.info/", }, { description: "SunalRahal - Your Spotify, in a SVG", repoName: "prabincankod/sunal-rahal", repo: "https://github.com/prabincankod/sunal-rahal", linkName: "SunalRhal", link: "https://sunal-rahal.vercel.app", }, { description: "RobertHavelaar.dev - A Modern Portfolio and Tool Showcase", repoName: "EastTexasElectronics/portfolio", repo: "https://github.com/EastTexasElectronics/portfolio", linkName: "RobertHavelaar.dev", link: "https://www.roberthavelaar.dev/", }, { description: "Tasky - Simplify task management", repoName: "ismaelherreradev/tasky", repo: "https://github.com/ismaelherreradev/tasky", linkName: "taskyboard", link: "https://taskyboard.vercel.app", }, { description: "Squeak - A multiplayer Solitaire-based game", repoName: "michaelongaro/Squeak", repo: "https://github.com/michaelongaro/Squeak", linkName: "Squeak", link: "https://playsqueak.com/", }, { description: "Ray - A full stack admin starter", repoName: "koujialong/ray-admin", repo: "https://github.com/koujialong/ray-admin", linkName: "Ray", link: "https://koujialong-ray.vercel.app/", }, { description: "Aiseka - Discover the best Color Palette & Color Tools", repoName: "meetqy/aiseka", repo: "https://github.com/meetqy/aiseka", linkName: "AISEKA", link: "https://aiseka.com/", }, ]; export default function OpenSourceAppList({ descriptionIntl = "Description", repoIntl = "Repo", linkIntl = "Link", }: { descriptionIntl: string; repoIntl: string; linkIntl: string; }) { return ( {projects.map((project) => ( ))}
{descriptionIntl} {repoIntl} {linkIntl}
{project.description} {project.repoName} {project.linkName} {project.linkExtra && {project.linkExtra}}
); } ================================================ FILE: www/src/components/docs/outdatedDocsBanner.astro ================================================ --- import { fetchGithub } from "../../utils/fetchGithub"; export interface Props { path: string; } const { path } = Astro.props; const [_1, _2, _3, ...rest] = path.split("/"); const englishPath = `src/pages/en/${rest.join("/")}`; const engHref = `/en/${rest.join("/").replace(/\.[^/.]+$/, "")}`; const url = `https://api.github.com/repos/t3-oss/create-t3-app/commits?path=www/${path}&per_page=1`; const engUrl = `https://api.github.com/repos/t3-oss/create-t3-app/commits?path=www/${englishPath}&per_page=1`; // File may not be on Github yet, so we only throw an error if we're in production async function getCommitDiff() { try { const thisLangCommit = await fetchGithub(url, { fetchType: "commits" }); const englishCommit = await fetchGithub(engUrl, { fetchType: "commits" }); const thisLangDate = thisLangCommit?.length ? new Date(thisLangCommit[0]?.commit.author.date ?? "") : new Date(); const englishDate = englishCommit?.length ? new Date(englishCommit[0]?.commit.author.date ?? "") : new Date(); const diffInDays = Math.round( (englishDate.getTime() - thisLangDate.getTime()) / (1000 * 3600 * 24), ); return diffInDays; } catch (e) { console.warn(`[warn] /src/components/outDatedDocsBanner.astro ${(e as Error)?.message ?? e} If you're in production, this is a problem. If you're in dev, it's probably fine. If this warning appears on a translated file that's not yet on origin, it's fine.`); return 0; } } const diffInDays = await getCommitDiff(); const ONE_WEEK = 1000 * 60 * 60 * 24 * 7; Astro.response.headers.set("Cache-Control", `s-max-age=${ONE_WEEK}`); --- { diffInDays > 0 && (

Attention: This page is{" "} {diffInDays} {diffInDays === 1 ? "day" : "days"} older than the English version and might be outdated. If you're a native speaker of this language and would like to contribute to the project, please consider updating this page to match the latest English version.

You can also view the English version of this page.

) } ================================================ FILE: www/src/components/docs/pageContent.astro ================================================ --- import { type MarkdownHeading } from "astro"; import { type Frontmatter, type KnownLanguageCode } from "../../config"; import { getIsRtlFromLangCode } from "../../languages"; import OnThisPageLinks from "../navigation/OnThisPageLinks"; import BreadCrumbs from "./breadCrumbs"; import OutdatedDocsBanner from "./outdatedDocsBanner.astro"; import Pagination from "./pagination.astro"; export interface Props { frontmatter: Frontmatter; path: string; headings: MarkdownHeading[]; } const { frontmatter, path, headings } = Astro.props; const title = frontmatter.title; const [_1, _2, lang] = path.split("/"); const isRtl = getIsRtlFromLangCode((lang || "en") as KnownLanguageCode); --- ================================================ FILE: www/src/components/docs/pagination.astro ================================================ --- import { type SidebarItemLink } from "../../config"; import { getIsRtlFromUrl, getLanguageFromURL } from "../../languages"; import { paginate } from "../../utils/pagination"; import LeftArrow from "../icons/leftArrow.astro"; import RightArrow from "../icons/rightArrow.astro"; const currentPage = Astro.url.pathname; const hasTrailing = currentPage.endsWith("/"); const currentPageMatch = currentPage.slice( 1, hasTrailing ? -1 : currentPage.length, ) as SidebarItemLink; const langCode = getLanguageFromURL(currentPage); const { next, prev } = paginate(langCode, currentPageMatch); const { pathname } = Astro.url; const isRtl = getIsRtlFromUrl(pathname); ---
{ prev ? ( ) : null }
{ next ? ( ) : null }

================================================ FILE: www/src/components/docs/tabs.astro ================================================ --- const { tabName, slotOne, slotTwo } = Astro.props; ---
================================================ FILE: www/src/components/footer/footer.astro ================================================ --- import AvatarList from "../docs/avatarList.astro"; export interface Props { path: string; isBlog: boolean; } const { path, isBlog } = Astro.props; ---
{isBlog && }
================================================ FILE: www/src/components/headCommon.astro ================================================ ================================================ FILE: www/src/components/headSeo.astro ================================================ --- import { OPEN_GRAPH, SITE, type Frontmatter } from "../config"; import { SITE_URL } from "../utils/siteUrl"; export interface Props { frontmatter?: Frontmatter; type?: string; } const { frontmatter, type } = Astro.props as Props; const canonicalUrl = new URL(Astro.url.pathname, Astro.site); const formattedContentTitle = frontmatter ? `${frontmatter.title} 🚀 ${SITE.title}` : SITE.title; const ogTitle = frontmatter ? frontmatter.title : SITE.title; const ogDescription = frontmatter ? frontmatter.description : SITE.description; const ogImageData = { title: ogTitle, description: ogDescription, pagePath: Astro.url.pathname, }; const imageSrc = frontmatter?.image?.src ?? `${SITE_URL}/og?${Object.entries(ogImageData) .map(([key, value]) => `${key}=${value}`) .join("&")}`; const imageUrl = new URL(imageSrc, Astro.url.origin); const imageAlt = frontmatter?.image?.alt ?? OPEN_GRAPH.image.alt; const ogType = type ?? "article"; --- ================================================ FILE: www/src/components/icons/leftArrow.astro ================================================ ================================================ FILE: www/src/components/icons/rightArrow.astro ================================================ ================================================ FILE: www/src/components/landingPage/ClipboardSelect.tsx ================================================ import { Menu, Transition } from "@headlessui/react"; import clsx from "clsx"; import { Fragment, useState } from "react"; const commands = [ { command: "create t3-app@latest", manager: "npm", }, { command: "create t3-app", manager: "yarn", }, { command: "create t3-app@latest", manager: "pnpm", }, { command: "create t3-app@latest", manager: "bun", }, ]; export default function ClipboardSelect() { const [coolDown, setCoolDown] = useState(false); const handleCopyToClipboard = async (manager: string, command: string) => { const nextClipboard = `${manager} ${command}`; try { await navigator.clipboard.writeText(nextClipboard); } catch (err) { console.error("Failed to copy text: ", err); } }; const checkStyles = { strokeDasharray: 450, strokeDashoffset: -30, }; return (
{commands.map(({ manager, command }) => ( {({ active }) => { return ( ); }} ))}
); } ================================================ FILE: www/src/components/landingPage/about.astro ================================================ --- import CliPreview from "./cli"; import PageSection from "./pageSection.astro"; ---

Typesafe From The Start

We made create-t3-app to do one thing: Streamline the setup of typesafe Next.js apps WITHOUT compromising modularity.

After countless projects and many years on this tech, we have lots of opinions and insights. We've done our best to encode them into this CLI.

This is NOT an all-inclusive template. We expect you to bring your own libraries. Check out our other recommendations for things like state management and deployment.

================================================ FILE: www/src/components/landingPage/banner.astro ================================================ --- import Button from "./button.astro"; import ClipboardSelect from "./ClipboardSelect"; ---

The best way to start a full-stack, typesafe Next.js app

npm create t3-app@latest
================================================ FILE: www/src/components/landingPage/button.astro ================================================ --- export interface Props { href?: string; openInNewTab?: boolean; rounded?: "full" | "md" | "none"; variant?: "primary" | "secondary"; specialHover?: boolean; className?: string; download?: string | false; } const { href = "", rounded = "md", variant = "primary", openInNewTab = false, className = "", specialHover = false, download = false, } = Astro.props; const roundedClass = rounded === "full" ? "rounded-full" : rounded === "md" ? "rounded-md" : ""; const variantClass = variant === "primary" ? "bg-t3-purple-100 hover:bg-t3-purple-200 text-slate-800" : "bg-white/10 hover:bg-white/20 text-white"; const specialHoverClass = specialHover ? "hover:shadow hover:shadow-[#300171] duration-500" : ""; --- ================================================ FILE: www/src/components/landingPage/cli.tsx ================================================ /** @jsxImportSource react */ import Typist from "react-typist"; export default function CodeCard() { return ( ); } ================================================ FILE: www/src/components/landingPage/community/community.astro ================================================ --- import PageSection from "../pageSection.astro"; import CommunityCard from "./communityCard.astro"; // eslint-disable-next-line -- need to export something to make it a module export interface Props {} ---

Community

Join our community to get help, share your projects, and even contribute to the project!

Discord GitHub Twitter
================================================ FILE: www/src/components/landingPage/community/communityCard.astro ================================================ --- export interface Props { title: string; text: string; href: string; } const { text, title, href } = Astro.props; ---

{title}

{text}

================================================ FILE: www/src/components/landingPage/pageSection.astro ================================================ --- export interface Props { size: "6" | "12" | "16" | "24" | "32"; className?: string; bottomPadding?: boolean; id?: string; } const sizes = { 6: "py-2 md:py-4 lg:py-6", 12: "py-4 md:py-8 lg:py-12", 16: "py-6 md:py-10 lg:py-16", 24: "py-8 md:py-16 lg:py-24", 32: "py-12 md:py-20 lg:py-32", }; const sizesOnlyTop = { 6: "pt-2 md:pt-4 lg:pt-6", 12: "pt-4 md:pt-8 lg:pt-12", 16: "pt-6 md:pt-10 lg:pt-16", 24: "pt-8 md:pt-16 lg:pt-24", 32: "pt-12 md:pt-20 lg:pt-32", }; const { size, className, bottomPadding = true, id = "" } = Astro.props; ---
================================================ FILE: www/src/components/landingPage/stack/card.astro ================================================ --- export interface Props { title: string; href: string; } const { title, href } = Astro.props; --- ================================================ FILE: www/src/components/landingPage/stack/stack.astro ================================================ --- import PageSection from "../pageSection.astro"; import Feature from "./card.astro"; ---

The best of the full stack TypeScript ecosystem...

...but ONLY the parts you need

Take what you want and nothing more!

Next.js offers a lightly opinionated, heavily optimized approach to creating applications using React. It's the industry standard and we're proud to build on top of it :) Prisma is the best way to work with databases in TypeScript. It provides a simple, type-safe API to query your database, and it can be used with most SQL dialects (and Mongo too!). We firmly believe TypeScript will help you be a better web developer. Whether you're new to JS or a seasoned pro, the "strictness" of TypeScript leads to smoother building. Tailwind CSS is a utility-first CSS framework that helps you build beautiful, responsive designs without any extra configuration. It’s built with utility-first principles, and is completely customizable and extendable. If your frontend and backend are TypeScript, it's really hard to beat the DX of tRPC. Kinda like GraphQL but without the work - seriously this lib is magic. NextAuth.js When you need flexible, secure, and scalable auth, NextAuth.js is top notch. It ties into your existing database and provides a simple API to manage users and sessions.
================================================ FILE: www/src/components/landingPage/tweets/featuredTweets.ts ================================================ export interface Tweet { id: string; handle: string; verified: boolean; author: string; avatar: string; date: Date; text: string; likes: number; retweets: number; replies: number; quotes: number; } // featured tweets for the testimonials on the landing page export const featuredTweets: Tweet[] = [ // https://twitter.com/ajcwebdev/status/1544909672137867264 { id: "1544909672137867264", handle: "ajcwebdev", author: "❤️ Anthony (ajcwebdev.x)", verified: false, avatar: "https://pbs.twimg.com/profile_images/1549247631867711488/hK_Qr-Dx_normal.png", date: new Date("2022-07-07T05:02:23.000Z"), text: "Now that Blitz.js has pivoted and Bison has stagnated, create-t3-app will be the only framework to give Redwood a run for its money in the quest to build a legitimate fullstack React framework.", likes: 32, retweets: 3, replies: 9, quotes: 2, }, // https://twitter.com/tomdoes_tech/status/1595652166173458434 { id: "1595652166173458434", handle: "tomdoes_tech", author: "Tom", verified: false, avatar: "https://pbs.twimg.com/profile_images/1551907445856997385/3m8M35Cn_400x400.png", date: new Date("2022-11-24T08:34:00.000Z"), text: "Used create-t3-app for a hackathon today. I don't think I've ever created an app that works so well and does so much in a single day before", likes: 194, retweets: 6, replies: 10, quotes: 2, }, // https://twitter.com/synecdokey/status/1553580714591158272 { id: "1553580714591158272", handle: "synecdokey", author: "Emilia Zapata", verified: false, avatar: "https://pbs.twimg.com/profile_images/1426010455055638531/xUFu5JP7_400x400.jpg", date: new Date("2022-07-31T07:18:00.000Z"), text: "Finally had the chance to play with @trpcio and it's scary how easy it makes to make your data flow, with full type safety and backend validation. With create-t3-app and @supabase , I was able to create a simple to-do app with auth in less than 3 hours, scary.", likes: 76, retweets: 10, replies: 2, quotes: 0, }, // https://twitter.com/royanger/status/1553191258771841024 { id: "1553191258771841024", handle: "royanger", author: "Roy Anger", verified: false, avatar: "https://pbs.twimg.com/profile_images/1346968887/dsc652499-2_400x400.jpg", date: new Date("2022-07-30T05:30:00.000Z"), text: "create-t3-app is amazing. I worked on a MVP over the last 10 days on the side, and easily built it out using the stack. Working with data via tRPC was amazing. Try out the t3 stack, or at least tRPC.", likes: 19, retweets: 2, replies: 0, quotes: 0, }, // https://twitter.com/jonhigger/status/1570054715240763393 { id: "1570054715240763393", handle: "jonhigger", author: "jon", verified: false, avatar: "https://pbs.twimg.com/profile_images/1466866060007723008/QXEf5Vbs_400x400.jpg", date: new Date("2022-09-14T18:19:00.000Z"), text: "If you're a Rails developer, you should give full stack TS frameworks a try. @remix_run 's stacks or @pingdotgg 's create-t3-app are two great options. If you learn these tools, your long term productivity will be faster than Rails. The performance benefits are just a side bonus.", likes: 5, retweets: 1, replies: 1, quotes: 0, }, ]; ================================================ FILE: www/src/components/landingPage/tweets/tweetCard.astro ================================================ --- import { Image } from "astro:assets"; import { type Tweet } from "./featuredTweets"; const { id, handle, author, verified, avatar, date, text, likes, retweets, replies, } = Astro.props as Tweet; --- ================================================ FILE: www/src/components/landingPage/tweets/tweetSlider.astro ================================================ --- import { type Tweet } from "./featuredTweets"; import TweetCard from "./tweetCard.astro"; export interface Props { tweets: Tweet[]; } const { tweets } = Astro.props as Props; ---
{ tweets.map((tweet) => (
)) }
{/* Pagination */}
{ tweets.map((_, idx) => ( // Larger click area while keeping the visual circles small )) }
================================================ FILE: www/src/components/landingPage/tweets/tweets.astro ================================================ --- import PageSection from "../pageSection.astro"; import { featuredTweets } from "./featuredTweets"; import TweetSlider from "./tweetSlider.astro"; ---

Well loved in production (and on Twitter)

Our users ship. Fast.

Mention create-t3-app if you want to pop up here ;)

================================================ FILE: www/src/components/navigation/LanguageSelect.tsx ================================================ import { Listbox, Transition } from "@headlessui/react"; import clsx from "clsx"; import { Fragment } from "react"; import { KNOWN_LANGUAGES, type KnownLanguageCode } from "../../config"; import { getIsRtlFromLangCode } from "../../languages"; interface LanguageSelectProps { language: KnownLanguageCode; } export default function LanguageSelect({ language }: LanguageSelectProps) { const handleSelect = (code: string) => { const [_1, _2, ...slug] = window.location.pathname.split("/"); window.location.pathname = `/${code}/${slug.join("/")}`; }; const isRTL = getIsRtlFromLangCode(language); return (
{Object.entries(KNOWN_LANGUAGES).map(([code, name]) => ( `focus-none relative cursor-pointer bg-t3-purple-200/50 px-4 py-2 text-slate-900 outline-none hover:bg-t3-purple-300/75 dark:bg-t3-purple-200/10 dark:text-t3-purple-100 dark:hover:bg-t3-purple-200/20 ${ selected && "bg-t3-purple-400/75 dark:bg-t3-purple-400/20" } ${active && "bg-t3-purple-300/75 dark:bg-t3-purple-200/20"}` } value={code} > {({ selected }) => ( {name} )} ))}
); } ================================================ FILE: www/src/components/navigation/OnThisPageLinks.tsx ================================================ import { Menu } from "@headlessui/react"; import { type MarkdownHeading } from "astro"; import clsx from "clsx"; import { useEffect, useMemo, useState } from "react"; interface OnThisPageLinksProps { headings: MarkdownHeading[]; title: string; isRtl: boolean; } export default function OnThisPageLinks({ headings, title, isRtl, }: OnThisPageLinksProps) { const isLtr = !isRtl; const memoedHeadings = useMemo(() => { // add isVisible flag in headers const headers = [ { depth: 2, slug: "overview", text: title, isVisible: true }, ...headings.map((h) => ({ ...h, isVisible: false })), ]; return headers.filter(({ depth }) => depth > 1 && depth < 4); }, [headings, title]); const [headingWithIsVisible, setHeadingWithIsVisible] = useState(memoedHeadings); useEffect(() => { const articleHeadings = Array.from( document.querySelectorAll("article :is(h1,h2,h3)"), ); const allObservers = articleHeadings.map((h) => { const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { const id = entry.target.id; const tocItem = Array.from( document.querySelectorAll(`a[href="#${id}"]`), ).at(-1); // occurs when the id = "toc-heading" if (!tocItem) return; if (entry.isIntersecting) { const headings = headingWithIsVisible.map((h) => h.slug === id ? { ...h, isVisible: true } : { ...h, isVisible: false }, ); setHeadingWithIsVisible(headings); } }); }, { rootMargin: "-100px 0% -66%", threshold: 1, }, ); observer.observe(h); return observer; }); return () => { // unobserve elements articleHeadings.map((h, index) => { allObservers[index]?.unobserve(h); }); }; }, [headings, title]); return (
{({ open }) => (
On this page
    {headingWithIsVisible.map((heading) => (
  • {({ active }) => ( {heading.text} )}
  • ))}
)}
); } ================================================ FILE: www/src/components/navigation/Search.tsx ================================================ /** @jsxImportSource react */ import { useCallback, useRef, useState } from "react"; import { ALGOLIA } from "../../config"; import "../../styles/algolia/style.css"; import * as docSearchReact from "@docsearch/react"; import clsx from "clsx"; import { createPortal } from "react-dom"; /** FIXME: This is still kinda nasty, but DocSearch is not ESM ready. */ const DocSearchModal = docSearchReact.DocSearchModal || // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access (docSearchReact as any).default.DocSearchModal; const useDocSearchKeyboardEvents = docSearchReact.useDocSearchKeyboardEvents || // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access (docSearchReact as any).default.useDocSearchKeyboardEvents; export default function Search({ isLanding }: { isLanding: boolean }) { const [isOpen, setIsOpen] = useState(false); const searchButtonRef = useRef(null); const [initialQuery, setInitialQuery] = useState(""); const onOpen = useCallback(() => { setIsOpen(true); }, [setIsOpen]); const onClose = useCallback(() => { setIsOpen(false); }, [setIsOpen]); const onInput = useCallback( (e: KeyboardEvent) => { setIsOpen(true); setInitialQuery(e.key); }, [setIsOpen, setInitialQuery], ); useDocSearchKeyboardEvents({ isOpen, onOpen, onClose, onInput, searchButtonRef, }); return ( <> {isOpen && createPortal(
{ return items.map((item) => { // We transform the absolute URL into a relative URL to // work better on localhost, preview URLS. const a = document.createElement("a"); a.href = item.url; const hash = a.hash === "#overview" ? "" : a.hash; return { ...item, url: `${a.pathname}${hash}`, }; }); }} />
, document.body, )} ); } ================================================ FILE: www/src/components/navigation/githubIcon.astro ================================================ --- import { getIsRtlFromUrl, getLanguageFromURL } from "../../languages"; import { fetchGithub } from "../../utils/fetchGithub"; let githubStars = 28000; try { const fetchedStars = await fetchGithub( "https://api.github.com/repos/t3-oss/create-t3-app", { throwIfNoAuth: false, fetchType: "repo", }, ).then((data) => data?.stargazers_count); githubStars = fetchedStars ?? 28000; } catch (e) { console.error("unable to fetch from github", e); } const ONE_HOUR = 1 * 60 * 60; Astro.response.headers.set("Cache-Control", `public, max-age=${ONE_HOUR}`); const { pathname } = Astro.url; const formattedStars = new Intl.NumberFormat(getLanguageFromURL(pathname), { notation: "compact", }).format(githubStars); const isRtl = getIsRtlFromUrl(pathname); --- GitHub ================================================ FILE: www/src/components/navigation/leftSidebar.astro ================================================ --- import { SIDEBAR, SIDEBAR_HEADER_MAP, type Frontmatter } from "../../config"; import { getIsRtlFromUrl } from "../../languages"; import Search from "./Search"; export interface Props { frontmatter?: Frontmatter; currentPage: string; isNotFound?: boolean; } const { frontmatter, currentPage, isNotFound } = Astro.props; const isLanding = currentPage === "/" || !!isNotFound; const hasTrailing = currentPage.endsWith("/"); const currentPageMatch = currentPage.slice( 1, hasTrailing ? -1 : currentPage.length, ); const isRtl = getIsRtlFromUrl(currentPage); const langCode = frontmatter?.lang ?? "en"; const englishSidebar = SIDEBAR["en"]; const nativeSidebar = SIDEBAR[langCode]; let sidebar: Record< string, { text: string; link: string; isTranslated?: boolean }[] > = englishSidebar; if (langCode !== "en") { sidebar = Object.fromEntries( Object.entries(englishSidebar).map(([header, items]) => { // Set header to translated one if it exists const innerHeader = SIDEBAR_HEADER_MAP[langCode]; const nativeHeader = innerHeader[header as keyof typeof innerHeader] ?? header; const nativeItems = items.map((item) => { const match = Object.values(nativeSidebar) .flat() .find( ({ link: nativeLink }) => // trailing slash + language code nativeLink.slice(langCode.length + 1) === item.link.slice(3), ); return { text: match?.text ?? item.text, link: match?.link ?? item.link, isTranslated: !!match, }; }); return [nativeHeader, nativeItems]; }), ); } ---
================================================ FILE: www/src/components/navigation/moreMenu.astro ================================================ --- import * as CONFIG from "../../config"; import { getIsRtlFromUrl, getLanguageFromURL } from "../../languages"; export interface Props { editHref: string; } const { editHref } = Astro.props; const { pathname } = Astro.url; const isRtl = getIsRtlFromUrl(pathname); const isLangEN = getLanguageFromURL(pathname) === "en"; --- ================================================ FILE: www/src/components/navigation/navbar.astro ================================================ --- import { getIsRtlFromUrl, getLanguageFromURL } from "../../languages"; import GithubIcon from "./githubIcon.astro"; import LanguageSelect from "./LanguageSelect"; import Search from "./Search"; import SidebarToggle from "./sidebarToggle.astro"; import ThemeToggleButton from "./themeToggleButton.astro"; export interface Props { isNotFound?: boolean; } const { pathname } = Astro.url; const isRtl = getIsRtlFromUrl(pathname); const isLtr = !isRtl; const isLanding = pathname === "/" || !!Astro.props.isNotFound; const hasTrailing = pathname.endsWith("/"); const currentPage = pathname.slice(0, hasTrailing ? -1 : pathname.length); const langCode = getLanguageFromURL(pathname); const navbarLinks: Array<{ href: string; label: string }> = [ { href: `/${langCode}/introduction`, label: "Docs", }, { href: `/${langCode}/faq`, label: "FAQ", }, ]; --- ================================================ FILE: www/src/components/navigation/rightSidebar.astro ================================================ --- import { type MarkdownHeading } from "astro"; import MoreMenu from "./moreMenu.astro"; import TableOfContents from "./tableOfContents.astro"; export interface Props { headings: MarkdownHeading[]; githubEditUrl: string; title: string; } const { headings, githubEditUrl, title } = Astro.props; --- ================================================ FILE: www/src/components/navigation/sidebarToggle.astro ================================================ --- export interface Props { isLanding: boolean; } const { isLanding } = Astro.props; --- ================================================ FILE: www/src/components/navigation/tableOfContents.astro ================================================ --- import { type MarkdownHeading } from "astro"; import { getIsRtlFromUrl } from "../../languages"; export interface Props { headings: MarkdownHeading[]; title: string; } let { headings, title } = Astro.props; headings = [{ depth: 2, slug: "overview", text: title }, ...headings].filter( ({ depth }) => depth > 1 && depth < 4, ); const { pathname } = Astro.url; const isRtl = getIsRtlFromUrl(pathname); ---

On this page

    { headings.map((heading) => { const { depth, slug, text } = heading; //add data-component-type to headings to be able to filter them in the sidebar let dataComponentType = ""; switch (text) { case "prisma": dataComponentType = "prisma"; break; case "src/types": case "src/server/auth.ts": dataComponentType = "nextauth"; break; case "postcss.config.js": case "prettier.config.mjs": dataComponentType = "tailwind"; break; case "src/server/api": case "src/server/api/routers": case "src/utils": dataComponentType = "trpc"; break; } return (
  • {text}
  • ); }) }
================================================ FILE: www/src/components/navigation/themeToggleButton.astro ================================================ ================================================ FILE: www/src/components/openGraph.tsx ================================================ interface OpenGraphProps { title: string; description: string; imageBase: string; pageUrl: string; rtl: boolean; } export default function OpenGraph({ title, description, imageBase, pageUrl, rtl, }: OpenGraphProps) { return (

15 ? "70px" : "90px", lineHeight: "5rem", fontWeight: 700, color: "#fff", maxWidth: "700px", }} > {title}

{description}

{pageUrl}

); } const Logo = ({ color }: { color: string }) => ( ); ================================================ FILE: www/src/config.ts ================================================ export const SITE = { title: "Create T3 App", description: "The best way to start a full-stack, typesafe Next.js app.", defaultLanguage: "en_US", }; export const OPEN_GRAPH = { image: { src: "images/og-image.png", alt: "Create T3 App: The best way to start a full-stack, typesafe Next.js app.", }, twitter: "t3dotgg", }; // This is the type of the frontmatter you put in the docs markdown files. export interface Frontmatter { title: string; description: string; layout: string; image?: { src: string; alt: string }; dir?: "ltr" | "rtl"; ogLocale?: string; lang?: KnownLanguageCode; isMdx?: boolean; } export const KNOWN_LANGUAGES = { // Add more languages here // sv: "Svenska", ar: "العربية", en: "English", es: "Español", fr: "Français", ja: "日本語", pt: "Português", ru: "Русский", no: "Norsk", pl: "Polski", uk: "Українська", "zh-hans": "简体中文", } as const; export type KnownLanguageCode = keyof typeof KNOWN_LANGUAGES; export const GITHUB_EDIT_URL = `https://github.com/t3-oss/create-t3-app/tree/main/www`; export const COMMUNITY_INVITE_URL = `https://t3.gg/discord`; // See "Algolia" section of the README for more information. export const ALGOLIA = { indexName: "create-t3-app", appId: "0LE5592BV4", apiKey: "892c4647b96fe1b3d0b7d8de1c5b5e40", }; export type OuterHeaders = "Create T3 App" | "Deployment" | "Usage"; export interface SidebarItem< TCode extends KnownLanguageCode = KnownLanguageCode, > { text: string; link: `${TCode}/${string}`; } export type SidebarItemLink = SidebarItem["link"]; export type Sidebar = { [TCode in KnownLanguageCode]: { [THeader in OuterHeaders]?: SidebarItem[]; }; }; export const SIDEBAR: Sidebar = { // For Translations: // Keep the "outer headers" in English so we can match them. // Translate the "inner headers" to the language you're translating to. // Omit any files you haven't translated, they'll fallback to English. // Example: // sv: { // "Create T3 App": [ // { text: "Introduktion", link: "sv/introduction" }, // { text: "Installation", link: "sv/installation" }, // ], // Usage: [{ text: "Miljövariabler", link: "sv/usage/env-variables" }], // }, ar: { "Create T3 App": [ { text: "مُقدمة", link: "ar/introduction" }, { text: "لماذا CT3A؟", link: "ar/why" }, { text: "التثبيت", link: "ar/installation" }, { text: "(Pages)بِنية المجلد", link: "ar/folder-structure-pages" }, { text: "أسئلة شائعة", link: "ar/faq" }, { text: "اعمال بواسطة T3", link: "ar/t3-collection" }, { text: "ترشيحات أُخري", link: "ar/other-recs" }, ], Usage: [ { text: "الخُطوات الأُولي", link: "ar/usage/first-steps" }, { text: "Next.js", link: "ar/usage/next-js" }, { text: "TypeScript", link: "ar/usage/typescript" }, { text: "tRPC", link: "ar/usage/trpc" }, { text: "Prisma", link: "ar/usage/prisma" }, { text: "NextAuth.js", link: "ar/usage/next-auth" }, { text: "Environment Variables", link: "ar/usage/env-variables", }, { text: "Tailwind CSS", link: "ar/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "ar/deployment/vercel" }, { text: "Docker", link: "ar/deployment/docker" }, ], }, en: { "Create T3 App": [ { text: "Introduction", link: "en/introduction" }, { text: "Why CT3A?", link: "en/why" }, { text: "Installation", link: "en/installation" }, { text: "Folder Structure (Pages)", link: "en/folder-structure-pages" }, { text: "Folder Structure (App)", link: "en/folder-structure-app" }, { text: "FAQ", link: "en/faq" }, { text: "T3 Collection", link: "en/t3-collection" }, { text: "Examples", link: "en/examples" }, { text: "Other Recommendations", link: "en/other-recs" }, ], Usage: [ { text: "First Steps", link: "en/usage/first-steps" }, { text: "Next.js", link: "en/usage/next-js" }, { text: "TypeScript", link: "en/usage/typescript" }, { text: "tRPC", link: "en/usage/trpc" }, { text: "Drizzle", link: "en/usage/drizzle" }, { text: "Prisma", link: "en/usage/prisma" }, { text: "NextAuth.js", link: "en/usage/next-auth" }, { text: "Environment Variables", link: "en/usage/env-variables", }, { text: "Tailwind CSS", link: "en/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "en/deployment/vercel" }, { text: "Netlify", link: "en/deployment/netlify" }, { text: "Docker", link: "en/deployment/docker" }, ], }, es: { "Create T3 App": [ { text: "Introducción", link: "es/introduction" }, { text: "¿Por qué CT3A?", link: "es/why" }, { text: "Instalación", link: "es/installation" }, { text: "Estructura de Carpetas (Pages)", link: "es/folder-structure-pages", }, { text: "Preguntas Frecuentes", link: "es/faq" }, { text: "Colección T3", link: "es/t3-collection" }, { text: "Otras Recomendaciones", link: "es/other-recs" }, ], Usage: [ { text: "Primeros Pasos", link: "es/usage/first-steps" }, { text: "Next.js", link: "es/usage/next-js" }, { text: "TypeScript", link: "es/usage/typescript" }, { text: "tRPC", link: "es/usage/trpc" }, { text: "Prisma", link: "es/usage/prisma" }, { text: "NextAuth.js", link: "es/usage/next-auth" }, { text: "Variables de Entorno", link: "es/usage/env-variables" }, { text: "Tailwind CSS", link: "es/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "es/deployment/vercel" }, { text: "Netlify", link: "es/deployment/netlify" }, { text: "Docker", link: "es/deployment/docker" }, ], }, ja: { "Create T3 App": [ { text: "イントロダクション", link: "ja/introduction" }, { text: "CT3A を選ぶ理由", link: "ja/why" }, { text: "インストール", link: "ja/installation" }, { text: "ファルダ構成 (Pages)", link: "ja/folder-structure-pages" }, { text: "FAQ", link: "ja/faq" }, { text: "T3 コレクション", link: "ja/t3-collection" }, { text: "その他のオススメ", link: "ja/other-recs" }, ], Usage: [ { text: "はじめの一歩", link: "ja/usage/first-steps" }, { text: "Next.js", link: "ja/usage/next-js" }, { text: "TypeScript", link: "ja/usage/typescript" }, { text: "tRPC", link: "ja/usage/trpc" }, { text: "Prisma", link: "ja/usage/prisma" }, { text: "NextAuth.js", link: "ja/usage/next-auth" }, { text: "環境変数", link: "ja/usage/env-variables", }, { text: "Tailwind CSS", link: "ja/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "ja/deployment/vercel" }, { text: "Netlify", link: "ja/deployment/netlify" }, { text: "Docker", link: "ja/deployment/docker" }, ], }, pl: { "Create T3 App": [ { text: "Wstęp", link: "pl/introduction" }, { text: "Dlaczego CT3A?", link: "pl/why" }, { text: "Instalacja", link: "pl/installation" }, { text: "Struktura Projektu (Pages)", link: "pl/folder-structure-pages" }, { text: "FAQ", link: "pl/faq" }, { text: "Kolekcja T3", link: "pl/t3-collection" }, { text: "Przykłady", link: "pl/examples" }, { text: "Inne Rekomendacje", link: "pl/other-recs" }, ], Usage: [ { text: "Pierwsze Kroki", link: "pl/usage/first-steps" }, { text: "Next.js", link: "pl/usage/next-js" }, { text: "TypeScript", link: "pl/usage/typescript" }, { text: "tRPC", link: "pl/usage/trpc" }, { text: "Prisma", link: "pl/usage/prisma" }, { text: "NextAuth.js", link: "pl/usage/next-auth" }, { text: "Zmienne Środowiskowe", link: "pl/usage/env-variables", }, { text: "Tailwind CSS", link: "pl/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "pl/deployment/vercel" }, { text: "Netlify", link: "pl/deployment/netlify" }, { text: "Docker", link: "pl/deployment/docker" }, ], }, uk: { "Create T3 App": [ { text: "Вступ", link: "uk/introduction" }, { text: "Чому CT3A?", link: "uk/why" }, { text: "Встановлення", link: "uk/installation" }, { text: "Структура папок (Pages)", link: "uk/folder-structure-pages" }, { text: "FAQ", link: "uk/faq" }, { text: "T3 Колекція", link: "uk/t3-collection" }, { text: "Приклади", link: "uk/examples" }, { text: "Інші рекомендації", link: "uk/other-recs" }, ], Usage: [ { text: "Перші кроки", link: "uk/usage/first-steps" }, { text: "Next.js", link: "uk/usage/next-js" }, { text: "TypeScript", link: "uk/usage/typescript" }, { text: "tRPC", link: "uk/usage/trpc" }, { text: "Drizzle", link: "uk/usage/drizzle" }, { text: "Prisma", link: "uk/usage/prisma" }, { text: "NextAuth.js", link: "uk/usage/next-auth" }, { text: "Змінні середовища", link: "uk/usage/env-variables", }, { text: "Tailwind CSS", link: "uk/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "uk/deployment/vercel" }, { text: "Netlify", link: "uk/deployment/netlify" }, { text: "Docker", link: "uk/deployment/docker" }, ], }, fr: { "Create T3 App": [ { text: "Introduction", link: "fr/introduction" }, { text: "Pourquoi CT3A?", link: "fr/why" }, { text: "Installation", link: "fr/installation" }, { text: "Structure des dossiers (Pages)", link: "fr/folder-structure-pages", }, { text: "FAQ", link: "fr/faq" }, { text: "Collection T3", link: "fr/t3-collection" }, { text: "Exemples", link: "fr/examples" }, { text: "Autres recommandations", link: "fr/other-recs" }, ], Usage: [ { text: "Premiers pas", link: "fr/usage/first-steps" }, { text: "Next.js", link: "fr/usage/next-js" }, { text: "TypeScript", link: "fr/usage/typescript" }, { text: "tRPC", link: "fr/usage/trpc" }, { text: "Prisma", link: "fr/usage/prisma" }, { text: "NextAuth.js", link: "fr/usage/next-auth" }, { text: "Variables d'environnement", link: "fr/usage/env-variables", }, { text: "Tailwind CSS", link: "fr/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "fr/deployment/vercel" }, { text: "Netlify", link: "fr/deployment/netlify" }, { text: "Docker", link: "fr/deployment/docker" }, ], }, pt: { "Create T3 App": [ { text: "Introdução", link: "pt/introduction" }, { text: "Por que o CT3A?", link: "pt/why" }, { text: "Instalação", link: "pt/installation" }, { text: "Estrutura de Pastas (Pages)", link: "pt/folder-structure-pages", }, { text: "Perguntas Frequentes", link: "pt/faq" }, { text: "Coleção T3", link: "pt/t3-collection" }, { text: "Outras Recomendações", link: "pt/other-recs" }, ], Usage: [ { text: "Primeiros Passos", link: "pt/usage/first-steps" }, { text: "Next.js", link: "pt/usage/next-js" }, { text: "TypeScript", link: "pt/usage/typescript" }, { text: "tRPC", link: "pt/usage/trpc" }, { text: "Prisma", link: "pt/usage/prisma" }, { text: "NextAuth.js", link: "pt/usage/next-auth" }, { text: "Variáveis de Ambiente", link: "pt/usage/env-variables", }, { text: "Tailwind CSS", link: "pt/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "pt/deployment/vercel" }, { text: "Netlify", link: "pt/deployment/netlify" }, { text: "Docker", link: "pt/deployment/docker" }, ], }, ru: { "Create T3 App": [ { text: "Введение", link: "ru/introduction" }, { text: "Почему CT3A?", link: "ru/why" }, { text: "Установка", link: "ru/installation" }, { text: "Файловая структура (Pages)", link: "ru/folder-structure-pages" }, { text: "FAQ", link: "ru/faq" }, { text: "T3 коллекция", link: "ru/t3-collection" }, { text: "Дополнительные рекомендации", link: "ru/other-recs" }, ], Usage: [ { text: "Первые шаги", link: "ru/usage/first-steps" }, { text: "Next.js", link: "ru/usage/next-js" }, { text: "TypeScript", link: "ru/usage/typescript" }, { text: "tRPC", link: "ru/usage/trpc" }, { text: "Prisma", link: "ru/usage/prisma" }, { text: "NextAuth.js", link: "ru/usage/next-auth" }, { text: "Переменные среды", link: "ru/usage/env-variables", }, { text: "Tailwind CSS", link: "ru/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "ru/deployment/vercel" }, { text: "Docker", link: "ru/deployment/docker" }, { text: "Netlify", link: "ru/deployment/netlify" }, ], }, no: { "Create T3 App": [ { text: "Introduksjon", link: "no/introduction" }, { text: "Hvorfor CT3A?", link: "no/why" }, { text: "Installasjon", link: "no/installation" }, { text: "Mappestruktur (Pages)", link: "no/folder-structure-pages" }, { text: "FAQ", link: "no/faq" }, { text: "T3-Kolleksjonen", link: "no/t3-collection" }, { text: "Andre Anbefalinger", link: "no/other-recs" }, ], Usage: [ { text: "Første Steg", link: "no/usage/first-steps" }, { text: "Next.js", link: "no/usage/next-js" }, { text: "TypeScript", link: "no/usage/typescript" }, { text: "tRPC", link: "no/usage/trpc" }, { text: "Prisma", link: "no/usage/prisma" }, { text: "NextAuth.js", link: "no/usage/next-auth" }, { text: "Miljøvariabler", link: "no/usage/env-variables", }, { text: "Tailwind CSS", link: "no/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "no/deployment/vercel" }, { text: "Netlify", link: "no/deployment/netlify" }, { text: "Docker", link: "no/deployment/docker" }, ], }, "zh-hans": { "Create T3 App": [ { text: "简介", link: "zh-hans/introduction" }, { text: "为什么选择 CT3A?", link: "zh-hans/why" }, { text: "安装", link: "zh-hans/installation" }, { text: "文件夹结构 (Pages)", link: "zh-hans/folder-structure-pages" }, { text: "常见疑问", link: "zh-hans/faq" }, { text: "T3 合集", link: "zh-hans/t3-collection" }, { text: "其他推荐", link: "zh-hans/other-recs" }, ], Usage: [ { text: "第一步", link: "zh-hans/usage/first-steps" }, { text: "Next.js", link: "zh-hans/usage/next-js" }, { text: "TypeScript", link: "zh-hans/usage/typescript" }, { text: "tRPC", link: "zh-hans/usage/trpc" }, { text: "Drizzle", link: "zh-hans/usage/drizzle" }, { text: "Prisma", link: "zh-hans/usage/prisma" }, { text: "NextAuth.js", link: "zh-hans/usage/next-auth" }, { text: "环境变量", link: "zh-hans/usage/env-variables", }, { text: "Tailwind CSS", link: "zh-hans/usage/tailwind" }, ], Deployment: [ { text: "Vercel", link: "zh-hans/deployment/vercel" }, { text: "Netlify", link: "zh-hans/deployment/netlify" }, { text: "Docker", link: "zh-hans/deployment/docker" }, ], }, }; export const SIDEBAR_HEADER_MAP: Record< Exclude, Record > = { es: { "Create T3 App": "Create T3 App", Usage: "Uso", Deployment: "Despliegue", }, // Translate the sidebar's "outer headers" here // sv: { // "Create T3 App": "Create T3 App", // Usage: "Användarguide", // Deployment: "Deployment", // }, ja: { "Create T3 App": "Create T3 App", Usage: "使用法", Deployment: "デプロイ", }, pl: { "Create T3 App": "Create T3 App", Usage: "Korzystanie Z Narzędzia", Deployment: "Deployment", }, uk: { "Create T3 App": "Create T3 App", Usage: "Використання", Deployment: "Деплоймент", }, ar: { "Create T3 App": "Create T3 App", Usage: "كيفية الإستخدام؟", Deployment: "نَشر تطبيقك", }, fr: { "Create T3 App": "Create T3 App", Usage: "Utilisation", Deployment: "Déploiement", }, pt: { "Create T3 App": "Create T3 App", Usage: "Uso", Deployment: "Deploy", }, ru: { "Create T3 App": "Create T3 App", Usage: "Использование", Deployment: "Развертывание", }, no: { "Create T3 App": "Create T3 App", Usage: "Bruk", Deployment: "Utrulling", }, "zh-hans": { "Create T3 App": "Create T3 App", Usage: "用法", Deployment: "部署", }, }; ================================================ FILE: www/src/env.d.ts ================================================ /// ================================================ FILE: www/src/languages.ts ================================================ import { type KnownLanguageCode } from "./config"; export { KNOWN_LANGUAGES, type KnownLanguageCode } from "./config"; export const langPathRegex = /\/([a-z]{2,3}-?[a-zA-Z]{0,4})\//; export function getLanguageFromURL(pathname: string) { const langCodeMatch = langPathRegex.exec(pathname); const langCode = langCodeMatch ? langCodeMatch[1] : "en"; return langCode as KnownLanguageCode; } // all RTL languages according to: https://lingohub.com/academy/best-practices/rtl-language-list const rtlLanguages = [ "ar", "arc", "dv", "fa", "ha", "he", "khw", "ks", "ku", "ps", "ur", "yi", ]; export function getIsRtlFromUrl(pathname: string) { const language = getLanguageFromURL(pathname); return getIsRtlFromLangCode(language); } export function getIsRtlFromLangCode(language: KnownLanguageCode) { if (rtlLanguages.includes(language)) { return true; } return false; } ================================================ FILE: www/src/layouts/docs.astro ================================================ --- import { type MarkdownHeading } from "astro"; import PageContent from "../components/docs/pageContent.astro"; import Footer from "../components/footer/footer.astro"; import HeadCommon from "../components/headCommon.astro"; import HeadSEO from "../components/headSeo.astro"; import LeftSidebar from "../components/navigation/leftSidebar.astro"; import Navbar from "../components/navigation/navbar.astro"; import RightSidebar from "../components/navigation/rightSidebar.astro"; import * as CONFIG from "../config"; import "../styles/global.css"; import "../styles/accessibility.css"; import "@fontsource-variable/inter"; import JumpToContent from "../components/accessibility/jumpToContent.astro"; import MoreMenu from "../components/navigation/moreMenu.astro"; export interface Props { frontmatter: CONFIG.Frontmatter; headings: MarkdownHeading[]; } const { frontmatter, headings } = Astro.props; const currentPage = Astro.url.pathname; const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.${ frontmatter.isMdx ? "mdx" : "md" }`; const githubEditUrl = `${CONFIG.GITHUB_EDIT_URL}/${currentFile}`; --- {frontmatter.title} • {CONFIG.SITE.title}
================================================ FILE: www/src/layouts/landingPage.astro ================================================ --- import Footer from "../components/footer/footer.astro"; import LeftSidebar from "../components/navigation/leftSidebar.astro"; import Navbar from "../components/navigation/navbar.astro"; import "../styles/global.css"; import "../styles/algolia/style.css"; import "../styles/accessibility.css"; import HeadCommon from "../components/headCommon.astro"; import HeadSeo from "../components/headSeo.astro"; import { SITE } from "../config"; import "@fontsource-variable/inter"; import JumpToContent from "../components/accessibility/jumpToContent.astro"; export interface Props { isNotFound?: boolean; } const currentPage = Astro.url.pathname; const { isNotFound } = Astro.props; --- {SITE.title}
================================================ FILE: www/src/pages/404.astro ================================================ --- import Button from "../components/landingPage/button.astro"; import LandingPage from "../layouts/landingPage.astro"; ---

Page not found... 404!

================================================ FILE: www/src/pages/ar/deployment/docker.md ================================================ --- title: Docker description: النشر مع Docker layout: ../../../layouts/docs.astro lang: ar dir: rtl --- يمكنك إستخدام الـ Stack داخل Docker Container أو كجزء من مجموعة containers عن طريق docker-compose، إقرأ المزيد هنا [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) ## تهيئة مشروع Docker يَجب أن تضع في حُسبانك أن Next.js يتطلب process مُنفصلة لـ buildtime و runtime. يمكنك الوصول لـ runtime environment فقط في الـ Server. في هذا المثال نستخدم مُتغيرين فقط لذلك عليك أن تٌبقي في بالك موقعها في الـ `Dockerfile` والـ command-line arguments, والـ `docker-compose.yml`: - `DATABASE_URL` (تُستخدم في الـ server) - `NEXT_PUBLIC_CLIENTVAR` (تُستخدم في الـ client) ### 1. إعداد Next في ملف [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js) قم بإضافة `standalone` حتى [تُقلل حجم الصور تلقائيا](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. إنشاء ملف dockerignore
إضغط هنا لتقرأ محتوي الملف .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. إنشاء Dockerfile > بما أننا لا نقوم بجلب الـ server environment variables إلى داخل الـ container، فإن [environment schema validation](/en/usage/env-variables) سيفشل لتجنب هذا أضف علم `SKIP_ENV_VALIDATION=1` الي الـ command حتى تُوقف عملية الـ validation
إضغط هنا لتقرأ محتوي الملف Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # تثبيت Prisma Client - أزلها إن لم تكن تُستخدم Prisma COPY prisma ./ # تثبيت المتطلبات وفقا للـ package manager الذي تُفضلة COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_مُلاحظات_** > > - مُحاكاة `--platform=linux/amd64` قد لا تكون ضرورية في Node 18 > - إقرأ [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) لتعلم لماذا قد تحتاج `libc6-compat`. > - تقوم Next.js بجمع [بيانات خفية عن الاستختدام](https://nextjs.org/telemetry). > - قٌم بإالغاء تعليق الـ instance الاولي من ENV NEXT_TELEMETRY_DISABLED 1 حتي تُعيق الـ telemetry أثناء الـ build، قٌم بإلغاء تعليق الـ instance الثانية تٌعيق الـ telemetry أثناء الـ runtime
## البناء والتشغيل locally قم ببناء وتشغيل هذه الصورة Locally باستخدام الأوامر التالية: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` إفتح [localhost:3000](http://localhost:3000/) لتري تطبيقك يَعمل ## الـ Docker Compose يُمكنك أيضا إستخدام Docker Compose لبناء وتشغيل الـ Container
بعد إتباع الخطوات من 1 إلى 4 في الاعلي إضغط هنا وأضف الملفات الي docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` قٌم بتشغيل أمر `docker compose up` ```bash docker compose up ``` الآن إفتح [localhost:3000](http://localhost:3000/) لترى تطبيقك يُعمل.
## الـ Deploy علي Railway يُمكنك أن تستخدم PaaS كـ [Railway's](https://railway.app) كـ [Dockerfile deployments](https://docs.railway.app/deploy/dockerfiles) إذا أردت أن تستخدم [Railway CLI installed](https://docs.railway.app/develop/cli#install) يُمكنك أن تُشغل هذا الأمر: ```bash railway login railway init railway link railway up railway open ``` إذهب الي "Variables" وأضف `DATABASE_URL` ثُم الي "Settings" واختر "Generate Domain." لتري أمثلة علي Railway زُر [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## مصدر مُفيدة | المصدر | الرابط | | ------------------------------------ | -------------------------------------------------------------------- | | Dockerfile reference | https://docs.docker.com/engine/reference/builder/ | | Compose file version 3 reference | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Docker CLI reference | https://docs.docker.com/engine/reference/commandline/docker/ | | Docker Compose CLI reference | https://docs.docker.com/compose/reference/ | | Next.js Deployment with Docker Image | https://nextjs.org/docs/deployment#docker-image | | Next.js in Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Next.js with Docker Example | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Create Docker Image of a Next.js app | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/ar/deployment/vercel.md ================================================ --- title: Vercel description: النَشر الي Vercel layout: ../../../layouts/docs.astro lang: ar dir: rtl --- ننصحك بنشر تطبيقك عبر [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) فهو أسهل، خاصة لتطبيقات Next.js. ## إعداد المشروع بطبيعة الأمر فإن vercel ستقوم بإعداد المشروع عنك لكن بإمكانك التعديل عليها من خلال إنشاء ملف [`vercel.json`](https://vercel.com/docs/project-configuration) ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## استخدام لوحة تحكم Vercel 1. بعد دفع مشروعك الي Github سجل الدخول إلى [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) عن طريق Github واضغط علي **Add New Project** ![New project on Vercel](/images/vercel-new-project.webp) 2. قم بإستيراد GitHub repository ![Import repository](/images/vercel-import-project.webp) 3. إضف environment variables. ![Add environment variables](/images/vercel-env-vars.webp) 4. إضغط علي **Deploy** الان مع كل مرة تعمل فيها Push ستقوم vercel بإعادة بناء المشوع تلقائيا ## إستخدام Vercel CLI حتي تنشر مشروع عن طريق CLI [install the Vercel CLI globally](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` شَغل أمر [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) ```bash vercel ``` لا تنس اضافة environment variables مثل `--env DATABASE_URL=YOUR_DATABASE_URL_HERE`، أضف عَلم `--yes` لتخطي كل الاسئلة ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` بطبيعة الامر فإن النشر سيطون علي `preview branch` لكن إذا ما كُنت تريد النشر الي `production` فقُم بإضافة علم `--prod` ```bash vercel --prod ``` ================================================ FILE: www/src/pages/ar/faq.md ================================================ --- title: الأسئلة الشائعة description: الأسئلة المتكررة حول إنشاء تطبيق T3 layout: ../../layouts/docs.astro lang: ar dir: rtl --- إليك ما يَكثُر من سٌؤاله عن Create T3 App. ## ماذا بعد؟ كيف استعمل `create-t3-app` لإنشاء تطبيق؟ نحن نحاول أن نُبقي هذا المشروع بَسيطاًً قدر الإمكان، لذلك فَقد وضعنا حَجر الأساس لك، ويمكن إضافة ما تريد وقتما تُريد. إذا كًنت غير مُلم ببعض التقنيات المُستخدمة في هذا المَشروع فأتجه إلى الـ Docs المَعنية بها، فإذا واجهتك مُشكلة ما فالرجاء الانضمام إلى سيرفر الديسكورد [Discord](https://t3.gg/discord) وأطلب المُساعدة. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [TailwindCSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## ما هي مصادر التعلم المتاحة في الوقت الحالي؟ بالرغم من أن المصادر المذكورة في الأسفل هي من أفضل المصادر الموجودة لتعلم مسار T3، فإننا (و [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) ننصحك أن تبدأ باستخدام الـ Stack وتَعلمه عن طريق أن تبني شيئاَ به. إذا كُنت تخطط لاستخدام Create T3 App فَربما تكون دِراية ببعض أجزاء هذا الـ Stack، فلماذا لا تَستغل مَعرفتك تِلك لتبدأ العمل مباشرة. نَحن نُدرك أن هذا المسار لا يَصلح للجميع. لِذلك ، إذا كنت تشعر أنك قد جَربت ما أوصيناك به وما زلت ترغب في المزيد من مصادر التَعلم، أو في حال أن لم تكن واثقًا من قَدرتك علي ذلك وَحدك، فراجع هذه البرامج التعليمية الرائعة عن Create T3 App: ### مقالات - [Build a full stack app with Create T3 App](https://www.nexxel.dev/blog/ct3a-guestbook) - [A first look at Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) ### فيديوهات - [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [An overview of the Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## لماذا هُناك ملفات بامتداد `.js` في هذا المشروع؟ وِفقًا لـ [T3-Axiom #3](/en/introduction#typesafety-isnt-optional) ، نَحن نَعتبر Typesafety مواطناََ مِن الدرجة الأولى. لِسوء الحَظ، لا يُدعم TypeScript في بَعض الـ Frameworks والاضافات مما يعني ضرورى أن تكتب بعض ملفات للإعدادات (configuration) بلغة JS وهو مُر لابُد منة. نُحن نُحاول التأكيد على أن هذه الملفات هي `Javascript` لأسباب خَارجة عن إرادتنا وذلك عن طريق إستخدام (cjs أو mjs)، لكن لا تزال جميع البيانات في ملفات js في هذا المشروع، تخضع لفحص باستخدام تعليق `@ts-check` في الأعلى. ## أجد صعوبة في إضافة i18n إلى تطبيقي. هل هناك أي مرجع يمكنني الرجوع إلية؟ لقد قررنا عدم تضمين i18n افتراضيًا في Create T3 App وذلك لأنه موضوع شائك للغاية وهناك العديد من الطرق لتنفيذه. ومع ذلك، إذا كنان لابُد من تَنفيذه وترغب في رؤية مشروع مَرجعي، فلدينا [مَرجع](https://github.com/juliusmarminge/t3-i18n) يُوضح كيف يمكنك إضافة i18n إلى تَطبيق T3 باستخدام [next-i18next](https://github.com/i18next/next-i18next). ## لماذا نستخدم `/pages` وليس `/app` في Next.js 13؟ كما ذكرنا مُسبقا في [T3-Axiom #2](/en/introduction#bleed-responsibly)، نحن نُحب الأشياء المتطورة لكننا نُقدر الاستقرار، فمن الصعب تَحويل الـ router إلي النظام الجديد، شاهد [not a great place to bleed](https://youtu.be/mnwUbtieOuI?t=1662). فإن `/app` [مُجرد لَمحة من المستقبل](https://youtu.be/rnsC-12PVlM?t=818)، فهو ليس جاهزًا للإنتاج؛ غير إن (API) في مَرحلة تجريبية ومن المتوقع أن تحدث له تغييرات جذرية. لمعرفة الميزات المدعومة والمخطط العمل عليها في dir المسمى `/app`، زر [beta Next.js docs](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/ar/folder-structure-pages.mdx ================================================ --- title: (Pages)بِنية المجلد description: بنية مجلد T3 App layout: ../../layouts/docs.astro lang: ar dir: rtl isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; اختار أدواتك لترى بنية المجلد عند إنشاء تطبيقك بواسطة تلك ألاختيارات. بالأسفل ستجد وصف عن غرض كلاً منهم.
## `prisma` مجلد prisma يحتوي على `prisma.schema` الذي بدوره يحتوي ملف تكوين الاتصال مع قاعدة البيانات كما يحتوي أيضا علي schema التي تحدد شكل الـ tables هذا إلى جانب ملفات الـ migrations و/أو كود لإضافة المعلومات إلى Database، اقرآ [كيفية استخدام Prisma](/ar/usage/prisma). ## `public` يحتوي مُجلد Public على الملفات الثابتة التي يقوم السيرفر بتقديمها، يكون ملف `favicon.ico` مثال على ذلك. ## `src/env` يستخدم للتحقق من صحة الـ environment variable وتحديد الأنواع (types)، اقرأ المزيد [Environment Variables](usage/env-variables) ## `src/pages` مجلد `pages` يحتوي علي كل الصفحات التي يحتوي عليها تطبيق Next.js، يعمل ملف `index.tsx` في مستند root من `/pages` كالصفحة الأساسية للتطبيق، يعمل ملف `__app.tsx` لإمداد التطبيق بما تحتاجه من موفر، لقراءة المزيد [Next.js documentation](https://nextjs.org/docs/basic-features/pages).
### `src/pages/api` يحتوي مجلد `api` علي كل Api routes في تطبيقك الـ Next.js، يحتوي `example.ts` على مثال عن route الذي يستخدم [Next.js API route](https://nextjs.org/docs/api-routes/introduction) مع Prisma. يحتوي ملف `restricted.ts` علي مثال عن route الذي يستخدم خاصية في [Next.js API route](https://nextjs.org/docs/api-routes/introduction) ومحمي من [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` إن ملف `[...nextauth].ts` خط سير التوثيق (authentication slug route) في NextAuth.js، ويستخدم في طلبات التوثيق. اقرأ [NextAuth.js usage](usage/next-auth) و [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) لمزيد من المعلومات حول catch-all/slug.
#### `src/pages/api/trpc/[trpc].ts` يُعتبر ملف `[trpc].ts` المدخل لـ API في tRPC، ويكون مسئول عن تنفيذ طلبات tRPC. اقرأ [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) و [tRPC usage](usage/trpc#-pagesapitrpctrpcts) لمزيد من المعلومات حول catch-all/slug.
## `src/server` يُستخدم مجلد `server` للفصل بوضوح بين الكود من جانب السيرفر (Server Side) والكود من جانب العميل (Client Side).
### `src/server/auth.ts` يحتوي على الوظائف الخادمة (utilities) التي تقوم بالتوثيق مثل استرداد جلسة المستخدم (user session) من جانب السيرفر. اقرأ [NextAuth.js](https://create.t3.gg/ar/usage/next-auth#usage-with-trpc) للمزيد من المعلومات.
#### `src/server/db.ts` يستخدم ملف `db.ts` ليوضح استخدام Prisma client بشكل شامل. اقرأ [استخدام Prisma](https://create.t3.gg/ar/usage/prisma#prisma-client) و[افضل الطرق لاستخدام Prisma client مع Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) لمزيد من المعلومات.
#### `src/server/db/client.ts` يستخدم ملف `client.ts` لإنشاء نُسخة لـ Prisma Client علي مستوي التطبيق كلة، اقرأ [Prisma usage](usage/prisma#prisma-client) لمزيد من المعلومات.
### `src/server/api` مجلد tRPC يحتوي على كود tRPC للسيرفر.
#### `src/server/api/routers` يُحتوي مجلد `routers` على كل وطائفك الوصول الفرعية (sub-routers) لمكتبة tRPC.
#### `src/server/api/routers/example.ts` إن ملف `example.ts` مثال عن وظيفة الوصول tRPC التي تستخدم `publicProcedure` الوطيفة المساعدة (helper) لتوضيح كيف ينشئ وظيفة وصول من tRPC. سوف يحتوي الملف على وظائف بأعداد مختلفة طبقا لأختيراتك، لتوضيح الاستخدام حسب احتياجك.
#### `src/server/api/trpc.ts` إن ملف `trpc.ts` الملف الرئيسي لإعدادات back-end في tRPC. هنا: 1. يحدد المحتوى المستخدم في طلبات tRPC. اقرأ [استعمال tRPC](https://create.t3.gg/ar/usage/trpc#-serverapitrpcts) للمزيد من المعلومات. 2. يورد (export) نهج الوطيفة المساعدة. اقرأ [استعمال tRPC](https://create.t3.gg/ar/usage/trpc#-serverapitrpcts) للمزيد من المعلومات.
#### `src/server/api/root.ts` يُستخدم ملف `root.ts` لدمج وظائف tRPC للوصول, وتوريدهم كوظيفة وصول واحدة، وكذلك أنواع بيانات الوظيفة المحددة، اقرأ المزيد هنا [tRPC usage](https://create.t3.gg/ar/usage/trpc#-serverapirootts).
### `src/styles` يحتوي مجلد `styles` على التصميمات العامة للتطبيق.
### `src/types` يحتوي مجلد `types` على الأنواع البيانات (Types) أو الأنواع المحددة (type declarations).
#### `src/types/next-auth.d.ts` يُستخدم ملف `next-auth.d.ts` للتعديل على إعدادات NextAuth الافتراضية، لمزيد من المعلومات [NextAuth.js usage](usage/next-auth#inclusion-of-userid-on-the-session).
### `src/utils` يُستخدم مجلد `utils` لحفظ الـ functions التي يكثر استخدامها.
#### `src/utils/trpc.ts` يُستخدم ملف `trpc.ts` كـ entrypoint لـ front-end إلى tRPC، اقرآ المزيد هنا [tRPC usage](usage/trpc#-utilstrpcts).
### `.env` يُستخدم ملف `.env` لتخزين environment variables، اقرأ المزيد [Environment Variables](usage/env-variables). يجب ألا يضاف هذا الملف في تاريخ Git.
### `.env.example` ملف `.env.example` هو مثال لاستخدام example environment مبني على المكتبات المختارة. يجب أن يضاف هذا الملف في تاريخ Git.
### `.eslintrc.cjs` يٌستخدم ملف `.eslintrc.cjs` لإعداد ESLint، اقرأ المزيد هنا [ESLint Docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files).
### `next-env.d.ts` يحقق ملف `next-env.d.ts` أن Typescript تأخذ Types في Next.js في الحسبان. **لا تُعدل عليها ولا تحذفها لأنها تتغير باستمرار**، لمزيد من المعلومات [Next.js Docs](https://nextjs.org/docs/basic-features/typescript#existing-projects).
### `next.config.js` يستخدم ملف `next.config.js` لإعداد Next.js، لمزيد من المعلومات [Next.js Docs](https://nextjs.org/docs/api-reference/next.config.js/introduction). تلميح: يسمح تمديد `.mjx` باستخدام ESM imports.
### `postcss.config.js` إن ملف `postcss.config.js` ضروري عند استخدام TailwindCSS PostCSS، لمزيد من المعلومات [Taiwind PostCSS Docs](https://tailwindcss.com/docs/installation/using-postcss).
### `prettier.config.js` إن ملف `prettier.config.js` ضروري عند استخدام Prettier ولإضافة prettier-plugin-tailwindcss لتنظيم الفئات (classes) مع Tailwind CSS، لمزيد من المعلومات [Tailwind CSS blog post](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier).
### `tsconfig.json` إن ملف `tsconfig.json` ضروري عند استخدام TypeScript. فعيلت بعض الإعدادات الغير افتراضية، مثل `strict mode`، لتوفير أفضل إمكانية للاستخدام الـ TypeScript في Create T3 App ومكتباتها، لمزيد من المعلومات اقرأ [TypeScript Docs](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) أو [TypeScript Usage](usage/typescript).
================================================ FILE: www/src/pages/ar/installation.mdx ================================================ --- title: التثبيت description: تعليمات التثبيت لـ Create T3 App layout: ../../layouts/docs.astro lang: ar dir: rtl isMdx: true --- import Callout from "../../components/docs/callout.tsx"; لإنشاء تَطبيق بإستخدام `create-t3-app`، نفيذ أي من الأوامر الثلاث التالية وأجب عن أسئلة مٌوجه الأوامر (command prompt): ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` بعد أن تَنتهي عَملية إنشاء التطبيق، اَلق نَظرة عَلي [الخطوات الأولى](/ar/usage/first-steps) للبدء في تطبيقك الجديد. ## الخصائص المَتقدمة | خصائص | الوظيفة | | ----------------- | ------------------------------------------------------------ | | `[dir]` | تحتوي على معطى الملف الذي سيسما عليه التطبيق | | `--noGit` | أخبار الـ CLI إلا يهيئ GIT repo للتطبيق قصداً | | `-y`, `--default` | تعدي أسئلة الـ CLI وإنشاء تطبيق t3 مع اختيار جميع الاختيارات | | `--noInstall` | إنشاء تطبيق دون تثبيت الـ dependencies | ## خصائص تجريبية هُناك بعض خصائص التجريبية (Flags) التي تسمح لك بإنشاء أي تطبيق دون أي أسئلة من مُوجة الأوامر. إذا كانت حالة الاستخدام هذه تنطبق عليك ، فيمكنك إستخدام هذه خصائص. يٌرجى مٌلاحظة أن هذه خصائص تجريبية وقد تتغير في المستقبل دون أن نُخصص لها semver versioning. | خصائص | الوظيفة | | ------------ | ---------------------------- | | `--CI` | دع CLI يعرف أنك في وضع CI | | `--trpc` | أضف tRPC إلى التطبيق | | `--prisma` | أضف Prisma إلى التطبيق | | `--nextAuth` | أضف NextAuth.js الي التطبيق | | `--tailwind` | أضف Tailwind CSS الي التطبيق | ملاحظة: إذا لم تضع الخاصية `CI` ، فلن يكون لبقية هذه الخصائص أي تأثير. يمكنك الا تُضيف الخيارات في حال لم تكن تريد ذلك، لكننا ننصحك بذلك تحرياََ للدقة فعليك تمرير `false` مع الخاصية مثل `--nextAuth false`. ### أمثلة الامر التالي سَيٌنشئ تطبيق T3 باستخدام tRPC و Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/ar/introduction.md ================================================ --- title: مقدمة description: مقدمة إلى T3 Stack layout: ../../layouts/docs.astro lang: ar dir: rtl ---
## T3 Stack الـ T3 Stack هو Stack لتطوير تطبيقات الويب بُني علي فَلسفة البساطة، قابلية إعادة الاستخدام (فصل المركبات وإعادة استخدامها) و الحصول على typesafety في جميع أزكان التطبيق. الأجزاء الأساسية هم [**Next.js**](https://nextjs.org/)، و [**TypeScript**](https://typescriptlang.org/)، و [**Tailwind CSS**](https://tailwindcss.com/) ودائما ما تَكون مُضافة، إذا كان مشروعك يتطلب Backend فَيُمكنك إضافة [**tRPC**](https://trpc.io/)، و [**Prisma**](https://prisma.io/)، و [**NextAuth.js**](https://next-auth.js.org/). لعلك لاحظت أن هناك الكثير من... الأجزاء. هذا ما رغبنا بتصميمه, لك مُطلق الحرية أن تُضف جزء وتَحذف أُخرى كما تحتاج - يكون أساس Stack خاضع لاحتياجاتك. ## حسنا… ما هو create-t3-app؟ أهو قالب (template)؟ نوعا من. أُنشئ `create-t3-app` بواسطة مطورون متمرسون ليبسط CLI إعداد تطبيق T3 قَدر الإمكان. فَكُل جزء اختياري، هذا يعني أنك تنشئ قالبك حسب احتياجك. بعد أنشاء العديد من المشروعات وممارسة تلك الأدوات لسنين، كسبنا العديد من الخبرات والآراء، وفعلنا قدر المستطاع لوضعهم داخل CLI. **أنه ليس** بقالب شامل علي كل ما تحتاجة، نتوقع منك أن تُضيف مكتباتك الخاصة لحل ما سيواجهك من مشكلات **في تطبيقك**. علي الرغم من أننا نريد أن نترك لك مُطلق الحرية في حل المشاكل مثل state management ونشر التطبيق، [لكن لدينا بعض الترشيحات لك](/ar/other-recs). ## T3 Axioms لنكون شفافون - هذا _مشروع متعدد الآراء_. نتشارك جميعاََ في عدد قليل من المعتقدات الأساسية حَول كيفية البناء ونتخذها كأساس قراراتنا. ### Solve Problems من السهل أن تَقع في فخ فتُضيف "كل شيئ" - لكننا لا نريد ذَلك صراحتاً، يَجب أن يكون لكل شيئ مَغزي وهدف مُحدد كما هو الحال في ما أضفناه مُسبقا ويَترتب علي ذلك عدم إضافة مكتبات مِثل (`zustand`, `redux`)، لكننا نُضيف مكتبات أُخري مُثل NextAuth.js, و Prisma و tRPC. ### غامر، لكن علي مسؤوليتك نحن نحب التقنيات الحديثة، تجرِبة الأشياء الجديدة تجلب السعادة، لكن عليك تَوخى الحذر فلا تَستخدام التقنيات الغير مستقرة في الأماكن الحساسة، فمثلا نحن لا نراهن ⛔️ على قواعد بيانات حديثة (فإن SQL رائعة)، لكننا نراهن ✅ علي tRPC لأنها مجرد Functions ومن السهل الاستغناء عنها. ### Typesafety ليس خياراََ قد حددنا مُسبقاَ أن الهدف الأوحد من `create-t3-app` هو أن نوفر بِداية سَريعة لتطوير تطبيقات full-stack مع اتخاذ **typesafe** كأولوية, فهو أمر لابد منه لتحسين فاعليتك كمطور بواسطة تقليل عدد Bugs قدر الإمكان. أي حل وسط يضر typesafe في `create-t3-app` يَجب اتخاذه في مشروع مختلف. ================================================ FILE: www/src/pages/ar/other-recs.md ================================================ --- title: ترشيحات أخرى description: مكتبات وخدمات نرشحها لك layout: ../../layouts/docs.astro lang: ar dir: rtl --- نٌدرك تمام الإدراك أن المَكتبات الإفتراضية في `create-t3-app` لا تَحٌل كُل المشاكل التي قد تواجهك، بغض النظر عن ذلك فنحن ننصحك أن تبدأ بالأشياء التي نٌقدمها، فعاجلاََ أم أجلا سيأتي عليك وقت تحتاج فيه الي إستخدام مكتبات خَارجية، أنت وَحدك مَن يدري إحتياجات مشروعك. هَاك بَعضٌ المَصادر التي نٌشجعك على أن تّتبعها. بَعض الترشيحات التالية قَدمها مٌساهمون مٌنفردون فلا يَجب أن تُؤخذ على أنها تَرشيحات رَسمية من فريق T3 أو T3-OSS، لذلك **قُم بالبحث، خاصة قبل أن تشترك في خدمات مدفوعة** ## State Management **ملحوظة المحرر**: مكتبات الـ State Management رائعة لكنها في مٌعظم الحالات ليست ضَرورية، فـ tRPC و ReactQuery تقوم بالوظيفة بشكل جيد، لذلك إبدأ بـ`useState` وانتقل إلى أحد البدائل المتاحة فقط عندما يلزم الأمر. ### Zustand **حتي لا تستعمل Redux أبداََ مرة اخرى** بديل Redux الذي لم تكن تعلم إنك تحتاجة، غير إمكانية الوثوق بها فأنة بإستخدام [Poimandres](https://github.com/pmndrs) يمكنك صُنع **كل شي** من تطبيقات المكالمات الي الالعاب وغيرها. - [Zustand Homepage](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ## Component Libraries تحتاج مٌعظم التطبيقات إلى نَفس مَجمٌوعة المٌكونات - أزرار التبديل والقوائم المنسدلة وغيرها. تٌوفر هَذه المَكتبات مُكونات رَائعة يُمكنك إستخدامها وتَخصيصها حَسب رَغبتك. ### Unstyled Component Libraries تُعرف أيضًا باسم Headless libraries ، فهي تٌوفر مٌكونات رائعة يمكنك عمل styling لها كما تشاء, هذا الي جانب كونها Accessible و يمكنك تخصيصها حسب رغبتك. - [Radix UI](https://www.radix-ui.com/) - [Headless UI](https://headlessui.com/) - [React Aria](https://react-spectrum.adobe.com/react-aria/) ### Styled Component Libraries في بعض الأحيان تقوم ببناء مشروع وكل ما تريدة هو أن تبدو واجهة المستخدم جيدة. كـ لوحات التحكم وغيرها ، سَتٌنجز أي هذه المكتبات هذه المهمة على أكمل وجه - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) ### Class Variance Authority ** لبناء مكتبات UI ** تُساعدك علي إنشاء مكتبات واجهة مستخدم بشكل تصريحي (Declarative) بألوان وأحجام مختلفة ومتغيرات مختلفة. عندما يصل مشروعك إلى حجم يلزمك فيه مجموعة موحدة من مكونات الـ UI مع مُتغيرات مٌتعددة لـ Tailwind CSS ، فإن CVA هي أداة رائعة. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Animations ### AutoAnimate **الانميشن بسطر كود واحد** تحاول معظم مكتبات الانميشن إرضاء كل حالات الاستخدام الممكنة ، نتيجة لذلك تصبح غير مرغوب فيها. AutoAnimate هي أداة لا تحتاج إلى تكوين تٌوفر تَحسينًا كبيرًا في UX دون بَذل جٌهد إضافي من المطور. - [AutoAnimate Homepage](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [AutoAnimate Component Snippet](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **الرسوم المتحركة المعقدة** تٌوفر FramerMotion اٌسلوبا بَسيطا لكتابة الكود، فَتسمح لك بكتابة رسوميات معقدة بكود أقل - [Framer Motion Homepage](https://framer.com/motion) - [Framer Motion Documentation](https://www.framer.com/docs/) ## Deployments، و Infrastructure، و Databases، و CI ### Vercel **إستضافة موقعك** قامت Vervel بتسهيل استضافة موقعك بعد أن كان جِحِيماََ في الماضي، فإنها تَعتمد عََلي AWS لكن مع واجهة أفضل. - [Vercel Homepage](https://vercel.com/) - [Create T3 App Vercel deployment guide](/en/deployment/vercel) ### PlanetScale **حتي لا تقلق علي قواعد بياناتك بعد الآن** تُعد PlanetScale أحد أفضل مٌزودي خِدمة قَواعد البيانات عَلي الإطلاق لقٌدرتها عَلى التَوسع وتجربة المطور الممتازة والأسعار المعقولة. - [PlanetScale Homepage](https://planetscale.com/) ### Railway **لإستضافة البنية التحتية للموقع** فهي Heroku العصر الحديث، إذا كان Vercelli , PlanetScale غير كافيين لك، فـ Railway سَتكون خيارك المناسب. - [Railway Homepage](https://railway.app/) ### Upstash نحن نٌحب Prisma و Planet Scale لكن بعض المشاريع تتطلب حٌلولا أكثر سٌرعة. فإن **Upstash** تٌعطيك القٌدرة علي إستخدام Redis في مشروعك دون الحاجة أن تٌدير البنية التحتية. - [Upstash موقع](https://upstash.com/) ### Pusher **For serverless WebSockets** إذا كان الـ WebSockets مُهمة بالنسبة لك، فإنه مِن الأفضل الإعتماد على سيرفر تقليدي مثل [Fastify](https://www.fastify.io/) والتي أيضا تدعم [tRPC](https://trpc.io/docs/v10/fastify) لكن إذا كانت السرعة مهمة لك فإن Pusher هو الخيار المناسب لك. - [Pusher Homepage](https://pusher.com/) ### Soketi Soketi هو بديل بسيط وسريع للاستضافة الذاتية لـ Pusher. إنه متوافق تمامًا مع Pusher SDK الذي يمكنك استخدامه للاتصال بالخادم. Soketi serverless هو أيضًا في مرحلة تجريبية. - [Soketi Homepage](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Analytics تعتبر بيانات المستخدم ذات قيمة كبيرة عند إنشاء تطبيق. فيما يلي بعض موفري التحليلات الذين نوصيك بهم. ### Plausible هل تحتاج إلى تحليلات؟ Plausible هو أحد أسرع الطرق للحصول عليها [simple plugin for Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Plausible Homepage](https://plausible.io/) ### Umami يعد Umami بديلًا بسيطًا وسريعًا لبرنامج Google Analytics. يمكنك استخدامه بسهولة في Vercel و Railway وما إلى ذلك باستخدام PlanetScale كقاعدة بيانات. - [Umami Homepage](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) ## غيرها ### Next Bundle Analyzer قد يكون من الصعب أحيانًا تحديد ما سيتم تضمينه في الإصدار النهائي لتطبيقك. يُعد Next Bundle Analyzer طريقة سهلة لتصور وتحليل حزم JavaScript التي تم إنشاؤها. - [next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/ar/t3-collection.mdx ================================================ --- title: اعمال بواسطة T3 description: تطبيقات مفتوحة المصدر صنعت باستخدام Stack T3 layout: ../../layouts/docs.astro lang: ar dir: rtl isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; هل قٌمت بعمل تطبيق بإستخدام T3 Stack وتريد مشاركته؟ أضافه إلى القائمة التالية! ## تطبيقات مفتوحة المصدر صُنعت بواسطة T3 ## شركات تَستخدم T3 Stack نَوَدٌ مَعرفة الشَركات التي تَستخدم Stack T3 لتَطبيقاتها. إذا كانت شَركتك تَستخدم Stack T3 وتَرغب في مشاركته؟ أضافه إلى القائمة التالية! _أصنعت مشروعاً باستخدام T3 Stack؟ أنشئ [Pull Request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx). وضفه هنا!_ ================================================ FILE: www/src/pages/ar/usage/env-variables.md ================================================ --- title: Environment Variables description: بدء الاستخدام مع create-t3-app layout: ../../../layouts/docs.astro lang: ar dir: rtl --- إن `Create-T3-App` تستخدم [Zod](https://github.com/colinhacks/zod) للتأكد من صلاحية الـ environment variables أثناء الـ runtime عن طريق توفير بعض الملفات الاضافة 📁 src/env ┫ 📄 client.mjs ┫ 📄 schema.mjs ┫ 📄 server.mjs قد يبدو محتوى هذه الملفات مخيفًا للوهلة الأولى ، لكن لا تقلق ، فهو ليس معقدًا كما يبدو. دعنا نلقي نظرة عليها واحدة تلو الأخرى ، ونسير خلال عملية إضافة environment variables إضافية. إذا كنت تريد إضافة environment variable جديد ، فيجب عليك إضافته إلى كل من ".env" وكذلك في `env / schema.mjs`.\_ ## ملف schema.mjs هذا هو الملف الذي ستعمل علية. يَحتوي على مُخططين ، أحدهما environment variables من جانب الخادم والآخر من جانب العميل بالإضافة إلى Object الـ "clientEnv". ```ts:env/schema.mjs export const serverSchema = z.object({ // DATABASE_URL: z.string().url(), }); export const serverEnv = { // DATABASE_URL: process.env.DATABASE_URL, }; export const clientSchema = z.object({ // NEXT_PUBLIC_WS_KEY: z.string(), }); export const clientEnv = { // NEXT_PUBLIC_WS_KEY: process.env.NEXT_PUBLIC_WS_KEY, }; ``` ### الـ Server Schema أنشئ الـ environment variables schema من جانب الخادم هنا. تأكد أن لا تضيف `NEXT_PUBLIC` قبل اسم المتغير، سيفشل الـ Validation إذا ما فعلت هذا. ### الـ Client Schema أنشئ الـ client-side environment variables هنا، حتى تجعلهم متاحيت للـ client أضف `NEXT_PUBLIC` قبل الاسم. ### الـ clientEnv Object هُنا حيثُ تقوم بعمل Destruct لـ `process.env` تحتاج Zod الي Object لتكون قادرة على تصحيح المُدخلات وبسبب طريقة عمل Next.js فلن نستطيع فعل هذا تلقائيا لذلك يجب أن تتم هذة العملية يدويا، لا تقلق فـ Typescript تقوم بتحذيرك إذا ارتكبت خطاّ. ```ts // ❌ This doesn't work, we need to destruct it manually const schema = z.object({ NEXT_PUBLIC_WS_KEY: z.string(), }); const validated = schema.parse(process.env); ``` ## الـ server.mjs & client.mjs هذا هو المكان الذي يتم فيه التحقق من الـ Object ومن ثم تصديرها. لن تحتاج إلى تعديل هذه الملفات. ## إستخدام الـ Environment Variables إذا أردت إستخدام الـ env vars فيمكنك إستيراد` env.js` واستعمالهم طبيعيا. إذا إستيرادت الملف في Client وحولت استعمال قيم الserver-side، ستنذر بوجود خطأ في run-time. ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` is fully typesafe and provides autocompletion const dbUrl = env.DATABASE_URL; ``` ## الـ .env.example بما أن ملف `.env` ليس مُضمناَ في الـ version control، فقد أضفنا ملف `.env.example` والذي يمكنك أن تتركه، وأضياََ ننصحك أن تُبقي هذا الملف متزامنا مع الملف الاساسي حتي تحصل علي أفضل تجربة تطوير ممكنة ## أضف Environment Variables حتى نتأكد من أنك ستضيف الـ environment variables، يجب عليك أن تضيفها في مكانين مختلفين 📄 ملف `.env`: هنا نضيف المتغيرات بشكل طبيعي كانك تتعامل مع ملف `.env` عادي 📄 ملف `schema.mjs`: هنا تضيف الـ Logic التي ستستخدمة Zod لفحص صلاحية المتغيرات 📄 ملف `.env.example`: هنا تضيف المتغيرات لكن بدون أي كلمات سرية للحفاظ على أمانك ### أمثلة 1. أضف the environment variable الي `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. أضف environment variable to `schema.mjs`: ```ts export const serverSchema = z.object({ // ... TWITTER_API_TOKEN: z.string(), }); export const serverEnv = { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }; ``` **ملحوطة:** أضف إسم المُتغيير `TWITTER_API_TOKEN` في example.env ولكن لا تُضف الـ token ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/ar/usage/first-steps.md ================================================ --- title: الخطوات الاول description: الخطوات الاولي في create-t3-app layout: ../../../layouts/docs.astro lang: ar dir: rtl --- لقد أنشأت للتو مَشروعا باستخدام T3 Stack ، هذة هي الخُطوات الاجبارية التي يجب إتباعها قبل أن تتمكن من تشغيل المشروع. ## قواعد البيانات اذا كان مشروعك يحتوي على Prisma فعليك ان تقوم بتشغيل أمر `npx prisma db push` ، هذا الأمر يقوم بمزامنة الـ Schema مع قاعدة البيانات ليضمن الـ Typesafety عند الـ Client، **لاحظ أن هذه الخطوة تتطلب إعادة تشغيل الخادم** ## الـمصادقة إذا كان تطبيقك يتضمن NextAuth.js ، سنبدأ مع Discord Provider. يُعد هذا أحد أبسط مُزودي الخدمة التي تدععمها NextAuth.js ، لكنه لا يزال يتطلب القليل من الإعداد الأولي من جانبك. بالطبع ، إذا كنت تفضل استخدام موفر مصادقة مختلف ، فيمكنك أيضًا استخدام أحد المزودين العديدين الذين يقدمهم NextAuth.js. ستحتاج إلى حساب Discord ، أنشئ واحداََ إذا لم تكن قد قمت بذلك بالفعل. اذهب https://discord.com/developers/applications واضغط علي New Application في الجانب الايمن الاعلي . ثم اذهب **<** Settings **<** OAuth2 **<** General قم بنسخ Client ID وضعه في `.env `كـ `AUTH_DISCORD_ID` اضغط علي Reset Secret ثم انسخ كلمة السر الجديدة وضعها في .env كـ AUTH_DISCORD_SECRET اضغط علي Add Redirect واضف http://localhost:3000/api/auth/callback/discord اضف AUTH_SECRET الي .env كـ String، في الـ Production اضف كلمة سر قوية. ================================================ FILE: www/src/pages/ar/usage/next-auth.md ================================================ --- title: NextAuth.js description: إستخدام NextAuth.js layout: ../../../layouts/docs.astro lang: ar dir: rtl --- عندما تُريد نِظام مُصادقة في تَطبيق Next.js ، فإن NextAuth.js يُعد حلاً ممتازًا دون الحاجة إلى إنشائة بنفسك. يأتي مزودًا بقائمة واسعة من الموفرين لإضافة مصادقة OAuth بسرعة ويوفر Adapters للعديد من قواعد البيانات و ORMs. ## Context Provider في نقطة الدُخول إلي تطبيقك ، سترى أن تطبيقك في [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx ``` يسمح الـ context Provider لـ تطبيقك ان يصل إلى بيانات المستخدم دون الحاجة الى ادخال اي بيانات اضافيه ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Handle unauthenticated state, e.g. render a SignIn component return ; } return

Welcome {session.user.name}!

; }; ``` ## تضمين `user.id` في الـ Session يُستخدم `create-t3-app` الـ Session callback الموجودة في ملف تكوين NextAuth.js ليضيف الـ User ID الي Session Object. ```ts:pages/api/auth/[...nextauth].ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` ```ts:types/next-auth.d.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` بنفس الطريقة يمكن اضافة اي بيانات الى الـ Session Object ## Usage with tRPC عند استخدام NextAuth.js مع tRPC، يمكنك إنشاء producers وحمايتها باستخدام [middleware](https://trpc.io/docs/v10/middlewares)، وهذا يسمح لك بإنشاء procedures لا يمكن الوصول لها إلا بواسطة أشخاص معينين يُمكن فَعل هذا في خطوتين: 1. للحصول علي Object الـ Session يمكنك استخدام getServerSession، نفضل getServerSession عن getSession لانها تعمل علي الخام فلا يحدث invoke غير مرغوب فيه ، قد تحملت `create-t3-app` عناء إنشاء هذه المادة عنك : ```ts:server/common/get-server-auth-session.ts export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return await getServerSession(ctx.req, ctx.res, nextAuthOptions); }; ``` باستخدام هذه الاداة يمكنك الحصول علي الـ Session وتمريرها إلى الـ tRPC Context ```ts:server/trpc/context.ts import { getServerAuthSession } from "../common/get-server-auth-session"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. أنشئ tRPC Middleware وتأكد ما اذا كان هذا المستخدم يملك الصلاحيات اللازمة أم لا. ```ts:server/trpc/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }); ``` الـ Session Object صغير ويحتوي علي عدد قليل من الخانات، وعند استخدامك لـ `protectedProcedures`يمكنك الوصول الى هذة البيانات منها الـ UserId وعندها يمكنك عمل fetch لبيانات اخرى من قاعدة البيانات. ```ts:server/trpc/router/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## الاستخدام مع Prisma يتطلب إستخدام NextAuth.js للعمل مع Prisma الكثير من الإعداد الأولي. يتعامل تطبيق create-t3-app مع كل هذا من أجلك ، وإذا حددت كل من Prisma و NextAuth.js ، فستحصل على نظام مصادقة يعمل بكامل طاقته مع جميع النماذج المطلوبة التي تم تكوينها مسبقًا. نقوم بشحن تطبيقك الاولي مع مزود Discord OAuth المكون مسبقًا ، والذي اخترناه لأنه من أسهل البدء معة - ما عليك سوى توفير الرموز المميزة في .env وستكون جاهزًا للبدء. ومع ذلك ، يمكنك بسهولة إضافة المزيد من مقدمي الخدمة باتباع NextAuth.js Docs. لاحظ أن بعض مقدمي الخدمة يطلبون إضافة حقول إضافية إلى نماذج معينة. نوصيك بقراءة الـ Docs الخاصة بالموفر الذي ترغب في استخدامه للتأكد من أن لديك جميع الحقول المطلوبة. ### إضافة المزيد من الحقول إلى الـ models عند الحاجة إلى إضافة حقول إضافية الي `User` أو `Account` أو `Session` -على اغلب الظن انك لن تحتاج الى تعديل شئ غير `User` اَ بق في بالك أن Prisma Adapter سينشئ هذا الحقل تلقائيا مع كل مستخدم جديد لذا عليك أن تضيف قيمة افتراضية Default Value. ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## الاستخدام مع Next.js Middleware. يتطلب استخدام NextAuth.js مع Middleware Next.js استخدام [JWT Session Strategy](https://next-auth.js.org/configuration/nextjs#caveats). هذا لأن الـ Middleware قادرة فقط على الوصول إلى ملف تعريف ارتباط JWT بشكل افتراضي ، يتم تكوين التطبيق create-t3-app لاستخدام استراتيجية قاعدة البيانات Database Strategy ، بالاشتراك مع Prisma كـ Adapter لـ قاعدة البيانات. ## إعداد DiscordProvider 1. اتجه الى [the Applications section in the Discord Developer Portal](https://discord.com/developers/applications) واضغط على New Application. 2. في settings menu اضغط على OAuth2 ثم General 3. إنسخ الـ Client ID وضعة في `.env` كـ AUTH_DISCORD_ID 4. تحت Client Secret اضغط على "Reset Secret" ونسخ النص الجديد وضعه في `.env` كـ `AUTH_DISCORD_SECRET `. كن حذرًا لأنك لن تتمكن من رؤية هذا كلمة السر مرة أخرى ، ستؤدي إعادة تعيينها إلى انتهاء صلاحية كلمة السر الحالية 5. اضغط على Add Redirect واضف رابط إعادة التوجيه`http://localhost:3000/api/auth/callback/discord` كمثال 6. احفظ التعديلات - It is possible, but not recommended, to use the same Discord Application for both development and production. You could also consider [Mocking the Provider](https://github.com/trpc/trpc/blob/next/examples/next-prisma-starter-websockets/src/pages/api/auth/%5B...nextauth%5D.ts) during development. ## مصادر مُفيدة | المَصدر | الرابط | | --------------------------------- | --------------------------------------- | | NextAuth.js Docs | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/ar/usage/next-js.md ================================================ --- title: Next.js description: إستخدام Next.js lang: ar dir: rtl layout: ../../../layouts/docs.astro --- Next.js هو إطار عمل Backend لتطبيقات React
شاهد هذا الفيديو لتعرف المزيد عن [Theo's Next.js Conf talk](https://www.youtube.com/watch?v=W4UhNo3HAMw) ## لماذا عَلي أن أستخدمها ؟ نحن نحب React. لقد جعلت تطوير واجهة المستخدم سهلا بطرية لم نتخيلها من قبل. لكنها يمكن أن تؤدي بالمطورين إلى أن يتبع بعض المسارات الصعبة. تقدم Next.js أسلوبًا سلساََ لإنشاء التطبيقات باستخدام React. بداية بالـ Routing انتقالا إلى الـ Api ومن ثم الصور ، نضع ثقتنا فى Next.js لتقود المطورين نحو قرارات جيدة. إقران Next.js مع Vercel يجعل تطوير تطبيقات الويب ونشرها أسهل من أي وقت مضى. فتوفر طرية مجانية لنشر موقعك (نحن ❤️ Vercel) ## Get Static/Server Props الميزة الرئيسية لـ Next.js هي الـ Data Fetching. نوصيك بشدة أن تقرأ الـ Docs لكي تفهم كيفية استخدام كل واحدة منهما. لا يُنصح باستخدام getServerSideProps عمومًا ما لم يكن هناك سبب وجيه لذلك ، نظرًا لأنها مُكلفة ماديا وتُؤدي إلى إبطاء موقعك. Incremental Static Regeneration هو بديل رائع لـ getServerSideProps عندما تكون البيانات ديناميكية ويمكن جلبها بشكل متزايد. ## مصادر مفيدة | المَصدر | الرابط | | ------------------------------ | ---------------------------------- | | Next.js Documentation | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js Blog | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Vercel/Next.js YouTube Channel | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/ar/usage/prisma.md ================================================ --- title: Prisma description: إستخدام Prisma layout: ../../../layouts/docs.astro lang: ar dir: rtl --- مكتبة Prisma هي ORM مكتوب لغة TypeScript والتي تسمح للمطور أن يُخصص Schema لقاعدة البيانات في ملف `schema.prisma` من ثَم يقوم بتوليد type-safe client والذي بدوره يتفاعل مع قاعدة البيانات. ## ما هو Prisma Client يقع في `/server/db/client.ts` ويعمل كـ global variable كما تنصح الـ docs الرسمية [best practice](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) ,وننصحك باستخدامها كذلك بدلا من ندائها في ملف بشكل منفرد [Context](usage/trpc#-serverapitrpcts) ## الـ Schema سوف تجد الـ Schema في `/prisma/schema.prisma` حيث تقوم بتخصيص الـ schema الخاص بقاعدة البيانات والتي يتم استخدامها لتوليد Prisma Client. ### مع NextAuth.js عندما تستخدم Prisma مع NextAuth فان الـ Adapter يتحمل عنك عناء توليد الـ `User`, `Session`, `Account`, and `VerificationToken` mo، إقرأ المزيد هنا [NextAuth.js documentation](https://next-auth.js.org/adapters/prisma). ## Default Database قاعدة البيانات الافتراضية هي قاعدة بيانات SQLite ، وهي رائعة لتطوير وتسريع عملية proof-of-concept ولكن لا يوصى بها للإنتاج. يمكنك تغيير قاعدة البيانات لاستخدامها عن طريق تغيير `provider` في الـ DataSource Object إلى `postgresql` أو `mysql` ، ثم تحديث URL داخل `.env` إلى قاعدة البيانات الخاصة بك. ## Seeding your Database [يُعد زرع قاعدة البيانات](https://www.prisma.io/docs/guides/database/seed-database) طريقة رائعة لملء قاعدة البيانات بسرعة ببيانات الاختبار لمساعدتك على البدء. من أجل إعداد البذور من أجل إعداد البذر ، ستحتاج إلى إنشاء `seed.ts` في مُجلد `prisma/` ، ثم قم بإضافة `seed` script إلى ملف package.json الخاص بك. ستحتاج أيضًا إلى مُشغل TypeScript يمكنه تنفيذ الـ script. نحن نُوصي بـ tsx ، وهو عِبارة عَن مُشغل TypeScript عالي الأداء يَستخدم esbuild ولا يتطلب أي تكوين ESM ، ولكن ts-node أو أي من المُشغلين الاخرين سيفي بالغرض. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db/client"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` ثُم قُم بتشغيل `pnpm db-seed` او `npm` او `yarn` ## مصادر مُفيدة | المَصدر | الرابط | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Prisma Docs | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | NextAuth.JS Prisma Adapter | https://next-auth.js.org/adapters/prisma | | PlanetScale Connection Guide | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/ar/usage/tailwind.md ================================================ --- title: Tailwind CSS description: إستخدام Tailwind CSS layout: ../../../layouts/docs.astro lang: ar dir: rtl --- ## ما هي Tailwind CSS ؟ مكتبة Tailwind CSS هي مكتبة صغيرة، بُنيت على فلسفة [utility first](https://tailwindcss.com/docs/utility-first)، وتُستخدم في التصميم والبناء دون الحاجة إلي إستخدام Pure CSS، فهي مكتبة نقية لا تُقدم أي Components مُسبقة التصميم، غير أنها توفر مزايا عديدة بالمقارنة بباقي المكتبات، لمزيد من المعلومات شاهد a very different set of benefits](https://www.youtube.com/watch?v=CQuTF-bkOgc) فهي تجعل عملية التصميم سريعة بشكل لا يُصدق. 1. في الماضي كنا نكتب css في ملفات منفصلة ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. ثم نقوم استدعائها في مكان آخر ```jsx import "./my-class.css"; ``` 3. ثم نُضيف الـ class الي HTML ```html
...
``` المقابل لكل الخطوات السابقة في Tailwind CSS 1. فقط اكتب الـ Class في HTML وانتهى الموضوع ```html
...
``` إستخدام هذة الخصائص جَنبا إلى جَنب مع React Components يُعطيك قُوة هائلة لا مَثيل لها. تأتي Tailwind CSS بنظام مُدمج وجميل مُزود بمجموعة الوان واحجام و styles مُختارة بعناية كالطول والعرض والحواف، كما أيضا تاني مع breakpoints لإنشاء تصميم متوافق.
أعطي [mewtru](https://twitter.com/trunarla) خطابا رائعا عن [building a design system using Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## الاستخدام تأكد من تثبيت إضافة Tailwind CSS لمحرر اكوادك لتُحسن جودة العمل. ### الاضافات - [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### الـ Formatting الـ Classes في TailwindCSS يمكن أن تصبح فوضوية بسهولة، لذلك الـ Formating هو أمر لابد منه، إضافة [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) تحمل عنك هَم تنظيمها، لمزيد من المعلومات إقرأ [recommended order](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) ### إضافة الـ classes إختبارياَ - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## مصادر مفيدة | المصدر | الرابط | | ---------------------------- | -------------------------------------------------------- | | Tailwind Docs | https://tailwindcss.com/docs/editor-setup/ | | Tailwind Cheat Sheet | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Tailwind Discord Server | https://tailwindcss.com/discord/ | | TailwindLabs Youtube Channel | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/ar/usage/trpc.md ================================================ --- title: tRPC description: Usage of tRPC layout: ../../../layouts/docs.astro lang: ar dir: rtl --- تسمح لك tRPC بكتابة type safe api دُون الحَاجة إلى تَوليد كود فتُنحي عنك حَدوث أخطاء مفاجئة أثناء الـ runtime، حيث إنها تَستغل خاصية الـ inference في Typescript حتى تضمن الـ type safety عِند نداء الـ Api من الـ Frontend

I built tRPC to allow people to move faster by removing the need of a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.

Avatar of @alexdotjs
Alex - creator of tRPC @alexdotjs
## Files لسوء الحظ فإن tRPC تتطلب قليلاً من الـ boilerplate ولكن لحسن الحظ فان `create-t3-app` تحمل عنك هذا العبء. ### 📄 ملف `pages/api/trpc/[trpc].ts` هذة هي نقطة دخولك الي tRPC Api، في الأوضاع الطبيعية لن تحتاج الي أن تَمس هذا الملف كثيرا. فيمكنك تغييره عند تفعيل CORS Middleware او شئ من هذا القبيل ويقوم بعمل export لـ `createNextHandler` [Next.js API handler](https://nextjs.org/docs/api-routes/introduction) والذي يقبل [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) و [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) مما يعني أنك قادر على استخدام `createNextApiHandler` في أي middleware تريده، إقرأ [example snippet](#enabling-cors) ### ملف 📄 `server/trpc/context.ts` في هذا الملف تقوم بانشاء الـ Context التي سيتم تمريره الي tRPC Procedure ، الـ Context هو عبارة عن البيانات التي سيكون لكل الـ Procedures وصول لها وهي مكان مُناسب لتضع أشياء مثل database connections ومعلومات المصادقة وغيرها. - ما هو `createContextInner`: هُنا تَقوم بإنشاء الـ Context الذي لا يَعتمد عَلى الـ request مِثل إتصال قاعدة البيانات. ويمكنك إستخدام function لـ [integration testing](#sample-integration-test) او [ssg-helpers](https://trpc.io/docs/v10/ssg-helpers) - ما هو `createContext` ؟ هُنا حَيث تَقوم بإنشاء الـ Context الذي يعتمد على الـ request فيمكنك الوصول الى الـ `req Object` عن طريق `opts.req` ومن ثُم تَمريرة الي `createContextInner`لإنشاء الـ Context النهائي ### 📄ملف `server/trpc/trpc.ts` في هذا حَيثُ يمكنك تحديد الـ [procedures](https://trpc.io/docs/v10/procedures) و [middlewares](https://trpc.io/docs/v10)، من الافضل ان لا تقوم بعمل export لـ t Object كاملا /middlewares) بل قم بتصدير procedures و middlewares ستلاحظ أننا نستخدم `superjson` كـ [data transformer](https://trpc.io/docs/v10/data-transformers)، ذلك حتى نحفظ الـ Types لحين إستخدامها في في الـ client، فمثلا إذا كان الـ Type هو Date فإن الـ client سَيُعيد Date ,gds string ### 📄 ملف `server/trpc/router/*.ts` هنا يمكنك تحديد الـ route ,والـ procedure للـ API، من الافضل [أن تُنشئ routers](https://trpc.io/docs/v10/router) مُنفصلة للـ procedures المتقاربة ومن ثَم [دمجها](https://trpc.io/docs/v10/merging-routers) في router واحد في `server/trpc/router/_app.ts` ### 📄 ملف `utils/trpc.ts` هذه هي نقطة دخول الواجهة الأمامية لـ tRPC. هذا هو المكان الذي ستقوم فيه باستيراد **type definition** الخاص بالـ procedure وإنشاء tRPC client الخاص بك جنبًا إلى جنب مع react query hooks. نظرًا لأننا قمنا بتفعيل "superjson" في الواجهة الخلفية فنحن بحاجة إلى تفعيلة على الواجهة الأمامية أيضًا. هذا لان البيانات التي يحدث لها serialized في الـ client يتم عمل deserialized لها في الـ client. هنا تقوم بتحديد [روابط](https://trpc.io/docs/v10/links) الـ tRPC حيث تُُحدد المسار الذي سيمر به الـ request من الـ client إلى الـ server نحن نستخدم [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink) بشكل إفتراضي مع تفعيل [request batching](https://cloud.google.com/compute/docs/api/how-tos/batch) و [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink) وفي الاخير نقوم بتصدير [helper type](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type) حتى نستعمل الـ type infre في الـ frontend ## كيف أستخدم tRPC ؟
ننصحك بمشاهدة هذا[a killer talk at Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) من [trashh_dev](https://twitter.com/trashh_dev) مع tRPCتكتب Function في الـ backend والتي يمكن مناداتها من الـ frontend ```ts:server/trpc/router/user.ts const userRouter = t.router({ getById: t.procedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` في نهاية الأمر تتحول tRPC procedure الي backend عادي فيقوم بفحص الـ input ويمرر الـ request إذا كان صحيحاًويعيد رسالة خطأ إذا كانت المدخلات غير صحيحة. بعد التأكد من صحة البيانات يتم نداء function والتي إما لجلب بيانات ([query](https://trpc.io/docs/v10/react-queries)) أو أن تغير في البانات ([mutation](https://trpc.io/docs/v10/react-mutations)) أنت ```ts:server/trpc/router/_app.ts const appRouter = t.router({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` لاحظ أننا نقوم بعمل export فقط لـ router's type أي أننا لا نستخدم اي من الـ server code في الـ client الان دعنا ننادي الـ procedure من الـ frontend ، tRPC توفر wrapper لمكتبة `@tanstack/react-query` مما يسمح لك بإستخدام المكتبة بكامل قوتها. ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; const UserPage = () => { const { query } = useRouter(); const userQuery = trpc.user.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` ستلاحظ على الفور مدى جودة الإكمال التلقائي والـ typesafety. بمجرد كتابة "trpc." ، ستظهر `router` الخاصة بك في الإكمال التلقائي ، وعندما تحدد الـ `router`، ستظهر الـ procedures. وستحصل أيضًا على خطأ TypeScript إذا كانت المُدخلات الخاص بك لا يتطابق مع الـ schema الذي حددته مسبقا. ## كيف اُنادي API خارجي ؟ باستخدام الـ API العادية ، يمكنك استدعاء الـ End point الخاصة بك باستخدام أي عميل HTTP مثل `curl` أو` Postman` أو `fetch` أو مباشرة من متصفحك. مع tRPC ، الأمر مختلف بعض الشيء. إذا كنت ترغب في الاتصال بالـ procedure بدون عميل tRPC ، فهناك طريقتان موصى بهما للقيام بذلك: ### Expose a single procedure externally إذا أردت أن تُتيح procedure للـ Apis الخارجية الق نظرة علي [server side calls](https://trpc.io/docs/v10/server-side-calls)، مما سيسمح لك بعمل Next.js Api إعتيادية ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### تحول كل الـ Procedures الي REST endpoint ؟ إذا كنت ترغب في كشف كل الـ ؛قخؤثيعقثس ، الق نظرة علي [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). ### It's just HTTP Requests tRPC communicates over HTTP, so it is also possible to call your tRPC procedures using "regular" HTTP requests. However, the syntax can be cumbersome due to the [RPC protocol](https://trpc.io/docs/v10/rpc) that tRPC uses. If you're curious, you can check what tRPC requests and responses look like in your browser's network tab, but we suggest doing this only as an educational exercise and sticking to one of the solutions outlined above. ## Comparison to a Next.js API endpoint دعنا نقارن " Next.js Endpoint" بـ "tRPC procedure". لنفترض أننا نريد جلب "Object" مستخدم معين من قاعدة بياناتنا وإعادته إلى الواجهة الأمامية. يمكننا كتابة Next.js API Endpoint مثل هذا: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db/client"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` قارن هذا بمثال tRPC أعلاه ويمكنك رؤية بعض مزايا tRPC: - بدلاً من تحديد عنوان url لكل مسار ، والذي يمكن أن يصبح مزعجًا إذا حاولت نقل شيء ما ، فإن الـ `router` بأكمله عبارة عن `Object` مع الإكمال التلقائي. - لست بحاجة إلى التحقق من HTTP method التي تم استخدامها. - لا تحتاج إلى التحقق من أن الطلب أو الـ `query` ، لأن Zod يعتني بذلك. - بدلاً من إنشاء الـ responde object ، يمكنك إرجاع أخطاء او قيمة أو Object كما تفعل في أي function. ## snippets مفيدة ### تفعيل CORS ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/trpc/router/_app"; import { createContext } from "~/server/trpc/context"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Enable cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext, })(req, res); }; export default handler; ``` ### Optimistic updates الـ Optimistic updates هي تحديثات تحديث واجهة المستخدم قبل أن ينتهي الـ Request مما يُحسن تجربة المستخدم، لكن التطبيقات التي تُفضل دقة المعلومات يجب أن تتجنب الـ Optimistic updates، للمزيد من المعلومات إقرا [React Query docs](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = trpc.post.list.useQuery(); const utils = trpc.useContext(); const postCreate = trpc.post.create.useMutation({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await utils.post.list.cancel(); // Get the data from the queryCache const prevData = utils.post.list.getData(); // Optimistically update the data with our new post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Return the previous data so we can revert if something goes wrong return { prevData }; }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sync with server once mutation has settled utils.post.list.invalidate(); }, }); }; ``` ### عينة من Integration Test إقرأ [Vitest](https://vitest.dev) ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/router/_app"; import { createContextInner } from "~/server/router/context"; test("example router", async () => { const ctx = await createContextInner({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` ## Useful Resources | Resource | Link | | ---------------------- | ------------------------------------------------------- | | tRPC Docs | https://www.trpc.io | | Bunch of tRPC Examples | https://github.com/trpc/trpc/tree/next/examples | | React Query Docs | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/ar/usage/typescript.md ================================================ --- title: TypeScript description: إستخدام TypeScript layout: ../../../layouts/docs.astro lang: ar dir: rtl ---

Build safety nets, not guard rails

Avatar of @alexdotjs
Theo - creator of the T3 Stack @t3dotgg
سواءََ كُنت مُبرمجا مُبتدئاََ أو مُتمرسًا ، فعلبك أن تتأكد تمام الثقة أن TypeScript أمر لا بد منه. قد تبدو مخيفتاََ في البداية ، ولكنها في الواقع فحالها كحال الأدوات الأخرى، وهي شيء لا يَنظر فية الكثيرون أبدًا بعد البدء في استخدامها. فتُوفر مٌلاحظات مٌباشرة أثناء كِتابة الكود عَن طَريق تَحديد أنواع البيانات المتوقعة ، وأيضا تٌوفر خاصية الإكمال التلقائي في الـ Editor الخاص بك ، أو ربما ستصرخ عَليك بِخطوط مُتعرجة حمراء إذا حاولت الوصول إلى خاصية غير موجودة أو إذا قمت بتمرير قيمة من النوع الخطأ ، التي قد تضطر إلى تصحيحها. ## الـ Type Inference يميل الكثير من مٌطوري Typescript إلى كِتابة الكثير من الـ types وفي واقع الامر أن هذا ليس ضرورياََ أبداََخاصتاََ مع وجود الـ Inference. لكن ... إنتظر لحظة .. ، ما هو الـ Inference اصلا ؟ حسنا .. الـ Inference هو تَتَبُع الـ Types لك في كل مكان في الكود، يُفيدك في أنه يُغنيك عن تِكرار كتابة الـ Types في أماكن أخرى.
الق نظرة علي [you might be using TypeScript wrong](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## إستخدامات الـ type inference ### مكتبة Zod مكتبة [Zod](https://github.com/colinhacks/zod) هي schema validation library تم بِنائوها بـ Typescript. فَقط اٌكتب الـ Schema التي تٌمثل مَصدر الحقيقة الاساسي single source of truth ودع الباقي لـ zod وهي ستتكفل بة. ### مكتبة Tanstack Query تُسهل عليك كِتابة وإدارة الـ queries و الـ mutations مما يٌدوي الي تحسين تجربة المٌطور والمستخدم. ## مصادر مفيدة | المصدر | الرابط | | --------------------------------------------------------- | ----------------------------------------------------------------- | | TypeScript Handbook | https://www.typescriptlang.org/docs/handbook/ | | Beginners TypeScript Tutorial | https://github.com/total-typescript/beginners-typescript-tutorial | | Type Challenges | https://github.com/type-challenges/type-challenges | | Rodney Mullen of TypeScript (Matt Pocock) Youtube Channel | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/ar/why.md ================================================ --- title: لماذا CT3A؟ description: لماذا يجب عليك اختيار Create T3 App لتطبيقك المقبل layout: ../../layouts/docs.astro lang: ar dir: rtl --- لقد أنشئنا Create T3 App لان [Theo](https://twitter.com/t3dotgg) رفض أن يصنع قالب محضَر لتِقنيته المُفضلة. ألهمنا حُبنا لـ typesafety، جَنباََ إلى جَنب مع create-next-app، و [Astro's CLI](https://astro.build)، للعمل عَلى قَدم وساق لصُنع أفضل نُقطة بِداية مُمكنة لمشروعات T3. إذا كُنت مُهتمًا باستخدام Next.js بطريقةِِ آمنة، فَهذا هو المكان المُناسب للبدء. إذا كُنت مهتمًا بأي من تِلك التقنيات، فاقرأ :) ## لماذا TypeScript؟ إن JavaScript صَعبة. لماذا نُضيف المزيد من القوَاعد؟ لأننا نَعتقد اعتقادًا راسخًا أن التَجربة التي توفرها TypeScript سَتدفعك إلى أن تَكون مٌبرمجا أَفضل، فَتوفر لك المُلاحظات مُباشرة خلال كِتابة الكُود من خِلال تحديد أنواع البيانات المُتوقعة (Types)، هذا إلى جانب ميزة الإكمال التلقائي، غَير أنهُ يُنبهك في حَال ارتكبت خطأَ بوضع خط أحمر تحت الخطأ عند المحاولة لوصول إلى خاصية غَير مَوجودة أو تُمرير قِيمة من النوع الخَطأ فَيجبرك أن تصححها. سَواء كُنت مٌبتدئًا في تَطوير الويب أو محترفًا متمرسًا، فإن "صَرامة" TypeScript سَتُوفر لك تَجربة أقل إحباطًا وأكثر اتساقًا من Vanilla JS. هل مازلت غَير مُقتنع إن Typesafety تجعلك أسرع؟ إذاََ شَاهد هَذا [might be using TypeScript wrong…](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## لماذا Next.js؟ نَحن نُحب React، بفضلها أصبح تَطوير واجهات المُستخدم UI سَهل المَنال بَطريقة لم نَكن نَتخيلها. لقد أَخَذَت بِيد المُطورين وعَبرت به أوديةَ سَحيقة وطرقاََ وَعرة. عَلي الرَغم أن Next.js تِقنية عَنيدة قليلاَ، إلا أنها تُقدم تَجربة أقرب إلى الكَمال مع React في شَتى النَواحي، من routing و API حَتي عَرض الصُور، فَنحن نَثق تمام الثِقةٍٍ أن Next.js سَتقود المُطورين إلى الأمام بِقراراتها الحَكيمة. ## لماذا tRPC/Prisma/Tailwind وغيرها؟ بينما نؤمن بالحفاظ عَلى بَساطة الأشياء قَدرَ الإمكان، فإن هُناك أجزاء بَرمجيةَّ صَغيرة تَتكرر في كُل "تَطبيق" مثل المشروعات التي ننشئها، `create-t3-app` تَسمح لك بتبني الجزء بصورةِِ رائعة. ### لماذا tRPC؟ توفر tRPC تجرِبة سلسة في تطوير تطبيقات المستخدم (Client Side) كما في GraphQl دون الحاجة لكتابة كود متداول (boilarplate)، فإنها نستعمل TypeScript قدر ألإمكان لتوفر تجرِبة ممتازة للمطور. ### لماذا Prisma؟ إن Prisma بالنسبة لـ SQL كـ Typescript بالنسبة لـ Javascript، فإنها تخلق تجربة لمطور لم تكن موجودة من قبل، فتولد الأنواع (Types) من Schema يقوم المطور بوضعها، ذلك إلى جانب توافقها مع [العديد من قواعد البيانات](https://www.prisma.io/docs/concepts/database-connectors) فبذلك تضمن Prisma الـ TypeSafety بين التطبيق وقاعدة البيانات توفر Prisma [مجموعة كاملة من الأدوات](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) التي تجعل التفاعلات اليومية مع قاعدة البيانات أسهل. والجدير بالذكر أن Prisma Client مسؤول عن الاستعلام عن SQL وجعله أمرًا سهلاً للغاية لدرجة أنك بالكاد ستلاحظ أنك تستخدمه. ويكون Prisma Studio واجهة مستخدم رسومية (GUI) ملائمة لقاعدة بياناتك التي تتيح لك قراءة بياناتك ومعالجتها بسرعة دون الحاجة إلى كتابة الكود. ### لماذا Tailwind CSS؟ يوفر Tailwind "أسلوب مرن مع CSS" توفر لك Tailwind العناصر الإنشائية اللازمة من الألوان وتباعد وغيرها، فتُسهل أنشاء تطبيق جيد المظهر. وعلى خلاف المكتبات الأخرى فإنها لا تعيقك عندما تريد نقل تطبيقك إلى مستوي اعلي و أنشاء شيئ جميل وفريد من نوعه. بالإضافة، باتخاذها أسلوب كتابة التصميم داخل المكون (inline-like)، تشجعك Tailwind على عمل دون قلق على عبء التسمية، أو تنظيم الملفات، أو مواجهة معدلة ليست بصلة بالمشكلة التي تريد حلها. ### لماذا NextAuth.js؟ إذا كنت تريد نظام توثيق صالحيه في تطبيقك NextJS، فإن NextAuth.js يعد حلاً ممتازًا دون الحاجة إلى إنشاءه بنفسك. فيأتي مزودًا بقائمة واسعة من المزودين (Providers) إضافة مصادقة OAuth بسرعة ويوفر محولات (Adapters) للعديد من قواعد البيانات و ORMs. ================================================ FILE: www/src/pages/branding.astro ================================================ --- import JumpToContent from "../components/accessibility/jumpToContent.astro"; import Asset from "../components/branding/Asset.astro"; import LandingPage from "../layouts/landingPage.astro"; ---

Brand assets

================================================ FILE: www/src/pages/en/deployment/docker.md ================================================ --- title: Docker description: Deployment with Docker layout: ../../../layouts/docs.astro lang: en --- You can containerize this stack and deploy it as a single container using Docker, or as a part of a group of containers using docker-compose. See [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) for an example repo based on this doc. ## Docker Project Configuration Please note that Next.js requires a different process for build time (available in the frontend, prefixed by `NEXT_PUBLIC`) and runtime environment, server-side only, variables. In this demo we are using two variables, pay attention to their positions in the `Dockerfile`, command-line arguments, and `docker-compose.yml`: - `DATABASE_URL` (used by the server) - `NEXT_PUBLIC_CLIENTVAR` (used by the client) ### 1. Next Configuration In your [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), add the `standalone` output-option configuration to [reduce image size by automatically leveraging output traces](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Create dockerignore file
Click here and include contents in .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Create Dockerfile > Since we're not pulling the server environment variables into our container, the [environment schema validation](/en/usage/env-variables) will fail. To prevent this, we have to add a `SKIP_ENV_VALIDATION=1` flag to the build command so that the env-schemas aren't validated at build time.
Click here and include contents in Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:20-alpine AS deps RUN apk add --no-cache libc6-compat openssl WORKDIR /app # Install Prisma Client - remove if not using Prisma COPY prisma ./ # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then npm install -g pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:20-alpine AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then npm install -g pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 gcr.io/distroless/nodejs20-debian12 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static EXPOSE 3000 ENV PORT 3000 CMD ["server.js"] ``` > **_Notes_** > > - _Emulation of `--platform=linux/amd64` may not be necessary after moving to Node 18._ > - _See [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) to understand why `libc6-compat` might be needed._ > - _Using Alpine 3.17 based images [can cause issues with Prisma](https://github.com/t3-oss/create-t3-app/issues/975). Setting `engineType = "binary"` solves the issue in Alpine 3.17, [but has an associated performance cost](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ > - _Next.js collects [anonymous telemetry data about general usage](https://nextjs.org/telemetry). Uncomment the first instance of `ENV NEXT_TELEMETRY_DISABLED 1` to disable telemetry during the build. Uncomment the second instance to disable telemetry during runtime._
## Build and Run Image Locally Build and run this image locally with the following commands: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Open [localhost:3000](http://localhost:3000/) to see your running application. ## Docker Compose You can also use Docker Compose to build the image and run the container.
Follow steps 1-3 above, click here, and include contents in docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Build and run this using the `docker compose up --build` command: ```bash docker compose up --build ``` Open [localhost:3000](http://localhost:3000/) to see your running application.
## Deploy to Railway You can use a PaaS such as [Railway's](https://railway.app) automated [Dockerfile deployments](https://docs.railway.app/deploy/dockerfiles) to deploy your app. If you have the [Railway CLI installed](https://docs.railway.app/develop/cli#install) you can deploy your app with the following commands: ```bash railway login railway init railway link railway up railway open ``` Go to "Variables" and include your `DATABASE_URL`. Then go to "Settings" and select "Generate Domain." To view a running example on Railway, visit [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Useful Resources | Resource | Link | | ------------------------------------ | -------------------------------------------------------------------- | | Dockerfile reference | https://docs.docker.com/engine/reference/builder/ | | Compose file version 3 reference | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Docker CLI reference | https://docs.docker.com/engine/reference/commandline/docker/ | | Docker Compose CLI reference | https://docs.docker.com/compose/reference/ | | Next.js Deployment with Docker Image | https://nextjs.org/docs/deployment#docker-image | | Next.js in Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Next.js with Docker Example | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Create Docker Image of a Next.js app | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/en/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Deployment", layout: "docs", description: "Learn how to deploy your T3 app to production.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/en/deployment/netlify.mdx ================================================ --- title: Netlify description: Deploying to Netlify layout: ../../../layouts/docs.astro lang: en isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Netlify is an alternative deployment provider in a similar vein to Vercel. See [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) for an example repo based on this doc. ## Why Host on Netlify Conventional wisdom says Vercel has superior Next.js support because Vercel develops Next.js. They have a vested interest in ensuring the platform is tuned for optimal performance and DX with Next.js. For the majority of use cases, this will be true and it won't make sense to deviate from the standard path. There's also a common sentiment that many Next.js features are only supported on Vercel. While it's true that new Next.js features will be tested and supported on Vercel at the time of release by default, it's also the case that other providers like Netlify will [quickly implement and release support](https://www.netlify.com/blog/deploy-nextjs-13/) for [stable Next.js features](https://docs.netlify.com/integrations/frameworks/next-js/overview/). There are relative pros and cons for all deployment providers since no single host can have the best support for all use cases. For example, Netlify built their own [custom Next.js runtime](https://github.com/netlify/next-runtime) for Netlify's Edge Functions (which run on Deno Deploy) and [maintain unique middleware to access and modify HTTP responses](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). To track the status of non-stable Next 13 features see [Using the Next 13 `app` directory on Netlify](https://github.com/netlify/next-runtime/discussions/1724). ## Project Configuration There are numerous ways to configure your build instructions including directly through the Netlify CLI or Netlify dashboard. While not required, it is advisable to create and include a [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/) file. This ensures that forked and cloned versions of the project will be easier to reproducibly deploy. ```toml [build] command = "next build" publish = ".next" ``` ## Using the Netlify Dashboard 1. Push your code to a GitHub repository and sign up for [Netlify](https://app.netlify.com/signup). After you've created an account, click on **Add new site** and then **Import an existing project**. ![New project on Netlify](/images/netlify-01-new-project.webp) 2. Connect your Git provider. ![Import repository](/images/netlify-02-connect-to-git-provider.webp) 3. Select your project's repository. ![Select your project's repository](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify will detect if you have a `netlify.toml` file and automatically configure your build command and publish directory. ![Nextjs build settings](/images/netlify-04-configure-build-settings.webp) 5. Click **Show advanced** and then **New variable** to add your environment variables. ![Add environment variables](/images/netlify-05-env-vars.webp) 6. Click **Deploy site**, wait for the build to complete, and view your new site. ## Using the Netlify CLI To deploy from the command line you must first push your project to a GitHub repo and [install the Netlify CLI](https://docs.netlify.com/cli/get-started/). You can install `netlify-cli` as a project dependency or install it globally on your machine with the following command: ```bash npm i -g netlify-cli ``` To test your project locally, run the [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) command and open [`localhost:8888`](http://localhost:8888/) to view your locally running Netlify app: ```bash ntl dev ``` Run the [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) command to configure your project: ```bash ntl init ``` Import your project's environment variables from your `.env` file with [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Deploy your project with [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). You'll need to pass the `--build` flag to run the build command before deployment and the `--prod` flag to deploy to your site's main URL: ```bash ntl deploy --prod --build ``` To view a running example on Netlify, visit [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/en/deployment/vercel.md ================================================ --- title: Vercel description: Deploying to Vercel layout: ../../../layouts/docs.astro lang: en --- We recommend deploying your app to [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). It makes it super easy to deploy Next.js apps. ## Project Configuration Vercel will likely configure your build command and publish the directory automatically. However, you can also specify this information along with other configurations by creating a file called [`vercel.json`](https://vercel.com/docs/project-configuration) and including the following commands. **This is not required for most projects.** ```json { "buildCommand": "npm run build", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Using the Vercel Dashboard 1. After pushing your code to a GitHub repository, sign up for [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) with GitHub and click on **Add New Project**. ![New project on Vercel](/images/vercel-new-project.webp) 2. Import the GitHub repository with your project. ![Import repository](/images/vercel-import-project.webp) 3. Add your environment variables. ![Add environment variables](/images/vercel-env-vars.webp) 4. Click **Deploy**. Now whenever you push a change to your repository, Vercel will automatically redeploy your app! ## Using the Vercel CLI To deploy from the command line you must first [install the Vercel CLI globally](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Run the [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) command to deploy your project. ```bash vercel ``` Include `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` for environment variables like the database connection string. Use `--yes` if you want to skip the deployment questions and give the default answer for each. ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` After the first deployment, this command will deploy to a preview branch. You will need to include `--prod` to push changes directly to the live site for future deployments. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/en/examples.mdx ================================================ --- title: Examples description: Examples of different live apps layout: ../../layouts/docs.astro lang: en isMdx: true --- import Callout from "../../components/docs/callout.tsx"; import Form from "../../components/docs/exampleOptionForm.astro"; You can try out different combinations of technologies that create-t3-app offers. You cannot select `prisma` and `drizzle` at the same time. Some features might not work unless you create an env file ================================================ FILE: www/src/pages/en/faq.mdx ================================================ --- title: FAQ description: Frequently asked questions about Create T3 App layout: ../../layouts/docs.astro lang: en isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Here are some commonly asked questions about Create T3 App. ## What's next? How do I make an app with this? We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary. If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) - [Drizzle](https://orm.drizzle.team/docs/overview) ## How do I keep my app up to date? Create T3 App is a scaffolding tool, not a framework. This means that once you initialize an app, it's yours. There is no postinstall CLI tool similar to help you stay up to date. If you want to keep track of any improvements we make to the template, you could [enable notifications for releases](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository) on our repository. That being said it is not really necessary to implement every change we make to the template in your app. ## What learning resources are currently available? Although the resources listed below are some of the best that exist for the T3 Stack, the community (and [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) recommend that you just start using the stack and learn along the way by building with it. If you are considering Create T3 App, chances are you might have already used some of the parts of the stack. So why not just dive in head first and learn the other parts while you build something? Now, we realize this path doesn't work for everyone. So, if you feel like you've tried the recommendation and would still like some resources, or you just aren't confident doing it by yourself and/or feel overwhelmed by the stack, checkout these awesome tutorials on Create T3 App: ### Articles Some of these might be outdated. - [A first look at Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Integrating Stripe into your T3 App](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Videos - [From 0 to Production - The Modern React Tutorial (RSCs, Next.js, Shadui, Drizzle, TS and more)](https://www.youtube.com/watch?v=d5x0JCZbAJs) **(recommended)** (updated 2024)* - [Jack Herrington - Build a Note Taking app with the T3 Stack](https://www.youtube.com/watch?v=J1gzN1SAhyM) - [Build a Twitter Clone with the T3 Stack - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [An overview of the Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Why are there `.js` files in the project? As per [T3-Axiom #3](/en/introduction#typesafety-isnt-optional), we treat typesafety as a first class citizen. Unfortunately, not all frameworks and plugins support TypeScript which means some of the configuration files have to be `.js` files. We try to emphasize that these files are JavaScript for a reason, by explicitly declaring each file's type (`cjs` or `mjs`) depending on what's supported by the library it is used by. Also, all the `js` files in this project are still typechecked using a checkJs option in the compiler (tsconfig). ## I'm struggling to add i18n to my app. Is there any reference I can use? We have decided against including i18n by default in `create-t3-app` because it's a very opinionated topic and there are many ways to implement it. However, if you struggle to implement it and want to see a reference project, we have a [reference repo](https://github.com/juliusmarminge/t3-i18n) that shows how you can add i18n to a T3 App using [next-i18next](https://github.com/i18next/next-i18next). ## Should I use `/app` from Next.js 13 or the more mature `/pages` paradigm? You have the option when scaffolding an app using Create T3 App to opt into the `/app` directory structure. As of the time of writing, this feature is generally considered mature enough to be used in production by the T3 stack community. For an explanation of why it took us so long to include, you can watch [this video](https://www.youtube.com/watch?v=PmBfq-SpzCU). Nevertheless, if you strongly prefer to use the old `/pages` paradigm, that's still an option. Porting over your existing router can be a monumental effort, so do not feel undue pressure to do so unnecessarily. For a list of supported, planned, and worked on features in the `/app` dir, visit the [beta Next.js docs](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/en/folder-structure-app.mdx ================================================ --- title: Folder Structure (App) description: Folder structure of a newly scaffolded T3 App with the App Router layout: ../../layouts/docs.astro lang: en isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramApp.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Please select your packages to see the folder structure of a newly scaffolded app with those selections. Further down, you will find a description of each entry.
### `prisma` The `prisma` folder contains the `schema.prisma` file which is used to configure the database connection and the database schema. It is also the location to store migration files and/or seed scripts, if used. See [Prisma usage](/en/usage/prisma) for more information.
### `public` The `public` folder contains static assets that are served by the web server. The `favicon.ico` file is an example of a static asset.
### `src/env` Used for environment variable validation and type definitions - see [Environment Variables](usage/env-variables).
### `src/app` The `app` folder contains all the routes of the Next.js application. The `page.tsx` file at the root directory of `/app` is the homepage of the application. The `layout.tsx` file is used to wrap the application with providers. See [Next.js documentation](https://nextjs.org/docs/basic-features/pages) for more information.
#### `src/app/_components/post.tsx` The `post.tsx` file is an example of a client component that calls a tRPC mutation.
#### `src/app/api/auth/[...nextauth]/route.ts` The `[...nextauth]/route.ts` file is the NextAuth.js authentication slug route. It is used to handle authentication requests. See [NextAuth.js usage](usage/next-auth) for more information on NextAuth.js, and [Next.js Dynamic Routes Docs](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) for info on catch-all/slug routes.
#### `src/app/api/trpc/[trpc]/route.ts` The `[trpc].ts` file is the tRPC API entrypoint. It is used to handle tRPC requests. See [tRPC usage](usage/trpc#-pagesapitrpctrpcts) for more information on this file, and [Next.js Dynamic Routes Docs](https://nextjs.org/docs/app/routing/dynamic-routes) for info on catch-all/slug routes.
### `src/server` The `server` folder is used to clearly separate code that is only used on the server.
#### `src/server/auth.ts` The main entrypoint for server-side authentication logic. Here, we setup the NextAuth.js [configuration options](usage/next-auth), perform [module augmentation](usage/next-auth#inclusion-of-userid-on-the-session) as well as provide some DX utilities for authentication such as retrieving the user's session on the server-side. See [NextAuth.js usage](usage/next-auth#usage-with-trpc) for more information.
#### `src/server/db.ts` The `db.ts` file is used to instantiate the Prisma client at global scope. See [Prisma usage](usage/prisma#prisma-client) and [best practices for using Prisma with Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) for more information.
### `src/server/db` The `db` folder contains the Drizzle client and schema. Note that drizzle also requires the `drizzle.config.ts` file (see below).
#### `src/server/db/index.ts` The `index.ts` file is used to instantiate the Drizzle client at global scope. See [Drizzle usage](usage/drizzle#drizzle-client) for more information.
#### `src/server/db/schema.ts` The `schema.ts` file is used to define the database schema. See [Drizzle usage](usage/drizzle#drizzle-client) and [Drizzle schema docs](https://orm.drizzle.team/docs/sql-schema-declaration) for more information.
### `src/server/api` The `api` folder contains the tRPC server-side code.
#### `src/server/api/routers` The `routers` folder contains all your tRPC sub-routers.
#### `src/server/api/routers/example.ts` The `example.ts` file is an example tRPC router utilizing the `publicProcedure` helper to demonstrate how to create a public tRPC route. Depending on your chosen packages this router contains more or less routes to best demonstrate the usage to your needs.
#### `src/server/api/trpc.ts` The `trpc.ts` file is the main configuration file for your tRPC back-end. In here we: 1. Define context used in tRPC requests. See [tRPC usage](usage/trpc#-serverapitrpcts) for more information. 2. Export procedure helpers. See [tRPC usage](usage/trpc#-serverapitrpcts) for more information.
#### `src/server/api/root.ts` The `root.ts` file is used to merge tRPC routers and export them as a single router, as well as the router's type definition. See [tRPC usage](usage/trpc#-serverapirootts) for more information.
### `src/trpc` The `trpc` folder contains the setup to let you call tRPC procedures from server components and client components.
#### `src/trpc/query-client.ts` The `query-client.ts` file creates the Query Client that tRPC uses to cache and deduplicate data in client components.
#### `src/trpc/react.tsx` The `react.tsx` file is the front-end entrypoint to tRPC. It also contains utility types for the router inputs and outputs. See [tRPC usage](usage/trpc#-utilsapits) for more information.
#### `src/trpc/server.ts` The `server.ts` file is the entrypoint for using tRPC in Server Components.
### `.env` The `.env` file is used to store environment variables. See [Environment Variables](usage/env-variables) for more information. This file should **not** be committed to git history.
### `.env.example` The `.env.example` file shows example environment variables based on the chosen libraries. This file should be committed to git history.
### `.eslintrc.cjs` The `.eslintrc.cjs` file is used to configure ESLint. See [ESLint Docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) for more information.
### `db.sqlite (sqlite only)` The `db.sqlite` file contains your development database. This file is only created after running the `db:push` parseCommandLine, and ignored by git.
### `drizzle.config.ts` The `drizzle.config.ts` file is used to configure drizzle kit. See [the documentation](https://orm.drizzle.team/kit-docs/config-reference) for more information.
### `next-env.d.ts` The `next-env.d.ts` file ensures Next.js types are picked up by the TypeScript compiler. **You should not remove it or edit it as it can change at any time.** See [Next.js Docs](https://nextjs.org/docs/app/building-your-application/configuring/typescript) for more information.
### `next.config.mjs` The `next.config.mjs` file is used to configure Next.js. See [Next.js Docs](https://nextjs.org/docs/api-reference/next.config.js/introduction) for more information. Note: The .mjs extension is used to allow for ESM imports.
### `postcss.config.js` The `postcss.config.js` file is used for Tailwind PostCSS usage. See [Tailwind PostCSS Docs](https://tailwindcss.com/docs/installation/using-postcss) for more information.
### `prettier.config.mjs` The `prettier.config.mjs` file is used to configure Prettier to include the prettier-plugin-tailwindcss for formatting Tailwind CSS classes. See the [Tailwind CSS blog post](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) for more information.
### `start-database.sh (mysql or postgres only)` The `start-database.sh` file is used to start the database. Please see the comments inside the file for information on how to start the database with your operating system.
### `tsconfig.json` The `tsconfig.json` file is used to configure TypeScript. Some non-defaults, such as `strict mode`, have been enabled to ensure the best usage of TypeScript for Create T3 App and its libraries. See [TypeScript Docs](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) or [TypeScript Usage](usage/typescript) for more information.
================================================ FILE: www/src/pages/en/folder-structure-pages.mdx ================================================ --- title: Folder Structure (Pages) description: Folder structure of a newly scaffolded T3 App with the Pages Router layout: ../../layouts/docs.astro lang: en isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Please select your packages to see the folder structure of a newly scaffolded app with those selections. Further down, you will find a description of each entry.
### `prisma` The `prisma` folder contains the `schema.prisma` file which is used to configure the database connection and the database schema. It is also the location to store migration files and/or seed scripts, if used. See [Prisma usage](/en/usage/prisma) for more information.
### `public` The `public` folder contains static assets that are served by the web server. The `favicon.ico` file is an example of a static asset.
### `src/env` Used for environment variable validation and type definitions - see [Environment Variables](usage/env-variables).
### `src/pages` The `pages` folder contains all the pages of the Next.js application. The `index.tsx` file at the root directory of `/pages` is the homepage of the application. The `_app.tsx` file is used to wrap the application with providers. See [Next.js documentation](https://nextjs.org/docs/basic-features/pages) for more information.
#### `src/pages/api` The `api` folder contains all the API routes of the Next.js application. See [Next.js Api Routes Docs](https://nextjs.org/docs/api-routes/introduction) for info on api routes.
#### `src/pages/api/auth/[...nextauth].ts` The `[...nextauth].ts` file is the NextAuth.js authentication slug route. It is used to handle authentication requests. See [NextAuth.js usage](usage/next-auth) for more information on NextAuth.js, and [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) for info on catch-all/slug routes.
#### `src/pages/api/trpc/[trpc].ts` The `[trpc].ts` file is the tRPC API entrypoint. It is used to handle tRPC requests. See [tRPC usage](usage/trpc#-pagesapitrpctrpcts) for more information on this file, and [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) for info on catch-all/slug routes.
### `src/server` The `server` folder is used to clearly separate code that is only used on the server.
#### `src/server/auth.ts` The main entrypoint for server-side authentication logic. Here, we setup the NextAuth.js [configuration options](usage/next-auth), perform [module augmentation](usage/next-auth#inclusion-of-userid-on-the-session) as well as provide some DX utilities for authentication such as retrieving the user's session on the server-side. See [NextAuth.js usage](usage/next-auth#usage-with-trpc) for more information.
#### `src/server/db.ts` The `db.ts` file is used to instantiate the Prisma client at global scope. See [Prisma usage](usage/prisma#prisma-client) and [best practices for using Prisma with Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) for more information.
### `src/server/db` The `db` folder contains the Drizzle client and schema. Note that drizzle also requires the `drizzle.config.ts` file (see below).
#### `src/server/db/index.ts` The `index.ts` file is used to instantiate the Drizzle client at global scope. See [Drizzle usage](usage/drizzle#drizzle-client) for more information.
#### `src/server/db/schema.ts` The `schema.ts` file is used to define the database schema. See [Drizzle usage](usage/drizzle#drizzle-client) and [Drizzle schema docs](https://orm.drizzle.team/docs/sql-schema-declaration) for more information.
### `src/server/api` The `api` folder contains the tRPC server-side code.
#### `src/server/api/routers` The `routers` folder contains all your tRPC sub-routers.
#### `src/server/api/routers/example.ts` The `example.ts` file is an example tRPC router utilizing the `publicProcedure` helper to demonstrate how to create a public tRPC route. Depending on your chosen packages this router contains more or less routes to best demonstrate the usage to your needs.
#### `src/server/api/trpc.ts` The `trpc.ts` file is the main configuration file for your tRPC back-end. In here we: 1. Define context used in tRPC requests. See [tRPC usage](usage/trpc#-serverapitrpcts) for more information. 2. Export procedure helpers. See [tRPC usage](usage/trpc#-serverapitrpcts) for more information.
#### `src/server/api/root.ts` The `root.ts` file is used to merge tRPC routers and export them as a single router, as well as the router's type definition. See [tRPC usage](usage/trpc#-serverapirootts) for more information.
### `src/styles` The `styles` folder contains the global styles of the application.
### `src/utils` The `utils` folder is used to store commonly re-used utility functions.
#### `src/utils/api.ts` The `api.ts` file is the front-end entrypoint to tRPC. See [tRPC usage](usage/trpc#-utilsapits) for more information.
### `.env` The `.env` file is used to store environment variables. See [Environment Variables](usage/env-variables) for more information. This file should **not** be committed to git history.
### `.env.example` The `.env.example` file shows example environment variables based on the chosen libraries. This file should be committed to git history.
### `.eslintrc.cjs` The `.eslintrc.cjs` file is used to configure ESLint. See [ESLint Docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) for more information.
### `db.sqlite (sqlite only)` The `db.sqlite` file contains your development database. This file is only created after running the `db:push` parseCommandLine, and ignored by git.
### `drizzle.config.ts` The `drizzle.config.ts` file is used to configure drizzle kit. See [the documentation](https://orm.drizzle.team/kit-docs/config-reference) for more information.
### `next-env.d.ts` The `next-env.d.ts` file ensures Next.js types are picked up by the TypeScript compiler. **You should not remove it or edit it as it can change at any time.** See [Next.js Docs](https://nextjs.org/docs/basic-features/typescript#existing-projects) for more information.
### `next.config.mjs` The `next.config.mjs` file is used to configure Next.js. See [Next.js Docs](https://nextjs.org/docs/api-reference/next.config.js/introduction) for more information. Note: The .mjs extension is used to allow for ESM imports.
### `postcss.config.js` The `postcss.config.js` file is used for Tailwind PostCSS usage. See [Tailwind PostCSS Docs](https://tailwindcss.com/docs/installation/using-postcss) for more information.
### `prettier.config.mjs` The `prettier.config.mjs` file is used to configure Prettier to include the prettier-plugin-tailwindcss for formatting Tailwind CSS classes. See the [Tailwind CSS blog post](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) for more information.
### `start-database.sh (mysql or postgres only)` The `start-database.sh` file is used to start the database. Please see the comments inside the file for information on how to start the database with your operating system.
### `tsconfig.json` The `tsconfig.json` file is used to configure TypeScript. Some non-defaults, such as `strict mode`, have been enabled to ensure the best usage of TypeScript for Create T3 App and its libraries. See [TypeScript Docs](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) or [TypeScript Usage](usage/typescript) for more information.
================================================ FILE: www/src/pages/en/installation.mdx ================================================ --- title: Installation description: Installation instructions for Create T3 App layout: ../../layouts/docs.astro lang: en isMdx: true --- import Callout from "../../components/docs/callout.tsx"; To scaffold an app using `create-t3-app`, run any of the following commands and answer the command prompt questions: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` After your app has been scaffolded, check out the [first steps](/en/usage/first-steps) to get started on your new application. ## Advanced usage | Option/Flag | Description | | ----------------- | ----------------------------------------------------------------------- | | `[dir]` | Include a directory argument with a name for the project | | `--noGit` | Explicitly tell the CLI to not initialize a new git repo in the project | | `-y`, `--default` | Bypass the CLI and bootstrap a new t3-app with all options selected | | `--noInstall` | Generate project without installing dependencies | ## Experimental usage For our CI, we have some experimental flags that allow you to scaffold any app without any prompts. If this use case applies to you, you can use these flags. Please note that these flags are experimental and may change in the future without following semver versioning. | Flag | Description | | ------------------------- | -------------------------------------------- | | `--CI` | Let the CLI know you're in CI mode | | `--trpc` | Include tRPC in the project | | `--prisma` | Include Prisma in the project | | `--drizzle` | Include Drizzle in the project | | `--nextAuth` | Include NextAuth.js in the project | | `--tailwind` | Include Tailwind CSS in the project | | `--dbProvider [provider]` | Include a configured database in the project | | `--appRouter` | Use Next.js App Router in the project | If you don't provide the `CI` flag, the rest of these flags have no effect. You don't need to explicitly opt-out of the packages you don't want. However, if you prefer to be explicit, you can pass `false`, e.g. `--nextAuth false`. The --dbProvider command has 4 database values to choose from: mysql, postgres, planetscale, sqlite. If the command is not provided the default value will be sqlite. ### Example The following would scaffold a T3 App with tRPC and Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` The following would scaffold a T3 App with NextAuth.js, Tailwind CSS, Drizzle, and PostgreSQL. ```bash pnpm dlx create-t3-app@latest --CI --nextAuth --tailwind --drizzle --dbProvider postgres ``` ================================================ FILE: www/src/pages/en/introduction.mdx ================================================ --- title: Introduction description: Introduction to the T3 Stack layout: ../../layouts/docs.astro lang: en --- import { IntroductionTab } from "../../components/docs/introductionTab"; ## The T3 Stack The _"T3 Stack"_ is a web development stack made by [Theo](https://twitter.com/t3dotgg) focused on simplicity, modularity, and full-stack typesafety. The core pieces are [**Next.js**](https://nextjs.org/) and [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) is almost always included. If you're doing anything resembling backend, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), and [**NextAuth.js**](https://next-auth.js.org/) are great additions too. You may have noticed that there are a… lot of pieces. That's by design. Swap pieces in and out as you need - this stack is modular at its core :) ## So... what is create-t3-app? A template? Kind of? `create-t3-app` is a CLI built by seasoned T3 Stack devs to streamline the setup of a modular T3 Stack app. This means each piece is optional, and the "template" is generated based on your specific needs. After countless projects and many years on this tech, we have lots of opinions and insights. We've done our best to encode them into this CLI. This is **NOT** an all-inclusive template. We **expect** you to bring your own libraries that solve the needs of **YOUR** application. While we don't want to prescribe solutions to more specific problems like state management and deployment, we [do have some recommendations listed here](/en/other-recs). ## T3 Axioms We'll be frank - this is an _opinionated project_. We share a handful of core beliefs around building and we treat them as the basis for our decisions. ### Solve Problems It's easy to fall into the trap of "adding everything" - we explicitly don't want to do that. Everything added to `create-t3-app` should solve a specific problem that exists within the core technologies included. This means we won't add things like state libraries (`zustand`, `redux`) but we will add things like NextAuth.js and integrate Prisma and tRPC for you. ### Bleed Responsibly We love our bleeding edge tech. The amount of speed and, honestly, fun that comes out of new shit is really cool. We think it's important to bleed responsibly, using riskier tech in the less risky parts. This means we wouldn't ⛔️ bet on risky new database tech (SQL is great!). But we happily ✅ bet on tRPC since it's just functions that are trivial to move off. ### Typesafety Isn't Optional The stated goal of Create T3 App is to provide the quickest way to start a new full-stack, **typesafe** web application. We take typesafety seriously in these parts as it improves our productivity and helps us ship fewer bugs. Any decision that compromises the typesafe nature of Create T3 App is a decision that should be made in a different project. ================================================ FILE: www/src/pages/en/other-recs.md ================================================ --- title: Other Recommendations description: Libraries and Services that we recommend for many projects layout: ../../layouts/docs.astro lang: en --- We recognize that the libraries included in `create-t3-app` don't solve every problem. While we encourage you to begin your project with the things that we provide, there will come a time when you need to bring in other packages. Only you can know what your project needs, but here are some things that we find ourselves recommending frequently. These are recommendations by individual Create T3 App contributors and should not be seen as "official" endorsements by the Create T3 App team or T3-OSS. _**Please do your own research, especially before committing to paid services**_. ## State Management _**Editor's Note**_: State management libraries can be great, but often aren't necessary. tRPC's React Query hooks should be able to take care of your server state. For client state, start with React's `useState`, and reach for one of these options when you need more. ### Zustand **For never using Redux again** The "modern, simple Redux" you didn't know you needed. [Poimandres](https://github.com/pmndrs) can always be trusted. You can build everything from video call apps to games to servers with this little library. - [Zustand Homepage](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **For never using Context again** For a more atomic approach, Jotai is hard to beat. Also by [Poimandres](https://github.com/pmndrs), Jotai lets you define singletons that feel like global useState. A great option for stateful behaviors that don't need a state machine just yet. - [Jotai Homepage](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## Component Libraries Most apps need the same handful of components - toggle buttons, dropdown menus, modals, and so on. These libraries provide great, accessible components that you can use and customize to your liking. ### Unstyled Component Libraries Also known as headless libraries, they provide great unstyled, and accessible components that you can customize to your liking. Here are a few recommendations. - [Radix UI](https://www.radix-ui.com/) gives you a powerful set of convenient and accessible primitives that you can style with vanilla or Tailwind CSS. - [Headless UI](https://headlessui.com/) made by the Tailwind CSS team also provides unstyled, accessible components that integrate seamlessly with Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) provides accessible UI primitives for your design system. Their Date Picker component is top tier. ### Styled Component Libraries **For when you just want your app to look OK** Sometimes you're building a project where you just want the UI to look decent out of the box. For Admin Dashboards and other similar projects, any of these component libraries will get the job done. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **For building UI Libraries** Declaratively build a UI Library with different color, size, etc. variants. When your project reaches a scale where you want a standardized set of UI components with multiple variants using Tailwind CSS, CVA is a great tool. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Animations For when you need animations in your app, here are our recommendations. ### AutoAnimate **For animations with a single line of code** Most animation libraries try to satisfy every possible use case, and become clunky as a result. AutoAnimate is a zero-configuration tool that will give you a significant improvement in UX with no additional developer effort. - [AutoAnimate Homepage](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [AutoAnimate Component Snippet](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **For complex animations with declarative code** Framer Motion provides a simple, declarative syntax and allows you to write less code to craft everything from complex animations to even gestures. - [Framer Motion Homepage](https://framer.com/motion) - [Framer Motion Documentation](https://www.framer.com/docs/) ## Deployments, Infrastructure, Databases and CI ### Vercel **For hosting your app** Vercel took the hell of web deployments and made it a set-and-forget GitHub integration. We've scaled to hundreds of thousands of users without issue. AWS-powered, just a way better interface :) - [Vercel Homepage](https://vercel.com/) - [Create T3 App Vercel deployment guide](/en/deployment/vercel) ### PlanetScale **For databases without the worry** PlanetScale is the best "serverless database platform" we've used by far. Insane scale, great developer experience, and fantastic pricing. If you're using SQL (and hopefully Prisma), this is hard to beat. - [PlanetScale Homepage](https://planetscale.com/) ### Railway **For hosting your infra** "Modern Heroku". The easiest way to get a real server up and running. If Vercel and PlanetScale aren't enough, Railway probably is. Point it at a GitHub repo and go. - [Railway Homepage](https://railway.app/) ### Upstash **For serverless Redis** We love Prisma and PlanetScale, but some projects require a more performant solution. Upstash allows you to get the in-memory performance of Redis in your serverless project, without having to manage the infrastructure and scaling yourself. - [Upstash Homepage](https://upstash.com/) ### Pusher **For serverless WebSockets** If WebSockets are the primary focus of your project, you may want to consider a more traditional backend such as [Fastify](https://www.fastify.io/) (which [also works with tRPC!](https://trpc.io/docs/v10/fastify)). But for quickly adding WebSockets to a T3 App, Pusher is an excellent choice. - [Pusher Homepage](https://pusher.com/) ### Soketi Soketi is a self-hostable, simple, and fast alternative to Pusher. It's fully compatible with the Pusher SDK which you can use to connect to the server. Soketi serverless is also in beta. - [Soketi Homepage](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Analytics User data is very valuable when you're building an app. Here are some analytics providers we recommend. ### PostHog PostHog is a full-featured, open-source, and self-hostable solution for bringing in depth analytics to your product. They have SDKs for every library/framework imaginable. - [PostHog Homepage](https://posthog.com/) ### Plausible Need analytics? Plausible is one of the quickest ways to get them. Super minimal. It even has a [simple plugin for Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Plausible Homepage](https://plausible.io/) ### Umami Umami is an open-sourced, self-hostable, simple, fast, privacy-focused alternative to Google Analytics. You can deploy it really easily to Vercel, Railway, etc. with PlanetScale as your database or you can also use its cloud version. - [Umami Homepage](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) - [Umami Cloud](https://cloud.umami.is/) ## Other ### Next Bundle Analyzer It can sometimes be difficult to determine what will be included in the build output for your app. Next Bundle Analyzer is an easy way to visualize and analyze the JavaScript bundles that are generated. - [@next/bundle-analyzer on npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/en/t3-collection.mdx ================================================ --- title: T3 Collection description: Cool open source projects and companies using the T3 stack layout: ../../layouts/docs.astro lang: en isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import Callout from "../../components/docs/callout.tsx"; Made a project using the T3 stack and want to share it? Add it to the list! ## Open Source apps made using the T3 Stack ## Companies using the T3 Stack We'd love to know of companies that use the T3 stack for their apps. Is your company using the T3 stack and would like to share it? Add it to the list! Have a cool project using the T3 stack? Make a [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) and add it here! ================================================ FILE: www/src/pages/en/usage/_next-auth-app-router.mdx ================================================ import Callout from "../../../components/docs/callout.tsx"; import Tabs from "../../../components/docs/tabs.astro"; The newest version of NextAuth has migrated to [Auth.js](https://authjs.dev/) ## Retrieving session server side Sometimes you might want to request the session on the server. To do so, use the `auth` helper function that `create-t3-app` provides. ```tsx:app/page.tsx import { auth } from "~/server/auth"; export default async function Home() { const session = await auth(); ... } ``` ## Inclusion of `user.id` on the Session Create T3 App is configured to utilise the [session callback](https://authjs.dev/guides/extending-the-session) in the NextAuth.js config to include the user's ID within the `session` object. ```ts:server/auth/config.ts callbacks: { session: ({ session, user }) => ({ ...session, user: { ...session.user, id: user.id, }, }), }, ``` This is coupled with a type declaration file to make sure the `user.id` is typed when accessed on the `session` object. Read more about [`Module Augmentation`](https://authjs.dev/getting-started/typescript#resources*module-augmentation) on NextAuth.js's docs. ```ts:server/auth/config.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session extends DefaultSession { user: { id: string; } & DefaultSession["user"]; } ``` The same pattern can be used to add any other data to the `session` object, such as a `role` field, but **should not be misused to store sensitive data** on the client. ## Usage with tRPC When using NextAuth.js with tRPC, you can create reusable, protected procedures using [middleware](https://trpc.io/docs/v10/middlewares). This allows you to create procedures that can only be accessed by authenticated users. `create-t3-app` sets all of this up for you, allowing you to easily access the session object within authenticated procedures. This is done in a two step process: 1. Pass the authentication session into the tRPC context: ```ts:server/api/trpc.ts import { auth } from "~/server/auth"; import { db } from "~/server/db"; export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth(); return { db, session, ...opts, }; }; ``` 2. Create a tRPC middleware that checks if the user is authenticated. We then use the middleware in a `protectedProcedure`. Any caller to these procedures must be authenticated, or else an error will be thrown which can be appropriately handled by the client. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure .use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { session: { ...ctx.session, user: ctx.session.user }, }, }); }); ``` The session object is a light, minimal representation of the user and only contains a few fields. When using the `protectedProcedures`, you have access to the user's id which can be used to fetch more data from the database. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Usage with a database provider
Getting NextAuth.js to work with Prisma requires a lot of [initial setup](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` handles all of this for you, and if you select both Prisma and NextAuth.js, you'll get a fully working authentication system with all the required models preconfigured. We ship your scaffolded app with a preconfigured Discord OAuth provider, which we chose because it is one of the easiest to get started with - just provide your tokens in the `.env` and you're good to go. However, you can easily add more providers by following the [Auth.js docs](https://authjs.dev/getting-started/authentication/oauth). Note that certain providers require extra fields to be added to certain models. We recommend you read the documentation for the provider you would like to use to make sure you have all the required fields.
Getting NextAuth.js to work with Drizzle requires a lot of [initial setup](https://authjs.dev/getting-started/adapters/drizzle). `create-t3-app` handles all of this for you, and if you select both Drizzle and NextAuth.js, you'll get a fully working authentication system with all the required models preconfigured. We ship your scaffolded app with a preconfigured Discord OAuth provider, which we chose because it is one of the easiest to get started with - just provide your tokens in the `.env` and you're good to go. However, you can easily add more providers by following the [Auth.js docs](https://authjs.dev/getting-started/authentication/oauth). Note that certain providers require extra fields to be added to certain models. We recommend you read the documentation for the provider you would like to use to make sure you have all the required fields.
### Adding new fields to your models When adding new fields to any of the `User`, `Account`, `Session`, or `VerificationToken` models (most likely you'd only need to modify the `User` model), you need to keep in mind that the [Prisma adapter](https://authjs.dev/reference/adapter/prisma/) automatically creates fields on these models when new users sign up and log in. Therefore, when adding new fields to these models, you must provide default values for them, since the adapter is not aware of these fields. If for example, you'd like to add a `role` to the `User` model, you would need to provide a default value to the `role` field. This is done by adding a `@default` value to the `role` field in the `User` model: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Usage with Next.js middleware With Next.js 12+, the easiest way to protect a set of pages is using the [middleware file](https://authjs.dev/getting-started/session-management/protecting?framework=express#nextjs-middleware). You can create a middleware.ts file in your root pages directory with the following contents. ```middleware.ts export { auth as middleware } from "@/auth" ``` Then define authorized callback in your auth.ts file. For more details check out the [reference docs.](https://authjs.dev/reference/nextjs#authorized) ```app/auth.ts async authorized({ request, auth }) { const url = request.nextUrl if(request.method === "POST") { const { authToken } = (await request.json()) ?? {} // If the request has a valid auth token, it is authorized const valid = await validateAuthToken(authToken) if(valid) return true return NextResponse.json("Invalid auth token", { status: 401 }) } // Logged in users are authenticated, otherwise redirect to login page return !!auth.user } ``` You should not rely on middleware exclusively for authorization. Always ensure that the session is verified as close to your data fetching as possible. ## Setting up the default DiscordProvider 1. Head to [the Applications section in the Discord Developer Portal](https://discord.com/developers/applications), and click on "New Application" 2. In the settings menu, go to "OAuth2 => General" - Copy the Client ID and paste it in `DISCORD_CLIENT_ID` in `.env`. - Under Client Secret, click "Reset Secret" and copy that string to `DISCORD_CLIENT_SECRET` in `.env`. Be careful as you won't be able to see this secret again, and resetting it will cause the existing one to expire. - Click "Add Redirect" and paste in `/api/auth/callback/discord` (example for local development: http://localhost:3000/api/auth/callback/discord) - Save your changes - It is possible, but not recommended, to use the same Discord Application for both development and production. You could also consider [Mocking the Provider](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) during development. ## Useful Resources | Resource | Link | | --------------------------------- | --------------------------------------- | | NextAuth.js Docs | https://authjs.dev/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/en/usage/_next-auth-pages.mdx ================================================ import Callout from "../../../components/docs/callout.tsx"; ## Context Provider In your app's entrypoint, you'll see that your application is wrapped in a [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx ``` This context provider allows your application to access the session data from anywhere in your application, without having to pass it down as props: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Handle unauthenticated state, e.g. render a SignIn component return ; } return

Welcome {session.user.name}!

; }; ``` ## Retrieving session server-side Sometimes you might want to request the session on the server. To do so, prefetch the session using the `getServerAuthSession` helper function that `create-t3-app` provides, and pass it down to the client using `getServerSideProps`: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // NOTE: `session` wont have a loading state since it's already prefetched on the server ... } ``` ## Inclusion of `user.id` on the Session Create T3 App is configured to utilise the [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) in the NextAuth.js config to include the user's ID within the `session` object. ```ts:server/auth.ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` This is coupled with a type declaration file to make sure the `user.id` is typed when accessed on the `session` object. Read more about [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) on NextAuth.js's docs. ```ts:server/auth.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` The same pattern can be used to add any other data to the `session` object, such as a `role` field, but **should not be misused to store sensitive data** on the client. ## Usage with tRPC When using NextAuth.js with tRPC, you can create reusable, protected procedures using [middleware](https://trpc.io/docs/v10/middlewares). This allows you to create procedures that can only be accessed by authenticated users. `create-t3-app` sets all of this up for you, allowing you to easily access the session object within authenticated procedures. This is done in a two step process: 1. Grab the session from the request headers using the [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession) function. The advantage of using `getServerSession` instead of the regular `getSession` is that it's a server-side only function and doesn't trigger unnecessary fetch calls. `create-t3-app` creates a helper function that abstracts this peculiar API away so that you don't need to import both your NextAuth.js options as well as the `getServerSession` function every time you need to access the session. ```ts:server/auth.ts export const getServerAuthSession = (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); }; ``` Using this helper function, we can grab the session and pass it through to the tRPC context: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Create a tRPC middleware that checks if the user is authenticated. We then use the middleware in a `protectedProcedure`. Any caller to these procedures must be authenticated, or else an error will be thrown which can be appropriately handled by the client. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }) ``` The session object is a light, minimal representation of the user and only contains a few fields. When using the `protectedProcedures`, you have access to the user's id which can be used to fetch more data from the database. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Usage with Prisma Getting NextAuth.js to work with Prisma requires a lot of [initial setup](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` handles all of this for you, and if you select both Prisma and NextAuth.js, you'll get a fully working authentication system with all the required models preconfigured. We ship your scaffolded app with a preconfigured Discord OAuth provider, which we chose because it is one of the easiest to get started with - just provide your tokens in the `.env` and you're good to go. However, you can easily add more providers by following the [NextAuth.js docs](https://next-auth.js.org/providers/). Note that certain providers require extra fields to be added to certain models. We recommend you read the documentation for the provider you would like to use to make sure you have all the required fields. ### Adding new fields to your models When adding new fields to any of the `User`, `Account`, `Session`, or `VerificationToken` models (most likely you'd only need to modify the `User` model), you need to keep in mind that the [Prisma adapter](https://next-auth.js.org/adapters/prisma) automatically creates fields on these models when new users sign up and log in. Therefore, when adding new fields to these models, you must provide default values for them, since the adapter is not aware of these fields. If for example, you'd like to add a `role` to the `User` model, you would need to provide a default value to the `role` field. This is done by adding a `@default` value to the `role` field in the `User` model: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Usage with Next.js middleware Usage of NextAuth.js with Next.js middleware [requires the use of the JWT session strategy](https://next-auth.js.org/configuration/nextjs#caveats) for authentication. This is because the middleware is only able to access the session cookie if it is a JWT. By default, Create T3 App is configured to use the **default** database strategy, in combination with Prisma as the database adapter. Using database sessions is the recommended approach and you should read up on JWTs before switching to the JWT session strategy to avoid any security issues. After switching to the JWT session strategy. Make sure to update the `session` callback in `src/server/auth.ts`. The `user` object will be `undefined`. Instead, retrieve the user's ID from the `token` object. I.e.: ```diff:server/auth.ts export const authOptions: NextAuthOptions = { + session: { + strategy: "jwt", + }, callbacks: { - session: ({ session, user }) => ({ + session: ({ session, token }) => ({ ...session, user: { ...session.user, - id: user.id, + id: token.sub, }, }), }, } ``` ## Setting up the default DiscordProvider 1. Head to [the Applications section in the Discord Developer Portal](https://discord.com/developers/applications), and click on "New Application" 2. In the settings menu, go to "OAuth2 => General" - Copy the Client ID and paste it in `DISCORD_CLIENT_ID` in `.env`. - Under Client Secret, click "Reset Secret" and copy that string to `DISCORD_CLIENT_SECRET` in `.env`. Be careful as you won't be able to see this secret again, and resetting it will cause the existing one to expire. - Click "Add Redirect" and paste in `/api/auth/callback/discord` (example for local development: http://localhost:3000/api/auth/callback/discord) - Save your changes - It is possible, but not recommended, to use the same Discord Application for both development and production. You could also consider [Mocking the Provider](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) during development. ## Useful Resources | Resource | Link | | --------------------------------- | --------------------------------------- | | NextAuth.js Docs | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/en/usage/drizzle.mdx ================================================ --- title: Drizzle description: Usage of Drizzle layout: ../../../layouts/docs.astro lang: en isMdx: true --- Drizzle is a headless Typescript ORM with [relational](https://orm.drizzle.team/docs/rqb) and [SQL-like](https://orm.drizzle.team/docs/select) query APIs. It can handle database migrations and schemas, and provides a type safe database client. It also comes with [Drizzle-Kit](https://orm.drizzle.team/drizzle-studio/overview), a set of companion tools that help with querying your database. ## Drizzle Client The Drizzle Client is located at `src/server/db/index.ts`. In this file, you can define your database connection url and connect your schema to the database object. ```ts:src/server/db/index.ts import { env } from "~/env"; import * as schema from "./schema"; import postgres from "postgres"; const conn = postgres(env.DATABASE_URL) export const db = drizzle(conn, { schema }); ``` We reccommend including the database client in your tRPC Context: ```ts:src/server/api/trpc.ts import { db } from "~/server/db"; export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth(); return { db, session, ...opts, }; }; ``` ## Schema The Drizzle schema file can be found at `src/server/db/schema.ts`. This file is where you can define your database schema and models, and connects to the Drizzle Client. When you select NextAuth.js in combination with Drizzle, the schema file is generated and set up for you with the recommended values for the `User`, `Session`, `Account`, and `VerificationToken` models, as per the [Auth.js documentation](https://authjs.dev/getting-started/adapters/drizzle). ## Drizzle Kit Drizzle Kit is a collection of command line tools designed to help you manage your database. T3 Stack automatically includes drizzle kit when you select Drizzle as your ORM. ```json:package.json "scripts": { ... "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", ... }, ``` ### Script Explanations `db:generate` Generates TypeScript types and models from your database schema, ensuring type safety and easy integration with Drizzle ORM. `db:migrate` Applies pending migrations to your database, keeping your schema in sync with changes and updates in your project. `db:push` Pushes local schema changes directly to the database without needing explicit migration files. This can be useful for quick syncing in development. `db:studio` Opens a visual interface for managing and inspecting your database tables, data, and relationships. ## Useful Resources | Resource | Link | | --------------------------- | --------------------------------------------------- | | Drizzle Docs | https://orm.drizzle.team/docs/overview | | Drizzle GitHub | https://github.com/drizzle-team/drizzle-orm | | Auth.JS Drizzle Adapter | https://authjs.dev/getting-started/adapters/drizzle | | Drizzle Kit Migration Guide | https://orm.drizzle.team/docs/kit-overview | ================================================ FILE: www/src/pages/en/usage/env-variables.mdx ================================================ --- title: Environment Variables description: Getting started with Create T3 App layout: ../../../layouts/docs.astro lang: en isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Create T3 App uses its own package [@t3-oss/env-nextjs](https://env.t3.gg) along with [zod](https://zod.dev) under the hood for validating environment variables at runtime _and_ buildtime by providing a simple logic in `src/env.js`. ## env.js _TLDR; If you want to add a new environment variable, you must add a validator for it in `src/env.js`, and then add the KV-pair in `.env`_ ```ts:env.js import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, }, }); ``` T3 Env uses the `createEnv` function to create the schema validate both client and server-side environment variables. For more information about how `createEnv` works internally, check out the [T3 Env](https://env.t3.gg/docs/introduction) docs ## Using Environment Variables When you want to use your environment variables, you can import them from the created `env.js` and use them as you would normally do. If you import this on the client and try accessing a server-side environment variable, you will get a runtime error. ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` is fully typesafe and provides autocompletion const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ This will throw a runtime error const dbUrl = env.DATABASE_URL; // ✅ This is fine const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example Since the default `.env` file is not committed to version control, we have also included a `.env.example` file, in which you can optionally keep a copy of your `.env` file with any secrets removed. This is not required, but we recommend keeping the example up to date to make it as easy as possible for contributors to get started with their environment. Some frameworks and build tools, like Next.js, suggest that you store secrets in a `.env.local` file and commit `.env` files to your project. This is not recommended, as it could make it easy to accidentally commit secrets to your project. Instead, we recommend that you store secrets in `.env`, keep your `.env` file in your `.gitignore` and only commit `.env.example` files to your project. ## Adding Environment Variables To ensure your build never completes without the environment variables the project needs, you will need to add new environment variables in **two** locations: 📄 `.env`: Enter your environment variable like you would normally do in a `.env` file, i.e. `KEY=VALUE` 📄 `env.js`: Add the appropriate validation logic for the environment variables by defining a Zod schema inside `createEnv` for each one, e.g. `KEY: z.string()`. Besides that, make sure to destruct them in the `runtimeEnv` option, e.g.: `KEY: process.env.KEY` Why do I need to destructure the environment variable in the `runtimeEnv`? This is due to how Next.js bundles environment variables in certain runtimes. By destructuring it manually, you ensure that the variable will never be stripped out from the bundle. Optionally, you can also keep `.env.example` updated: 📄 `.env.example`: Enter your environment variable, but be sure to not include the value if it is secret, i.e. `KEY=VALUE` or `KEY=` ### Example _I want to add my Twitter API Token as a server-side environment variable_ 1. Add the environment variable to `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Add the environment variable to `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { TWITTER_API_TOKEN: z.string(), }, // ... runtimeEnv: { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }, }); ``` 3. _Optional:_ Add the environment variable to `.env.example` and make sure not to include the secret in the `runtimeEnv` option ```bash TWITTER_API_TOKEN= ``` ## Type Coercion All variables you add to `.env` will be imported as strings, even if their value is intended to represent a different type. If you want to use your environment variables as a different type at runtime, you can use Zod's `coerce` to convert the string to the type you want. It will throw if the coercion fails. Add the variables to your `.env`: ``` SOME_NUMBER=123 SOME_BOOLEAN=true ``` Then, validate them in `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { SOME_NUMBER: z.coerce.number(), SOME_BOOLEAN: z.coerce.boolean(), }, // ... runtimeEnv: { SOME_NUMBER: process.env.SOME_NUMBER, SOME_BOOLEAN: process.env.SOME_BOOLEAN, }, }); ``` ================================================ FILE: www/src/pages/en/usage/first-steps.md ================================================ --- title: First Steps description: Getting started with your new T3 App layout: ../../../layouts/docs.astro lang: en --- You just scaffolded a new T3 App and are ready to go. Here is the bare minimum to get your app working. ## Database ### MySQL, PostgreSQL If you chose MySQL or PostgreSQL as your database, your T3 app will come with a `start-database.sh` bash script that can create a docker container with a database for local development. If you already have a database, feel free to delete this file and put your database credentials in `.env`. On macOS, you can also use [DBngin](https://dbngin.com/) if you don't want to use docker. ### Prisma If your app includes Prisma, make sure to run `npx prisma db push` from the root directory of your app. This command will sync your Prisma schema with your database and will generate the TypeScript types for the Prisma Client based on your schema. Note that you need to [restart the TypeScript server](https://tinytip.co/tips/vscode-restart-ts/) after doing this so that it can detect the generated types. ### Drizzle If your app includes Drizzle, check the `.env` file for instructions on how to construct your `DATABASE_URL` env variable. Once your env file is ready, run `pnpm db:push` (or the equivalent for other package managers) to push your schema. ## Authentication If your app includes NextAuth.js, we get you started with the `DiscordProvider`. This is one of the simplest providers that NextAuth.js offers, but it still requires a bit of initial setup on your part. Of course, if you prefer to use a different auth provider, you can also use one of the [many providers](https://next-auth.js.org/providers/) that NextAuth.js offers. 1. You will need a Discord account, so register one if you haven't already. 2. Navigate to https://discord.com/developers/applications and click "New Application" in the top right corner. Give your application a name and agree to the Terms of Service. 3. Once your application has been created, navigate to "Settings → OAuth2 → General". 4. Copy the "Client ID" and add it to your `.env` as `AUTH_DISCORD_ID`. 5. Click "Reset Secret", copy the new secret, and add it to your `.env` as `AUTH_DISCORD_SECRET`. 6. Click "Add Redirect" and type in `http://localhost:3000/api/auth/callback/discord`. - For production deployment, follow the previous steps to create another Discord Application, but this time replace `http://localhost:3000` with the URL that you are deploying to. 7. Save Changes. You should now be able to log in. ## Editor Setup The following extensions are recommended for an optimal developer experience. The links below provide editor specific plugin support. - [Prisma Extension](https://www.prisma.io/docs/guides/development-environment/editor-setup) - [Tailwind CSS IntelliSense Extension](https://tailwindcss.com/docs/editor-setup) - [Prettier Extension](https://prettier.io/docs/en/editors.html) ## Next Steps - If your app includes tRPC, check out `src/pages/index.tsx` and `src/server/api/routers/post.ts` to see how tRPC queries work. - Have a look around the Create T3 App docs, as well as the docs of the packages that your app includes. - Join our [Discord](https://t3.gg/discord) and give us a star on [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/en/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Usage", layout: "docs", description: "Learn how to use the different technology from the T3 Stack.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/en/usage/next-auth.mdx ================================================ --- title: NextAuth.js description: Usage of NextAuth.js layout: ../../../layouts/docs.astro lang: en isMdx: true --- import Tabs from "../../../components/docs/tabs.astro"; import AppRouter from "./_next-auth-app-router.mdx"; import Pages from "./_next-auth-pages.mdx"; When you want an authentication system in your Next.js application, NextAuth.js is an excellent solution to bring in the complexity of security without the hassle of having to build it yourself. It comes with an extensive list of providers to quickly add OAuth authentication and provides adapters for many databases and ORMs. ================================================ FILE: www/src/pages/en/usage/next-js.md ================================================ --- title: Next.js description: Usage of Next.js layout: ../../../layouts/docs.astro lang: en --- Next.js is a backend framework for your React applications.
Check out [Theo's Next.js Conf talk](https://www.youtube.com/watch?v=W4UhNo3HAMw) to get a better understanding of what Next.js is and how it works.

## Why should I use it? We love React. It has made UI development accessible in ways we never imagined before. It also can lead developers down some rough paths. Next.js offers a lightly opinionated, heavily optimized approach to creating applications using React. From routing to API definitions to image rendering, we trust Next.js to lead developers towards good decisions. Pairing Next.js with [Vercel](https://vercel.com/) makes developing and deploying web apps easier than ever before. Their extremely generous free-tier and super intuitive interface provides a point and click solution to deploy your site (We ❤️ Vercel) ## Get Static/Server Props A key feature of Next.js is its data fetching capabilities. We highly recommend reading through the [official documentation](https://nextjs.org/docs/basic-features/data-fetching) to understand how to use each method and how they differ. `getServerSideProps` is generally discouraged unless there is a good reason for it, due to the fact that it is a blocking call and will slow down your site. [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) is a great alternative to `getServerSideProps` when the data is dynamic and can be fetched incrementally. If you need to use this feature anyway, check these links out: [Advanced tRPC - Callers, functions, and gSSP](https://www.youtube.com/watch?v=G2ZzmgShHgQ) and [SSG-Helpers](https://trpc.io/docs/v9/ssg-helpers) ## Useful Resources | Resource | Link | | ------------------------------ | ---------------------------------- | | Next.js Documentation | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js Blog | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Vercel/Next.js YouTube Channel | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/en/usage/prisma.md ================================================ --- title: Prisma description: Usage of Prisma layout: ../../../layouts/docs.astro lang: en --- Prisma is an ORM for TypeScript, that allows you to define your database schema and models in a `schema.prisma` file, and then generate a type-safe client that can be used to interact with your database from your backend. ## Prisma Client Located at `src/server/db.ts`, the Prisma Client is instantiated as a global variable (as recommended as [best practice](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) by the team at Prisma) and exported to be used in your API routes. We include the Prisma Client in [Context](/en/usage/trpc#-serverapitrpcts) by default and recommend using this instead of importing it separately in each file. ## Schema You will find the Prisma schema file at `/prisma/schema.prisma`. This file is where you define your database schema and models, and is used when generating the Prisma Client. ### With NextAuth.js When you select NextAuth.js in combination with Prisma, the schema file is generated and set up for you with the recommended values for the `User`, `Session`, `Account`, and `VerificationToken` models, as per the [NextAuth.js documentation](https://next-auth.js.org/adapters/prisma). ## Default Database The default database is an SQLite database, which is great for development and quickly spinning up a proof-of-concept but is not recommended for production. You can change the database to use by changing the `provider` in the `datasource` block to either `postgresql` or `mysql`, and then updating the connection string within environment variables to point to your database. ## Seeding your Database [Seeding your database](https://www.prisma.io/docs/guides/database/seed-database) is a great way to quickly populate your database with test data to help you get started. In order to setup seeding, you will need to create a `seed.ts` file in the `/prisma` directory, and then add a `seed` script to your `package.json` file. You'll also need a TypeScript runner that can execute the seed-script. We recommend [tsx](https://github.com/esbuild-kit/tsx), which is a very performant TypeScript runner that uses esbuild and doesn't require any ESM configuration, but `ts-node` or other runners will work as well. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Then, just run `pnpm db-seed` (or `npm`/`yarn`) to seed your database. ## Useful Resources | Resource | Link | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Prisma Docs | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | Prisma Migrate Playground | https://playground.prisma.io/guides | | NextAuth.JS Prisma Adapter | https://next-auth.js.org/adapters/prisma | | PlanetScale Connection Guide | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/en/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Usage of Tailwind CSS layout: ../../../layouts/docs.astro lang: en --- ## What is Tailwind CSS? Tailwind CSS is a tiny, [utility first](https://tailwindcss.com/docs/utility-first) CSS framework for building custom designs, without the context switching that regular CSS requires. It is purely a CSS framework and does not provide any pre-built components or logic, and provides [a very different set of benefits](https://www.youtube.com/watch?v=CQuTF-bkOgc) compared to a component library like Material UI. It makes CSS incredibly easy and quick to write, as shown by the following example: Old CSS: 1. Write CSS, often in a separate file ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Import CSS into your component ```jsx import "./my-class.css"; ``` 3. Add the class to your HTML ```html
...
``` Equivalent in Tailwind: 1. Just write classes in your HTML ```html
...
``` When used together with React Components, it is extremely powerful for quickly building UIs. Tailwind CSS has a beautiful built-in design system, that comes out of the box with a carefully chosen color palette, sizing patterns for styles such as width/height and padding/margin for a uniform design, as well as media breakpoints for creating responsive layouts. This design system can be customized and extended to create the exact toolbox of styles that your project needs.
Tru Narla better known as [mewtru](https://twitter.com/trunarla) gave an amazing talk on [building a design system using Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Usage Make sure you have editor plugins for Tailwind installed to improve your experience writing Tailwind. ### Extensions and Plugins - [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Formatting Tailwind CSS classes can easily get a bit messy, so a formatter for the classes is a must have. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) sorts the classes in the [recommended order](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) so that the classes match the outputted css bundle. When selecting Tailwind in the CLI, we will install and configure this for you. ### Conditionally Applying Classes Conditionally adding classes using ternaries can get very messy and hard to read. These packages help in organizing your classes when using some conditional logic. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Useful Resources | Resource | Link | | ---------------------------- | -------------------------------------------------------- | | Tailwind Docs | https://tailwindcss.com/docs/editor-setup/ | | Tailwind Cheat Sheet | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Tailwind Discord Server | https://tailwindcss.com/discord/ | | TailwindLabs Youtube Channel | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/en/usage/trpc.md ================================================ --- title: tRPC description: Usage of tRPC layout: ../../../layouts/docs.astro lang: en --- tRPC allows us to write end-to-end typesafe APIs without any code generation or runtime bloat. It uses TypeScript's great inference to infer your API router's type definitions and lets you call your API procedures from your frontend with full typesafety and auto-completion. When using tRPC, your frontend and backend feel closer together than ever before, allowing for an outstanding developer experience.

I built tRPC to allow people to move faster by removing the need for a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.

Avatar of @alexdotjs
Alex - creator of tRPC @alexdotjs
## How do I use tRPC?
tRPC contributor [trashh_dev](https://twitter.com/trashh_dev) made [a killer talk at Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) about tRPC. We highly recommend you watch it if you haven't already. With tRPC, you write TypeScript functions on your backend, and then call them from your frontend. A simple tRPC procedure could look like this: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` This is a tRPC procedure (equivalent to a route handler in a traditional backend) that first validates the input using Zod (which is the same validation library that we use for [environment variables](./env-variables)) - in this case, it's making sure that the input is a string. If the input is not a string it will send an informative error instead. After the input, we chain a resolver function which can be either a [query](https://trpc.io/docs/client/react/useQuery), [mutation](https://trpc.io/docs/v11/client/react/useMutation), or a [subscription](https://trpc.io/docs/v11/subscriptions). In our example, the resolver calls our database using our [prisma](./prisma) client and returns the user whose `id` matches the one we passed in. You define your procedures in `routers` which represent a collection of related procedures with a shared namespace. You may have one router for `users`, one for `posts`, and another one for `messages`. These routers can then be merged into a single, centralized `appRouter`: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Notice that we only need to export our router's type definitions, which means we are never importing any server code on our client. Now let's call the procedure on our frontend. tRPC provides a wrapper for `@tanstack/react-query` which lets you utilize the full power of the hooks they provide, but with the added benefit of having your API calls typed and inferred. We can call our procedures from our frontend like this: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` You'll immediately notice how good the autocompletion and typesafety is. As soon as you write `api.`, your routers will show up in autocomplete, and when you select a router, its procedures will show up as well. You'll also get a TypeScript error if your input doesn't match the validator that you defined on the backend. ## Inferring errors By default, `create-t3-app` sets up an [error formatter](https://trpc.io/docs/v11/server/error-formatting) that lets you infer your Zod Errors if you get validation errors on the backend. Example usage: ```tsx function MyComponent() { const { mutate, error } = api.post.create.useMutation(); return ( { e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ title: formData.get('title') }); }}> {error?.data?.zodError?.fieldErrors.title && ( {/** `mutate` returned with an error on the `title` */} {error.data.zodError.fieldErrors.title} )} ... ); } ``` ## Files tRPC requires quite a lot of boilerplate that `create-t3-app` sets up for you. Let's go over the files that are generated: ### 📄 `pages/api/trpc/[trpc].ts` This is the entry point for your API and exposes the tRPC router. Normally, you won't touch this file very much, but if you need to, for example, enable CORS middleware or similar, it's useful to know that the exported `createNextApiHandler` is a [Next.js API handler](https://nextjs.org/docs/api-routes/introduction) which takes a [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. This means that you can wrap the `createNextApiHandler` in any middleware you want. See below for an [example snippet](#enabling-cors) of adding CORS. ### 📄 `server/api/trpc.ts` This file is split up in two parts, context creation and tRPC initialization: 1. We define the context that is passed to your tRPC procedures. Context is data that all of your tRPC procedures will have access to, and is a great place to put things like database connections, authentication information, etc. In create-t3-app we use two functions, to enable using a subset of the context when we do not have access to the request object. - `createInnerTRPCContext`: This is where you define context which doesn't depend on the request, e.g. your database connection. You can use this function for [integration testing](#sample-integration-test) or [ssg-helpers](https://trpc.io/docs/v10/client/nextjs/server-side-helpers) where you don't have a request object. - `createTRPCContext`: This is where you define context which depends on the request, e.g. the user's session. You request the session using the `opts.req` object, and then pass the session down to the `createInnerTRPCContext` function to create the final context. 2. We initialize tRPC and define reusable [procedures](https://trpc.io/docs/v11/server/procedures) and [middlewares](https://trpc.io/docs/v11/server/middlewares). By convention, you shouldn't export the entire `t`-object but instead, create reusable procedures and middlewares and export those. You'll notice we use `superjson` as [data transformer](https://trpc.io/docs/v10/server/data-transformers). This makes it so that your data types are preserved when they reach the client, so if you for example send a `Date` object, the client will return a `Date` and not a string which is the case for most APIs. ### 📄 `server/api/routers/*.ts` This is where you define the routes and procedures of your API. By convention, you [create separate routers](https://trpc.io/docs/v11/server/routers) for related procedures. ### 📄 `server/api/root.ts` Here we [merge](https://trpc.io/docs/v11/server/merging-routers) all the sub-routers defined in `routers/**` into a single app router. ### 📄 `utils/api.ts` This is the frontend entry point for tRPC. This is where you'll import the router's **type definition** and create your tRPC client along with the react-query hooks. Since we enabled `superjson` as our data transformer on the backend, we need to enable it on the frontend as well. This is because the serialized data from the backend is deserialized on the frontend. You'll define your tRPC [links](https://trpc.io/docs/v11/client/links) here, which determines the request flow from the client to the server. We use the "default" [`httpBatchLink`](https://trpc.io/docs/v11/client/links/httpBatchLink) which enables [request batching](https://cloud.google.com/compute/docs/api/how-tos/batch), as well as a [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink) which outputs useful request logs during development. Lastly, we export a [helper type](https://trpc.io/docs/client/vanilla/infer-types) which you can use to infer your types on the frontend.
Create T3 App contributor [Christopher Ehrlich](https://twitter.com/ccccjjjjeeee) made [a video about data flows in tRPC](https://www.youtube.com/watch?v=x4mu-jOiA0Q). This video is recommended if you have used tRPC but still feel a bit unclear about how it works. ## How do I call my API externally? With regular APIs, you can call your endpoints using any HTTP client such as `curl`, `Postman`, `fetch` or straight from your browser. With tRPC, it's a bit different. If you want to call your procedures without the tRPC client, there are two recommended ways to do it: ### Expose a single procedure externally If you want to expose a single procedure externally, you're looking for [server side calls](https://trpc.io/docs/server/server-side-calls). That would allow you to create a normal Next.js API endpoint, but reuse the resolver part of your tRPC procedure. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Exposing every procedure as a REST endpoint If you want to expose every single procedure externally, checkout the community built plugin [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). By providing some extra meta-data to your procedures, you can generate an OpenAPI compliant REST API from your tRPC router. ### It's just HTTP Requests tRPC communicates over HTTP, so it is also possible to call your tRPC procedures using "regular" HTTP requests. However, the syntax can be cumbersome due to the [RPC protocol](https://trpc.io/docs/rpc) that tRPC uses. If you're curious, you can check what tRPC requests and responses look like in your browser's network tab, but we suggest doing this only as an educational exercise and sticking to one of the solutions outlined above. ## Comparison to a Next.js API endpoint Let's compare a Next.js API endpoint to a tRPC procedure. Let's say we want to fetch a user object from our database and return it to the frontend. We could write a Next.js API endpoint like this: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Compare this to the tRPC example above and you can see some of the advantages of tRPC: - Instead of specifying a url for each route, which can become annoying to debug if you move something, your entire router is an object with autocomplete. - You don’t need to validate which HTTP method was used. - You don’t need to validate that the request query or body contains the correct data in the procedure, because Zod takes care of this. - Instead of creating a response, you can throw errors and return a value or object as you would in any other TypeScript function. - Calling the procedure on the frontend provides autocompletion and type safety. ## Useful snippets Here are some snippets that might come in handy. ### Enabling CORS If you need to consume your API from a different domain, for example in a monorepo that includes a React Native app, you might need to enable CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Enable cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### Optimistic updates Optimistic updates are when we update the UI before the API call has finished. This gives the user a better experience because they don't have to wait for the API call to finish before the UI reflects the result of their action. However, apps that value data correctness highly should avoid optimistic updates as they are not a "true" representation of backend state. You can read more on the [React Query docs](https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useUtils(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await utils.post.list.cancel(); // Get the data from the queryCache const prevData = utils.post.list.getData(); // Optimistically update the data with our new post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Return the previous data so we can revert if something goes wrong return { prevData }; }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sync with server once mutation has settled utils.post.list.invalidate(); }, }); }; ``` ### Sample Integration Test Here is a sample integration test that uses [Vitest](https://vitest.dev) to check that your tRPC router is working as expected, the input parser infers the correct type, and that the returned data matches the expected output. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` If your procedure is protected, you can pass in a mocked `session` object when you create the context: ```ts test("protected example router", async () => { const ctx = await createInnerTRPCContext({ session: { user: { id: "123", name: "John Doe" }, expires: "1", }, }); const caller = appRouter.createCaller(ctx); // ... }); ``` ## Useful Resources | Resource | Link | | ---------------------- | ----------------------------------------------- | | tRPC Docs | https://www.trpc.io | | Bunch of tRPC Examples | https://github.com/trpc/trpc/tree/next/examples | | React Query Docs | https://tanstack.com/query/latest/docs | ================================================ FILE: www/src/pages/en/usage/typescript.md ================================================ --- title: TypeScript description: Usage of TypeScript layout: ../../../layouts/docs.astro lang: en ---

Build safety nets, not guard rails

Avatar of @t3dotgg
Theo - creator of the T3 Stack @t3dotgg
Whether you're a new or seasoned developer, we think that TypeScript is a must have. It can look intimidating at first, but much like a lot of tools, is something that many never look back from after starting to use it. It provides live feedback as you write your code by defining expected data types, and either provides helpful autocomplete in your code editor, or yells at you with red squiggly lines if you're trying to access a property that doesn't exist or trying to pass a value of the wrong type, which you would otherwise have to debug further down the line. It is, perhaps, the tool that provides the most productivity to developers; providing documentation of the code you're writing or consuming directly in your editor, and having instant feedback as you inevitably make mistakes is absolutely priceless. ## Type Inference While many new TypeScript developers are concerned with _writing_ TypeScript, many of its benefits don't actually require you to change your code at all, in particular inference. Inference means that if something is typed, that type will follow it throughout the flow of the application without having to be re-declared in other places. This means that for example once you have defined the types of the arguments that a function takes, the remainder of the function will usually be typesafe without requiring any further TypeScript-specific code. Library developers put a ton of work into maintaining the types for their libraries, which means that we as application developers can benefit from both the inference and the built-in documentation in your code editor that these types provide.
Check out Theo's video on how [you might be using TypeScript wrong](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Powerful uses of type inference ### Zod [Zod](https://github.com/colinhacks/zod) is a schema validation library that is built on top of TypeScript. Write a schema that represents a single source of truth for your data, and Zod will ensure that your data is valid throughout your application, even across network boundaries and external APIs. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) gives you declarative, always-up-to-date auto-managed queries and mutations that directly improve both your developer and user experiences. ## Useful Resources | Resource | Link | | --------------------------------------------------------- | ----------------------------------------------------------------- | | TypeScript Handbook | https://www.typescriptlang.org/docs/handbook/ | | Beginners TypeScript Tutorial | https://github.com/total-typescript/beginners-typescript-tutorial | | Type Challenges | https://github.com/type-challenges/type-challenges | | Rodney Mullen of TypeScript (Matt Pocock) Youtube Channel | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/en/why.md ================================================ --- title: Why CT3A? description: Why you should pick Create T3 App for your next project layout: ../../layouts/docs.astro lang: en --- We started Create T3 App because [Theo](https://twitter.com/t3dotgg) refused to make a template of his favorite technologies. Inspired by create-next-app, [Astro's CLI](https://astro.build), and a general love for typesafety, the Create T3 App team worked hard to build the best possible starting point for new T3 Stack projects. If you're interested in using Next.js in a typesafe way, this is the place to start. If you're curious about any of the specific technology choices we made, read on :) ## Why TypeScript? JavaScript is hard. Why add more rules? We firmly believe the experience TypeScript provides will help you be a better developer. It provides live feedback as you write your code by defining expected data types, and either provides helpful autocomplete in your editor or yells at you with red squiggly lines if you're trying to access a property that doesn't exist or trying to pass a value of the wrong type, which you would otherwise have to debug further down the line. Whether you're new to web development or a seasoned pro, the "strictness" of TypeScript will provide a less frustrating, more consistent experience than vanilla JS. Typesafety makes you faster. If you're not convinced, you [might be using TypeScript wrong...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## Why Next.js? We love React. It has made UI development accessible in ways we never imagined before. It also can lead developers down some rough paths. Next.js offers a lightly opinionated, heavily optimized approach to creating applications using React. From routing to API definitions to image rendering, we trust Next.js to lead developers toward good decisions. ## Why tRPC/Prisma/Tailwind/etc? While we believe in keeping things as simple as possible, we find these pieces being used in every "app" like project we build. `create-t3-app` does a great job of letting you adopt the pieces you need. ### tRPC tRPC delivers on GraphQL's promise of seamless client development against a typesafe server without all of the boilerplate. It's a clever abuse of TypeScript that provides an incredible dev experience. ### Prisma Prisma is to SQL what TypeScript is to JS. It created a developer experience that didn't exist before. By generating types from a user-defined schema compatible with [several databases](https://www.prisma.io/docs/concepts/database-connectors), Prisma guarantees end-to-end typesafety from your database to your app. Prisma provides a whole [suite of tools](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) making daily interactions with your database easier. Notably, the Prisma Client is responsible for querying and making SQL so easy you'll barely notice you're using it, and Prisma Studio is a convenient GUI for your database that lets you read and manipulate your data quickly without having to write code. ### Tailwind CSS Tailwind feels like "zen-mode CSS". By providing building blocks in the form of good default colors, spacing, and other primitives, Tailwind makes it easy to create a good-looking app. And unlike component libraries, it does not hold you back when you want to take your app to the next level and create something beautiful and unique. Additionally, with its inline-like approach, Tailwind encourages you to style without worrying about naming classes, organizing files, or any other issue not directly tied to the problem you're trying to solve. ### NextAuth.js When you want an authentication system in your NextJS application, NextAuth.js is an excellent solution to bring in the complexity of security without the hassle of having to build it yourself. It comes with an extensive list of providers to quickly add OAuth authentication and provides adapters for many databases and ORMs. ================================================ FILE: www/src/pages/es/deployment/docker.md ================================================ --- title: Docker description: Desplegando con Docker layout: ../../../layouts/docs.astro lang: es --- Puedes contenerizar este stack y desplegarlo como un solo contenedor mediante Docker, o como parte de un grupo de contenedores mediante docker-compose. Consulta [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) para ver un repositorio de ejemplo basado en este documento. ## Configuración del proyecto Docker Ten en cuenta que Next.js requiere un proceso diferente para compilar (disponible en el frontend, con el prefijo `NEXT_PUBLIC`) y las variables de entorno en tiempo de ejecución, solo del lado del servidor. En esta demostración estamos usando dos variables, presta atención a sus posiciones en `Dockerfile`, argumentos de la línea de comandos y `docker-compose.yml`: - `DATABASE_URL` (utilizado por el servidor) - `NEXT_PUBLIC_CLIENTVAR` (utilizado por el cliente) ### 1. Configuración de Next.js En tu [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), agrega la configuración de output `standalone` para [reducir el tamaño de la imagen aprovechando automáticamente el output tracing](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Crear archivo dockerignore
Haz clic aquí e incluye el contenido en .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Crear archivo Docker > Como no estamos introduciendo las variables de entorno del servidor en nuestro contenedor, la [validación del esquema de entorno](/en/usage/env-variables) fallará. Para evitar esto, debemos agregar un indicador `SKIP_ENV_VALIDATION=1` al comando de compilación para que las variables de entorno no se validen en el momento de la compilación.
Haz clic aquí e incluye el contenido en Dockerfile:
```docker ##### DEPENDENCIAS FROM --platform=linux/amd64 node:16-alpine AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Instala el Prisma Client - quitar si no estás usando Prisma COPY prisma ./ # Instala dependencias basadas en tu administrador de paquetes preferido COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### CONSTRUCTOR FROM --platform=linux/amd64 node:16-alpine AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### LANZADOR FROM --platform=linux/amd64 node:16-alpine AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Notas_** > > - _La emulación de `--platform=linux/amd64` puede no ser necesaria tras moverse a Node 18._ > - _Puedes ver [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) para entender porqué `libc6-compat` puede ser necesario._ > - _Next.js recolecta [datos anónimos de uso general para telemetría](https://nextjs.org/telemetry). Descomenta la primera instancia de `ENV NEXT_TELEMETRY_DISABLED 1` para deshabilitar la telemetría durante el tiempo de compilación. Descomenta la segunda instancia para deshabilitar la telemetría durante tiempo de ejecución._
## Crea y ejecuta la imagen localmente Crea y ejecuta esta imagen localmente con los siguientes comandos: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Abre [localhost:3000](http://localhost:3000/) para ver tu aplicación en ejecución. ## Docker Compose También puedes usar Docker Compose para crear la imagen y ejecutar el contenedor.
Sigue los pasos 1-4 anteriores, haz clic aquí e incluye el contenido en docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Ejecuta esto usando el comando `docker compose up`: ```bash docker compose up ``` Abre [localhost:3000](http://localhost:3000/) para ver tu aplicación en ejecución.
## Desplegar en Railway Puedes usar una PaaS como los [despligues de Dockerfile](https://docs.railway.app/deploy/dockerfiles) automatizados de [Railway](https://railway.app) para desplegar tu aplicación. Si tienes instalada la [CLI de Railway](https://docs.railway.app/develop/cli#install), puedes desplegar tu aplicación con los siguientes comandos: ```bash railway login railway init railway link railway up railway open ``` Dirígite a "Variables" e incluye tu `DATABASE_URL`. Luego dirígite a "Configuración" y selecciona "Generar dominio". Para ver un ejemplo en ejecución en Railway, visita [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Otros recursos útiles | Recurso | Link | | ------------------------------------------------- | -------------------------------------------------------------------- | | Referencia Docker | https://docs.docker.com/engine/reference/builder/ | | Referencia archivo Compose version 3 | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Referencia Docker CLI | https://docs.docker.com/engine/reference/commandline/docker/ | | Referencia Docker Compose CLI | https://docs.docker.com/compose/reference/ | | Despligue Next.js utilizando una imagen de Docker | https://nextjs.org/docs/deployment#docker-image | | Next.js en Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Ejemplo Next.js con Docker | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Crear una imagen Docker de una aplicación Next.js | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/es/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Despliegue", layout: "docs", description: "Aprende a desplegar tu aplicación T3 a producción.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/es/deployment/netlify.md ================================================ --- title: Netlify description: Desplegando en Netlify layout: ../../../layouts/docs.astro lang: es --- Netlify es una alternativa al proveedor de despliegue en un sentido similar a Vercel. Ver [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) para ver un ejemplo de un repositorio basado en este documento. ## ¿Por qué desplegar en Netlify? La sabiduría convencional dice que Vercel tiene un mejor soporte para Next.js, porque Vercel desarrolla Next.js. Tienen un interés en asegurar que la plataforma esté optimizada para un rendimiento y una experiencia de desarrollo óptimos con Next.js. Para la mayoría de los casos de uso, esto será cierto y no tendrá sentido desviarse del camino estándar. También hay un sentimiento común de que muchas características de Next.js solo son compatibles con Vercel. Si bien es cierto que las nuevas características de Next.js se probarán y admitirán en Vercel en el momento del lanzamiento de forma predeterminada, también es cierto que otros proveedores como Netlify [implementarán y lanzarán rápidamente el soporte](https://www.netlify.com/blog/deploy-nextjs-13/) para [características de Next.js estables](https://docs.netlify.com/integrations/frameworks/next-js/overview/). Hay pros y contras relativos para todos los proveedores de despliegue, ya que ningún host puede tener el mejor soporte para todos los casos de uso. Por ejemplo, Netlify construyó su propio [tiempo de ejecución personalizado de Next.js](https://github.com/netlify/next-runtime) para las funciones de _edge_ de Netlify (que se ejecutan en Deno Deploy) y [mantienen un middleware único para acceder y modificar las respuestas HTTP](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). > _NOTA: Para rastrear el estado de las funciones no estables de Next 13, consulta [Using the Next 13 `app` directory on Netlify](https://github.com/netlify/next-runtime/discussions/1724)._ ## Configuración del proyecto Hay numerosas formas de configurar tus instrucciones de compilación, incluido el uso directo de la CLI de Netlify o el panel de control de Netlify. Si bien no es obligatorio, es recomendable crear e incluir un archivo [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Esto garantiza que las versiones bifurcadas y clonadas del proyecto sean más fáciles de desplegar de forma reproducible. ```toml [build] command = "next build" publish = ".next" ``` ## Usando el panel de control de Netlify 1. Empuja tu código a un repositorio de GitHub y regístrate en [Netlify](https://app.netlify.com/signup). Después de crear una cuenta, haz clic en **Add new site** y luego en **Import an existing project**. ![Nuevo proyecto en Netlify](/images/netlify-01-new-project.webp) 2. Conecta tu proveedor de Git. ![Importar repositorio](/images/netlify-02-connect-to-git-provider.webp) 3. Selecciona el repositorio de tu proyecto. ![Selecciona el repositorio de tu proyecto](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify detectará si tienes un archivo `netlify.toml` y configurará automáticamente tu comando de compilación y tu directorio de publicación. 5. Haz clic en **Show advanced** y luego en **New variable** para agregar tus variables de entorno. ![Agregar variables de entorno](/images/netlify-05-env-vars.webp) 6. Haz clic en **Deploy site** y espera a que se complete la compilación para ver tu nuevo sitio. ## Usando la CLI de Netlify Para desplegar desde la línea de comandos, primero debes empujar tu proyecto a un repositorio de GitHub y [instalar la CLI de Netlify](https://docs.netlify.com/cli/get-started/). Puedes instalar `netlify-cli` como una dependencia de proyecto o instalarlo globalmente en tu máquina con el siguiente comando: ```bash npm i -g netlify-cli ``` Para probar tu proyecto localmente, ejecuta el comando [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) y abre [`localhost:8888`](http://localhost:8888/) para ver tu aplicación Netlify en ejecución localmente: ```bash ntl dev ``` Corre el comando [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) para configurar tu proyecto: ```bash ntl init ``` Importa las variables de entorno de tu proyecto desde tu archivo `.env` con [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Despliega tu proyecto con [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Necesitarás pasar la bandera `--build` para ejecutar el comando de compilación antes del despliegue y la bandera `--prod` para desplegar en la URL principal de tu sitio: ```bash ntl deploy --prod --build ``` Para ver un ejemplo en ejecución en Netlify, visita [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/es/deployment/vercel.md ================================================ --- title: Vercel description: Desplegando en Vercel layout: ../../../layouts/docs.astro lang: es --- Recomendamos desplegar tu aplicación en [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Vercel hace que sea muy fácil desplegar aplicaciones Next.js. ## Configuración del proyecto Es probable que Vercel configure el comando de compilación y publique el directorio automáticamente. Sin embargo, también puedes especificar esta información junto con otra configuración creando un archivo llamado [`vercel.json`](https://vercel.com/docs/project-configuration) e incluyendo los siguientes comandos: ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Uso del panel de control de Vercel 1. Después de enviar tu código a un repositorio de GitHub, registrate en [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) con GitHub y haz clic en **Agregar nuevo proyecto**. ![Nuevo proyecto en Vercel](/images/vercel-new-project.webp) 2. Importa el repositorio de GitHub con tu proyecto. ![Importar repositorio](/images/vercel-import-project.webp) 3. Agrega tus variables de entorno. ![Agregando variables de entorno](/images/vercel-env-vars.webp) 4. Haz clic en **Desplegar**. Ahora, cada vez que envíes un cambio a tu repositorio, ¡Vercel volverá a desplegar automáticamente tu aplicación! ## Uso de la CLI de Vercel Para desplegar desde la línea de comandos, primero debes [instalar la CLI de Vercel globalmente](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Ejecuta el comando [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) para desplegar tu proyecto. ```bash vercel ``` Incluye `--env DATABASE_URL=SU_URL_DE_BASE_DE_DATOS_AQUI` para variables de entorno, como el string de conexión de la base de datos. Utiliza `--yes` si deseas omitir las preguntas de despliegue y dar la respuesta predeterminada para cada una. ```bash vercel --env DATABASE_URL=SU_URL_DE_BASE_DE_DATOS_AQUI --yes ``` Después del primer despliegue, este comando desplegará en una rama de vista previa. Deberás incluir `--prod` para enviar los cambios directamente a producción en despliegues futuros. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/es/faq.md ================================================ --- title: Preguntas Frecuentes description: Preguntas frecuentes acerca de create T3 app layout: ../../layouts/docs.astro lang: es --- Aquí hay algunas preguntas frecuentes sobre `create-t3-app`. ## ¿Qué sigue? ¿Cómo hago una aplicación con esto? Tratamos de mantener este proyecto lo más simple posible, para que puedas comenzar solo con el esqueleto que configuramos para ti y agregar cosas adicionales más adelante cuando sea necesario. Si no estás familiarizado con las diferentes tecnologías utilizadas en este proyecto, consulta la documentación respectiva. Si todavía no estás seguro, únete a nuestro [Discord](https://t3.gg/discord) y solicita ayuda. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [TailwindCSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## ¿Qué recursos de aprendizaje están disponibles actualmente? Aunque los recursos que se enumeran a continuación son algunos de los mejores que existen para T3 Stack, la comunidad (y [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) recomiendan que comiences a usar el stack y aprendas en el camino construyendo con él. Si estás considerando `create-t3-app`, es probable que ya hayas usado algunas de las partes del stack. Entonces, ¿por qué no simplemente sumergirse en el y aprender las otras partes mientras construyes algo? Ahora, sabemos que este camino no funciona para todos. Por lo tanto, si crees que has probado las recomendaciones y todavía te gustarían algunos recursos, o simplemente no estás seguro de hacerlo solo y/o te sientes abrumado por el stack, consulta estos increíbles tutoriales de `create-t3-app `: ### Artículos - [Build a full stack app with create-t3-app](https://www.nexxel.dev/blog/ct3a-guestbook) - [A first look at create-t3-app](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) ### Vídeos - [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [An overview of the create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## ¿Por qué hay archivos `.js` en el proyecto? De acuerdo con [Axioma-T3 #3](/es/introduction#la-seguridad-de-tipos-no-es-opcional), tomamos la seguridad de tipos como un ciudadano de primera clase. Desafortunadamente, no todos los frameworks y complementos admiten TypeScript, lo que significa que algunos de los archivos de configuración deben ser archivos `.js`. Intentamos enfatizar que estos archivos son javascript por una razón, declarando explícitamente el tipo de cada archivo (`cjs` o `mjs`) dependiendo de lo que admita la librería que lo utiliza. Además, todos los archivos `js` en este proyecto aún tienen verificación de tipos usando un comentario `@ts-check` en la parte superior. ## Tengo problemas para agregar i18n a mi aplicación. ¿Hay alguna referencia que pueda usar? Hemos decidido no incluir i18n por defecto en `create-t3-app` porque es un tema muy criticado y hay muchas formas de implementarlo. Sin embargo, si tienes dificultades para implementarlo y quieres ver un proyecto de referencia, tenemos un [repositorio de referencia](https://github.com/juliusmarminge/t3-i18n) que muestra cómo puedes agregar i18n a una aplicación T3 usando [next-i18next](https://github.com/i18next/next-i18next). ## ¿Por qué usamos `/pages` y no `/app` de Next.js 13? Según [Axioma-T3 #2](/es/introduction#responsablemente-vanguardista), nos encantan las cosas innovadoras, pero valoramos la estabilidad, todo tu enrutador es difícil de portar, [no es un buen lugar para ser vanguardista](https://youtu.be/mnwUbtieOuI?t=1662). Si bien `/app` es [un vistazo al futuro](https://youtu.be/rnsC-12PVlM?t=818), no está lista para producción; La API está en versión beta y se espera que tenga cambios significativos. Para obtener una lista de funciones compatibles, planificadas y en las que se ha trabajado en el directorio `/app`, visita [la documentación beta de Next.js](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/es/folder-structure-pages.mdx ================================================ --- title: Estructura de Carpetas (Pages) description: Estructura de carpetas de una aplicación T3 recién creada layout: ../../layouts/docs.astro lang: es isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Por favor selecciona las librerías que deseas utilizar para ver la estructura de carpetas de una aplicación T3 recién creada con esas selecciones. Más abajo, encontrarás una descripción de cada entrada.
### `prisma` La carpeta `prisma` contiene el archivo `schema.prisma` que se utiliza para configurar la conexión de la base de datos y su esquema. También es la ubicación para almacenar archivos de migración y/o scripts que inyectan valores iniciales a la base de datos (seed scripts), si se utilizan. Consulta [uso de Prisma](/es/usage/prisma) para obtener más información.
### `public` La carpeta `public` contiene archivos estáticos que son servidos por el servidor web. El archivo `favicon.ico` es un ejemplo de un archivo estático.
### `src/env` Se utiliza para la validación de variables de entorno y definiciones de tipo; consulta [variables de entorno](usage/env-variables).
### `src/pages` La carpeta `pages` contiene todas las páginas de la aplicación Next.js. El archivo `index.tsx` en el directorio raíz de `/pages` es la página de inicio de la aplicación. El archivo `_app.tsx` se usa para componer la aplicación con otros proveedores. Consulta la [documentación de Next.js](https://nextjs.org/docs/basic-features/pages) para obtener más información.
#### `src/pages/api` La carpeta `api` contiene todas las rutas API de la aplicación Next.js. El archivo `examples.ts` (con Prisma) contiene un ejemplo de una ruta que hace uso de [rutas API Next.js](https://nextjs.org/docs/api-routes/introduction) junto con Prisma. El archivo `restricted.ts` (con Next-Auth) contiene un ejemplo de una ruta que hace uso de [rutas API Next.js](https://nextjs.org/docs/api-routes/introduction) y está protegida por [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` El archivo `[...nextauth].ts` es la ruta _slug_ de autenticación NextAuth.js. Se utiliza para manejar solicitudes de autenticación. Consulta [uso de NextAuth.js](usage/next-auth) para obtener más información sobre NextAuth.js y [la documentación de rutas dinámicas de Next.js](https://nextjs.org/docs/routing/dynamic-routes) para obtener información sobre rutas _catch-all/slug_.
#### `src/pages/api/trpc/[trpc].ts` El archivo `[trpc].ts` es el punto de entrada de la API de tRPC. Se utiliza para manejar solicitudes tRPC. Consulta [uso de tRPC](usage/trpc#-pagesapitrpctrpcts) para obtener más información sobre este archivo y [la documentación de rutas dinámicas de Next.js](https://nextjs.org/docs/routing/dynamic-routes) para obtener información sobre rutas _catch-all/slug_.
### `src/server` La carpeta `server` se usa para separar claramente el código del lado del servidor del código del lado del cliente.
#### `src/server/auth.ts` La entrada principal para la lógica de autenticación del lado del servidor. Aquí, configuramos las [opciones de configuración](usage/next-auth) de NextAuth.js, realizamos [ampliación de módulo](usage/next-auth#inclusion-of-userid-on-the-session) y también proporcionamos algunas utilidades DX para la autenticación, como la recuperación de la sesión del usuario en el lado del servidor. Consulta [uso de NextAuth.js](usage/next-auth#usage-with-trpc) para obtener más información.
#### `src/server/db.ts` El archivo `db.ts` se usa para instanciar el cliente Prisma en el ámbito global. Consulta [uso de Prisma](usage/prisma#prisma-client) y [mejores prácticas para usar Prisma con Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) para obtener más información.
### `src/server/api` La carpeta `api` contiene el código del lado del servidor de tRPC.
#### `src/server/api/routers` La carpeta `routers` contiene todos tus sub-routers de tRPC.
#### `src/server/api/routers/example.ts` El archivo `example.ts` es un ejemplo de un sub-router tRPC utilizando el helper `publicProcedure` para demostrar cómo crear una ruta pública tRPC. Dendiendo de tus paquetes elegidos este router contiene más o menos rutas para demostrar mejor el uso a tus necesidades.
#### `src/server/api/trpc.ts` El archivo `trpc.ts` es el archivo de configuración principal de tu back-end tRPC. Aquí: 1. Define el contexto de tRPC. Consulta [uso de tRPC](usage/trpc#-serverapitrpcts) para obtener más información. 2. Exporta el router principal de tRPC. Consulta [uso de tRPC](usage/trpc#-serverapitrpcts) para obtener más información.
#### `src/server/api/root.ts` El archivo `root.ts` se usa para combinar los sub-routers de tRPC y exportarlos como un solo router, así como la definición de tipo del router. Consulta [uso de tRPC](usage/trpc#-serverapirootts) para obtener más información.
### `src/styles` La carpeta `styles` contiene los estilos globales de la aplicación.
### `src/utils` La carpeta `utils` se usa para almacenar funciones de utilidad comúnmente reutilizadas.
#### `src/utils/api.ts` El archivo `api.ts` es el punto de entrada del front-end a tRPC. Consulta [uso de tRPC](usage/trpc#-utilsapits) para obtener más información.
### `.env` El archivo `.env` se usa para almacenar variables de entorno. Consulta [Variables de entorno](usage/env-variables) para obtener más información. Este archivo **no** debe ser enviado al historial de git.
### `.env.example` El archivo `.env.example` muestra las variables de entorno de ejemplo basadas en las bibliotecas elegidas. Este archivo debe ser enviado al historial de git.
### `.eslintrc.cjs` El archivo `.eslintrc.cjs` se usa para configurar ESLint. Consulta [la documentación de ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) para obtener más información.
### `next-env.d.ts` El archivo `next-env.d.ts` asegura que los tipos de Next.js se detecten en el compilador de TypeScript. **No debe eliminarlo ni editarlo, ya que puede cambiar en cualquier momento.** Consulta [la documentación de Next.js](https://nextjs.org/docs/basic-features/typescript#existing-projects) para obtener más información.
### `next.config.mjs` El archivo `next.config.mjs` se usa para configurar Next.js. Consulta [la documentación de Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction) para obtener más información. Nota: La extensión .mjs se usa para permitir importaciones ESM.
### `postcss.config.js` El archivo `postcss.config.js` se usa para la configuración de Tailwind PostCSS. Consulta [la documentación de Tailwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss) para obtener más información.
### `prettier.config.mjs` El archivo `prettier.config.mjs` se usa para configurar Prettier para incluir el prettier-plugin-tailwindcss para formatear las clases de Tailwind CSS. Consulta [el artículo del blog de Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) para obtener más información.
### `tsconfig.json` El archivo `tscconfig.json` se usa para configurar TypeScript. Algunos no predeterminados, como `modo estricto`, se han habilitado para garantizar el mejor uso de TypeScript para Create T3 App y sus bibliotecas. Consulta [la documentación de TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) o [uso de TypeScript](usage/typescript) para obtener más información.
================================================ FILE: www/src/pages/es/installation.mdx ================================================ --- title: Instalación description: Instrucciones de instalación para Create T3 App layout: ../../layouts/docs.astro lang: es isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Para crear una aplicación usando `create-t3-app`, ejecuta cualquiera de los siguientes tres comandos y responde las preguntas en la línea de comandos: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` Después de que tu aplicación haya sido creada, consulta los [primeros pasos](/es/usage/first-steps) para comenzar con tu nueva aplicación. ## Uso avanzado | Opción/Bandera | Descripción | | ----------------- | ----------------------------------------------------------------------------------------- | | `[dir]` | Incluye un argumento de directorio con un nombre para el proyecto | | `--noGit` | Dile explícitamente a la CLI que no inicialice un nuevo repositorio de git en el proyecto | | `-y`, `--default` | Omite la CLI y arranca una nueva aplicación t3 con todas las opciones seleccionadas | | `--noInstall` | Generar proyecto sin instalar dependencias | ## Uso experimental Para nuestro CI, tenemos algunas banderas experimentales que te permiten crear cualquier aplicación sin indicaciones. Si este caso de uso se aplica a ti, puedes usar estas banderas. Ten en cuenta que estas banderas son experimentales y pueden cambiar en el futuro sin seguir las versiones de semver. | Bandera | Descripción | | ------------ | ------------------------------------- | | `--CI` | Informar a la CLI que está en modo CI | | `--trpc` | Incluir tRPC en el proyecto | | `--prisma` | Incluir Prisma en el proyecto | | `--nextAuth` | Incluir NextAuth.js en el proyecto | | `--tailwind` | Incluir Tailwind CSS en el proyecto | Si no proporcionas la bandera `CI`, el resto de estas banderas no tendrá efecto. No es necesario que se excluya explícitamente los paquetes que no deseas. Sin embargo, si prefieres ser explícito, puedes pasar `false`, ejemplo: `--nextAuth false`. ### Ejemplo Lo siguiente sería una aplicación T3 con tRPC y Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/es/introduction.md ================================================ --- title: Introducción description: Introducción al T3 Stack layout: ../../layouts/docs.astro lang: es ---
## El T3 Stack El _"T3 Stack"_ es un stack de desarrollo web creado por [Theo](https://twitter.com/t3dotgg) que está enfocado en simplicidad, modularidad, y seguridad (full-stack) de tipos. Las piezas centrales son [**Next.js**](https://nextjs.org/) y [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) está casi siempre incluído. Si estás haciendo algo parecido a un backend, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), y [**NextAuth.js**](https://next-auth.js.org/) son muy buenas adiciones también. Es posible que hayas notado que hay muchas... piezas, esto es por diseño. Intercambia piezas dentro y fuera según lo necesites. Este stack es modular desde la base :) ## Entonces... ¿Qué es create-t3-app? ¿Una plantilla? ¿Más o menos? `create-t3-app` es una CLI creada por desarrolladores experimentados del T3 Stack para agilizar la configuración de una aplicación modular T3 Stack. Esto significa que cada pieza es opcional, y la "plantilla" es generada en función de tus necesidades específicas. Después de innumerables proyectos y muchos años en esta tecnología, tenemos muchas opiniones e ideas. Hemos hecho todo lo posible para codificarlos en esta CLI. Esta **NO** es una plantilla con todo incluido. **Esperamos** que traigas tus propias librerías que resuelvan las necesidades de **TU** aplicación. Si bien no queremos prescribir soluciones a problemas más específicos, como la administración de estado o la implementación de la aplicación como tal, [tenemos algunas recomendaciones enumeradas aquí](/es/other-recs). ## Axiomas T3 Seremos francos: este es un _proyecto dogmático_. Compartimos muchas creencias fundamentales sobre la construcción y las tratamos como la base de nuestras decisiones. ### Resolver Problemas Es fácil caer en la trampa de "agregar todo"; explícitamente no queremos hacer eso. Todo lo agregado a `create-t3-app` debería resolver un problema específico que existe dentro de las tecnologías principales incluidas. Esto significa que no agregaremos cosas como librerías de manejo de estado (`zustand`, `redux`), pero agregaremos cosas como NextAuth.js e integraremos Prisma y tRPC para ti. ### Responsablemente Vanguardista Nos encanta nuestra tecnología de vanguardia. La velocidad y, sinceramente, la diversión que se obtiene de las nuevas tecnologías es realmente genial. Creemos que es importante ser vanguardista de manera responsable, usando tecnología más riesgosa en las partes con menos riesgo. Esto significa que no apostaríamos ⛔️ por una nueva tecnología de base de datos arriesgada (¡SQL es genial!). Pero felizmente ✅ apostamos por tRPC ya que son sólo funciones que son triviales de remover. ### La Seguridad de Tipos no es Opcional El objetivo declarado de `create-t3-app` es proporcionar la forma más rápida de iniciar una nueva aplicación web full-stack **segura en tipos**. Nos tomamos muy en serio la seguridad de tipos en todas las partes, ya que mejora nuestra productividad y nos ayuda a enviar menos errores a producción. Cualquier decisión que comprometa la seguridad de tipos de `create-t3-app` es una decisión que debe tomarse en un proyecto diferente. ================================================ FILE: www/src/pages/es/other-recs.md ================================================ --- title: Otras Recomendaciones description: Librerías y Servicios que nosotros recomendamos para todos los proyectos layout: ../../layouts/docs.astro lang: es --- Reconocemos que las librerías incluidas en `create-t3-app` no resuelven todos los problemas. Si bien te alentamos a comenzar tu proyecto con las cosas que proporcionamos, llegará un momento en que necesitarás traer otros paquetes. Solo tu puedes saber qué necesita tu proyecto, pero aquí hay algunas cosas que recomendamos con frecuencia. Estas son recomendaciones de colaboradores individuales de create-t3-app y no deben verse como respaldos "oficiales" por parte del equipo de create-t3-app o T3-OSS. _**Haz tu propia investigación, especialmente antes de comprometerte con servicios pagados**_. ## Administración de Estado _**Nota del editor**_: Las librerías de administración de estado pueden ser excelentes, pero a menudo no son necesarias. Los hooks React Query de tRPC deberían poder encargarse del estado de tu servidor. Para el estado del cliente, comienza con `useState` de React y busca una de estas opciones cuando necesites más. ### Zustand **Para no volver a usar Redux nunca más** El "Redux moderno y simple" que no sabías que necesitabas. Siempre se puede confiar en [Poimandres](https://github.com/pmndrs). Puede crear todo, desde aplicaciones de videollamadas hasta juegos y servidores con esta pequeña librería. - [Página de inicio de Zustand](https://zustand-demo.pmnd.rs/) - [GitHub de Zustand](https://github.com/pmndrs/zustand) ### Jotai **Para nunca volver a usar Context** Para un enfoque más atómico, Jotai es difícil de superar. También por [Poimandres](https://github.com/pmndrs), Jotai te permite definir singletons que se sienten como estado de uso global. Una excelente opción para comportamientos con estado que todavía no necesiten una máquina de estado. - [Página de inicio de Jotai](https://jotai.org/) - [GitHub de Jotai](https://github.com/pmndrs/jotai) ## Librerías de Componentes La mayoría de las aplicaciones necesitan la misma cantidad de componentes: botones de alternancia, menús desplegables, diálogos, etc. Estas librerías proporcionan componentes excelentes y accesibles que puedes usar y personalizar a tu gusto. ### Librerías de componentes sin estilo También conocidas como librerías headless, proporcionan excelentes componentes sin estilo y accesibles que puedes personalizar a tu gusto. Aquí hay algunas recomendaciones. - [Radix UI](https://www.radix-ui.com/) te brinda un poderoso conjunto de primitivas convenientes y accesibles que puedes diseñar con Vanilla o Tailwind CSS. - [Headless UI](https://headlessui.com/) creado por el equipo de Tailwind CSS también proporciona componentes accesibles y sin estilo que se integran a la perfección con Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) proporciona primitivas de interfaz de usuario accesibles para tu sistema de diseño. Su componente Selector de Fecha es de primer nivel. ### Librerías de componentes con estilo **Para cuando solo quieres que tu aplicación se vea bien** A veces, estás creando un proyecto en el que solo deseas que la interfaz de usuario se vea decente desde el primer momento. Para paneles de administración y otros proyectos similares, cualquiera de estas librerías de componentes hará el trabajo. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) ### Class Variance Authority **Para crear librerías de interfaz de usuario** Crea declarativamente una librería de interfaz de usuario con diferentes variaciones de color, tamaño, etc. Cuando tu proyecto alcanza un punto en el que deseas tener un conjunto estandarizado de componentes de interfaz de usuario con múltiples variaciones usando Tailwind CSS, CVA es una gran herramienta. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Animaciones Para cuando necesites animaciones en tu aplicación, aquí están nuestras recomendaciones. ### AutoAnimate **Para animaciones con una sola línea de código** La mayoría de las librerías de animación intentan satisfacer todos los casos de uso posibles y, como resultado, se vuelven torpes. AutoAnimate es una herramienta de cero configuración que te brindará una mejora significativa en UX sin esfuerzo adicional del desarrollador. - [Página de inicio de AutoAnimate](https://auto-animate.formkit.com/) - [GitHub de AutoAnimate](https://github.com/formkit/auto-animate) - [Ejemplo de un Componente con AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **Para animaciones complejas con código declarativo** Framer Motion proporciona una sintaxis declarativa simple y te permite escribir menos código para crear todo, desde animaciones complejas hasta incluso gestos. - [Página de inicio de Framer Motion](https://framer.com/motion) - [Documentación de Framer Motion](https://www.framer.com/docs/) ## Despliegues, Infraestructura, Bases de Datos y CI ### Vercel **Para alojar tu aplicación** Vercel tomó el infierno de los despliegues web y lo convirtió en una integración de GitHub de configurar y olvidar. Hemos escalado a cientos de miles de usuarios sin problemas. Impulsado por AWS, simplemente una interfaz mucho mejor :) - [Página de inicio de Vercel](https://vercel.com/) - [Guía Create T3 App para desplegar en Vercel](/es/deployment/vercel) ### PlanetScale **Para bases de datos sin preocupaciones** PlanetScale es la mejor "plataforma de base de datos sin servidor" que hemos usado hasta ahora. Escala increíblemente, excelente experiencia de desarrollador y precios fantásticos. Si estás utilizando SQL (y con suerte Prisma), esto es difícil de superar. - [Página de inicio de PlanetScale](https://planetscale.com/) ### Railway **Para alojar tu infraestructura** "Heroku moderno". La forma más fácil de poner en marcha un servidor real. Si Vercel y PlanetScale no son suficientes, probablemente Railway lo sea. Apúntalo a un repositorio de GitHub y listo. - [Página de inicio de Railway](https://railway.app/) ### Upstash **Para Redis sin servidor** Nos encantan Prisma y PlanetScale, pero algunos proyectos requieren una solución de mayor rendimiento. Upstash te permite obtener el rendimiento en memoria de Redis en tu proyecto sin servidor, sin tener que administrar la infraestructura y escalar por tu cuenta. - [Página de inicio de Upstash](https://upstash.com/) ### Pusher **Para WebSockets sin servidor** Si los WebSockets son el enfoque principal de tu proyecto, es posible que desees considerar un backend más tradicional como [Fastify](https://www.fastify.io/) (que [¡también funciona con tRPC!](https://trpc.io/docs/v10/fastify)). Pero para agregar rápidamente WebSockets a una aplicación T3, Pusher es una excelente opción. - [Página de inicio de Pusher](https://pusher.com/) ### Soketi Soketi es una alternativa autohospedable, simple y rápida a Pusher. Es totalmente compatible con Pusher SDK que puedes usar para conectarte al servidor. Soketi serverless también está en versión beta. - [Página de inicio de Soketi](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Análisis de Datos Los datos de los usuarios son muy valiosos cuando creas una aplicación. Aquí hay algunos proveedores de análisis que recomendamos. ### Plausible ¿Necesitas análisis? Plausible es una de las formas más rápidas de obtenerlos. Súper minimalista. Incluso tiene un [complemento simple para Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Página de inicio de Plausible](https://plausible.io/) ### Umami Umami es una alternativa a Google Analytics de código abierto, autohospedable, simple, rápida y centrada en la privacidad. Puedes desplegarlo muy fácilmente en Vercel, Railway, etc. con PlanetScale como tu base de datos. - [Página de inicio de Umami](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) ## Otros ### Next Bundle Analyzer A veces puede ser difícil determinar qué se incluirá en el resultado de compilación de tu aplicación. Next Bundle Analyzer es una manera fácil de visualizar y analizar los paquetes de JavaScript que se generan. - [@next/bundle-analyzer en npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/es/t3-collection.mdx ================================================ --- title: Colección T3 description: Proyectos geniales open source y compañias utilizando el T3 Stack layout: ../../layouts/docs.astro lang: es isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; ¿Hiciste un proyecto usando el T3 Stack y quieres compartirlo? ¡Añádelo a la lista! ## Aplicaciones open source creadas con el T3 Stack ## Empresas que utilizan el T3 Stack Nos encantaría saber de empresas que usan el T3 Stack para sus aplicaciones. ¿Tu empresa utiliza el T3 Stack y te gustaría compartirlo? ¡Añádelo a la lista! _¿Tienes un proyecto genial usando el T3 Stack? Haz una [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/pages/en/t3-collection.md) y agrégalo aquí._ ================================================ FILE: www/src/pages/es/usage/env-variables.md ================================================ --- title: Variables de Entorno description: Empezando con create-t3-app layout: ../../../layouts/docs.astro lang: es --- Create-T3-App usa [Zod](https://github.com/colinhacks/zod) para validar tus variables de entorno en tiempo de ejecución _y_ tiempo de compilación proporcionando algunos archivos adicionales en el directorio `env`: 📁 src/env ┣ 📄 client.mjs ┣ 📄 schema.mjs ┣ 📄 server.mjs El contenido de estos archivos puede parecer aterrador a primera vista, pero no te preocupes, no es tan complicado como parece. Echemos un vistazo a ellos uno por uno y recorramos el proceso de agregar variables de entorno adicionales. _En pocas palabras; Si deseas agregar una nueva variable de entorno, debes agregarla tanto a tu `.env` como definir el validador en `env/schema.mjs`._ ## schema.mjs Este es el archivo que realmente modificarás. Contiene dos esquemas, uno para las variables de entorno del lado del servidor y otro para el lado del cliente, así como un objeto `clientEnv`. ```ts:env/schema.mjs export const serverSchema = z.object({ // DATABASE_URL: z.string().url(), }); export const clientSchema = z.object({ // NEXT_PUBLIC_WS_KEY: z.string(), }); export const clientEnv = { // NEXT_PUBLIC_WS_KEY: process.env.NEXT_PUBLIC_WS_KEY, }; ``` ### Esquema del Servidor Define tu esquema de variables de entorno del lado del servidor aquí. Asegúrate de no anteponer el nombre de las variables de entorno aquí con `NEXT_PUBLIC`. La validación fallará si lo haces para ayudarte a detectar una configuración no válida. ### Esquema del Cliente Define tu esquema de variables de entorno del lado del cliente aquí. Para exponerlos al cliente, debes anteponerlos con `NEXT_PUBLIC`. La validación fallará si no lo haces para ayudarte a detectar una configuración no válida. ### Objeto clientEnv Destructuramos el `process.env` aquí. Necesitamos un objeto de JavaScript con el que podamos analizar nuestros esquemas de Zod y, debido a la forma en que Next.js maneja las variables de entorno, no puede destructurar `process.env` como un objeto normal, por lo que debemos hacerlo manualmente. TypeScript te ayudará a asegurarte de haber ingresado las claves tanto en `clientEnv` como en `clientSchema`. ```ts // ❌ Esto no funciona, necesitamos destructurarlo manualmente const schema = z.object({ NEXT_PUBLIC_WS_KEY: z.string(), }); const validated = schema.parse(process.env); ``` ## server.mjs & client.mjs Aquí es donde ocurre la validación y se exporta los objetos validados. No deberías necesitar modificar estos archivos. ## Utilizando las Variables de Entorno Cuando quieras utilizar tus variables de entorno, puedes importarlas desde `env/client.mjs` o `env/server.mjs` dependiendo de dónde quieras utilizarlas: ```ts:pages/api/hello.ts import { env } from "../../env/server.mjs"; // `env` es completamente seguro en tipos y provee autocompletado const dbUrl = env.DATABASE_URL; ``` ## .env.example Dado que el archivo `.env` predeterminado no está guardado en el control de versiones, también hemos incluido un archivo `.env.example`, en el que, de forma opcional, puedes guardar una copia de tu archivo `.env` con los valores secretos eliminados. Esto no es obligatorio, pero recomendamos mantener el ejemplo actualizado para que a los colaboradores les resulte lo más fácil posible comenzar con tu entorno. ## Agregando Variables de Entorno Para asegurarse de que tu compilación nunca se complete sin las variables de entorno que necesita el proyecto, deberás agregar nuevas variables de entorno en **dos** ubicaciones: 📄 `.env`: Introduce tu variable de entorno como lo harías normalmente en un archivo `.env`, es decir, `LLAVE=VALOR` 📄 `schema.mjs`: agrega la lógica de validación adecuada para la variable de entorno definiendo un esquema Zod, p. `CLAVE: z.string()` Opcionalmente, también puedes mantener `.env.example` actualizado: 📄 `.env.example`: ingresa tu variable de entorno, pero asegúrate de no incluir el valor si es secreto, es decir, `KEY=VALUE` o `KEY=` ### Ejemplo _Quiero agregar mi API token de Twitter como una variable de entorno del lado del servidor_ 1. Agrega la variable de entorno a `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Agrega la variable de entorno a `schema.mjs`: ```ts export const serverSchema = z.object({ // ... TWITTER_API_TOKEN: z.string(), }); ``` 3. Opcional: agrega la variable de entorno a `.env.example`, pero no incluyas el token ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/es/usage/first-steps.md ================================================ --- title: Primeros Pasos description: Empezando con tu nueva aplicación T3 layout: ../../../layouts/docs.astro lang: es --- Acabas de crear una nueva aplicación T3 y estás listo para comenzar. Aquí está lo mínimo para que tu aplicación funcione. ## Base de datos Si tu aplicación incluye Prisma, asegúrate de ejecutar `npx prisma db push` desde el directorio raíz de tu aplicación. Este comando sincronizará tu esquema de Prisma con tu base de datos y generará los tipos de TypeScript para el Prisma Client en función de tu esquema. Ten en cuenta que debes reiniciar el servidor TypeScript después de hacer esto para que pueda detectar los tipos generados. ## Autenticación Si tu aplicación incluye NextAuth.js, te ayudaremos a comenzar con `DiscordProvider`. Este es uno de los proveedores más simples que ofrece NextAuth.js, pero aún requiere un poco de configuración inicial de tu parte. Por supuesto, si prefieres usar un proveedor de autenticación diferente, también puedes usar uno de los [muchos proveedores](https://next-auth.js.org/providers/) que ofrece NextAuth.js. 1. Necesitarás una cuenta de Discord, así que crea una cuenta si aún no lo has hecho. 2. Dirígite a [https://discord.com/developers/applications](https://discord.com/developers/applications) y haz clic en "New Application" en la esquina superior derecha. Asigna un nombre a tu aplicación y acepta los términos de servicio. 3. Una vez creada tu aplicación, dirígite a "Settings → OAuth2 → General". 4. Copia el "Client ID" y agrégalo a tu `.env` como `AUTH_DISCORD_ID`. 5. Haz clic en "Reset Secret", copia el nuevo valor secreto y agrégalo a tu `.env` como `AUTH_DISCORD_SECRET`. 6. Haz clic en "Add Redirect" y escribe `http://localhost:3000/api/auth/callback/discord`. - Para la implementación de producción, sigue los pasos anteriores para crear otra aplicación Discord, pero esta vez reemplaza `http://localhost:3000` con la URL de producción en la que está implementando. 7. Guarda los cambios. Ahora deberías poder iniciar sesión. ## Siguientes pasos - Si tu aplicación incluye tRPC, consulta `src/pages/index.tsx` y `src/server/trpc/router/post.ts` para ver cómo funcionan las consultas de tRPC. - Echa un vistazo a la documentación de `create-t3-app`, así como la documentación de los paquetes que incluye tu aplicación. - ¡Únete a nuestro [Discord](https://t3.gg/discord) y danos una estrella en [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/es/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Uso", layout: "docs", description: "Aprende a utilizar las diferentes tecnologías del stack T3.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/es/usage/next-auth.md ================================================ --- title: NextAuth.js description: Uso de NextAuth.js layout: ../../../layouts/docs.astro lang: es --- Cuando desees utilizar un sistema de autenticación en tu aplicación Next.js, NextAuth.js es una excelente solución para incorporar la complejidad de la seguridad sin la molestia de tener que crearla por tu cuenta. Viene con una extensa lista de proveedores para agregar rápidamente la autenticación OAuth y proporciona adaptadores para muchas bases de datos y ORMs. ## Proveedor de contexto En el punto de entrada de tu aplicación, verás que tu aplicación está envuelta en un [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx ``` Este proveedor de contexto permite que tu aplicación acceda a los datos de la sesión desde cualquier lugar de tu aplicación, sin tener que pasarlos como propiedades: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Manejar el estado no autenticado, ejemplo: renderizar un componente SignIn return ; } return

Bienvenido {session.user.name}!

; }; ``` ## Incluir `user.id` en la Sesión `create-t3-app` está configurado para utilizar el [callback de sesión](https://next-auth.js.org/configuration/callbacks#session-callback) en la configuración de NextAuth.js para incluir el ID del usuario dentro del objeto `session`. ```ts:pages/api/auth/[...nextauth].ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Esto se combina con un archivo de declaración de tipos para asegurarse de que se pueda escribir `user.id` cuando se acceda al objeto `session`. Obtén más información sobre [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) en la documentación de NextAuth.js. ```ts:types/next-auth.d.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` El mismo patrón se puede usar para agregar cualquier otro dato al objeto `session`, como un campo `role`, pero **no se debe usar incorrectamente para almacenar datos confidenciales** en el cliente. ## Uso con tRPC Cuando utilices NextAuth.js con tRPC, puedes crear procedimientos protegidos reutilizables usando [middleware](https://trpc.io/docs/v10/middlewares). Esto te permite crear procedimientos a los que solo los usuarios autenticados pueden acceder. `create-t3-app` establece todo esto para ti, lo que te permite acceder fácilmente al objeto de sesión dentro de los procedimientos autenticados. Esto se hace en un proceso de dos pasos: 1. Toma la sesión de las cabeceras de la solicitud utilizando la función [`unstable_getServerSession`](https://next-auth.js.org/configuration/nextjs#unstable_getserversession). No te preocupes, esta función es segura: el nombre incluye `unstable` (_inestable_) solo porque la implementación de la API podría cambiar en el futuro. La ventaja de usar `unstable_getServerSession` en lugar de la función `getSession` regular es que es una función solo del lado del servidor y no activa llamadas innecesarias. `create-t3-app` crea una función de ayuda que abstrae a esta API peculiar. ```ts:server/common/get-server-auth-session.ts export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return await unstable_getServerSession(ctx.req, ctx.res, nextAuthOptions); }; ``` Usando esta función auxiliar, podemos obtener la sesión y pasarla al contexto de tRPC: ```ts:server/trpc/context.ts import { getServerAuthSession } from "../common/get-server-auth-session"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Crea un middleware tRPC que verifique si el usuario está autenticado. Luego usamos el middleware en una `protectedProcedure`. Cualquier persona que llama a estos procedimientos debe de estar autenticada, o de lo contrario se lanzará un error que el cliente puede manejar adecuadamente. ```ts:server/trpc/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }) ``` El objeto de sesión es una representación ligera y mínima del usuario y solo contiene algunos campos. Cuando uses `protectedProcedures`, tienes acceso al ID del usuario que puede usarse para obtener más datos de la base de datos. ```ts:server/trpc/router/user.ts const userRouter = router({ me: protectedProcedure.query(({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Uso con Prisma Hacer que NextAuth.js funcione con Prisma requiere una gran cantidad de [configuración inicial](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` maneja todo esto para ti, y si seleccionas Prisma y NextAuth.js, obtendrás un sistema de autenticación completamente funcional con todos los modelos requeridos preconfigurados. Creamos tu aplicación con un proveedor de Discord Oauth preconfigurado, que elegimos porque es uno de los más fáciles de comenzar, solo proporciona tus tokens en el `.env` y listo. Sin embargo, puedes agregar fácilmente más proveedores siguiendo la documentación de [NextAuth.JS](https://next-auth.js.org/providers/). Ten en cuenta que ciertos proveedores requieren que se agregen campos adicionales a ciertos modelos. Te recomendamos que leas la documentación del proveedor que deseas utilizar para asegurarte de tener todos los campos requeridos. ### Agregar nuevos campos a tus modelos Al agregar nuevos campos a cualquiera de los modelos `User`, `Account`, `Session` o `VerificationToken` (lo más probable es que solo necesites modificar el modelo `User`), debes tener en cuenta que el [Adaptador de Prisma](https://next-auth.js.org/adapters/prisma) crea automáticamente campos en estos modelos cuando los nuevos usuarios se registran e inician sesión. Por lo tanto, al agregar nuevos campos a estos modelos, debes proporcionar un valor predeterminado por defecto para ellos, ya que el adaptador no es consciente de estos campos. Si, por ejemplo, deseas agregar un campo `role` al modelo `User`, necesitarías proporcionar un valor predeterminado al campo `role`. Esto se hace agregando un valor `@default` al campo `role` en el modelo `User`: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Uso con el middleware Next.js El uso de NextAuth.js con el middleware Next.js [requiere el uso de la estrategia de sesión JWT](https://next-auth.js.org/configuration/nextjs#caveats) para la autenticación. Esto se debe a que el middleware solo puede acceder a la cookie de sesión si es un JWT. De forma predeterminada, `create-t3-app` está configurado para usar la estrategia de base de datos **predeterminada**, en combinación con Prisma como adaptador de base de datos. ## Configuración del DiscordProvider predeterminado 1. Dirígete a [la sección de aplicaciones en el portal del desarrollador de Discord](https://discord.com/developers/applications) y haz clic en "New Application" 2. En el menú de configuración, dirígite a "OAuth2 => General" - Copia el Client ID y pégalo en `AUTH_DISCORD_ID` en `.env`. - En Client Secret, haz clic en "Reset Secret" y copia ese string en `AUTH_DISCORD_SECRET` en `.env`. Ten cuidado ya que no podrás volver a ver este valor secreto, y restablecerlo hará que el existente expire. - Haz clic en "Add Redirect" y pega en `/api/auth/callback/discord` (Ejemplo para desarrollo local: http://localhost:3000/api/auth/callback/discord) - Guarda tus cambios - Es posible, pero no recomendado, usar la misma aplicación de Discord tanto para desarrollo como para producción. También puedes considerar hacer un [mock del proveedor](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) durante el desarrollo. ## Recursos útiles | Recurso | Enlace | | --------------------------------- | --------------------------------------- | | Documentación de NextAuth.js | https://next-auth.js.org/ | | GitHub de NextAuth.js | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/es/usage/next-js.md ================================================ --- title: Next.js description: Uso de Next.js layout: ../../../layouts/docs.astro lang: es --- Next.js es un framework de backend para tus aplicaciones de React.
Échale un vistazo a la [charla de Theo en la Next.js Conf](https://www.youtube.com/embed/W4UhNo3HAMw) para comprender mejor qué es Next.js y cómo funciona. ## ¿Por qué debería usarlo? Amamos React. Ha hecho que el desarrollo de interfaces de usuario sea accesible de una manera que nunca antes imaginamos. También puede llevar a los desarrolladores por algunos caminos difíciles. Next.js ofrece un enfoque ligeramente dogmático y muy optimizado para crear aplicaciones utilizando React. Desde el enrutamiento, las definiciones de APIs hasta la representación de imágenes, confiamos que Next.js lleve a los desarrolladores hacia la toma de buenas decisiones. Utilizar Next.js junto con [Vercel](https://vercel.com/) hace que el desarrollo y despliegue de aplicaciones web sea más fácil que nunca. Su opción gratuita extremadamente generosa y su interfaz super intuitiva proporcionan una solución de un solo clic para desplegar tu sitio (nosotros ❤️ Vercel) ## Get Static/Server Props Una característica clave de Next.js son sus capacidades de obtención de datos. Recomendamos fuertemente leer la [documentación oficial](https://nextjs.org/docs/basic-features/data-fetching) para comprender cómo usar cada método y cómo difieren. `getServerSideProps` generalmente se desaconseja a menos que haya una buena razón para ello, debido al hecho de que es una llamada bloqueante y reducirá el rendimiento de tu sitio. La [regeneración estática incremental](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) es una gran alternativa a `getServerSideProps` cuando los datos son dinámicos y se pueden obtener incrementalmente. ## Recursos útiles | Recurso | Enlace | | ---------------------------------- | ---------------------------------- | | Documentación de Next.js | https://nextjs.org/docs | | GitHub de Next.js | https://github.com/vercel/next.js | | Blog de Next.js | https://nextjs.org/blog | | Discord de Next.js | https://nextjs.org/discord | | Twitter de Next.js | https://twitter.com/nextjs | | Canal de Youtube de Vercel/Next.js | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/es/usage/prisma.md ================================================ --- title: Prisma description: Uso de Prisma layout: ../../../layouts/docs.astro lang: es --- Prisma es un ORM para TypeScript, que te permite definir el esquema de tu base de datos y los modelos en un archivo `schema.prisma`, y luego generar un cliente seguro en tipos que se puede usar para interactuar con tu base de datos desde tu backend. ## Prisma Client Ubicado en `/server/db/client.ts`, el Prisma Client se instancia como una variable global (como se recomienda como [mejor práctica](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) por el equipo de Prisma) y exportado para ser utilizado en tus rutas API. Incluimos el Prisma Client en el [contexto](/es/usage/trpc#-serverapitrpcts) de tRPC de forma predeterminada y recomendamos usar esto en lugar de importarlo por separado en cada archivo. ## Esquema Encontrarás el archivo de esquema de Prisma en `/prisma/schema.prisma`. Este archivo es donde defines el esquema y los modelos de tu base de datos, y se usa al generar el cliente Prisma. ### Con NextAuth.js Cuando seleccionas NextAuth.js en combinación con Prisma, el archivo de esquema se genera y se configura para ti con los valores recomendados para los modelos `User`, `Session`, `Account` y `VerificationToken`, según la [documentación de NextAuth.js](https://next-auth.js.org/adapters/prisma). ## Base de datos predeterminada La base de datos predeterminada es una base de datos SQLite, que es excelente para el desarrollo y para crear rápidamente una prueba de concepto, pero no se recomienda para producción. Puede cambiar la base de datos a utilizar cambiando el `provider` en el bloque `datasource` a `postgresql` o` mysql`, y luego actualizando el string de conexión dentro de las variables de entorno para apuntar a tu base de datos. ## Llenando (seeding) tu base de datos [Llenar (_seeding_) tu base de datos](https://www.prisma.io/docs/guides/database/seed-database) es una excelente manera de llenar (_seed_) rápidamente tu base de datos con datos de prueba para ayudarte a comenzar. Para configurar el llenado, deberás crear un archivo `seed.ts` en el directorio `/prisma`, y luego agregar un script `seed` a tu archivo `package.json`. También necesitarás algún corredor de TypeScript que pueda ejecutar el script de llenado. Recomendamos [tsx](https://github.com/esbuild-kit/tsx), que es un corredor de TypeScript muy optimizado que usa esbuild y que no requiere ninguna configuración de ESM, pero `ts-node` u otros corredores pueden funcionar también. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db/client"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Luego, simplemente ejecuta `pnpm db-seed` (o `npm`/`yarn`) para llenar (_seed_) tu base de datos. ## Recursos útiles | Recurso | Enlace | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Documentación de Prisma | https://www.prisma.io/docs/ | | GitHub de Prisma | https://github.com/prisma/prisma | | Adaptador Prisma para NextAuth.JS | https://next-auth.js.org/adapters/prisma | | Guía de conexión de PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/es/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Uso de Tailwind CSS layout: ../../../layouts/docs.astro lang: es --- ## ¿Qué es Tailwind CSS? Tailwind CSS es un pequeño framework, [_utility-first_](https://tailwindcss.com/docs/utility-first), de CSS para construir diseños personalizados, sin el cambio de contexto que requiere el CSS normal. Es puramente un framework de CSS y no proporciona componentes o lógica preconstruida, y proporciona [un conjunto muy diferente de beneficios](https://www.youtube.com/watch?v=CQuTF-bkOgc) en comparación con una librería de componentes como Material UI. Hace que CSS sea increíblemente fácil y rápido de escribir, como se muestra en el siguiente ejemplo: Viejo CSS: 1. Escribir CSS, generalmente en un archivo separado ```css .mi-clase { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Importar el CSS en tu componente ```jsx import "./mi-clase.css"; ``` 3. Agrega la clase a tu HTML ```html
...
``` Equivalente en Tailwind: 1. Simplemente escribe clases en tu HTML ```html
...
``` Cuando se usa junto con los componentes de React, es extremadamente potente para construir UIs (_interfaces de usuario_) rápidamente. Tailwind CSS tiene un hermoso sistema de diseño incorporado, que sale de la caja con una paleta de colores cuidadosamente elegida, patrones de dimensionamiento para estilos como width/height y padding/margin para un diseño uniforme, así como puntos de quiebre para crear diseños responsive. Este sistema de diseño se puede personalizar y extender para crear la caja de herramientas exacta de los estilos que necesita tu proyecto.
Tru Narla mejor conocida como [mewtru](https://twitter.com/trunarla) dio una charla increíble de [cómo construir un sistema de diseño utilizando Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Uso Asegúrate de tener complementos de editor para Tailwind CSS instalados para mejorar tu experiencia escribiendo Tailwind. ### Extensiones y complementos - [Extensión VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [Integración de JetBrains](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Formateo Las clases CSS de Tailwind pueden volverse un poco caóticas fácilmente, por lo que es imprescindible un formateo para las clases. [El complemento de Prettier de Tailwind CSS](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) clasifica las clases en el [orden recomendado](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) para que las clases coincidan con el paquete (_bundle_) CSS generado. Al seleccionar Tailwind CSS en la CLI, instalaremos y configuraremos esto para ti. ### Aplicando clases condicionalmente Agregar condicionalmente clases con operadores ternarios puede ser muy desordenado y difícil de leer. Estos paquetes ayudan a organizar tus clases cuando se usa lógica condicional. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Recursos útiles | Recurso | Enlace | | -------------------------------- | -------------------------------------------------------- | | Documentación de Tailwind | https://tailwindcss.com/docs/editor-setup/ | | Tailwind Cheat Sheet | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Comunidad de Tailwind | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Servidor Discord de Tailwind | https://tailwindcss.com/discord/ | | Canal de Youtube de TailwindLabs | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/es/usage/trpc.md ================================================ --- title: tRPC description: Uso de tRPC layout: ../../../layouts/docs.astro lang: es --- tRPC nos permite escribir APIs con seguridad de tipos de extremo a extremo sin ninguna generación de código o con un incremento en el tiempo de ejecución. Utiliza la increíble inferencia de TypeScript para inferir las definiciones de tipos de tu enrutador API y te permite llamar a tus procedimientos de API desde tu frontend con seguridad de tipos y con autocompletado. Al usar TRPC, tu backend y frontend se sienten más juntos que nunca, lo que permite una experiencia de desarrollo excepcional.

I built tRPC to allow people to move faster by removing the need of a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.

Avatar of @alexdotjs
Alex - creador de tRPC @alexdotjs
## Archivos tRPC requiere una gran cantidad de archivos que `create-t3-app` genera para ti. Revisemos los archivos que se generan: ### 📄 `pages/api/trpc/[trpc].ts` Este es el punto de entrada para tu API y expone el enrutador tRPC. Normalmente, no tocarás mucho este archivo, pero si necesitas, por ejemplo, habilitar el middleware CORS o similar, es útil saber que el `createNextApiHandler` exportado es un [manejador API de Next.js](https://nextjs.org/docs/api-routes/introduction) que tiene un objeto de [solicitud](https://developer.mozilla.org/en-us/docs/web/api/request) y [respuesta](https://developer.mozilla.org/en-US/docs/Web/API/Response?retiredLocale=sv-SE). Esto significa que puedes envolver el `createNextApiHandler` en cualquier middleware que desees. Consulta a continuación un [fragmento de ejemplo](#habilitando-cors) de agregar CORS. ### 📄 `server/trpc/context.ts` Este archivo es donde defines el contexto que se pasa a tus procedimientos tRPC. El contexto son datos a los que todos tus procedimientos tRPC tendrán acceso, y es un excelente lugar para poner cosas como conexiones de bases de datos, información de autenticación, etc. En create-t3-app, usamos dos funciones, para habilitar el uso de un subconjunto del contexto cuando no tenemos acceso al objeto de la solicitud. - `createContextInner`: aquí es donde defines el contexto que no depende de la solicitud, ejemplo: tu conexión de base de datos. Puedes usar esta función para [Pruebas de integración](#muestra-integración-test) o [ssg-helpers](https://trpc.io/docs/v10/ssg-helpers) donde no tienes un objeto de solicitud. - `createContext`: Aquí es donde defines el contexto que depende de la solicitud, ejemplo: la sesión del usuario. Solicita la sesión usando el objeto `opts.req` y luego pasa la sesión a la función `createContextInner` para crear el contexto final. ### 📄 `server/trpc/trpc.ts` Aquí es donde inicializas tRPC y defines [procedimientos](https://trpc.io/docs/v10/procedures) reutilizables y [middlewares](https://trpc.io/docs/v10/middlewares). Por convención, no debes exportar todo el objeto `t`, sino crear procedimientos y middlewares reutilizables y exportarlos. Notarás que usamos `superjson` como [transformador de datos](https://trpc.io/docs/v10/data-transformers). Esto hace que tus tipos de datos se conserven cuando llegan al cliente, por lo que si por ejemplo envías un objeto `Date`, el cliente devolverá un `Date` y no una string, que es el caso para la mayoría de las APIs. ### 📄 `server/trpc/router/*.ts` Aquí es donde defines las rutas y procedimientos de tu API. Por convención, tu deberías [crear enrutadores separados](https://trpc.io/docs/v10/router) para procedimientos relacionados, luego [fusionar](https://trpc.io/docs/v10/merging-routers) todos ellos en un solo enrutador en `servidor/trpc/router/_app.ts`. ### 📄 `utils/trpc.ts` Este es el punto de entrada frontend para tRPC. Aquí es donde importarás la definición de tipo **del enrutador** y crearás tu cliente tRPC junto con los hooks react-query. Dado que habilitamos `superjson` como nuestro transformador de datos en el backend, también debemos habilitarlo en el frontend. Esto se debe a que los datos serializados del backend están deserializados en la parte del frontend. Definirás tus [enlaces](https://trpc.io/docs/v10/links) tRPC aquí, que determinan el flujo de la solicitud del cliente al servidor. Utilizamos el "predeterminado" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpbatchlink) que habilita [solicitudes en conjunto](https://cloud.google.com/compute/docs/api/how-tos/batch), así como un [`loggerLink`](https://trpc.io/docs/v10/links/loggerlink) que genera registros de solicitudes útiles durante el desarrollo. Por último, exportamos un [tipo de ayuda](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type) que puedes usar para inferir tus tipos en el frontend. ## ¿Cómo uso tRPC?
El contribuidor de tRPC [trashh_dev](https://twitter.com/trashh_dev) dió [una charla en la Next.js conf](https://www.youtube.com/embed/2LYM8gf184U) sobre tRPC. Te recomendamos que lo veas si aún no lo has hecho. Con tRPC, escribes funciones TypeScript en tu backend y luego las llamas desde tu frontend. Un simple procedimiento de tRPC podría verse así: ```ts:server/trpc/router/user.ts const userRouter = t.router({ getById: t.procedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Este es un procedimiento TRPC (equivalente a un manejador de ruta en un backend tradicional) que primero valida la entrada usando Zod (que es la misma librería de validación que utilizamos para las [variables de entorno](./env-variables)) - en este caso, se asegura de que la entrada sea un string. Si la entrada no es un string, enviará un error informativo en su lugar. Después de la entrada, encadenamos una función de resolución que puede ser una [consulta](https://trpc.io/docs/v10/react-queries), [mutación](https://trpc.io/docs/v10/react-mutations), o una [suscripción](https://trpc.io/docs/v10/subscriptions). En nuestro ejemplo, la función de resolución llama a nuestra base de datos utilizando nuestro cliente [prisma](./prisma) y devuelve al usuario cuyo `id` coincide con el que pasamos. Tu defines tus procedimientos en `routers` que representan una colección de procedimientos relacionados con un nombre compartido. Es posible que tengas un enrutador para `users`, uno para `posts` y otro para `messages`. Estos enrutadores se pueden fusionar en un único `appRouter` centralizado: ```ts:server/trpc/router/_app.ts const appRouter = t.router({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Ten en cuenta que solo necesitamos exportar las definiciones de tipo de nuestro enrutador, lo que significa que nunca estamos importando ningún código de servidor en nuestro cliente. Ahora llamemos al procedimiento en nuestro frontend. tRPC proporciona un wrapper para `@tanstack/react-query` que te permite utilizar la potencia completa de los hooks que proporcionan, pero con el beneficio adicional de tener tus llamadas API seguras en tipos e inferidas. Podemos llamar a nuestros procedimientos desde nuestro frontend de la misma manera: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; const UserPage = () => { const { query } = useRouter(); const userQuery = trpc.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Inmediatamente notarás lo bueno que es el autocompletado y la seguridad tipos. Tan pronto como escribas `trpc.`, tus enrutadores aparecerán como sugerencias de autocompletado, y cuando selecciones un enrutador, tus procedimientos también aparecerán. También recibirás un error de TypeScript si tu entrada no coincide con el validador que se definió en el backend. ## ¿Cómo llamo a mi API externamente? Con APIs regulares, puedes llamar a tus endpoints utilizando cualquier cliente HTTP como `curl`, `Postman`, `fetch` o directamente desde tu navegador. Con tRPC, es un poco diferente. Si deseas llamar a tus procedimientos sin el cliente tRPC, hay dos formas recomendadas de hacerlo: ### Exponer un solo procedimiento externamente Si deseas exponer un solo procedimiento externamente, estás buscando [llamadas del lado del servidor](https://trpc.io/docs/v10/server-side-calls). Eso te permitirá crear un endpoint API normal Next.js, pero reutilizará la parte de la resolución de tu procedimiento tRPC. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Exponer cada procedimiento como endpoints REST Si deseas exponer cada procedimiento externamente, consulta el complemento construido por la comunidad [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Al proporcionar algunos metadatos adicionales a tus procedimientos, puedes generar una API REST compatible con OpenAPI desde tu enrutador tRPC. ### Son solo solicitudes HTTP tRPC se comunica a través de HTTP, por lo que también es posible llamar a tus procedimientos tRPC utilizando solicitudes HTTP "regulares". Sin embargo, la sintaxis puede ser engorrosa debido al [protocolo RPC](https://trpc.io/docs/v10/RPC) que tRPC usa. Si tienes curiosidad, puedes verificar cómo se ven las solicitudes y respuestas de tRPC en la pestaña de red de tu navegador, pero sugerimos hacerlo solo como un ejercicio educativo y apegarse a una de las soluciones descritas anteriormente. ## Comparación con un API endpoint de Next.js Comparemos un API endpoint de Next.js con un procedimiento tRPC. Supongamos que queremos obtener un objeto de usuario de nuestra base de datos y devolverlo al frontend. Podríamos escribir un API endpoint Next.js como este: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db/client"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Compara esto con el ejemplo de tRPC anterior, para poder ver algunas de las ventajas de tRPC: - En lugar de especificar una URL para cada ruta, que puede volverse molesto para depurar si mueves algo, todo tu enrutador es un objeto con autocompletado. - No necesitas validar qué método HTTP se utilizó. - No necesitas validar que la consulta de solicitud o el cuerpo contenga los datos correctos en el procedimiento, porque Zod se encarga de esto. - En lugar de crear una respuesta, puedes lanzar errores y devolver un valor u objeto como lo harías en cualquier otra función de TypeScript. - Llamar al procedimiento en el frontend proporciona autocompletado y seguridad de tipos. ## Fragmentos útiles Aquí hay algunos fragmentos que pueden ser útiles. ### Habilitando CORS Si necesitas consumir tu API desde un dominio diferente, por ejemplo, en un monorepo que incluye una aplicación React Native, es posible que debas habilitar CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/trpc/router/_app"; import { createContext } from "~/server/trpc/context"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Habilita cors await cors(req, res); // Crea y llama al manejador tRPC return createNextApiHandler({ router: appRouter, createContext, })(req, res); }; export default handler; ``` ### Actualizaciones optimistas Las actualizaciones optimistas son cuando actualizamos la interfaz de usuario antes de que la llamada API haya terminado. Esto le da al usuario una mejor experiencia porque no tiene que esperar a que la llamada API termine antes de que la interfaz de usuario refleje el resultado de tu acción. Sin embargo, las aplicaciones que valoran el estado correcto de los datos deben evitar actualizaciones optimistas, ya que no son una representación "verdadera" del estado del backend. Puede leer más en la documentación de [React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = trpc.post.list.useQuery(); const utils = trpc.useContext(); const postCreate = trpc.post.create.useMutation({ async onMutate(newPost) { // Cancela las solicitudes de salida (para que no sobrescriban nuestras actualizaciones optimistas) await utils.post.list.cancel(); // Obtener los datos del queryCache const prevData = utils.post.list.getData(); // Optimisticamente actualizamos los datos con nuestro nuevo post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Retornamos los datos previos para que podamos revertirlo si algo sale mal return { prevData }; }, onError(err, newPost, ctx) { // Si la mutación falla, utilizar el valor del contexto de onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sincronizamos el servidor una vez la mutación se haya completado utils.post.list.invalidate(); }, }); }; ``` ### Prueba de integración de muestra Aquí hay una prueba de integración de muestra que utiliza [Vitest](https://vitest.dev) para verificar que tu enrutador tRPC funcione como se esperaba, el analizador de entrada infiere el tipo correcto y que los datos devueltos coincidan con la salida esperada. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/router/_app"; import { createContextInner } from "~/server/router/context"; test("example router", async () => { const ctx = await createContextInner({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` ## Recursos útiles | Recurso | Enlace | | ----------------------------- | ------------------------------------------------------- | | Documentación de tRPC | https://www.trpc.io | | Un montón de ejemplos de tRPC | https://github.com/trpc/trpc/tree/next/examples | | Documentación de React Query | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/es/usage/typescript.md ================================================ --- title: TypeScript description: Uso de TypeScript layout: ../../../layouts/docs.astro lang: es ---

Build safety nets, not guard rails

Avatar of @t3dotgg
Theo - creador del T3 Stack @t3dotgg
Ya sea que seas un desarrollador nuevo o experimentado, creemos que TypeScript es imprescindible. Al principio puede parecer intimidante, pero al igual que muchas herramientas, es algo de lo que muchos nunca miran hacia atrás después de comenzar a usarlo. Proporciona comentarios en vivo a medida que escribes tu código definiendo los tipos de datos esperados, y proporciona un servicio automático útil en tu editor de código, o te dice con líneas rojas curveadas si estás intentando acceder a una propiedad que no existe o intentas pasar un valor del tipo incorrecto, que de otro modo tendrías que depurar más adelante. Es, tal vez, la herramienta que proporciona la mayor productividad a los desarrolladores; Proporciona documentación del código que estás escribiendo o consumiendo directamente en tu editor, y tener comentarios instantáneos a medida que inevitablemente cometes errores no tiene precio. ## Inferencia de Tipos Si bien muchos desarrolladores de TypeScript nuevos se preocupan por _escribir_ TypeScript, muchos de sus beneficios en realidad no requieren que cambies tu código en absoluto, en particular la inferencia. La inferencia significa que si se escribe algo, ese tipo seguirá siendo el mismo durante todo el flujo de la aplicación sin tener que volver a declararlo en otros lugares.Esto significa que, por ejemplo, una vez que hayas definido los tipos de los argumentos que toma una función, el resto de la función generalmente será segura en tipos sin requerir ningún código más específico de TypeScript. Los desarrolladores de librerías pusieron un montón de trabajo en el mantenimiento de los tipos de sus librerías, lo que significa que nosotros, como desarrolladores de aplicaciones, podemos beneficiarnos tanto de la inferencia como de la documentación incorporada en tu editor de código que proporcionan estos tipos.
Echa un vistazo al video de Theo sobre cómo [podrías estar usando TypeScript de manera incorrecta](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Usos poderosos de inferencia de tipos ### Zod [Zod](https://github.com/colinhacks/zod) es una librería de validación de esquema que se basa en TypeScript. Escribe un esquema que represente una sola fuente de verdad para tus datos, y Zod se asegurará de que tus datos sean válidos en toda tu aplicación, incluso a través de los límites de la red y las APIs externas. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) te ofrece consultas y mutaciones auto-gestionadas, siempre actualizadas, que mejoran directamente tu experiencia de desarrollo y usuario. ## Recursos útiles | Recurso | Enlace | | ------------------------------------------------------------- | ----------------------------------------------------------------- | | Manual de TypeScript | https://www.typescriptlang.org/docs/handbook/ | | Tutorial de TypeScript para principiantes | https://github.com/total-typescript/beginners-typescript-tutorial | | Type Challenges | https://github.com/type-challenges/type-challenges | | Canal de Youtube de Rodney Mullen de TypeScript (Matt Pocock) | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/es/why.md ================================================ --- title: ¿Por qué CT3A? description: ¿Por qué deberías elegir Create T3 App para tu siguiente proyecto? layout: ../../layouts/docs.astro lang: es --- Empezamos create-t3-app porque [Theo](https://twitter.com/t3dotgg) se negó a hacer una plantilla de sus tecnologías favoritas. Inspirándose en create-next-app, [CLI de Astro](https://astro.build) y un amor general por la seguridad de tipos, el equipo de create-t3-app trabajó arduamente para crear el mejor punto de partida posible para los nuevos proyectos de T3 Stack. Si estás interesado en usar Next.js de forma segura, este es el lugar para comenzar. Si tienes curiosidad acerca de alguna de las opciones tecnológicas específicas que tomamos, sigue leyendo :) ## ¿Por qué TypeScript? JavaScript es difícil. ¿Por qué agregar más reglas? Creemos firmemente que la experiencia que brinda TypeScript te ayudará a ser un mejor desarrollador. Proporciona retroalimentación en vivo a medida que escribes tu código definiendo los tipos de datos esperados, y proporciona un autocompletado útil en tu editor o te grita con líneas onduladas rojas si estás tratando de acceder a una propiedad que no existe o tratando de pasar un valor del tipo incorrecto, que de otro modo tendrías que depurar más adelante. Ya sea que seas nuevo en el desarrollo web o un profesional experimentado, el "rigor" de TypeScript te brindará una experiencia menos frustrante y más consistente que JS estándar. La seguridad de tipos te hace más rápido. Si no estás convencido, [podrías estar usando TypeScript de forma incorrecta...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## ¿Por qué Next.js? Nos encanta React. Ha hecho que el desarrollo de la interfaz de usuario sea accesible de maneras que nunca antes habíamos imaginado. También puede llevar a los desarrolladores por caminos difíciles. Next.js ofrece un enfoque ligeramente obstinado y muy optimizado para crear aplicaciones usando React. Desde el enrutamiento hasta las definiciones de API y la representación de imágenes, confiamos en Next.js para guiar a los desarrolladores hacia buenas decisiones. ## ¿Por qué tRPC/Prisma/Tailwind/etc? Si bien creemos en mantener las cosas lo más simples posible, encontramos que estas piezas se utilizan en cada proyecto similar a una "aplicación" que construimos. `create-t3-app` hace un gran trabajo al permitirte adoptar las piezas que necesitas. ### tRPC tRPC cumple con la promesa de GraphQL de un desarrollo de cliente sin interrupciones contra un servidor seguro en tipos sin todo lo repetitivo. Es un abuso inteligente de TypeScript que proporciona una experiencia de desarrollo increíble. ### Prisma Prisma es para SQL lo que TypeScript es para JS. Creó una experiencia de desarrollo que no existía antes. Al generar tipos a partir de un esquema definido por el usuario compatible con [varias bases de datos](https://www.prisma.io/docs/concepts/database-connectors), Prisma garantiza la seguridad de tipos de extremo a extremo desde su base de datos hasta su aplicación. Prisma proporciona un [conjunto de herramientas](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) completo, facilitando las interacciones diarias con tu base de datos. En particular, Prisma Client es responsable de consultar y hacer que SQL sea tan fácil que apenas notarás que lo estás usando, y Prisma Studio es una GUI conveniente para tu base de datos que te permite leer y manipular sus datos rápidamente sin tener que escribir código. ### Tailwind CSS Tailwind se siente como "CSS en modo zen". Al proporcionar bloques de construcción en forma de buenos colores predeterminados, espaciado y otras primitivas, Tailwind facilita la creación de una aplicación atractiva. Y a diferencia de las librerías de componentes, no te detiene cuando deseas llevar tu aplicación al siguiente nivel y crear algo hermoso y único. Además, con su enfoque en línea, Tailwind te alienta a diseñar sin preocuparte por nombrar clases, organizar archivos o cualquier otro problema que no esté directamente relacionado con el problema que estás tratando de resolver. ### NextAuth.js Cuando desees un sistema de autenticación en tu aplicación NextJS, NextAuth.js es una excelente solución para incorporar la complejidad de la seguridad sin la molestia de tener que construirlo por tu cuenta. Viene con una extensa lista de proveedores para agregar rápidamente la autenticación OAuth y proporciona adaptadores para muchas bases de datos y ORM. ================================================ FILE: www/src/pages/fr/deployment/docker.md ================================================ --- title: Docker description: Déploiement avec Docker layout: ../../../layouts/docs.astro lang: fr --- Vous pouvez containeriser cette stack et la déployer en tant que conteneur unique à l'aide de Docker, ou en tant que partie d'un ensemble de conteneurs en utilisant docker-compose. Voir [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) pour un exemple basé sur cette documentation. ## Configuration du projet sous Docker Veuillez noter que Next.js nécessite des variables d’environnements qui sont différentes entre le processus de génération (celles-ci vont être disponible seulement côté navigateur, et sont préfixées par `NEXT_PUBLIC`) et dans l’environnement d’exécution, qui est côté serveur seulement. Dans cette démo nous utilisons deux variables. Prêtez attention à leurs positions dans les fichiers `Dockerfile`, `docker-compose.yml` et dans la ligne de commande. - `DATABASE_URL` (utiliser par le serveur) - `NEXT_PUBLIC_CLIENTVAR` (utiliser par le client) ### 1. Configuration de Next Dans votre fichier [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), ajouter l'entrée `output` avec comme valeur `standalone` [réduit la taille de l'image Docker en se basant sur la sortie du processus de génération](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Créer un fichier dockerignore
Cliquez ici et incluez le contenue dans votre .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Créer un fichier Dockerfile > Étant donné que nous ne récupérons pas les variables d'environnement du serveur dans notre conteneur, la [validation du schéma d'environnement](/fr/usage/env-variables) échouera. Pour éviter cela, nous devons ajouter l'argument `SKIP_ENV_VALIDATION=1` à la commande de génération afin que les schémas d'environnement ne soient pas validé à ce moment là.
Cliquez ici et incluez le contenue dans votre Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Install Prisma Client - remove if not using Prisma COPY prisma ./ # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Notes_** > > - _L'émulation de `--platform=linux/amd64` n'est pas nécessaire à partir de Node 18._ > - _Voir [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) pour comprendre pourquoi `libc6-compat` pourrait être nécessaire._ > - _Next.js receuille [des données de télémétrie sur l'utilisation générale de façon anonyme](https://nextjs.org/telemetry). Décommentez la première occurrence de `ENV NEXT_TELEMETRY_DISABLED 1` pour désactiver la télémétrie durant le processus de génération. Décommentez la seconde occurence pour désactiver la télémétrie durant l'exécution._
## Génération et exécution de l'image localement Générer et exécuter l'image localement avec les commandes suivantes: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Ouvrez [localhost:3000](http://localhost:3000/) pour voir votre application s'exécuter. ## Docker Compose Vous pouvez également utiliser Docker Compose pour générer l'image et exécuter le conteneur.
Suivez les étapes 1 à 4 ci-dessus, cliquez ici et incluez le contenue dans votre docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Exécutez ceci à l'aide de la commande `docker compose up` : ```bash docker compose up ``` Ouvrez [localhost:3000](http://localhost:3000/) pour voir votre application en cours d'exécution.
## Déployer sur Railway Vous pouvez utiliser un service de type PaaS comme [Railway's](https://railway.app) pour automatiser le déploiement de votre application [Voir Dockerfile sur railway](https://docs.railway.app/deploy/dockerfiles). Si vous avez [Railway CLI d'installer](https://docs.railway.app/develop/cli#install) vous pouvez déployer votre application en suivant les commandes suivantes: ```bash railway login railway init railway link railway up railway open ``` Allez dans "Variables" et rajoutez votre `DATABASE_URL`. Ensuite, allez dans "Settings" et sélectionnez "Generate Domain.". Pour voir un exemple qui fonctionne sur Railway, visitez [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Ressources utiles | Ressources | Liens | | ------------------------------------------------ | -------------------------------------------------------------------- | | Dockerfile référence | https://docs.docker.com/engine/reference/builder/ | | Compose file version 3 référence | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Docker CLI référence | https://docs.docker.com/engine/reference/commandline/docker/ | | Docker Compose CLI référence | https://docs.docker.com/compose/reference/ | | Next.js déploiement avec une image Docker | https://nextjs.org/docs/deployment#docker-image | | Next.js dans Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Next.js exemple avec Docker | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Créer une image Docker d'une application Next.js | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/fr/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Déploiement", layout: "docs", description: "Apprenez comment déployer votre app T3 en production.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/fr/deployment/netlify.mdx ================================================ --- title: Netlify description: Déploiement sur Netlify layout: ../../../layouts/docs.astro lang: fr isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Netlify est un fournisseur de déploiement alternatif dans la même veine que Vercel. Voir [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) pour un exemple de dépôt basé sur ce document. ## Pourquoi héberger sur Netlify La sagesse conventionnelle dit que Vercel a un support Next.js supérieur parce que Vercel développe Next.js. Ils ont tout intérêt à s'assurer que la plate-forme est réglée pour des performances optimales et DX avec Next.js. Pour la majorité des cas d'utilisation, cela sera vrai et cela n'aura aucun sens de s'écarter du chemin standard. Il existe également un sentiment commun selon lequel de nombreuses fonctionnalités de Next.js ne sont prises en charge que sur Vercel. S'il est vrai que les nouvelles fonctionnalités Next.js seront testées et prises en charge sur Vercel au moment de leurs publications, il est également vrai que d'autres fournisseurs comme Netlify [implémenteront et publieront rapidement leur prise en charge](https://www.netlify.com/blog/deploy-nextjs-13/) pour les [fonctionnalités stables de Next.js](https://docs.netlify.com/integrations/frameworks/next-js/overview/). Il existe des avantages et des inconvénients relatifs pour chaque fournisseurs de déploiement, car aucun hébergeur ne peut avoir le meilleur support pour tous les cas d'utilisation. Par exemple, Netlify a construit son propre [environnement d'exécution Next.js personnalisé](https://github.com/netlify/next-runtime) pour les fonctions Edge de Netlify (qui s'exécutent sur Deno Deploy) et [maintient un middleware unique pour accéder et modifier les réponses HTTP](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). Pour suivre l'état des fonctionnalités instables de Next 13, consultez [Utilisation du répertoire "app" de Next 13 sur Netlify](https://github.com/netlify/next-runtime/discussions/1724). ## Configurer le projet Il existe de nombreuses façons de configurer vos instructions de build, y compris directement via la CLI Netlify ou le tableau de bord Netlify. Bien que cela ne soit pas obligatoire, il est conseillé de créer et d'inclure un fichier [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Cela garantit que les versions dérivées et clonées du projet seront plus faciles à déployer et de manière reproductible. ```toml [build] command = "next build" publish = ".next" ``` ## Utilisation du tableau de bord Netlify 1. Transférez votre code vers un dépôt GitHub et inscrivez-vous à [Netlify](https://app.netlify.com/signup). Après avoir créé un compte, cliquez sur **Ajouter un nouveau site** puis sur **Importer un projet existant**. ![Nouveau projet sur Netlify](/images/netlify-01-new-project.webp) 2. Connectez votre fournisseur de dépôt. ![Importation de votre dépôt](/images/netlify-02-connect-to-git-provider.webp) 3. Sélectionnez le dépôt de votre projet. ![Sélectionnez le dépôt de votre projet.](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify détectera si vous avez un fichier `netlify.toml` et configurera automatiquement votre commande de build et votre répertoire de publication. ![Paramètres de build de Nextjs](/images/netlify-04-configure-build-settings.webp) 5. Cliquez sur **Afficher les paramètres avancés**, puis sur **Nouvelle variable** pour ajouter vos variables d'environnement. ![Ajouter des variables d'environnement](/images/netlify-05-env-vars.webp) 1. Cliquez sur **Déployer le site**, attendez la fin de la compilation, puis, affichez votre nouveau site. ## Utilisation de la CLI Netlify Pour déployer à partir de la ligne de commande, vous devez d'abord pousser votre projet vers un dépôt GitHub et [installer la CLI Netlify](https://docs.netlify.com/cli/get-started/). Vous pouvez installer `netlify-cli` en tant que dépendance de projet ou l'installer globalement sur votre machine avec la commande suivante : ```bash npm i -g netlify-cli ``` Pour tester votre projet localement, exécutez la commande [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) et ouvrez [`localhost:8888 `](http://localhost:8888/) pour afficher votre application Netlify exécutée localement : ```bash ntl dev ``` Exécutez la commande [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) pour configurer votre projet : ```bash ntl init ``` Importez les variables d'environnement de votre projet à partir de votre fichier `.env` avec [`ntl env:import`](https://cli.netlify.com/commands/env#envimport) : ```bash ntl env:import .env ``` Déployez votre projet avec [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Vous devrez passer l'indicateur `--build` pour exécuter la commande build avant le déploiement et l'indicateur `--prod` pour déployer sur l'URL principale de votre site : ```bash ntl deploy --prod --build ``` Pour afficher un exemple d'exécution sur Netlify, visitez [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/fr/deployment/vercel.md ================================================ --- title: Vercel description: Déploiement sur Vercel layout: ../../../layouts/docs.astro lang: fr --- Nous vous recommandons de déployer votre application sur [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Ce service facilite le déploiement d'application Next.js. ## Configuration du projet Vercel va certainement configurer les commandes de génération et publier le répertoire automatiquement. Cependant, vous pouvez également spécifier ces informations manuellement avec vos propres configurations en créant un fichier appelé [`vercel.json`](https://vercel.com/docs/project-configuration) et en incluant les informations suivantes. **La plus part des projets ne le requiert pas.** ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Utilisation du tableau de bord Vercel 1. Après avoir envoyer votre code sur GitHub, Inscrivez-vous sur [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) avec GitHub et cliquez ensuite sur **Add New Project**. ![Nouveau projet sur Vercel](/images/vercel-new-project.webp) 2. Importez votre projet GitHub. ![Importation de votre projet](/images/vercel-import-project.webp) 3. Ajoutez vos variables d'environnement. ![Ajoutez vos variables d'environnement](/images/vercel-env-vars.webp) 4. Cliquez sur **Deploy**. À partir de maintenant, à chaque fois que vous pousserez une modification, Vercel redéploiera automatiquement votre application ! ## Utilisation de Vercel CLI Pour déployer à partir de la ligne de commande, vous devez d'abord [installer la CLI Vercel globalement](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Exécutez la commande [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) pour déployer votre projet. ```bash vercel ``` Ajoutez l'argument `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` pour ajouter une variable d'environnement comme la chaîne de connexion à la base de données. Utilisez `--yes` si vous souhaitez ignorer les questions lors des déploiements et donner une réponse par défaut. ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` Après le premier déploiement, cette commande déploiera sur une branche de prévisualisation. Vous deverez ajouter l'argument `--prod` pour pousser votre changement directement sur la production. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/fr/examples.mdx ================================================ --- title: Exemples description: Exemples d'application actives layout: ../../layouts/docs.astro lang: fr isMdx: true --- import Callout from "../../components/docs/callout.tsx"; import Form from "../../components/docs/exampleOptionForm.astro"; Vous pouvez essayer plusieurs combinaisons de technologies que create-t3-app offre. Vous ne pouvez pas sélectionner `prisma` et `drizzle` simultanément. Certaines fonctionnalités peuvent ne pas fonctionner à moins que vous créer un fichier d'environnement ================================================ FILE: www/src/pages/fr/faq.mdx ================================================ --- title: FAQ description: Foire aux questions sur Create T3 App layout: ../../layouts/docs.astro lang: fr isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Voici quelques questions fréquemment posées sur Create T3 App. ## Et après? Comment faire une application avec ça ? Nous essayons de garder ce projet aussi simple que possible, afin que vous puissiez commencer uniquement avec la configuration que nous avons fait pour vous, et ajouter des éléments supplémentaires plus tard, lorsqu'ils deviennent nécessaires. Si vous n'êtes pas familier avec les différentes technologies utilisées dans ce projet, veuillez vous référer aux docs respectives. Si vous êtes toujours dans le brouillard, veuillez rejoindre notre [Discord](https://t3.gg/discord) et demander de l'aide. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## Quelles ressources d'apprentissage sont actuellement disponibles? Bien que les ressources répertoriées ci-dessous soient parmi les meilleures qui existent pour la stack T3, la communauté (et [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) vous recommandent de commencer à utiliser la stack et d'apprendre en cours de route en codant avec elle. Si vous envisagez Create T3 App, il y a de fortes chances que vous ayez déjà utilisé certaines parties de la stack. Alors pourquoi ne pas simplement plonger la tête la première et apprendre les autres parties pendant que vous créez quelque chose ? Maintenant, nous comprenons que cette voie ne fonctionne pas pour tout le monde. Donc, si vous avez l'impression d'avoir suivi les recommandations et que vous êtes toujours en manque de ressources, ou si vous n'êtes tout simplement pas sûr de le faire vous-même et/ou si vous vous sentez dépassé par la stack, consultez ces tutoriels géniaux sur Create T3 App : ### Articles - [Créez une application complète avec Create T3 App](https://www.nexxel.dev/blog/ct3a-guestbook) - [Un premier aperçu de Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrer votre application T3 vers un Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Intégrer Stripe dans votre application T3](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Vidéos - [Créez un clone Twitter avec la stack T3 - tRPC, Next.js, Prisma, Tailwind et Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [Créez un blog avec la stack T3 - tRPC, TypeScript, Next.js, Prisma et Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Créer une application de chat en direct avec la stack T3 - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [La stack T3 - Comment nous l'avons créez](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [Un aperçu de Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Pourquoi y a-t-il des fichiers `.js` dans le projet ? Conformément à [T3-Axiom #3](/fr/introduction#la-sécurité-de-typage-nest-pas-facultative), nous traitons la sécurité de typage comme un citoyen de première classe. Malheureusement, tous les frameworks et plugins ne prennent pas en charge TypeScript, ce qui signifie que certains des fichiers de configuration doivent être des fichiers `.js`. Nous essayons de souligner que ces fichiers sont JavaScript pour une raison, en déclarant explicitement le type de chaque fichier (`cjs` ou `mjs`) en fonction de ce qui est pris en charge par la librairie par laquelle il est utilisé. De plus, tous les fichiers `js` de ce projet sont vérifiés systématiquement à l'aide d'un commentaire `@ts-check` en début de fichier. ## J'ai du mal à ajouter i18n à mon application. Y a-t-il une référence que je peux utiliser? Nous avons décidé de ne pas inclure i18n par défaut dans `create-t3-app` car c'est un sujet très opiniâtre et il existe de nombreuses façons de l'implémenter. Cependant, si vous avez du mal à l'implémenter et que vous souhaitez voir un projet de référence, nous avons un [repo de référence](https://github.com/juliusmarminge/t3-i18n) qui montre comment vous pouvez ajouter i18n à une application T3 en utilisant [next-i18next](https://github.com/i18next/next-i18next). ## Pourquoi utilisons-nous `/pages` et non `/app` de Next.js 13 ? Selon [T3-Axiom #2](/fr/introduction#etre-responsable), nous aimons les trucs à la pointe de la technologie, mais nous apprécions la stabilité, l'entièreté de votre routeur est difficile à porter, [pas le meilleur lieu pour les technologies de pointes](https://youtu.be/mnwUbtieOuI?t=1662). Bien que `/app` soit [un aperçu du futur](https://youtu.be/rnsC-12PVlM?t=818), il n'est pas prêt pour la production ; L'API est en version bêta et devrait subir des modifications avec changements majeurs pouvant casser le code. Pour obtenir une liste des fonctionnalités prises en charge, planifiées et en cours de développement dans le répertoire `/app`, consultez la [documentation de la beta de Next.js](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/fr/folder-structure-pages.mdx ================================================ --- title: Structure des dossiers (Pages) description: Structure des dossiers d'une application T3 nouvellement créée layout: ../../layouts/docs.astro lang: fr isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Veuillez sélectionner vos packages pour voir la structure des dossiers d'une application nouvellement créée avec ces sélections. Plus bas, vous trouverez une description de chaque entrée.
### `prisma` Le dossier `prisma` contient le fichier `schema.prisma` qui est utilisé pour configurer la connexion à la base de données et le schéma de la base de données. C'est également l'emplacement où stocker les fichiers de migration et/ou les scripts de départ, s'ils sont utilisés. Voir [Utilisation de Prisma](/fr/usage/prisma) pour plus d'informations.
### `public` Le dossier "public" contient des fichiers statiques qui sont servis par le serveur Web. Le fichier `favicon.ico` en est un exemple.
### `src/env` Utilisé pour la validation des variables d'environnement et les définitions de types - voir [Variables d'environnement](usage/env-variables).
### `src/pages` Le dossier `pages` contient toutes les pages de l'application Next.js. Le fichier `index.tsx` dans le répertoire racine de `/pages` est la page d'accueil de l'application. Le fichier `_app.tsx` est utilisé pour envelopper l'application avec des fournisseurs. Voir [Documentation Next.js](https://nextjs.org/docs/basic-features/pages) pour plus d'informations.
#### `src/pages/api` Le dossier `api` contient toutes les routes API de l'application Next.js. Le fichier `examples.ts` (avec Prisma) contient un exemple de route qui utilise la fonctionnalité [Next.js API route](https://nextjs.org/docs/api-routes/introduction) avec Prisma. Le fichier `restricted.ts` (avec Next-Auth) contient un exemple de route qui utilise la fonctionnalité [Next.js API route](https://nextjs.org/docs/api-routes/introduction) et est protégé par [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` Le fichier `[...nextauth].ts` est la route d'authentification NextAuth.js. Il est utilisé pour gérer les demandes d'authentification. Voir [utilisation de NextAuth.js](usage/next-auth) pour plus d'informations sur NextAuth.js, et la [documentation Next.js sur les routes dynamiques](https://nextjs.org/docs/routing/dynamic-routes) pour plus d'informations sur les routes fourre-tout / slug.
#### `src/pages/api/trpc/[trpc].ts` Le fichier `[trpc].ts` est le point d'entrée de l'API tRPC. Il est utilisé pour gérer les requêtes tRPC. Voir [utilisation de tRPC](usage/trpc#-pagesapitrpctrpcts) pour plus d'informations sur ce fichier, et la [documentation Next.js sur les routes dynamiques](https://nextjs.org/docs/routing/dynamic-routes) pour plus d'informations sur les routes fourre-tout / slug.
### `src/server` Le dossier `server` est utilisé pour séparer clairement le code côté serveur du code côté client.
### `src/server/auth.ts` Contient des utilitaires d'authentification tels que la récupération de la session de l'utilisateur côté serveur. Voir [utilisation de NextAuth.js](usage/next-auth#utilisation-avec-trpc) pour plus d'informations.
#### `src/server/db.ts` Le fichier `db.ts` est utilisé pour instancier le client Prisma au niveau global. Voir [Utilisation de Prisma](usage/prisma#prisma-client) et [meilleures pratiques pour utiliser Prisma avec Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) pour plus d'informations.
### `src/server/api` Le dossier `api` contient le code côté serveur de tRPC.
#### `src/server/api/routers` Le dossier `routers` contient tous vos sous-routeurs tRPC.
#### `src/server/api/routers/example.ts` Le fichier `example.ts` est un exemple de routeur tRPC utilisant le helper `publicProcedure` pour montrer comment créer une route tRPC publique. En fonction des packages choisis, ce routeur contient plus ou moins de routes pour démontrer au mieux l'utilisation à vos besoins.
#### `src/server/api/trpc.ts` Le fichier `trpc.ts` est le fichier de configuration principal de votre back-end tRPC. Ici, vous trouverez: 1. La définissons du contexte utilisé dans les requêtes tRPC. Voir [utilisation de tRPC](usage/trpc#-serverapitrpcts) pour plus d'informations. 2. Le helper de procédure d'exportation. Voir [utilisation de tRPC](usage/trpc#-serverapitrpcts) pour plus d'informations.
#### `src/server/api/root.ts` Le fichier `root.ts` est utilisé pour fusionner les routeurs tRPC et les exporter en tant que routeur unique, ainsi que la définition du type de routeur. Voir [utilisation du tRPC](usage/trpc#-serverapirootts) pour plus d'informations.
### `src/styles` Le dossier `styles` contient les styles globaux de l'application.
### `src/types` Le dossier `types` est utilisé pour stocker les types réutilisés ou les déclarations de type.
#### `src/types/next-auth.d.ts` Le fichier `next-auth.d.ts` est utilisé pour étendre le type de session par défaut NextAuth afin d'inclure l'ID utilisateur. Voir [utilisation de NextAuth.js](usage/next-auth#inclusion-de-userid-dans-la-session) pour plus d'informations.
### `src/utils` Le dossier `utils` est utilisé pour stocker les fonctions utilitaires couramment réutilisées.
#### `src/utils/api.ts` Le fichier `api.ts` est le point d'entrée frontal de tRPC. Voir [utilisation de tRPC](usage/trpc#-utilsapits) pour plus d'informations.
### `.env` Le fichier `.env` est utilisé pour stocker les variables d'environnement. Voir [Variables d'environnement](usage/env-variables) pour plus d'informations. Ce fichier ne doit **pas** être commité dans l'historique de git.
### `.env.example` Le fichier `.env.example` montre des exemples de variables d'environnement basées sur les librairies choisies. Ce fichier doit être commité dans l'historique de git.
### `.eslintrc.cjs` Le fichier `.eslintrc.cjs` est utilisé pour configurer ESLint. Voir la [documentation de ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) pour plus d'informations.
### `next-env.d.ts` Le fichier `next-env.d.ts` garantit que les types Next.js sont récupérés par le compilateur TypeScript. **Vous ne devez pas le supprimer ou le modifier car il peut changer à tout moment.** Voir la [documentation de Next.js](https://nextjs.org/docs/basic-features/typescript#existing-projects) pour plus informations.
### `next.config.mjs` Le fichier `next.config.mjs` est utilisé pour configurer Next.js. Voir la [documentation de Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction) pour plus d'informations. Remarque : L'extension .mjs est utilisée pour autoriser les importations ESM.
### `postcss.config.js` Le fichier `postcss.config.js` est utilisé pour l'utilisation de Tailwind PostCSS. Voir la [documentation de Tailwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss) pour plus d'informations.
### `prettier.config.mjs` Le fichier `prettier.config.mjs` est utilisé pour configurer Prettier afin d'inclure le prettier-plugin-tailwindcss pour le formatage des classes CSS Tailwind. Consultez l'[article de blog Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) pour plus d'informations.
### `tsconfig.json` Le fichier `tsconfig.json` est utilisé pour configurer TypeScript. Certaines valeurs autres que celles par défaut, telles que le `strict mode`, ont été activées pour garantir la meilleure utilisation de TypeScript pour Create T3 App et ses librairies. Voir la [documentation de TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) ou [Utilisation de TypeScript](usage/typescript) pour plus d'informations.
================================================ FILE: www/src/pages/fr/installation.mdx ================================================ --- title: Installation description: Instructions d'installation pour Create T3 App layout: ../../layouts/docs.astro lang: fr isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Pour configurer une application à l'aide de `create-t3-app`, exécutez l'une des trois commandes suivantes et répondez aux questions de l'invite de commande : ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` Une fois votre application configurée, consultez les [premières étapes](/fr/usage/first-steps) pour démarrer sur votre nouvelle application. ## Utilisation avancée | Option | Description | | ----------------- | ----------------------------------------------------------------------------------------------- | | `[dir]` | Inclure un argument de répertoire avec un nom pour le projet | | `--noGit` | Dites explicitement à la CLI de ne pas initialiser un nouveau dépôt git dans le projet | | `-y`, `--default` | Contourner la CLI et démarrer une nouvelle application t3 avec toutes les options sélectionnées | | `--noInstall` | Générer un projet sans installer de dépendances | ## Utilisation expérimentale Pour notre CI, nous avons des options expérimentales qui vous permettent de configurer n'importe quelle application sans aucun invite. Si ce cas d'utilisation s'applique à vous, vous pouvez utiliser ces options. Veuillez noter que ces options sont expérimentales et peuvent changer à l'avenir sans suivre la version de Semver. | Flag | Description | | ------------ | ----------------------------------------------- | | `--CI` | Faites savoir à la CLI que vous êtes en mode CI | | `--trpc` | Inclure tRPC dans le projet | | `--prisma` | Inclure Prisma dans le projet | | `--nextAuth` | Inclure NextAuth.js dans le projet | | `--tailwind` | Inclure Tailwind CSS dans le projet | Si vous ne fournissez pas l'option "CI", le reste de ces options n'a aucun effet. Vous n'avez pas besoin de vous désinscrire explicitement des packages que vous ne voulez pas.Cependant, si vous préférez être explicite, vous pouvez passer `false`, par exemple `--nextAuth false`. ### Exemple Ce qui suit configurera une application T3 avec tRPC et Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/fr/introduction.md ================================================ --- title: Introduction description: Introduction à la stack T3 layout: ../../layouts/docs.astro lang: fr ---
## La stack T3 La _"T3 Stack"_ est une stack de développement Web créée par [Theo](https://twitter.com/t3dotgg) axée sur la simplicité, la modularité et la sécurité de typage complète. Les éléments principaux sont [**Next.js**](https://nextjs.org/) et [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) est presque toujours inclus. Si vous faites quelque chose qui ressemble a du backend, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/) et [**NextAuth .js**](https://next-auth.js.org/) sont également d'excellents ajouts. Vous avez peut-être remarqué qu'il y a beaucoup… beaucoup de pièces. C'est par conception. Échangez les pièces selon vos besoins - cette stack est modulaire à la base :) ## Alors... qu'est-ce que create-t3-app ? Un modèle? `create-t3-app` est une CLI conçue par des développeurs chevronnés de T3 Stack pour rationaliser la configuration d'une application modulaire T3 Stack. Cela signifie que chaque pièce est facultative et que le "modèle" est généré en fonction de vos besoins spécifiques. Après d'innombrables projets et de nombreuses années sur cette technologie, nous avons beaucoup d'opinions et d'idées. Nous avons fait de notre mieux pour les encoder dans cette CLI. Ce n'est **PAS** un modèle tout compris. Nous **attendons** que vous apportiez vos propres librairies qui répondent aux besoins de **VOTRE** application. Bien que nous ne souhaitions pas prescrire de solutions à des problèmes plus spécifiques tels que la gestion de l'état et le déploiement, nous [avons quelques recommandations répertoriées ici](/fr/other-recs). ## T3 Axioms Nous allons être francs - c'est un _projet opiniâtre_. Nous partageons une poignée de croyances fondamentales autour de la création et nous les traitons comme la base de nos décisions. ### Résoudre des problèmes Il est facile de tomber dans le piège de "tout ajouter" - nous ne voulons explicitement pas le faire. Tout ce qui est ajouté à "create-t3-app" devrait résoudre un problème spécifique qui existe dans les technologies de base incluses. Cela signifie que nous n'ajouterons pas des choses comme des librairies d'état (`zustand`, `redux`) mais nous ajouterons des choses comme NextAuth.js et intégrerons Prisma et tRPC pour vous. ### Être responsable Nous aimons notre technologie de pointe. La vitesse et honnêtement le fun qu'apportent de nouvelles choses est vraiment cool. Nous pensons qu'il est important d'être responsable, en utilisant des technologies plus risquées dans les parties les moins risquées. Cela signifie que nous ne parierions pas ⛔️ sur une nouvelle technologie de base de données risquée (SQL est génial !). Mais nous parions volontiers ✅ sur tRPC car ce ne sont que des fonctions triviales à déplacer. ### La sécurité de typage n'est pas facultative L'objectif déclaré de Create T3 App est de fournir le moyen le plus rapide de démarrer une nouvelle application Web **typesafe** complète. Nous prenons la sécurité des caractères au sérieux dans ces parties, car elle améliore notre productivité et nous aide à expédier moins de bogues. Toute décision qui compromet la nature typesafe de Create T3 App est une décision qui doit être prise dans un projet différent. ================================================ FILE: www/src/pages/fr/other-recs.md ================================================ --- title: Autres recommandations description: Librairies et services que nous recommandons pour de nombreux projets layout: ../../layouts/docs.astro lang: fr --- Nous reconnaissons que les librairies incluses dans `create-t3-app` ne résolvent pas tous les problèmes. Bien que nous vous encourageons à commencer votre projet avec les éléments que nous fournissons, il viendra un moment où vous devrez ajouter d'autres librairies. Vous seul pouvez savoir ce dont votre projet a besoin, mais voici certaines choses que nous recommandons fréquemment. Ce sont des recommandations de contributeurs individuels de Create T3 App et ne doivent pas être considérées comme des approbations "officielles" par l'équipe de Create T3 App ou T3-OSS. _**Veuillez faire vos propres recherches, surtout avant de vous engager dans des services payants**_. ## State Management _**Note de l'éditeur**_: Les librairies de state management peuvent être excellentes, mais ne sont pas souvent nécessaires. Les hooks de React Query-tRPC devraient pouvoir prendre en charge le state de votre serveur. Pour le state du client, commencez par `useState` de React et utilisez l'une de ces options lorsque vous en avez besoin de plus. ### Zustand **Pour ne plus jamais utiliser Redux** Le "Redux moderne et simple" dont vous ne saviez pas avoir besoin. On peut toujours faire confiance à [Poimandres](https://github.com/pmndrs). Vous pouvez tout créer, des applications d'appel vidéo aux jeux en passant par les serveurs avec cette petite librairie. - [Page d'accueil Zustand](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **Pour ne plus jamais utiliser Context** Pour une approche plus atomique, Jotai est difficile à battre. Toujours par [Poimandres](https://github.com/pmndrs), Jotai vous permet de définir des singletons qui ressemblent à un useState global. Une excellente option pour les comportements avec état qui n'ont pas encore besoin d'une machine d'état. - [Page d'accueil Jotai](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## Librairies de composants La plupart des applications ont besoin de la même poignée de composants - boutons, menus déroulants, modaux, etc. Ces librairies fournissent d'excellents composants accessibles que vous pouvez utiliser et personnaliser à votre guise. ### Librairies de composants sans style Aussi connues sous le nom de librairies headless, elles fournissent d'excellents composants sans style et accessibles que vous pouvez personnaliser à votre guise. Voici quelques recommandations. - [Radix UI](https://www.radix-ui.com/) vous offre un ensemble puissant de primitives pratiques et accessibles que vous pouvez styliser avec du CSS vanilla ou Tailwind. - [Headless UI](https://headlessui.com/) créé par l'équipe Tailwind CSS fournit également des composants accessibles sans style qui s'intègrent de manière transparente avec Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) fournit des primitives d'interface utilisateur accessibles pour votre design. Leur composant Date Picker est de premier plan. ### Librairies de composants stylisés **Pour quand vous voulez juste que votre application ait l'air OK** Parfois, vous créez un projet où vous voulez juste que l'interface utilisateur ait l'air décente. Pour les tableaux de bord d'administration et d'autres projets similaires, n'importe laquelle de ces librairies de composants fera le travail. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) ### Class Variance Authority **Pour créer des librairies d'interface utilisateur** Créez de manière déclarative une librairie d'interface utilisateur avec différentes variantes de couleur, de taille, etc. Lorsque votre projet atteint une échelle où vous souhaitez un ensemble normé de composants d'interface utilisateur avec plusieurs variantes utilisant Tailwind CSS, CVA est un excellent outil. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Animations Lorsque vous avez besoin d'animations dans votre application, voici nos recommandations. ### AutoAnimate **Pour les animations avec une seule ligne de code** La plupart des librairies d'animation essaient de satisfaire tous les cas d'utilisation possibles et deviennent lourdes. AutoAnimate est un outil sans configuration qui vous apportera une amélioration significative de l'expérience utilisateur sans effort supplémentaire du développeur. - [Page d'accueil AutoAnimate](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [Extrait de composant AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **Pour les animations complexes avec du code déclaratif** Framer Motion fournit une syntaxe déclarative simple et vous permet d'écrire moins de code pour tout créer, des animations complexes aux gestes uniformes. - [Page d'accueil Framer Motion](https://framer.com/motion) - [Documentation Framer Motion](https://www.framer.com/docs/) ## Déploiements, Infrastructure, Bases de données et CI ### Vercel **Pour héberger votre application** Vercel a pris l'enfer des déploiements Web et en a fait une intégration GitHub prête à l'emploi. Nous sommes passés à des centaines de milliers d'utilisateurs sans problème. Alimenté par AWS, juste une meilleure interface :) - [Page d'accueil Vercel](https://vercel.com/) - [Guide de déploiement de Create T3 App sur Vercel](/fr/deployment/vercel) ### PlanetScale **Pour des bases de données sans souci** PlanetScale est de loin la meilleure "plate-forme de base de données serverless" que nous ayons utilisée. À une échelle folle, excellente expérience de développeur et prix fantastiques. Si vous utilisez SQL (et, espérons-le, Prisma), c'est difficile à battre. - [Page d'accueil PlanetScale](https://planetscale.com/) ### Railway **Pour héberger votre infrastructure** "Heroku moderne". Le moyen le plus simple de faire fonctionner un vrai serveur. Si Vercel et PlanetScale ne suffisent pas, Railway le sera probablement. Faites le pointer sur un dépôt GitHub et c'est parti. - [Page d'accueil Railway](https://railway.app/) ### Upstash **Pour du Redis serverless** Nous aimons Prisma et PlanetScale, mais certains projets nécessitent une solution plus performante. Upstash vous permet d'obtenir les performances en mémoire de Redis dans votre projet serverless, sans avoir à gérer l'infrastructure et à faire évoluer vous-même. - [Page d'accueil Upstash](https://upstash.com/) ### Pusher **Pour les WebSockets serverless** Si WebSockets est l'objectif principal de votre projet, vous pouvez envisager un backend plus traditionnel tel que [Fastify](https://www.fastify.io/) (qui [fonctionne également avec tRPC !](https:// trpc.io/docs/v10/fastify)). Mais pour ajouter rapidement des WebSockets à une application T3, Pusher est un excellent choix. - [Page d'accueil Pusher](https://pusher.com/) ### Soketi Soketi est une alternative à Pusher, auto-hébergée, simple et rapide. Il est entièrement compatible avec le SDK de Pusher que vous pouvez utiliser pour vous connecter au serveur. Soketi serverless est également en version bêta. - [Page d'accueil Soketi](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Analytics Les données utilisateur sont très précieuses lorsque vous créez une application. Voici quelques fournisseurs d'analyse que nous recommandons. ### Plausible Besoin d'analyses ? Plausible est l'un des moyens les plus rapides de les obtenir. Super minime. Il a même un [plugin, simple, pour Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Page d'accueil Plausible](https://plausible.io/) ### Umami Umami est une alternative open source à Google Analytics, auto-hébergée, simple, rapide et axée sur la confidentialité. Vous pouvez le déployer très facilement sur Vercel, Railway, etc. Avec PlanetScale comme base de données. - [Page d'accueil Umami](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) ## Autre ### Next Bundle Analyzer Il peut parfois être difficile de déterminer ce qui sera inclus dans la sortie de votre application générée. Next Bundle Analyzer est un moyen simple de visualiser et d'analyser les bundles JavaScript générés. - [@next/bundle-analyzer sur npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/fr/t3-collection.mdx ================================================ --- title: Collection T3 description: Projets open source sympas et d'entreprises utilisant la stack T3 layout: ../../layouts/docs.astro lang: fr isMdx: true --- Vous avez réalisé un projet à l'aide de la stack T3 et souhaitez le partager ? Ajoutez-le à la liste ! ## Applications Open Source créées à l'aide de la stack T3 import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import Callout from "../../components/docs/callout.tsx"; ## Entreprises utilisant la stack T3 Nous aimerions connaître les entreprises qui utilisent la stack T3 pour leurs applications. Votre entreprise utilise la stack T3 et souhaite la partager ? Ajoutez-le à la liste ! Vous avez un projet sympa utilisant la stack T3 ? Faites une [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) et ajoutez-la ici ! ================================================ FILE: www/src/pages/fr/usage/env-variables.mdx ================================================ --- title: Variables d'environnement description: Débuter avec Create T3 App layout: ../../../layouts/docs.astro lang: fr isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Create T3 App utilise son propre package [@t3-oss/env-nextjs](https://env.t3.gg), ainsi que [zod](https://zod.dev) en interne pour valider les variables d'environnement à l'exécution _et_ au moment du build, en fournissant une logique simple dans `src/env.js`. ## env.js _Résumé : Si vous voulez ajouter une nouvelle variable d'environnement, vous devez ajouter un validateur pour celle-ci dans `src/env.js`, puis ajouter la paire CLEF/VALEUR dans `.env`._ ```ts:env.js import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, }, }); ``` T3 Env introduit un nouveau concept à travers la fonction `createEnv`, qui est responsable de la création du schéma et inclura la logique de validation principale pour les variables d'environnement côté client et côté serveur. Pour plus d'informations sur le fonctionnement interne de `createEnv`, consultez la documentation de [T3 Env](https://env.t3.gg/docs/introduction). ## Utilisation de variables d'environnement Lorsque vous souhaitez utiliser vos variables d'environnement, vous pouvez les importer depuis `env.js` et les utiliser comme vous le feriez normalement. Si vous l'importez côté client et essayez d'accéder à une variable d'environnement du côté serveur, vous obtiendrez une erreur d'exécution. ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` is fully typesafe and provides autocompletion const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ This will throw a runtime error const dbUrl = env.DATABASE_URL; // ✅ This is fine const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example Puisque le fichier `.env` par défaut n'est pas enregistré dans le dépoôt de version, nous avons inclus un fichier `.env.example` dans lequel vous pouvez éventuellement conserver une copie de votre fichier `.env` sans les secrets. Cela n'est pas obligatoire, mais nous recommandons de maintenir le fichier exemple à jour pour faciliter autant que possible le démarrage des nouveaux contributeurs avec leur environnement. Certains frameworks et outils de build, comme Next.js, suggèrent de stocker les secrets dans un fichier `.env.local` et de commit les fichiers `.env` dans votre projet. Ceci n'est pas recommandé, car cela pourrait rendre facile de commit accidentellement des secrets dans votre projet. Au lieu de cela, nous recommandons de stocker les secrets dans le fichier `.env`, de rajouter votre fichier `.env` dans le fichier `.gitignore` et de ne commit que les fichiers `.env.example` dans votre projet. ## Ajout des variables d’environnement Pour vous assurer que votre build ne se termine jamais sans les variables d'environnement nécessaires au projet, vous devriez ajouter de nouvelles variables d'environnement dans **deux** emplacements: 📄 `.env`: Entrez votre variable d'environnement comme vous le feriez normalement dans un fichier `.env`, c'est-à-dire `CLEF=VALEUR` 📄 `env.js`: Ajoutez la logique de validation appropriée pour les variables d'environnement en définissant un schéma Zod à l'intérieur de `createEnv` pour chacune d'entre elles, par exemple `CLEF: z.string()`. En plus de cela, assurez-vous de la déstructuration dans l'option `runtimeEnv`, par exemple : `CLEF: process.env.CLEF` Pourquoi ai-je besoin de déstructurer la variable d'environnement dans `runtimeEnv` ? Cela est dû à la façon dont Next.js rassemble les variables d'environnement dans certains contextes d'exécution. En les déstructurant manuellement, vous vous assurez que la variable ne sera jamais supprimée du bundle. En option, vous pouvez également maintenir à jour le fichier `.env.example`: 📄 `.env.example`: Entrez votre variable d'environnement, mais assurez-vous de ne pas inclure la valeur si elle est secrète, c'est-à-dire `CLEF=VALUE` ou `CLEF=` ### Exemple _Je veux ajouter le jeton de l’API Twitter en tant que variable d’environnement côté serveur_ 1. Ajouter la variable d'environnement dans `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Ajouter la variable d'environnement dans `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { TWITTER_API_TOKEN: z.string(), }, // ... runtimeEnv: { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }, }); ``` 3. facultatif : ajoutez la variable d'environnement à `.env.example`, mais n'incluez pas le jeton ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/fr/usage/first-steps.md ================================================ --- title: Premiers pas description: Premiers pas avec votre nouvelle application T3 layout: ../../../layouts/docs.astro lang: fr --- Vous venez de créer une nouvelle application T3 et vous êtes prêt à démarrer. Voici le strict minimum pour que votre application puisse fonctionner. ## Base de données Si votre application inclut Prisma, assurez vous de lancer la commande `npx prisma db push` depuis la racine de votre application Cette commande va synchroniser votre schéma Prisma avec votre base de données et va générer les types TypeScript sur lequel se base le client Prisma. Notez que vous devez redémarrer le serveur TypeScript afin qu'il puisse détecter les nouveaux types générés. ## Authentification Si votre application inclut NextAuth.js, nous vous aidons à démarrer avec le `DiscordProvider`. C'est l'un des fournisseurs les plus simples proposés par NextAuth.js, mais il nécessite encore un peu de configuration initiale de votre part. Bien sûr, si vous préférez utiliser un autre fournisseur d'authentification, vous pouvez également utiliser l'un des [nombreux fournisseurs](https://next-auth.js.org/providers/) proposés par NextAuth.js. 1. Vous aurez besoin d'un compte Discord, créez-en un si vous ne l'avez pas déjà fait. 2. Accédez à https://discord.com/developers/applications et cliquez sur "New Application" dans le coin supérieur droit. Nommez votre application et acceptez les conditions d'utilisation. 3. Une fois votre application créée, accédez à "Settings → OAuth2 → General". 4. Copiez le "Client ID" et ajoutez-le à votre `.env` en tant que `AUTH_DISCORD_ID`. 5. Cliquez sur "Reset Secret", copiez le nouveau secret et ajoutez-le à votre `.env` en tant que `DISCORD CLIENT_SECRET`. 6. Cliquez sur "Add Redirect" et saisissez `http://localhost:3000/api/auth/callback/discord`. - Pour le déploiement en production, suivez les étapes précédentes pour créer une autre application Discord, mais cette fois remplacez `http://localhost:3000` par l'URL vers laquelle vous déployez. 7. Sauvegarder les modifications. Vous devriez maintenant pouvoir vous connecter. ## Prochaines étapes - Si votre application inclut tRPC, consultez `src/pages/index.tsx` et `src/server/api/routers/post.ts` pour voir comment fonctionnent les requêtes tRPC. - Consultez la documentation Create T3 App, ainsi que la documentation des packages inclus dans votre application. - Rejoignez notre [Discord](https://t3.gg/discord) et donnez-nous une étoile sur [GitHub](https://github.com/t3-oss/create-t3-app) ! :) ================================================ FILE: www/src/pages/fr/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Utilisation", layout: "docs", description: "Apprenez comment utiliser les différentes technologies de la stack T3.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/fr/usage/next-auth.mdx ================================================ --- title: NextAuth.js description: Utilisation de NextAuth.js layout: ../../../layouts/docs.astro lang: fr isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Lorsque vous souhaitez un système d'authentification dans votre application Next.js, NextAuth.js est une excellente solution pour apporter la sécurité sans avoir à le construire vous-même. Il vient avec une longue liste de fournisseurs pour ajouter rapidement de l'authentification OAuth et fournit des adaptateurs pour de nombreuses bases de données et ORM. ## Context Provider Dans le point d'entrée de votre application, vous verrez que votre application est encapsulée dans un [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider) : ```tsx:pages/_app.tsx ``` Ce fournisseur de contexte permet à votre application d'accéder aux données de session de n'importe où dans votre application, sans avoir à les transmettre en tant que props : ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Handle unauthenticated state, e.g. render a SignIn component return ; } return

Welcome {session.user.name}!

; }; ``` ## Récupérer la session côté serveur Parfois, vous souhaiterez demander la session côté serveur. Pour ce faire, prérécupérez la session à l'aide du helper `getServerAuthSession` fournie par `create-t3-app` et transmettez-le au client à l'aide de `getServerSideProps` : ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // NOTE: `session` wont have a loading state since it's already prefetched on the server ... } ``` ## Inclusion de `user.id` dans la Session Create T3 App est configuré pour utiliser le [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) dans la configuration NextAuth.js pour inclure l'ID de l'utilisateur dans le objet "session". ```ts:pages/api/auth/[...nextauth].ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Ceci est couplé avec un fichier de déclaration de type pour s'assurer que `user.id` est typé lors de l'accès à l'objet `session`. En savoir plus sur [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) sur la documentation de NextAuth.js. ```ts:types/next-auth.d.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` Le même modèle peut être utilisé pour ajouter toute autre donnée à l'objet `session`, comme un champ `role`, mais **ne doit pas être utilisé à mauvais escient pour stocker des données sensibles** sur le client. ## Utilisation avec tRPC Lorsque vous utilisez NextAuth.js avec tRPC, vous pouvez créer des procédures réutilisables et protégées à l'aide de [middleware](https://trpc.io/docs/v10/middlewares). Cela vous permet de créer des procédures accessibles uniquement aux utilisateurs authentifiés. `create-t3-app` configure tout cela pour vous, vous permettant d'accéder facilement à l'objet de session dans des procédures authentifiées. Cela se fait en deux étapes : 1. Récupérez la session à partir des en-têtes de requête à l'aide de la fonction [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). L'avantage d'utiliser `getServerSession` au lieu de `getSession` est qu'il s'agit d'une fonction côté serveur uniquement et qu'elle ne déclenche pas d'appels de récupération inutiles. `create-t3-app` crée une fonction d'assistance qui résume cette API particulière. ```ts:server/auth.ts export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return await getServerSession(ctx.req, ctx.res, nextAuthOptions); }; ``` En utilisant cette fonction d'assistance, nous pouvons récupérer la session et la transmettre au contexte tRPC : ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Créez un middleware tRPC qui vérifie si l'utilisateur est authentifié. Nous utilisons ensuite le middleware dans une `protectedProcedure`. Tout appelant à ces procédures doit être authentifié, sinon une erreur sera générée qui pourra être gérée de manière appropriée par le client. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }) ``` L'objet de session est une représentation légère et minimale de l'utilisateur et ne contient que quelques champs. Lorsque vous utilisez les `protectedProcedures`, vous avez accès à l'identifiant de l'utilisateur qui peut être utilisé pour extraire plus de données de la base de données. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Utilisation avec Prisma Faire fonctionner NextAuth.js avec Prisma nécessite beaucoup de [configuration initiale](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` gère tout cela pour vous, et si vous sélectionnez à la fois Prisma et NextAuth.js, vous obtiendrez un système d'authentification entièrement fonctionnel avec tous les modèles requis préconfigurés. Nous démarrons votre application avec un fournisseur Discord OAuth préconfiguré, que nous avons choisi car c'est l'un des plus faciles à démarrer - fournissez simplement vos jetons dans le `.env` et vous êtes prêt à partir. Cependant, vous pouvez facilement ajouter d'autres fournisseurs en suivant la [documentation NextAuth.js](https://next-auth.js.org/providers/). Notez que certains fournisseurs exigent que des champs supplémentaires soient ajoutés à certains modèles. Nous vous recommandons de lire la documentation du fournisseur que vous souhaitez utiliser pour vous assurer que vous disposez de tous les champs obligatoires. ### Ajout de nouveaux champs à vos modèles Lors de l'ajout de nouveaux champs à l'un des modèles `User`, `Account`, `Session` ou `VerificationToken` (il vous suffira très probablement de modifier le modèle `User` seulement), vous devez garder à l'esprit que l'[Adaptateur Prisma](https://next-auth.js.org/adapters/prisma) crée automatiquement des champs sur ces modèles lorsque de nouveaux utilisateurs s'inscrivent et se connectent. Par conséquent, lors de l'ajout de nouveaux champs à ces modèles, vous devez leur fournir des valeurs par défaut, car l'adaptateur n'a pas connaissance de ces champs. Si, par exemple, vous souhaitez ajouter un `role` au modèle `User`, vous devrez fournir une valeur par défaut au champ `role`. Cela se fait en ajoutant une valeur `@default` au champ `role` dans le modèle `User` : ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Utilisation avec le middleware Next.js Utilisation de NextAuth.js avec le middleware Next.js [nécessite l'utilisation de la stratégie de session JWT](https://next-auth.js.org/configuration/nextjs#caveats) pour l'authentification. En effet, le middleware ne peut accéder au cookie de session que s'il s'agit d'un JWT. Par défaut, Create T3 App est configuré pour utiliser la stratégie de base de données **default**, en combinaison avec Prisma comme adaptateur de base de données. L'utilisation de sessions en base de données est l'approche recommandée et vous devriez vous informer sur les JWT (JSON Web Token) avant de passer à la stratégie de session JWT, et ce, afin d'éviter tout problème de sécurité. Après avoir basculé vers la stratégie de session JWT, assurez-vous de mettre à jour le callback `session` dans `src/server/auth.ts`. L'objet `user` sera `undefined`. À la place, récupérez l'identifiant de l'utilisateur à partir de l'objet `token`. Ex.: ```diff:server/auth.ts export const authOptions: NextAuthOptions = { + session: { + strategy: "jwt", + }, callbacks: { - session({ session, user }) { + session({ session, token }) { - if (session.user) { + if (session.user && token.sub) { - session.user.id = user.id; + session.user.id = token.sub; } return session; } }, }; ``` ## Configuration du DiscordProvider par défaut 1. Rendez-vous dans [la section Applications du portail des développeurs Discord](https://discord.com/developers/applications), et cliquez sur "New Application" 1. Dans le menu des paramètres, allez dans "OAuth2 => General" - Copiez l'ID client et collez-le dans `AUTH_DISCORD_ID` dans `.env`. - Sous Client Secret, cliquez sur "Reset Secret" et copiez cette chaîne de caractères dans `DISCORD CLIENT_SECRET` dans `.env`. Soyez prudent car vous ne pourrez plus voir ce secret et le réinitialiser entraînera l'expiration du secret existant. - Cliquez sur "Add Redirect" et collez `/api/auth/callback/discord` (exemple pour le développement local : http://localhost:3000/api/auth/rappel/discord) - Enregistrez vos modifications - Il est possible, mais non recommandé, d'utiliser la même application Discord pour le développement et la production. Vous pouvez également envisager [moquer le fournisseur](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) pendant le développement. ## Ressources utiles | Ressource | Lien | | --------------------------------- | --------------------------------------- | | Documentation NextAuth.js | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/fr/usage/next-js.md ================================================ --- title: Next.js description: Utilisation de Next.js layout: ../../../layouts/docs.astro lang: fr --- Next.js est un framework backend pour vos applications React.
Découvrez [Theo's Next.js Conf talk](https://www.youtube.com/watch?v=W4UhNo3HAMw) pour mieux comprendre ce qu'est Next.js et son fonctionnement.

## Pourquoi devrais-je l'utiliser ? Nous aimons React. Il a rendu le développement des interfaces utilisateur accessible d'une manière que nous n'aurions jamais imaginée auparavant. Cela peut également conduire les développeurs sur des chemins difficiles. Next.js offre une approche légèrement opiniâtre et fortement optimisée pour créer des applications à l'aide de React. Du routage aux définitions d'API en passant par le rendu d'image, nous faisons confiance à Next.js pour guider les développeurs vers de bonnes décisions. L'association de Next.js avec [Vercel](https://vercel.com/) facilite plus que jamais le développement et le déploiement d'applications Web. Leur offre gratuite est extrêmement généreuse et leurs interfaces sont super intuitives, fournissant une solution pointer-cliquer pour déployer votre site (Nous ❤️ Vercel) ## Get Static/Server Props Une des caractéristique clé de Next.js sont ses capacités de récupération de données. Nous vous recommandons vivement de lire la [documentation officielle](https://nextjs.org/docs/basic-features/data-fetching) pour comprendre comment utiliser chaque méthode et en quoi elles diffèrent. `getServerSideProps` est généralement déconseillé à moins qu'il n'y ait une bonne raison, car il s'agit d'un appel bloquant qui ralentira votre site. [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) est une excellente alternative à `getServerSideProps` lorsque les données sont dynamiques et peuvent être récupérées de manière incrémentielle. ## Ressources utiles | Ressource | Lien | | ------------------------------ | ---------------------------------- | | Next.js Documentation | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js Blog | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Vercel/Next.js YouTube Channel | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/fr/usage/prisma.md ================================================ --- title: Prisma description: Utilisation de Prisma layout: ../../../layouts/docs.astro lang: fr --- Prisma est un ORM pour TypeScript, qui vous permet de définir votre schéma et vos modèles de base de données dans un fichier `schema.prisma`, puis de générer un client de type sécurisé qui peut être utilisé pour interagir avec votre base de données à partir de votre backend. ## Prisma Client Situé dans `/server/db.ts`, le client Prisma est instancié en tant que variable globale (cette façon est recommandé comme [meilleure pratique](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) par l'équipe de Prisma) et exporté pour être utilisé dans vos routes API. Nous incluons le client Prisma dans le [Context](/fr/usage/trpc#-serverapitrpcts) par défaut et recommandons de l'utiliser au lieu de l'importer séparément dans chaque fichier. ## Schéma Vous trouverez le fichier de schéma Prisma dans `/prisma/schema.prisma`. Ce fichier est l'endroit où vous définissez votre schéma et vos modèles de base de données, et est utilisé lors de la génération du client Prisma. ### Avec NextAuth.js Lorsque vous sélectionnez NextAuth.js en combinaison avec Prisma, le fichier de schéma est généré et configuré pour vous avec les valeurs recommandées pour les modèles `User`, `Session`, `Account` et `VerificationToken`, conformément à la [documentation de NextAuth .js](https://next-auth.js.org/adapters/prisma). ## Base de données par défaut La base de données par défaut est une base de données SQLite, idéale pour le développement et la mise en place rapide d'une preuve de concept, mais n'est pas recommandée pour la production. Vous pouvez changer de type de base de données à utiliser en changeant le `provider` dans le bloc `datasource` en `postgresql` ou `mysql`, puis en mettant à jour la chaîne de connexion dans les variables d'environnement pour pointer vers votre base de données. ## Amorçage de votre base de données [L'amorçage de votre base de données](https://www.prisma.io/docs/guides/database/seed-database) est un excellent moyen de remplir rapidement votre base de données avec des données de test pour vous aider à démarrer. Afin de configurer l'amorçage, vous devrez créer un fichier `seed.ts` dans le répertoire `/prisma`, puis ajouter un script `seed` à votre fichier `package.json`. Vous aurez également besoin d'un exécuteur TypeScript capable d'exécuter le script de départ. Nous recommandons [tsx](https://github.com/esbuild-kit/tsx), qui est un exécuteur TypeScript très performant qui utilise esbuild et ne nécessite aucune configuration ESM, mais `ts-node` ou d'autres exécuteurs le feront fonctionner aussi parfaitement. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Ensuite, exécutez simplement `pnpm db-seed` (ou `npm`/`yarn`) pour amorcer votre base de données. ## Ressources utiles | Ressource | Lien | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Documentation Prisma | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | Adaptateur Prisma pour NextAuth.JS | https://next-auth.js.org/adapters/prisma | | Guide de connexion à PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/fr/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Utilisation de Tailwind CSS layout: ../../../layouts/docs.astro lang: fr --- ## Qu'est-ce que Tailwind CSS ? Tailwind CSS est une toute petite librairie CSS, [utilitaire d'abord](https://tailwindcss.com/docs/utility-first), permettant de créer des designs personnalisées, sans le changement de contexte requis par le CSS classique. Il s'agit purement d'une librairie CSS et ne fournit aucun composant ou logique prédéfini, elle offre [un ensemble d'avantages très différent](https://www.youtube.com/watch?v=CQuTF-bkOgc) par rapport à une librairie de composants comme Material UI. Cela rend le CSS incroyablement facile et rapide à écrire, comme le montre l'exemple suivant : Ancien CSS : 1. Écrire du CSS, souvent dans un fichier séparé ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Importer le CSS dans votre composant ```jsx import "./my-class.css"; ``` 3. Ajouter la classe à votre code HTML ```jsx
...
``` Équivalent dans Tailwind : 1. Écrivez simplement des classes dans votre HTML ```jsx
...
``` Lorsqu'elle est utilisé avec des composantes React, cela permet de créer des interfaces utilisateur de façon extrêmement puissant et rapidement. Tailwind CSS dispose d'un magnifique système de conception intégré, qui sort de l'oeuf avec une palette de couleurs soigneusement choisie, des modèles de dimensionnement pour des styles tels que la width/height et le padding/margin pour une conception uniforme, ainsi que des points d'arrêt (breaking points) pour créer des mises en page. Ce système de conception peut être personnalisé et étendu pour créer la boîte à outils exacte de styles dont votre projet a besoin.
Tru Narla mieux connu sous le nom de [mewtru](https://twitter.com/trunarla) a donné une conférence étonnante sur [la construction d'un système de conception à l'aide de Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Utilisation Assurez-vous d'avoir installé des plugins pour Tailwind dans votre éditeur de code afin d'améliorer votre expérience d'écriture avec Tailwind. ### Extensions et plugins - [Extension VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [Integration avec JetBrains](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Formatage Les classes CSS Tailwind peuvent facilement devenir un peu désordonnées, donc un formateur pour les classes est indispensable. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) trie les classes dans [l'ordre recommandé](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) afin que les classes correspondent au bundle CSS généré. Lorsque vous sélectionnez Tailwind dans la ligne de commande, nous l'installons et le configurons pour vous. ### Appliquez les classes de façon conditionnelle L'ajout conditionnel de classes à l'aide de ternaires peut devenir très compliqué et difficile à lire. Ces packages vous aident à organiser vos classes lorsque vous utilisez une logique conditionnelle. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Ressources utiles | Ressource | Lien | | ------------------------------- | -------------------------------------------------------- | | Documentation Tailwind | https://tailwindcss.com/docs/editor-setup/ | | Feuille de triche pour Tailwind | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Communauté Tailwind | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Tailwind Discord Server | https://tailwindcss.com/discord/ | | TailwindLabs Youtube Channel | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/fr/usage/trpc.md ================================================ --- title: tRPC description: utilisation de tRPC layout: ../../../layouts/docs.astro lang: fr --- tRPC nous permet d'écrire des API fortement typées de bout en bout sans aucune génération de code ni surcharge d'exécution. Il utilise l'inférence de TypeScript pour déduire les définitions de type de votre routeur d'API et vous permet d'appeler vos procédures d'API à partir de votre client avec une sécurité de type complète et une saisie semi-automatique dans votre éditeur de code. Lorsque vous utilisez tRPC, vous sentirez votre frontend et votre backend plus proches que jamais, ce qui permet une expérience de développement exceptionnelle.

J'ai écrit tRPC pour permettre aux gens de coder plus rapidement en supprimant le besoin d'une couche API traditionnelle, tout en ayant la certitude que nos applications ne se briseront pas lorsque nous itérerons rapidement.

Avatar of @alexdotjs
Alex - créateur de tRPC @alexdotjs
## Comment utiliser tRPC ?
Le contributeur de tRPC [trashh_dev](https://twitter.com/trashh_dev) a fait [une présentation de malade à la Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) à propos de tRPC. Nous vous recommandons fortement de la regarder si vous ne l'avez pas déjà fait. Avec tRPC, vous écrivez des fonctions TypeScript sur votre backend, puis vous les appelez depuis votre frontend. Une procédure tRPC simple pourrait ressembler à ceci : ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Il s'agit d'une procédure tRPC (équivalente à un gestionnaire de route dans un backend traditionnel) qui valide d'abord l'entrée à l'aide de Zod (qui est la même bibliothèque de validation que nous utilisons pour les [variables d'environnement] (./env-variables)) - dans ce cas , il s'assure que l'entrée est une chaîne de caractères. Si l'entrée n'en est pas une, elle renverra une erreur informative à la place. Après l'entrée, nous enchaînons une fonction de résolveur qui peut être soit une [query](https://trpc.io/docs/v10/react-queries), [mutation](https://trpc.io/docs/v10/react-mutations), ou une [subscription](https://trpc.io/docs/v10/subscriptions). Dans notre exemple, le résolveur appelle notre base de données à l'aide de notre client [prisma](./prisma) et renvoie l'utilisateur dont l'`id` correspond à celui que nous avons transmis. Vous définissez vos procédures dans des "routeurs" qui représentent une collection de procédures liées avec un espace de noms partagé. Vous pouvez avoir un routeur pour les `utilisateurs`, un pour les `posts` et un autre pour les `messages`. Ces routeurs peuvent ensuite être fusionnés en un seul `appRouter` centralisé : ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Notez que nous n'avons besoin d'exporter que les définitions de type de notre routeur, ce qui signifie que nous n'importons jamais de code serveur sur notre client. Maintenant appelons la procédure sur notre frontend. tRPC fournit un wrapper pour `@tanstack/react-query` qui vous permet d'utiliser toute la puissance des hooks qu'il fournit, avec l'avantage supplémentaire d'avoir vos appels d'API typés. Nous pouvons appeler nos procédures depuis notre frontend comme ceci : ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Vous remarquerez immédiatement à quel point la saisie semi-automatique et la sécurité de typage sont bonnes. Dès que vous écrivez `trpc.`, vos routeurs s'affichent en saisie semi-automatique et lorsque vous sélectionnez un routeur, ses procédures s'affichent également. Vous obtiendrez également une erreur TypeScript si votre entrée ne correspond pas au validateur que vous avez défini du côté backend. ## Fichiers tRPC nécessite beaucoup de configuration que `create-t3-app` fait pour vous. Passons en revue les fichiers générés : ### 📄 `pages/api/trpc/[trpc].ts` Il s'agit du point d'entrée de votre API et expose le routeur tRPC. Normalement, vous ne toucherez pas beaucoup à ce fichier, mais si vous devez, par exemple, activer le middleware CORS ou similaire, il est utile de savoir que le `createNextApiHandler` exporté est un [gestionnaire d'API Next.js](https://nextjs.org/docs/api-routes/introduction) qui prend une [requête](https://developer.mozilla.org/en-US/docs/Web/API/Request) et [réponse](https://developer.mozilla.org/en-US/docs/Web/API/Response). Cela signifie que vous pouvez envelopper le `createNextApiHandler` dans n'importe quel middleware de votre choix. Voir ci-dessous pour un [exemple] (#enabling-cors) d'ajout de CORS. ### 📄 `server/api/trpc.ts` Ce fichier est divisé en deux parties, la création du contexte et l'initialisation de tRPC : 1. Nous définissons le contexte qui est passé à vos procédures tRPC. Le contexte sont des données auxquelles toutes vos procédures tRPC auront accès, et c'est un endroit idéal pour mettre des choses comme les connexions à la base de données, les informations d'authentification, etc. Dans create-t3-app, nous utilisons deux fonctions, pour activer l'utilisation d'un sous-ensemble du contexte lorsque nous n'avons pas accès à l'objet de requête. - `createInnerTRPCContext` : c'est ici que vous définissez le contexte qui ne dépend pas de la requête, par ex. votre connexion à la base de données. Vous pouvez utiliser cette fonction pour les [tests d'intégration](#exemple-de-test-dintégration) ou [ssg-helpers](https://trpc.io/docs/v10/ssg-helpers) où vous n'avez pas d'objet de requête . - `createTRPCContext` : c'est ici que vous définissez le contexte qui dépend de la requête, par ex. la session de l'utilisateur. Vous demandez la session à l'aide de l'objet `opts.req`, puis transmettez la session à la fonction `createInnerTRPCContext` pour créer le contexte final. 1. Nous initialisons tRPC et définissons des [procédures](https://trpc.io/docs/v10/procedures) et des [middlewares](https://trpc.io/docs/v10/middlewares) réutilisables. Par convention, vous ne devriez pas exporter l'intégralité de l'objet `t`, mais plutôt de créer des procédures et des middlewares réutilisables et de les exporter. Vous remarquerez que nous utilisons `superjson` comme [transformateur de données](https://trpc.io/docs/v10/data-transformers). Cela fait en sorte que vos types de données sont préservés lorsqu'ils atteignent le client, donc si vous envoyez par exemple un objet `Date`, le client renverra une `Date` et non une chaîne, ce qui est le cas pour la plupart des API. ### 📄 `server/api/routers/*.ts` C'est ici que vous définissez les routes et les procédures de votre API. Par convention, vous [créez des routeurs séparés](https://trpc.io/docs/v10/router) pour les procédures associées. ### 📄 `server/api/root.ts` Ici, nous [fusionnons](https://trpc.io/docs/v10/merging-routers) tous les sous-routeurs définis dans `routers/**` en un seul routeur d'application. ### 📄 `utils/api.ts` Il s'agit du point d'entrée frontend pour tRPC. C'est ici que vous allez importer la **définition de type** du routeur et créer votre client tRPC avec les hooks de react-query. Depuis que nous avons activé `superjson` comme transformateur de données sur le backend, nous devons également l'activer sur le frontend. En effet, les données sérialisées du backend sont désérialisées sur le frontend. Vous définirez ici vos [liens](https://trpc.io/docs/v10/links) tRPC, qui détermine le flux de requêtes du client vers le serveur. Nous utilisons le [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink) "par défaut" qui active [le traitement par lot des requêtes](https://cloud.google.com/compute/docs/api/how-tos/batch), ainsi qu'un [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink) qui génère des journaux de requêtes utiles pendant le développement. Enfin, nous exportons un [helper de type](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type) que vous pouvez utiliser pour déduire vos types sur le frontend.
Le contributeur de Create T3 App [Christopher Ehrlich](https://twitter.com/ccccjjjjeeee) a réalisé [une vidéo sur les flux de données dans tRPC](https://www.youtube.com/watch?v=x4mu-jOiA0Q). Cette vidéo est recommandée si vous avez utilisé tRPC mais que vous ne savez toujours pas comment cela fonctionne. ## Comment puis-je appeler mon API en externe ? Avec les API classiques, vous pouvez appeler vos points de terminaison à l'aide de n'importe quel client HTTP tel que `curl`, `Postman`, `fetch` ou directement depuis votre navigateur. Avec tRPC, c'est un peu différent. Si vous souhaitez appeler vos procédures sans le client tRPC, il existe deux méthodes recommandées : ### Exposez une seule procédure vers l'extérieur Si vous souhaitez exposer une seule procédure vers l'extérieur, vous cherchez des [appels côté serveur](https://trpc.io/docs/v10/server-side-calls). Cela vous permettrait de créer un point de terminaison API Next.js normal, et de réutiliser la partie résolveur de votre procédure tRPC. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Exposer chaque procédure en tant que point de terminaison REST Si vous souhaitez exposer chaque procédure vers l'extérieur, consultez le plugin créer par la communauté [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). En fournissant des métadonnées supplémentaires à vos procédures, vous pouvez générer une API REST compatible OpenAPI à partir de votre routeur tRPC. ### Ce ne sont que des requêtes HTTP tRPC communique via HTTP, il est donc également possible d'appeler vos procédures tRPC à l'aide de requêtes HTTP "régulières". Cependant, la syntaxe peut être fastidieuse en raison du [protocole RPC](https://trpc.io/docs/v10/rpc) utilisé par tRPC. Si vous êtes curieux, vous pouvez regarder à quoi ressemblent les demandes et les réponses tRPC dans l'onglet réseau de votre navigateur, mais nous vous suggérons de le faire uniquement à titre d'exercice pédagogique et de vous en tenir à l'une des solutions décrites ci-dessus. ## Comparaison avec un endpoint d'API Next.js Comparons un endpoint d'API Next.js à une procédure tRPC. Disons que nous voulons récupérer un objet utilisateur de notre base de données et le renvoyer au frontend. Nous pourrions écrire un endpoint d'API Next.js comme ceci : ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Comparez cela à l'exemple tRPC ci-dessus et vous pouvez voir certains des avantages de tRPC : - Au lieu de spécifier une url pour chaque route, ce qui peut devenir fastidieux à déboguer si vous déplacez quelque chose, votre routeur entier est un objet avec saisie semi-automatique. - Vous n'avez pas besoin de valider la méthode HTTP utilisée. - Vous n'avez pas besoin de valider que la requête ou le corps de la requête contient les données correctes dans la procédure, car Zod s'en charge. - Au lieu de créer une réponse, vous pouvez générer des erreurs et renvoyer une valeur ou un objet comme vous le feriez dans n'importe quelle autre fonction TypeScript. - L'appel de la procédure sur le frontend fournit l'auto-complétion et la sécurité de typage. ## Extraits de code utiles Voici quelques extraits de code qui pourraient être utiles. ### Activation de CORS Si vous avez besoin de consommer votre API à partir d'un domaine différent, par exemple dans un monorepo qui inclut une application React Native, vous devrez peut-être activer CORS : ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Enable cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### Mises à jour optimistes Les mises à jour optimistes se produisent lorsque nous mettons à jour l'interface utilisateur avant la fin de l'appel d'API. Cela donne à l'utilisateur une meilleure expérience car il n'a pas à attendre la fin de l'appel d'API pour que l'interface utilisateur reflète le résultat de son action. Cependant, les applications qui accordent une grande importance à l'exactitude des données doivent éviter les mises à jour optimistes car elles ne sont pas une "véritable" représentation de l'état du backend. Vous pouvez en savoir plus sur la [documentation de React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await utils.post.list.cancel(); // Get the data from the queryCache const prevData = utils.post.list.getData(); // Optimistically update the data with our new post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Return the previous data so we can revert if something goes wrong return { prevData }; }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sync with server once mutation has settled utils.post.list.invalidate(); }, }); }; ``` ### Exemple de test d'intégration Voici un exemple de test d'intégration qui utilise [Vitest](https://vitest.dev) pour vérifier que votre routeur tRPC fonctionne comme prévu, que l'analyseur d'entrée déduit le type correct et que les données renvoyées correspondent à la sortie attendue. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` ## Ressources utiles | Ressource | Lien | | ------------------------- | ------------------------------------------------------- | | Documentation tRPC | https://www.trpc.io | | Un tas d'exemples de tRPC | https://github.com/trpc/trpc/tree/next/examples | | Documentation React Query | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/fr/usage/typescript.md ================================================ --- title: TypeScript description: Utilisation de TypeScript layout: ../../../layouts/docs.astro lang: fr ---

Construisez des filets de sécurité, pas des garde-fous

Avatar of @t3dotgg
Theo - auteur de la T3 Stack @t3dotgg
Que vous soyez un développeur débutant ou chevronné, nous pensons que TypeScript est indispensable. Cela peut sembler intimidant au début, mais tout comme beaucoup d'outils, c'est quelque chose dont beaucoup ne garderont de mauvais souvenirs après avoir commencé à l'utiliser. Il fournit des commentaires en direct lorsque vous écrivez votre code en définissant les types de données attendus, et fournit soit une saisie semi-automatique dans votre éditeur de code, soit vous crie dessus avec des lignes ondulées rouges si vous essayez d'accéder à une propriété qui n'existe pas ou essayez de passez une valeur du mauvais type, que vous auriez sinon à déboguer plus tard, en ligne. C'est peut-être l'outil qui offre le plus de productivité aux développeurs. Il fournit la documentation de votre code lorsque vous l'écrivez ou le consommez dans votre éditeur en vous donnant une information directe car vous pouvez faire des erreurs et ceci est absolument inestimable. ## Inférence de type Alors que de nombreux nouveaux développeurs TypeScript sont concernés par _écrire_ du TypeScript, bon nombre de ses avantages ne vous obligent pas à modifier votre code, en particulier l'inférence. L'inférence signifie que si quelque chose est typé, ce type le suivra tout au long du flux de l'application sans avoir à être redéclaré à d'autres endroits. Cela signifie que, par exemple, une fois que vous avez défini les types d'arguments qu'une fonction prend, le reste de la fonction sera généralement typesafe sans nécessiter d'autre code spécifique à TypeScript. Les développeurs de librairies consacrent une tonne de travail à la maintenance des types de celles-ci, ce qui signifie que nous, en tant que développeurs d'applications, pouvons bénéficier à la fois de l'inférence et de la documentation intégrée dans notre éditeur de code grâce aux types que fournissent ces librairies.
Regardez la vidéo de Theo sur la façon dont [vous utilisez peut-être mal TypeScript](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Utilisations puissantes de l'inférence de type ### Zod [Zod](https://github.com/colinhacks/zod) est une librairie de validation de schéma construite avec TypeScript. Écrivez un schéma qui représente une source unique de vérité pour vos données, et Zod s'assurera que vos données sont valides dans toute votre application, même au-delà des limites du réseau et des API externes. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) vous propose des requêtes et des mutations déclaratives, toujours à jour et autogérées, qui améliorent directement votre expériences de développeur et d'utilisateur. ## Ressources utiles | Ressource | Lien | | --------------------------------------------------------- | ----------------------------------------------------------------- | | Manuel TypeScript | https://www.typescriptlang.org/docs/handbook/ | | Tutoriel TypeScript pour débutants | https://github.com/total-typescript/beginners-typescript-tutorial | | Défis de Type | https://github.com/type-challenges/type-challenges | | Rodney Mullen of TypeScript (Matt Pocock) Youtube Channel | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/fr/why.md ================================================ --- title: Pourquoi CT3A? description: Pourquoi devriez-vous choisir Create T3 App pour votre prochain projet layout: ../../layouts/docs.astro lang: fr --- Nous avons lancé Create T3 App parce que [Theo](https://twitter.com/t3dotgg) a refusé de créer un modèle de ses technologies préférées. Inspirée par create-next-app, [Astro's CLI](https://astro.build) avec un amour pour la sécurité des types, l'équipe de Create T3 App a travaillé dur pour créer le meilleur point de départ possible pour les nouveaux projets T3 Stack. Si vous souhaitez utiliser Next.js de manière sécurisée, c'est ici qu'il faut commencer. Si vous voulez en savoir plus sur l'un des choix technologiques spécifiques que nous avons faits, lisez la suite :) ## Pourquoi TypeScript ? JavaScript est difficile. Pourquoi ajouter plus de règles ? Nous croyons fermement que l'expérience fournie par TypeScript vous aidera à devenir un meilleur développeur. Il fournit des commentaires en direct lorsque vous écrivez votre code en définissant les types de données attendus, et fournit soit une saisie semi-automatique utile dans votre éditeur, soit vous crie dessus avec des lignes ondulées rouges si vous essayez d'accéder à une propriété qui n'existe pas ou essayez de passer une valeur du mauvais type, que vous auriez sinon à déboguer plus loin. Que vous soyez nouveau dans le développement Web ou un professionnel chevronné, la "strictité" de TypeScript fournira une expérience moins frustrante et plus cohérente que Vanilla JS. La sécurité de type vous rend plus rapide. Si vous n'êtes pas convaincu, vous [utilisez peut-être mal TypeScript...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## Pourquoi Next.js ? Nous aimons React. Il a rendu le développement d'interface utilisateur accessible d'une manière que nous n'avions jamais imaginé auparavant. Cela peut également conduire les développeurs sur des chemins difficiles. Next.js offre une approche légèrement opiniâtre et fortement optimisée pour créer des applications à l'aide de React. Du routage aux définitions d'API en passant par le rendu d'image, nous faisons confiance à Next.js pour guider les développeurs vers de bonnes décisions. ## Pourquoi tRPC/Prisma/Tailwind/etc? Bien que nous croyions qu'il faut garder les choses aussi simples que possible, nous constatons que ces éléments sont utilisés dans chaque "application" comme projet que nous créons. `create-t3-app` fait un excellent travail en vous permettant d'adopter les éléments dont vous avez besoin. ### tRPC tRPC tient la promesse de GraphQL d'un développement client transparent par rapport à un serveur typesafe sans tout le passe-partout. C'est un abus intelligent de TypeScript qui offre une expérience de développement incroyable. ### Prisma Prisma est à SQL ce que TypeScript est à JS. Il a créé une expérience de développement qui n'existait pas auparavant. En générant des types à partir d'un schéma défini par l'utilisateur compatible avec [plusieurs bases de données](https://www.prisma.io/docs/concepts/database-connectors), Prisma garantit la sécurité des types de bout en bout, de votre base de données à votre application. Prisma fournit toute une [suite d'outils](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) facilitant les interactions quotidiennes avec votre base de données. Notamment, Prisma Client est chargé d'interroger et de rendre SQL si simple que vous remarquerez à peine que vous l'utilisez, et Prisma Studio est une interface graphique pratique pour votre base de données qui vous permet de lire et de manipuler vos données rapidement sans avoir à écrire de code. ### Tailwind CSS Tailwind ressemble à un "CSS en mode zen". En fournissant des blocs de construction sous la forme de bonnes couleurs par défaut, d'un espacement et d'autres primitives, Tailwind facilite la création d'une belle application. Et contrairement aux librairies de composants, cela ne vous retient pas lorsque vous souhaitez faire passer votre application au niveau supérieur et créer quelque chose de beau et d'unique. De plus, avec son approche en ligne, Tailwind vous encourage à styliser sans vous soucier de nommer les classes, d'organiser les fichiers ou de tout autre problème non directement lié au problème que vous essayez de résoudre. ### NextAuth.js Lorsque vous souhaitez un système d'authentification dans votre application NextJS, NextAuth.js est une excellente solution pour apporter la complexité de la sécurité sans avoir à le coder vous-même. Il est livré avec une longue liste de fournisseurs pour ajouter rapidement l'authentification OAuth et fournit des adaptateurs pour de nombreuses bases de données et ORM. ================================================ FILE: www/src/pages/index.astro ================================================ --- import About from "../components/landingPage/about.astro"; import Banner from "../components/landingPage/banner.astro"; import Community from "../components/landingPage/community/community.astro"; import Features from "../components/landingPage/stack/stack.astro"; import LandingPage from "../layouts/landingPage.astro"; --- ================================================ FILE: www/src/pages/ja/deployment/docker.md ================================================ --- title: Docker description: Dockerへのデプロイ layout: ../../../layouts/docs.astro lang: ja --- このスタックをコンテナ化し、Docker を使用して単一のコンテナとしてデプロイすることができます。または、 docker-compose を使用して複数のコンテナのグループの一部としてデプロイすることもできます。このドキュメントに基づいたサンプルリポジトリは [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) をご覧ください。 ## Docker プロジェクトの設定 Next.js では、ビルド時の変数(フロントエンドで利用可能、接頭辞は`NEXT_PUBLIC`)と実行環境の変数(サーバサイドのみ)が異なる処理を必要とすることに注意してください。このデモでは 2 つの変数を使用しています。`Dockerfile`、コマンドライン引数、`docker-compose.yml`の中での位置に注意してください: - `DATABASE_URL` (サーバーが使用する) - `NEXT_PUBLIC_CLIENTVAR`(クライアントが使用する) ### 1. Next の設定 [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js) に、[出力ファイルのトレースを自動的に活用してイメージサイズを小さくする](https://nextjs.org/docs/advanced-features/output-file-tracing)ための`standalone`出力オプション設定を追加してください: ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. .dockerignore ファイルの作成
Click here and include contents in .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Dockerfile の作成 サーバ環境変数をコンテナに取り込んでいないため、[環境スキーマの検証](/en/usage/env-variables)が失敗します。これを防ぐために、ビルド時に環境スキーマが検証されないように、ビルドコマンドに `SKIP_ENV_VALIDATION=1` フラグを追加する必要があります。
Click here and include contents in Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-alpine3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Install Prisma Client - remove if not using Prisma COPY prisma ./ # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-alpine3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-alpine3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_備考_** > > - _Node 18 への移行後は、`--platform=linux/amd64`のエミュレーションが不要になる場合があります。_ > - _なぜ `libc6-compat` が必要なのかについては [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) を参照してください。._ > - _[Alpine 3.17 ベースのイメージを使用すると、Prisma で問題が発生することがあります](https://github.com/t3-oss/create-t3-app/issues/975)。`engineType="binary"`を設定すると、Alpine 3.17 の問題は解決されますが、[ただし、関連するパフォーマンスコストが発生します](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ > - _Next.js は[一般的な使用方法に関する匿名のテレメトリーデータ](https://nextjs.org/telemetry)を収集します。ビルド時にテレメトリを無効にするには、`ENV NEXT_TELEMETRY_DISABLED 1` の最初のインスタンスのコメントを外してください。実行時にテレメトリを無効にするには、2 番目のインスタンスのコメントを外してください。_
## ローカルでイメージをビルドし、実行する 以下のコマンドでこのイメージをローカルでビルドして実行します: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` [localhost:3000](http://localhost:3000/)を開き、実行中のアプリケーションを確認します。 ## Docker Compose また、Docker Compose でイメージを構築し、コンテナを実行することもできます。
Follow steps 1-4 above, click here, and include contents in docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` これを `docker compose up` コマンドで実行します: ```bash docker compose up ``` [localhost:3000](http://localhost:3000/)を開き、実行中のアプリケーションを確認します。
## Railway へのデプロイ アプリケーションのデプロイには、[Railway](https://railway.app)のような自動化された[Dockerfile deployments](https://docs.railway.app/deploy/dockerfiles) などの PaaS を使用することができます。[Railway CLI をインストールしている場合](https://docs.railway.app/develop/cli#install)は、以下のコマンドでアプリケーションをデプロイすることができます: ```bash railway login railway init railway link railway up railway open ``` "Variables"に行ってあなたの`DATABASE_URL`を指定します。その後、"Settings" に行き、"Generate Domain" を選択します。Railway 上での実行例を見るには[ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/)にアクセスしてください。 ## お役立ち情報 | リソース | リンク | | -------------------------------------- | -------------------------------------------------------------------- | | Dockerfile のリファレンス | https://docs.docker.com/engine/reference/builder/ | | Compose file バージョン 3 リファレンス | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Docker CLI リファレンス | https://docs.docker.com/engine/reference/commandline/docker/ | | Docker Compose CLI リファレンス | https://docs.docker.com/compose/reference/ | | Next.js の Docker イメージでのデプロイ | https://nextjs.org/docs/deployment#docker-image | | Next.js in Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Docker での Next.js の例 | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Next.js アプリの Docker イメージの作成 | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/ja/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Deployment", layout: "docs", description: "Learn how to deploy your T3 app to production.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/ja/deployment/netlify.mdx ================================================ --- title: Netlify description: Netlifyへのデプロイ layout: ../../../layouts/docs.astro lang: ja isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Netlify は Vercel の代替となり得る同系統のデプロイメントプロバイダです。本ドキュメントに基づいたサンプルリポジトリは [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) を参照してください。 ## Netlify でホスティングする理由 従来からの知識では、Vercel では Next.js のサポートが優れているとされています。これは Vercel が Next.js を開発しているためで、Next.js の最適なパフォーマンスと DX のためのチューニングに Vercel プラットフォームは大きな関心をもっています。多くのユースケースではそのとおりであり、この「標準的な道筋」から逸脱しても意味がありません。 また一般に、多くの Next.js の機能が Vercel でのみサポートされているという意見もあります。確かに新しい Next.js の機能はリリース時にデフォルトで Vercel でテストされサポートされることは事実ですが、Netlify のような他のプロバイダも、[安定した Next.js 機能](https://docs.netlify.com/integrations/frameworks/next-js/overview/)に対して[すぐに実装およびリリースサポート](https://www.netlify.com/blog/deploy-nextjs-13/)を提供することがあります。 結局のところ、すべてのデプロイメントプロバイダーには相対的な長所と短所があり、一つのホストがすべてのユースケースに対して最高のサポートを提供できるわけではありません。例えば、Netlify では、Netlify のエッジファンクション(Deno Deploy 上で動作)用に、独自の[カスタム Next.js ランタイム](https://github.com/netlify/next-runtime)を構築し、[HTTP レスポンスにアクセスへのアクセスと変更のための独自のミドルウェアをメンテナンス](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify)しています。 no-stableのNext 13の機能のステータスを追跡するには、[Next 13の`app` ディレクトリをNetlify上で使う](https://github.com/netlify/next-runtime/discussions/1724)を参照してください。 ## プロジェクトの設定 ビルド手順を設定する方法は、Netlify CLI や Netlify ダッシュボードを使って直接ビルド命令を設定するなど、たくさんあります。必須ではありませんが、[`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/)ファイルを作成して含めることをお勧めします。これにより、プロジェクトのフォークやクローン版を再現性をもって簡単にデプロイすることができます。 ```toml [build] command = "next build" publish = ".next" ``` ## Netlify Dashboard の使用方法 1. コードを GitHub リポジトリにプッシュし、[Netlify](https://app.netlify.com/signup)にサインアップしてください。アカウントを作成したら、**Add new site** から **Import an existing project** をクリックします。 ![Netlify の新プロジェクト](/images/netlify-01-new-project.webp) 2. Git プロバイダーを接続します。 ![リポジトリのインポート](/images/netlify-02-connect-to-git-provider.webp) 3. プロジェクトのリポジトリを選択します。 ![プロジェクトのリポジトリを選択する](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify は `netlify.toml` ファイルがあるかどうかを検出し、ビルドコマンドと公開ディレクトリを自動的に設定します。 ![Nextjs のビルド設定](/images/netlify-04-configure-build-settings.webp) 5. 「**Show advanced**」→「**New variable**」をクリックして、環境変数を追加します。 ![環境変数の追加](/images/netlify-05-env-vars.webp) 6. 「**Deploy site**」をクリックし、ビルドが完了するのを待ち、新しいサイトを表示します。 ## Netlify CLI の使用方法 コマンドラインからデプロイするには、まずプロジェクトを GitHub のリポジトリにプッシュし、[Netlify CLI をインストール](https://docs.netlify.com/cli/get-started/) する必要があります。`netlify-cli` はプロジェクトの依存関係としてインストールするか、以下のコマンドでマシンにグローバルにインストールすることができます: ```bash npm i -g netlify-cli ``` ローカルでプロジェクトをテストするには、[`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) コマンドを実行し、[`localhost:8888`](http://localhost:8888/) を開いてローカルで動いている Netlify アプリを表示します: ```bash ntl dev ``` [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment)コマンドを実行してプロジェクトを設定します: ```bash ntl init ``` `.env` ファイルからプロジェクトの環境変数を [`ntl env:import`](https://cli.netlify.com/commands/env#envimport) でインポートしてください: ```bash ntl env:import .env ``` [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys)でプロジェクトをデプロイします。デプロイ前にビルドコマンドを実行するには `--build` フラグを、サイトのメイン URL にデプロイするには `--prod` フラグを渡す必要があります: ```bash ntl deploy --prod --build ``` Netlify での実行例は、[ct3a.netlify.app](https://ct3a.netlify.app/)でご覧ください。 ================================================ FILE: www/src/pages/ja/deployment/vercel.md ================================================ --- title: Vercel description: Vercelへのデプロイ layout: ../../../layouts/docs.astro lang: ja --- アプリケーションは[Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss)にデプロイすることをお勧めします。Next.js のアプリケーションを超簡単にデプロイすることができます。 ## プロジェクト構成 Vercel はビルドコマンドの設定とディレクトリの公開を自動的にうまくやってくれる場合が多いでしょう。しかし、[`vercel.json`](https://vercel.com/docs/project-configuration)というファイルを作成し、以下のコマンドを含めることで、他の設定も含めてこれらの情報を明示指定することもできます。**これはほとんどのプロジェクトでは必要ありません**。 ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Vercel ダッシュボードを使う 1. コードを GitHub のリポジトリにプッシュした後、GitHub で[Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss)にサインアップし、**Add New Project**をクリックします。 ![Vercel の新プロジェクト](/images/vercel-new-project.webp) 2. GitHub のリポジトリをプロジェクトと一緒にインポートします。 ![インポートリポジトリ](/images/vercel-import-project.webp) 3. 環境変数を追加します。 ![環境変数を追加する](/images/vercel-env-vars.webp) 4. **Deploy**をクリックします。これで、リポジトリに変更をプッシュするたびに、Vercel は自動的にアプリケーションを再デプロイするようになります! ## Vercel CLI を使う コマンドラインからデプロイするには、まず [Vercel CLI をグローバルにインストール](https://vercel.com/docs/cli#installing-vercel-cli) する必要があります。 ```bash npm i -g vercel ``` [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) コマンドを実行してプロジェクトをデプロイします。 ```bash vercel ``` データベース接続文字列などの環境変数には、`--env DATABASE_URL=YOUR_DATABASE_URL_HERE`を含めるようにします。デプロイの質問を省略し、それぞれにデフォルトの回答を与えたい場合は `--yes` を使用します。 ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` 最初のデプロイの後、このコマンドは preview ブランチにデプロイされます。将来のデプロイのために、変更をライブサイトに直接プッシュするには、`--prod`を含める必要があります。 ```bash vercel --prod ``` ================================================ FILE: www/src/pages/ja/faq.mdx ================================================ --- title: FAQ description: T3 Appについてのよくある質問(FAQ) layout: ../../layouts/docs.astro lang: ja isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Create T3 App について、よくあるご質問をご紹介します。 ## 次に何をしたら良いですか?これでアプリケーションを作るにはどうすればいい? CT3A はできる限りシンプルにしたものなので、CT3A で生成した初期構成から始め、後で必要なものが出てきたらその時点で追加していくのが良いでしょう。 このプロジェクトで使用されているさまざまな技術に精通していない場合、それぞれのドキュメントを参照してください。それでも漠然としている場合は、私たちの[Discord](https://t3.gg/discord)に参加し質問してください。 - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## アプリケーションを最新の状態に保つにはどうすればよいですか? Create T3 App はフレームワークではなく、初期構成を生成するツールです。このことは、一度アプリケーションを初期化したなら、それはあなたのものになることを意味します。インストール後には、最新版へ追随するのを助けてくれる CLI ツールのようなものはありません。もし、私たちがテンプレートに加えた改良を追っていきたいのであれば、私たちのリポジトリで[リリースを通知できるようにする](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)と良いでしょう。とはいえ、私たちがテンプレートに加えたすべての変更を、あなたのアプリケーションに反映させる必要はないでしょう。 ## 現在、どのような学習リソースがありますか? 下記のリソースは T3 Stack のために存在する最高のものの一部ですが、コミュニティ(および[Theo](https://youtu.be/rzwaaWH0ksk?t=1436))としては、まずはスタックを使い始め、それを使って開発をしながら学んでいくことをお奨めします。 もしあなたが Create T3 App を検討しているなら、スタックの構成要素のいくつかをすでに使っているかもしれません。そうなら、思い切って飛び込んで、何かを作りながら他の構成要素を学んでいってみてはいかがでしょうか? とはいえ、この方法が誰にとってもうまくいくわけではないことも理解しています。もしあなたが、推奨される方法を試した上でもっとリソースが欲しいと感じたり、自分でやる自信がなかったり、スタックに圧倒されていると感じたら、Create T3 App に関する以下の素晴らしいチュートリアルをチェックしてみてください: ### 記事一覧 - [Create T3 App でフルスタックアプリを作る](https://www.nexxel.dev/blog/ct3a-guestbook) - [初めての Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [T3 アプリを Turborepo に移行する](https://www.jumr.dev/blog/t3-turbo) - [T3 アプリに Stripe を組み込む](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### 動画 - [T3 Stack チュートリアル - FROM 0 TO PROD FOR $0 (Next.js, tRPC, TypeScript, Tailwind, Prisma & More)](https://www.youtube.com/watch?v=YkOSUVzOAA4) **(recommended)** - [Jack Herrington - T3 Stack でメモ帳アプリを作る](https://www.youtube.com/watch?v=J1gzN1SAhyM) - [T3 Stack で Twitter クローンを作る - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [T3 スタックでブログを作る - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [T3 スタックによるライブチャットアプリケーションの構築 - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [T3 Stack - どのように作ったか](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [Create T3 App の概要(Next、Typescript、Tailwind、tRPC、Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## なぜプロジェクトに `.js` ファイルがあるのですか? [T3-原則#3](/ja/introduction#型安全は必須)にあるように、私たちは型安全性を第一級市民として扱っています。しかし残念ながら、すべてのフレームワークやプラグインが TypeScript をサポートしているわけではないので、設定ファイルの一部は `.js` ファイルでなければならないのです。 これらのファイルが JavaScript であることを強調するために、各ファイルの種別(`cjs`または`mjs`)を、それを使用するライブラリによってサポートするものに応じて明示的に宣言するように努めています。また、このプロジェクトのすべての `js` ファイルは、コンパイラ (tsconfig) の checkJs オプションを使用して型チェックが行われています。 ## アプリケーションを国際化対応するのに苦労しています。参考にできるものはありますか? `create-t3-app` にデフォルトで i18n を含めないことにした理由は、非常に意見が分かれるトピックであり、実装方法も多岐にわたるためです。 しかし、実装するのに苦労していて参考プロジェクトを見たい場合は、[next-i18next](https://github.com/i18next/next-i18next)を使って T3 アプリケーションに i18n を追加する方法を紹介する[参考リポジトリ](https://github.com/juliusmarminge/t3-i18n)があります。 ## Next.js 13 の /app ではなく、なぜ /pages を使っているのですか? [T3 原則 2](/ja/introduction#問題を解決する)の通り、私たちは最先端のものが大好きですが、安定性を重視しており、ルーター全体を移植するのは難しく、[血を流すのには適切なところではない](https://youtu.be/mnwUbtieOuI?t=1662)とみなしています。`/app` ディレクトリは [未来への一端を垣間見せてくれる](https://youtu.be/rnsC-12PVlM?t=818)ものではありますが、まだ本番環境には適していません。API はベータ版であり、破壊的な変更が予想されています。 `/app`ディレクトリでサポートされている機能、計画されている機能、作業中の機能の一覧は[ベータ版Next.js ドキュメント](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features)をご覧ください。 ================================================ FILE: www/src/pages/ja/folder-structure-pages.mdx ================================================ --- title: フォルダ構造 (Pages) description: 初期構成として新規に生成されたT3 Appのフォルダ構造 layout: ../../layouts/docs.astro lang: ja isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; 以下でパッケージを選択してみてください。すると、その選択したパッケージで新たに初期構成を生成したアプリケーションのフォルダ構造が表示されます。もっと下には各エントリーの説明があります。
### `prisma` `prisma` フォルダには、データベース接続とデータベーススキーマの設定に使用する `schema.prisma` ファイルが格納されています。 また、マイグレーションファイルやシードスクリプトを格納する場所でもあります(もし使用する場合)。 詳しくは、[Prisma の使い方](/ja/usage/prisma)を参照してください。
### `public` `public`フォルダには、Web サーバが提供する静的アセットが格納されています。`favicon.ico`ファイルは静的アセットの一例です。
### `src/env` 環境変数の検証や型定義に使用します。[環境変数](usage/env-variables)を参照。
### `src/pages` `pages`フォルダには、Next.js アプリケーションのすべてのページが格納されます。`pages`ディレクトリの直下にある `index.tsx` ファイルは、アプリケーションのホームページとなります。`_app.tsx`ファイルは、アプリケーションを各種プロバイダでラップするために使用されます。詳しくは、[Next.js ドキュメント](https://nextjs.org/docs/basic-features/pages)を参照してください。
#### `src/pages/api` `api`フォルダには、Next.js アプリケーションのすべての API ルーティングが格納されています。API ルートについては、[Next.js Api Routes Docs](https://nextjs.org/docs/api-routes/introduction)を参照してください。
#### `src/pages/api/auth/[...nextauth].ts` `[...nextauth].ts`ファイルは、NextAuth.js の認証スラッグルートです。認証リクエストを処理するために使用されます。NextAuth.js の詳細については [NextAuth.js usage](usage/next-auth) を、キャッチオール/スラッグルートについては [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) を参照してください。
#### `src/pages/api/trpc/[trpc].ts` `[trpc].ts`ファイルは、tRPC API のエントリポイントです。このファイルは、tRPC リクエストを処理するために使用されます。このファイルの詳細については[tRPC の使い方](usage/trpc#-pagesapitrpctrpcts)を、キャッチオール/スラッグルートの情報については[Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) を参照してください。
### `src/server` `server`フォルダは、サーバサイドのコードとクライアントサイドのコードを明確に分けるために使用されます。
#### `src/server/auth.ts` サーバーサイドの認証ロジックのメインエントリポイントです。 ここでは、NextAuth.js の[設定オプション](usage/next-auth)、[モジュール拡張](usage/next-auth#inclusion-of-userid-on-the-session) と、サーバサイドでユーザのセッションを取得するなどの認証に関する DX ユーティリティを提供しています。詳しくは [NextAuth.js usage](usage/next-auth#usage-with-trpc) をご覧ください。
#### `src/server/db.ts` `db.ts`ファイルは、グローバルスコープで Prisma クライアントをインスタンス化するために使用されます。詳しくは、[Prisma の使い方](usage/prisma#prisma-client)と[Next.js で Prisma を使うためのベストプラクティス](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices)をご覧ください。
### `src/server/api` `api`フォルダには、tRPC のサーバーサイドのコードが格納されています。
#### `src/server/api/routers` `routers`フォルダには、すべての tRPC サブルーターが格納されています。
#### `src/server/api/routers/example.ts` `example.ts`ファイルは、`publicProcedure`ヘルパーを利用した tRPC ルーターの例で、public な tRPC ルートを作成する方法を示しています。 このルーターには、あなたがどのパッケージを選択したかに応じて増減させたルーターが含められており、あなたが必要とする使用方法を示しています。
#### `src/server/api/trpc.ts` `trpc.ts`ファイルは、tRPC バックエンドのメイン設定ファイルです。この中で私たちは: 1. tRPC リクエストで使用するコンテキストを定義しています。詳しくは[tRPC の使い方](usage/trpc#-serverapitrpcts)を参照してください。 2. プロシージャヘルパーをエクスポートしています。詳しくは、[tRPC usage](usage/trpc#-serverapitrpcts) を参照してください。
#### `src/server/api/root.ts` `root.ts` ファイルは、ルータの型定義と同様に、tRPC ルータをマージして 1 つのルータとしてエクスポートするために使用されます。詳しくは[tRPC の使い方](usage/trpc#-serverapirootts)を参照してください。
### `src/styles` `styles`フォルダには、アプリケーションのグローバルスタイルが格納されます。
### `src/utils` `utils`フォルダは、よく再利用されるユーティリティ関数を置いておくために使用されます。
#### `src/utils/api.ts` `api.ts`ファイルは、tRPC のフロントエンドのエントリポイントです。詳しくは[tRPC の使い方](usage/trpc#-utilsapits)を参照してください。
### `.env` `.env`ファイルは、環境変数を格納するために使用されます。詳しくは [環境変数](usage/env-variables) を参照してください。このファイルは、git 履歴にコミットする必要はありません。
### `.env.example` `.env.example` ファイルは、選択したライブラリに基づく環境変数の例を示しています。このファイルは git 履歴にコミットしておく必要があります。
### `.eslintrc.cjs` `.eslintrc.cjs`ファイルは、ESLint の設定に使用されます。詳しくは[ESLint Docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files)を参照してください。
### `next-env.d.ts` `next-env.d.ts`ファイルは、Next.js の型が TypeScript コンパイラに拾われるようにするためのファイルです。**いつでも変化し得るものなので、削除や編集はしないでください。** 詳細は [Next.js Docs](https://nextjs.org/docs/basic-features/typescript#existing-projects) をご覧ください。
### `next.config.mjs` `next.config.mjs`ファイルは、Next.js を設定するためのものです。詳しくは[Next.js Docs](https://nextjs.org/docs/api-reference/next.config.js/introduction)を参照してください。注: .mjs という拡張子は、ESM でのインポートができるようにするために使用されます。
### `postcss.config.js` Tailwind の PostCSS の利用には、`postcss.config.js`ファイルが使用されます。詳しくは[Tailwind PostCSS Docs](https://tailwindcss.com/docs/installation/using-postcss)を参照してください。
### `prettier.config.mjs` `prettier.config.mjs`ファイルは、Tailwind CSS クラスをフォーマットするために prettier-plugin-tailwindcss を含めるように Prettier を設定するために使用します。詳しくは [Tailwind CSS ブログポスト](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) を参照してください。
### `tsconfig.json` `tsconfig.json`ファイルは、TypeScript を設定するために使用されます。Create T3 App とそのライブラリの TypeScript を最適に利用するために、`strict mode`のようないくつかの非デフォルトが有効になっています。詳細については、[TypeScript ドキュメント](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) または [使用法の TypeScript](usage/typescript) を参照してください。
================================================ FILE: www/src/pages/ja/installation.mdx ================================================ --- title: インストール description: Create T3 Appによるインストール手順 layout: ../../layouts/docs.astro lang: ja isMdx: true --- import Callout from "../../components/docs/callout.tsx"; `create-t3-app`を使用してアプリケーションの初期構成を生成するには、以下の 3 つのコマンドのいずれかを実行し、コマンドプロンプトの質問に答えてください: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` アプリケーションの初期構成が生成されたら、[ファーストステップ](/ja/usage/first-steps) をチェックして、新しいアプリケーションを開始しましょう。 ## 高度な使用法 | オプション/フラグ | 説明文 | | ----------------- | -------------------------------------------------------------------------------- | | `[dir]` | ディレクトリ引数にプロジェクト名を含める | | `--noGit` | プロジェクト内に新しい git リポジトリを初期化しないよう CLI に明示的に指示する | | `-y`, `--default` | CLI の確認をバイパスして、すべてのオプションを選択した新しい t3-app を立ち上げる | | `--noInstall` | 依存関係をインストールせずにプロジェクトを生成する | ## 実験的な使い方 CI 用として、プロンプトなしで任意のアプリケーションの初期構成を生成できる実験的なフラグをいくつか用意しています。 このユースケースがあなたに当てはまるなら、これらのフラグを使用することができます。これらのフラグは実験的なものであり、semver のバージョニングに従わずに将来的に変更されるかもしれないことに注意してください。 | フラグ | 説明文 | | ------------ | ------------------------------------- | | `--CI` | CI モードであることを CLI に知らせる | | `--trpc` | プロジェクト内に tRPC を含める | | `--prisma` | Prisma をプロジェクトに組み込む | | `--nextAuth` | NextAuth.js をプロジェクトに含める | | `--tailwind` | Tailwind CSS をプロジェクトに組み込む | `CI`フラグを指定しない場合、その他のフラグは効果を持ちません。 不要なパッケージを明示的にオプトアウトする必要はありません。しかし、明示的にそうしたい場合は、`--nextAuth false` のように `false` を渡すことができます。 ### 例 以下は、tRPC と Tailwind CSS を使った T3 App の初期構成を生成します。 ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/ja/introduction.md ================================================ --- title: イントロダクション description: T3 Stackの紹介 layout: ../../layouts/docs.astro lang: ja ---
## T3 Stack 「T3 Stack」は、[Theo](https://twitter.com/t3dotgg)が作った Web 開発スタックで、シンプルさ、モジュール性、フルスタックに渡っての型安全性に焦点を当てています。 核となるのは[**Next.js**](https://nextjs.org/)と[**TypeScript**](https://typescriptlang.org/)です。また、[**Tailwind CSS**](https://tailwindcss.com/)は、ほぼ必ず含まれています。バックエンドに近いことをするのであれば、[**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), [**NextAuth.js**](https://next-auth.js.org/) も素晴らしい追加要素になるでしょう。 お気づきかもしれませんが、構成要素の数が多いのですが、意図的にそうしており、必要に応じて構成要素を入れ替えることができます。このスタックはモジュール式です。:) ## さて、create-t3-app とは何でしょう?テンプレート? 一種そのようなものです。`create-t3-app`は、モジュール式の T3 Stack アプリケーションのセットアップを効率化するために、経験豊富な T3 Stack 開発者によって作られた CLI です。つまり、各パーツはオプションであり、「テンプレート」があなたの特定のニーズに基づいて生成されることを意味します。 長年の間、数え切れないほどのプロジェクトにおいてこの技術に携わってきた私たちには、たくさんの意見や洞察があります。私たちは、それらをこの CLI に反映させるために最善を尽くしました。 ただしこれは、すべてを網羅したテンプレートでは**ありません**。私たちは、あなたが **あなたの**アプリケーションのニーズを解決する独自のライブラリを持ってくることを**期待しています**。状態管理やデプロイメントなど、より具体的な問題に対する解決策を規定するつもりはありませんが、私たちは[ここにいくつかの推奨事項を挙げています](/en/other-recs). ## T3 原則 率直に言って、T3 Stack は「_主張の強い_」プロジェクトです。私たちは、開発に関する一握りの核となる信念を共有し、私たちの判断基準としています。 ### 問題を解決する 「何でもかんでも追加してしまう」という罠に容易に陥りがちですが、わたしたちは明示的にそれを避けます。`create-t3-app` に追加するものはすべて、その領域における固有の問題を解決している必要があります。したがって状態管理ライブラリ(Zustand、Redux 等)は含めませんが、NextAuth.js などは追加し、Prisma や tRPC などを統合します。 ### 責任を負って血を流す 私たちは、最先端の技術が大好きです。正直なところ、新しい技術から生まれる開発速度と楽しさは本当に素晴しいものです。ただし、責任を持って血を流すことが重要です。リスクが低い領域では高リスク技術を採用しつつ、冒険的な新しいデータベース技術は採用しません(SQL は偉大なり!)。しかし、tRPC は単なる関数であり、問題があれば簡単に交換できるので、私たちは喜んでそれに賭けるのです。 ### 型安全は必須 Create T3 App の目標は、**型安全な**フルスタック Web アプリケーションを新規開発するための最も迅速な方法を提供することです。生産性を高め、バグの少ない製品を提供するのに役立つので、型安全性を真剣に考えています。Create T3 App の型安全性を損なうような妥協は他のプロジェクトで行うべき決定です。 ================================================ FILE: www/src/pages/ja/other-recs.md ================================================ --- title: その他のオススメ description: 多くのプロジェクトに推奨しているライブラリ・サービス layout: ../../layouts/docs.astro lang: ja --- 私たちは、`create-t3-app`に含まれるライブラリがすべての問題を解決するわけではないことを認識しています。私たちの提供物でプロジェクトを始めることをお勧めしますが、他パッケージの導入が必要となる時も来るでしょう。あなたのプロジェクトに必要なものを知っているのはあなただけです。ここでは、私たちが頻繁に推奨しているものをいくつか紹介します。 これらは、個々の Create T3 App の貢献者による推奨であり、Create T3 App チームまたは T3-OSS による「公式」推奨とみなされるものではありません。 _**特に有料サービスを利用する前にはご自身で調査してください**_。 ## 状態管理 _**編注**_:tRPC の React Query フックでサーバー状態を管理することができるでしょう。クライアントの状態管理については、React の`useState`から始め、不足を感じた時になってからこれらの選択肢のいずれかを使用すれば良いでしょう。 ### Zustand **もう二度と Redux を使わないために** あなたが知らない「モダンでシンプルな Redux」。[Poimandres](https://github.com/pmndrs)はいつも信頼できます。この小さなライブラリで、ビデオ通話アプリからゲーム、サーバーまで、あらゆるものを作れます。 - [Zustand ホームページ](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **もう二度と Context を使わないために** よりアトミックなアプローチでは、Jotai が勝るとも劣りません。Zustand と同じく、[Poimandres](https://github.com/pmndrs)から提供される Jotai は、グローバルな useState のような感覚でシングルトンを定義することができます。ステートマシンまでは必要としないステートフルな振舞いに最適なオプションです。 - [Jotai ホームページ](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## コンポーネントライブラリ ほとんどのアプリケーションは、トグルボタン、ドロップダウンメニュー、モーダルなど、同じ一握りのコンポーネントを必要とします。これらのライブラリは、あなたの好みに合わせてカスタマイズできる、アクセスしやすい優れたコンポーネントを提供しています。 ### スタイル付けされていない(Unstyled)コンポーネントライブラリ 「ヘッドレスライブラリ」とも呼ばれ、スタイルにとらわれず、自分好みにカスタマイズできるアクセス性の高い素晴らしいコンポーネントが提供されています。ここでは、おすすめのものをいくつか紹介します。 - [Radix UI](https://www.radix-ui.com/) は、バニラまたは Tailwind CSS でスタイルを設定できる、便利でアクセスしやすいプリミティブの強力なセットを提供します。 - Tailwind CSS チームによって作られた[Headless UI](https://headlessui.com/)も、Tailwind CSS とシームレスに統合された、スタイルのないアクセシビリティを考慮したコンポーネント群を提供しています。 - [React Aria](https://react-spectrum.adobe.com/react-aria/)は、アクセシビリティを考慮した UI プリミティブをあなたのデザインシステムに提供します。彼らの Date Picker コンポーネントはトップクラスです。 ### スタイル付けされた(Styled)コンポーネントライブラリ **アプリケーションを見た目良くしたいだけの場合** プロジェクトでは、UI がすぐに格好良く見えるようにしたい場合があります。管理者用ダッシュボードや、他の類似のプロジェクトでは、以下のコンポーネントライブラリのいずれかを使用すれば、目的を達成できます。 - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **UI ライブラリ構築用** 色やサイズなどのバリエーションを持った UI ライブラリを宣言的に構築します。プロジェクトが大規模になり、複数のバリエーションがある 標準化した Tailwind CSS ベースの UI コンポーネントのセットが必要になったとき、CVA は素晴らしいツールになります。 - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## アニメーション アニメーションが必要なときのおすすめを紹介します。 ### AutoAnimate **1 行のコードでアニメーションを作成する場合** 多くのアニメーションライブラリはありとあらゆるユースケースを満たそうとするため、結果的に不格好になっています。AutoAnimate は設定不要のツールであり、開発者の労力を増やすことなく UX を大幅に向上させることができます。 - [AutoAnimate のホームページ](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [AutoAnimate コンポーネントスニペット](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **宣言的なコードで複雑なアニメーションを実現する**。 Framer Motion は、シンプルで宣言的なシンタックスを提供し、複雑なアニメーションからジェスチャーまで、より少ないコードで作成できるようにします。 - [Framer Motion ホームページ](https://framer.com/motion) - [Framer Motion ドキュメント](https://www.framer.com/docs/) ## デプロイメント、インフラ、データベース、CI ### Vercel **アプリケーションのホスティング用** Vercel は、デプロイ作業のひどいつらみを巻き取ってくれ、GitHub 連携を設定すれば放っておいてもよしなにやってくれます。また、数十万人もいる利用者に何の問題もなくプロダクトを展開できました。AWS の力も得て、インターフェイスもはるかに優れています :) - [Vercel のホームページ](https://vercel.com/) - [Create T3 App Vercel デプロイメントガイド](/ja/deployment/vercel) ### PlanetScale **心配いらずのデータベースのために** PlanetScale は、これまで使った中で最も優れた「サーバーレスデータベースプラットフォーム」です。驚異的なスケーラビリティ、優れた開発者体験、そして素晴しい価格設定が魅力です。SQL(できれば Prisma)を使っているのであれば、これに勝るものはありません。 - [PlanetScale ホームページ](https://planetscale.com/) ### Railway **インフラのホスティングに最適** 一言で言えば「モダンな Heroku」。本格的なサーバーを立ち上げる最も簡単な方法です。Vercel と PlanetScale だけでは十分でない場合、Railway がおそらくそれに対応します。GitHub のリポジトリを指定して実行してみてください。 - [Railway ホームページ](https://railway.app/) ### Upstash **サーバーレス Redis 用** 私たちは Prisma と PlanetScale が大好きですが、より高性能なソリューションが必要なプロジェクトもあります。Upstash を使うと、インフラとスケーリングを自分で管理することなく、サーバーレスプロジェクトで Redis のインメモリ性能を得ることができます。 - [Upstash ホームページ](https://upstash.com/) ### Pusher **サーバーレス WebSockets 用** WebSocket がプロジェクトの主な焦点である場合、[Fastify](https://www.fastify.io/) (これは[tRPC でも動作する!](https://trpc.io/docs/v10/fastify))のようなもっと伝統的なバックエンドを検討することをお勧めします。しかし、T3 アプリに WebSocket を素早く追加するには、Pusher は優れた選択です。 - [Pusher ホームページ](https://pusher.com/) ### Soketi Soketi は Pusher の代替となる、セルフホスティング可能でシンプルかつ高速なソフトウェアです。サーバーに接続するために使用できる Pusher SDK と完全に互換性があります。Soketi のサーバーレスもベータ版です。 - [Soketi ホームページ](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## アナリティクス(分析) アプリケーションを開発していく上で、ユーザーデータはとても貴重です。ここでは、私たちがおすすめするアナリティクスプロバイダーをご紹介します。 ### Plausible アナリティクスが必要ですか?Plausible は、それを得るための最も迅速な方法の 1 つです。非常にミニマムで、[Next.js 用のシンプルなプラグイン](https://plausible.io/docs/proxy/guides/nextjs)もあります。 - [Plausible ホームページ](https://plausible.io/) ### Umami Umami は、Google アナリティクスの代替であり、オープンソースでセルフホスティング可能、シンプルで高速、プライバシーに重点を置いたソフトウェアです。PlanetScale をデータベースとして、Vercel や Railway などに実に簡単にデプロイすることができます。 - [Umami ホームページ](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) # その他 ### Next Bundle Analyzer アプリケーションのビルド出力に何が含まれるかを判断するのが難しい場合があります。Next Bundle Analyzer は、生成される JavaScript バンドルを視覚化し分析する簡単な方法です。 - [@next/bundle-analyzer on npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/ja/t3-collection.mdx ================================================ --- title: T3 コレクション description: T3 Stackを使用するクールなOSSプロジェクトと企業 layout: ../../layouts/docs.astro lang: ja isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import Callout from "../../components/docs/callout.tsx"; T3 スタックを使用したプロジェクトを作成し、それを共有したいですか?リストに追加してください! ## T3 Stack を使用して作られたオープンソースのアプリケーション ## T3 Stack を利用している企業 アプリケーション開発に T3 スタックを使用している企業をぜひ教えてください。あなたの会社は T3 スタックを使っていることを共有したいですか?リストに追加してください! T3 スタックを使ったクールなプロジェクトをお持ちですか?[プルリクエスト](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx)を作成し、ここに追加してください! ================================================ FILE: www/src/pages/ja/usage/env-variables.mdx ================================================ --- title: 環境変数 description: Create T3 Appを始めるにあたって layout: ../../../layouts/docs.astro lang: ja isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Create T3 App は [Zod](https://github.com/colinhacks/zod) を使用して、`src/env.js`にいくつかの追加ロジックを提供することにより、実行時*および*構築時に環境変数の検証を行います。 ## env.js _要約; 環境変数を追加する場合は、`.env`に追加するとともに、`src/env.js`にバリデーターを定義する必要があります。_ このファイルは、スキーマと、オブジェクトの検証ロジックおよび再構築の、2 つの部分に分かれています。検証ロジックは触る必要がありません。 ```ts:env.js const server = z.object({ NODE_ENV: z.enum(["development", "test", "production"]), }); const client = z.object({ // NEXT_PUBLIC_CLIENTVAR: z.string(), }); const processEnv = { NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }; ``` ### サーバースキーマ サーバーサイドの環境変数スキーマをここで定義します。 重要なシークレットがクライアント側に漏れないようにするため、ここでのキーには `NEXT_PUBLIC` の接頭辞をつけないようにしてください。 ### クライアントスキーマ クライアント側の環境変数のスキーマをここで定義します。 クライアントに公開するには接頭辞 `NEXT_PUBLIC` を付ける必要があります。接頭辞を付けない場合、検証が失敗するので不正な構成を検出できます。 ### processEnv オブジェクト ここで `process.env` を再構築します。 Zod スキーマを解析できる JavaScript オブジェクトが必要で、Next.js が環境変数を扱う方法のため、通常のオブジェクトのように process.env を再構築できません。そのため、手動で行う必要があります TypeScript は、両方のスキーマからすべてのキーを再構築したことを確認するのに役立ちます。 ```ts // ❌ This doesn't work, we need to destruct it manually const schema = z.object({ NEXT_PUBLIC_WS_KEY: z.string(), }); const validated = schema.parse(process.env); ``` ### バリデーションロジック _興味のある読者のために:_
高度な話題: バリデーションロジック 環境(サーバーまたはクライアント)に応じて、両方のスキーマが検証されるのか、クライアントのスキーマだけが検証されるのかが決まります。つまり、サーバーの環境変数が未定義であっても、バリデーションが失敗することはありません。つまり、環境変数のエントリーポイントを 1 つにすることができます。 ```ts // src/env.js const isServer = typeof window === "undefined"; const merged = server.merge(client); const parsed = isServer ? merged.safeParse(processEnv) // <-- on server, validate all : client.safeParse(processEnv); // <-- on client, validate only client if (parsed.success === false) { console.error( "❌ Invalid environment variables:\n", ...formatErrors(parsed.error.format()), ); throw new Error("Invalid environment variables"); } ``` そして、プロキシオブジェクトを使って、クライアントでサーバーサイドの環境変数にアクセスしようとすると、エラーを投げるようにしています。 ```ts // src/env.js // proxy allows us to remap the getters export const env = new Proxy(parsed.data, { get(target, prop) { if (typeof prop !== "string") return undefined; // on the client we only allow NEXT_PUBLIC_ variables if (!isServer && !prop.startsWith("NEXT_PUBLIC_")) throw new Error( "❌ Attempted to access serverside environment variable on the client", ); return target[prop]; // <-- otherwise, return the value }, }); ```
## 環境変数の使用 環境変数を使いたいときは、`env.js`からインポートして、通常と同じように使うことができます。これをクライアントでインポートして、サーバー側の環境変数にアクセスしようとすると、ランタイムエラーが発生します。 ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` is fully typesafe and provides autocompletion const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ This will throw a runtime error const dbUrl = env.DATABASE_URL; // ✅ This is fine const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example デフォルトの `.env` ファイルはバージョンコントロールにコミットされないので、初期構成プロジェクトには`.env.example` ファイルも用意しました。このファイルは、シークレット情報が削除された `.env` ファイルのコピーを保存しておくことができます。これは必須ではありませんが、貢献者が自分の環境をできるだけ簡単に立ち上げられるようにするために、example を最新に保つことをお勧めします。 Next.js などのいくつかのフレームワークやビルドツールでは、シークレット情報を `.env.local` ファイルに保存し、 `.env` ファイルをプロジェクトにコミットすることを推奨するものがあります。しかしこれだと誤ってプロジェクトにシークレット情報をコミットしてしまうかもしれないため、あまりお勧めできません。その代わりに、シークレット情報を `.env` に保存し、 `.env` ファイルを `.gitignore` に保存し、 `.env.example` ファイルのみをプロジェクトにコミットすることを推奨します。 ## 環境変数を追加する プロジェクトが必要とする環境変数がない状態でビルドが完了しないようにするために、**2 箇所** に新しい環境変数を追加する必要があります: 📄 `.env`: 通常の`.env`ファイルに入力するように、環境変数を入力します(例:`KEY=VALUE`)。 📄 `env.js`:Zod スキーマを定義することで、環境変数に適切な検証ロジックを追加します。(例:`KEY: z.string()` )。そして、環境変数を `process.env` から `processEnv` オブジェクトに再構築します(例:`KEY: process.env.KEY` )。 オプションで、`.env.example`を最新化しておいても良いでしょう: 📄 `.env.example`:環境変数を入力します。ただし、シークレット値は含めないように注意してください。(例: `KEY=VALUE` or `KEY=`) ### 例 _サーバーサイドの環境変数として、Twitter API Token を追加したい_。 1. 環境変数を `.env` に追加する: ``` TWITTER_API_TOKEN=1234567890 ``` 2. 環境変数を `env.js` に追加する: ```ts export const server = z.object({ // ... TWITTER_API_TOKEN: z.string(), }); export const processEnv = { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }; ``` 3. _オプション:_ 環境変数を `.env.example` に追加しますが、トークン値自体は含めないようにします。 ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/ja/usage/first-steps.md ================================================ --- title: はじめの一歩 description: 新しいT3アプリを使い始める layout: ../../../layouts/docs.astro lang: ja --- 新しい T3 アプリのために初期構成を生成し、準備ができたとして、ここではアプリを動作させるために必要な最低限の手順を説明します。 ## データベース アプリケーションに Prisma を含めた場合には、アプリケーションのルートディレクトリから`npx prisma db push`を実行してください。このコマンドは Prisma スキーマをデータベースと同期させ、スキーマに基づいて Prisma クライアントの TypeScript 型を生成します。なお、このコマンドを実行した後、生成された型を検出できるように [TypeScript サーバーを再起動する](https://tinytip.co/tips/vscode-restart-ts/) 必要があることに注意して下さい。 ## 認証 アプリケーションに NextAuth.js を含めた場合、まず`DiscordProvider`から始めます。これは NextAuth.js が提供するプロバイダの中で最もシンプルなものの一つですが、初期設定が少し必要です。 もちろん、別の認証プロバイダを使いたい場合は、NextAuth.js が提供する[多数のプロバイダ](https://next-auth.js.org/providers/)から選んで利用することができます。 1. Discord のアカウントが必要になりますので、まだの方は登録してください。 2. https://discord.com/developers/applications に移動し、右上の「New Application」をクリックします。アプリケーションの名前を付け、利用規約に同意してください。 3. アプリケーションの作成が完了したら、「Settings → OAuth2 → General」に移動してください。 4. 「Client ID」をコピーし、`AUTH_DISCORD_ID`として`.env`に追加します。 5. 「Reset Secret」をクリックし、新しいシークレット情報をコピーし、`AUTH_DISCORD_SECRET`として`.env`に追加します。 6. 「Add Redirect」をクリックし、`http://localhost:3000/api/auth/callback/discord`と入力します。 - 本番環境でのデプロイの場合は、前述の手順で別の Discord アプリケーションを作成しますが、今回は`http://localhost:3000`をデプロイ先の URL で置き換えてください。 7. 変更を保存します。 これでログインできるようになります。 ## エディターの設定 開発者体験を最適にするために、以下の拡張機能を推奨します。以下のリンクは、それぞれのエディター用のプラグインサポートを提供します。 - [Prisma Extension](https://www.prisma.io/docs/guides/development-environment/editor-setup) - [Tailwind CSS IntelliSense Extension](https://tailwindcss.com/docs/editor-setup) - [Prettier Extension](https://prettier.io/docs/en/editors.html) ## 次のステップへ - もしあなたのアプリケーションが tRPC を含んでいるならば、`src/pages/index.tsx` と `src/server/api/routers/post.ts` をチェックして、tRPC クエリがどのように動作するかを見てください。 - Create T3 App のドキュメントや、あなたのアプリケーションに含まれるパッケージのドキュメントを見てみてください。 - [Discord](https://t3.gg/discord)に参加し、[GitHub](https://github.com/t3-oss/create-t3-app)でスターを付けてください!:) ================================================ FILE: www/src/pages/ja/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Usage", layout: "docs", description: "Learn how to use the different technology from the T3 Stack.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/ja/usage/next-auth.md ================================================ --- title: NextAuth.js description: NextAuth.jsの使い方 layout: ../../../layouts/docs.astro lang: ja --- Next.js アプリケーションが認証システムを必要とするとき、NextAuth.js は、自分で構築する手間をかけずに、複雑なセキュリティを導入できる優れたソリューションです。NextAuth.js には OAuth 認証をすばやく追加するための豊富なプロバイダーの一覧が付属しており、多くのデータベースや ORM のためのアダプターを提供しています。 ## コンテクストプロバイダー アプリケーションのエントリーポイントでは、アプリケーションが [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider)でラップされていることがわかります: ```tsx:pages/_app.tsx ``` このコンテキストプロバイダーによって、アプリケーションはセッションデータを props として渡すことなく、アプリケーションのどこからでもアクセスできるようになります: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Handle unauthenticated state, e.g. render a SignIn component return ; } return

Welcome {session.user.name}!

; }; ``` ## サーバーサイドでセッションを取得する 時には、サーバー上でセッションを要求したいこともあるかもしれません。そのためには、`create-t3-app`が提供するヘルパー関数 `getServerAuthSession` を使ってセッションをプリフェッチし、`getServerSideProps` を使ってクライアントに渡します: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // NOTE: `session` wont have a loading state since it's already prefetched on the server ... } ``` ## セッションに `user.id` を含める Create T3 App は、NextAuth.js の設定にある[session callback](https://next-auth.js.org/configuration/callbacks#session-callback)を利用して、ユーザー ID を`session`オブジェクトに含めるように設定します。 ```ts:server/auth.ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` これは、`session`オブジェクトにアクセスしたときに `user.id` が型付けされることを確認するために型宣言ファイルと組合せて使用されます。詳細については NextAuth.js のドキュメントにある [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) を参照ください。 ```ts:server/auth.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` 同じパターンを使って、`role`フィールドのような他のデータを`session`オブジェクトに追加することができますが、**クライアントに機密データを保存するために使用してはなりません**。 ## tRPC との併用 NextAuth.js を tRPC で利用する場合、[ミドルウェア](https://trpc.io/docs/v10/middlewares) を使って、再利用可能で保護されたプロシージャを作成することができます。これにより、認証されたユーザーのみがアクセスできるプロシージャを作成することができます。`create-t3-app`は、認証されたプロシージャの中でセッションオブジェクトに簡単にアクセスできるように、すべてセットアップしてくれます。 これは、2 段階のプロセスで行われます: 1. [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession) 関数を使用して、リクエストヘッダーからセッションを取得します。通常の `getSession`の代わりに`getServerSession` を使用する利点は、サーバーサイドのみの関数であるため、不要なフェッチ呼び出しが発生しないことです。`create-t3-app`は、この特殊な API を抽象化するヘルパー関数を作成するので、セッションにアクセスするたびに、NextAuth.js のオプションと`getServerSession` 関数の両方をインポートする必要がありません。 ```ts:server/auth.ts export const getServerAuthSession = (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); }; ``` このヘルパー関数を使って、セッションを取得し、tRPC コンテキストに渡すことができます: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. ユーザーが認証されているかどうかをチェックする tRPC ミドルウェアを作成します。そして、そのミドルウェアを `protectedProcedure` で使用します。これらのプロシージャの呼び出し元はすべて認証されていなければなりません。そうでなければ、エラーが投げられるので、クライアントで適切にエラー処理を行えます。 ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }) ``` セッションオブジェクトは、ユーザーの軽くて最小限の表現であり、いくつかのフィールドしか含んでいません。`protectedProcedures`を使用するとユーザー id にアクセスでき、データベースからさらにデータを取得するのに使用できます。 ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Prisma との併用法 NextAuth.js を Prisma と共に動作させるには、多くの[初期設定](https://authjs.dev/reference/adapter/prisma/)が必要ですが、`create-t3-app` はこのすべてを処理します。`create-t3-app` の実行において Prisma と NextAuth.js の両方を選択すると、必要なモデルがすべて設定された、完全に動作する認証システムを手に入れることができます。初期構成として生成されたアプリケーションには、Discord OAuth プロバイダがあらかじめ設定されています。これを使っているのは一番簡単に始められるからで、`.env`にトークンを設定するだけで準備完了です。しかし、[NextAuth.js ドキュメント](https://next-auth.js.org/providers/) に従えば、簡単に他のプロバイダを追加することもできます。なお、プロバイダによっては、特定のモデルに余分のフィールドを追加しなければならない場合があります。利用したいプロバイダのドキュメントを読んで、必要なフィールドがすべて揃っていることを確認することをお勧めします。 ### モデルに新しいフィールドを追加する `User`、`Account`、`Session`、`VerificationToken`のいずれかのモデルに新しいフィールドを追加する場合(ほとんどの場合、変更する必要があるのは`User`モデルのみです)、[Prisma アダプター](https://next-auth.js.org/adapters/prisma) が新しいユーザーのサインアップやログイン時に自動的にこれらのモデル上にフィールドを作成することを念頭に置いておく必要があります。したがって、これらのモデルに新しいフィールドを追加する場合は、デフォルト値を指定する必要があります。 例えば、`User`モデルに `role` を追加したい場合、`role` フィールドにデフォルト値を指定する必要があります。これは `User` モデルの `role` フィールドに `@default` 値を追加することで実現できます: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Next.js ミドルウェアを使った利用法 NextAuth.js を Next.js ミドルウェアで利用する場合、認証に [JWT セッション戦略](https://next-auth.js.org/configuration/nextjs#caveats)を利用する必要があります。これは、ミドルウェアが JWT である場合にのみ、セッションクッキーにアクセスすることができるためです。デフォルトでは、Create T3 App は、データベースアダプターとして Prisma と組み合わせて、**default**データベースストラテジーを使用するように構成されています。 ## デフォルトの DiscordProvider を設定する 1. [Discord Developer Portal の Application セクション](https://discord.com/developers/applications)に向かい「New Application」をクリックします。 2. 設定メニューの 「OAuth2 ⇒ General」に行きます - Client ID をコピーして、`.env`の`AUTH_DISCORD_ID`に貼り付けます。 - Client Secret の下にある 「Reset Secret」をクリックし、その文字列を`.env`の`AUTH_DISCORD_SECRET`にコピーしてください。このシークレット情報は二度と表示されないことと、リセットすると既存のシークレット情報は失効してしまうことについて注意してください。 - 「Add Redirect」をクリックし、`/api/auth/callback/discord` を貼り付ける(ローカル開発サーバの場合の例:http://localhost:3000/api/auth/callback/discord) - 変更を保存します - 開発用と本番用で同じ Discord Application を使用できますが、推奨はしません。また、開発時には[プロバイダをモックする](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts)こと検討するのもよいでしょう。 ## お役立ち情報 | リソース | リンク | | ----------------------------------------- | --------------------------------------- | | NextAuth.js ドキュメント | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC キッチンシンク - NextAuth と併用して | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/ja/usage/next-js.md ================================================ --- title: Next.js description: Next.jsの使い方 layout: ../../../layouts/docs.astro lang: ja --- Next.js は、React アプリケーションのためのバックエンドフレームワークです。
[Theo's Next.js Conf talk](https://www.youtube.com/watch?v=W4UhNo3HAMw) をチェックして、Next.js とは何であり、どのように機能するのかをより深く理解しましょう。

## なぜ Next.js を使う必要があるの? 私たちは React が大好きです。これまで想像もしなかったような方法で UI 開発を身近なものにしてくれました。しかし、その反面、開発者を険しい道へ導くこともあります。Next.js は、React を使ったアプリケーションを作成するための、思想に拘りすぎず、かつ強力に最適化されたアプローチを提供します。ルーティングから API 定義、画像レンダリングに至るまで、私たちは Next.js が開発者を正しい判断に導いてくれると信じています。 Next.js と[Vercel](https://vercel.com/)を組み合わせることで、これまで以上に簡単に Web アプリケーションの開発とデプロイができるようになります。非常に寛大な無料利用枠と直感的なインターフェイスが、「ポイント&クリック」でサイト公開を行えるソリューションを提供します(We ❤️ Vercel)。 ## Static/Server Props を取得する Next.js の大きな特徴はデータフェッチ機能です。各メソッドの使い方や違いを理解するために、[公式ドキュメント](https://nextjs.org/docs/basic-features/data-fetching)を一読することを強くお勧めします。`getServerSideProps`は、ブロッキングする同期呼び出しであり、サイトの速度を低下させるため、特別な理由がない限りは基本的には非推奨です。データが動的でインクリメンタルに取得できる場合、[Incremental Static Regeneration(インクリメンタルな静的再生成)](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration)は`getServerSideProps`に代わる素晴らしい方法です。 ## お役立ち情報 | リソース | リンク | | --------------------------------- | ---------------------------------- | | Next.js ドキュメント | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js ブログ | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Vercel/Next.js YouTube チャンネル | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/ja/usage/prisma.md ================================================ --- title: Prisma description: Prismaの使用方法 layout: ../../../layouts/docs.astro lang: ja --- Prisma は TypeScript のための ORM で、`schema.prisma`ファイルでデータベーススキーマとモデルを定義し、バックエンドからデータベースとやり取りするための型安全なクライアントを生成することができます。 ## Prisma Client `src/server/db.ts`にある Prisma Client は、グローバル変数としてインスタンス化され(Prisma チームが[ベストプラクティス](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem)として推奨)、API ルートで使用するためにエクスポートされます。Prisma クライアントは、デフォルトで [コンテキスト](/ja/usage/trpc#-serverapitrpcts) に含まれており、各ファイルで個別にインポートするのではなく、こちらを使用することを推奨します。 ## スキーマ Prisma のスキーマファイルは、`/prisma/schema.prisma`にあります。このファイルでデータベーススキーマとモデルを定義し、Prisma クライアントを生成する際に使用されます。 ### NextAuth.js と組み合わせた場合 NextAuth.js と Prisma を組み合わせて選択すると、`User`, `Session`, `Account`, `VerificationToken`モデルの推奨値が[NextAuth.js ドキュメント](https://next-auth.js.org/adapters/prisma)に従って設定されてスキーマファイルが生成されます。 ## デフォルトのデータベース デフォルトのデータベースは SQLite データベースで、開発や PoC(概念実証) を素早く行うのには適していますが、本番環境での使用はお勧めできません。使用するデータベースを変更するには、`datasource` ブロックの `provider` を `postgresql` または `mysql` に変更し、環境変数内の接続文字列でデータベースを指定します。 ## データベースのシーディング [データーベースのシーディング](https://www.prisma.io/docs/guides/database/seed-database)(訳註: データベース構築時にダミーデータや初期データを投入すること)は、データベースに素早くテストデータを投入して開始するのにとても役立ちます。シーディングを設定するには、`/prisma`ディレクトリに `seed.ts` ファイルを作成し、シードスクリプトを `package.json` ファイルに追加する必要があります。また、シードスクリプトを実行できる TypeScript ランナーも必要です。TypeScript ランナーには、esbuild を使用し、ESM 設定を必要としない非常にパフォーマンスの高い [tsx](https://github.com/esbuild-kit/tsx) をお勧めしますが、`ts-node`やその他のランナーも使えます。 ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` その後、`pnpm db-seed` (または `npm`/`yarn`) を実行して、データベースへのシーディングを実行します。 ## お役立ち情報 | リソース | リンク | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Prisma ドキュメント | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | Prisma マイグレーションプレイグラウンド | https://playground.prisma.io/guides | | NextAuth.JS Prisma アダプタ | https://next-auth.js.org/adapters/prisma | | PlanetScale 接続ガイド | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/ja/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Tailwind CSSの使い方 layout: ../../../layouts/docs.astro lang: ja --- ## Tailwind CSS とは何ですか? Tailwind CSS は、通常の CSS では必要になるコンテキスト切り替え無しで、カスタムデザインを構築するための小さな[ユーティリティファースト](https://tailwindcss.com/docs/utility-first) CSS フレームワークです。純粋な CSS フレームワークであり、あらかじめ構築されたコンポーネントやロジックを提供しません。また、Material UI のようなコンポーネントライブラリと比較して、[全く異なる一連の利点](https://www.youtube.com/watch?v=CQuTF-bkOgc)を提供します。 これにより、以下の例のように、CSS が驚くほど簡単に、素早く書けるようになります: 従来の CSS の場合: 1. CSS を(多くの場合別ファイルで)書く。 ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. CSS をコンポーネントにインポートする ```jsx import "./my-class.css"; ``` 3. HTML にクラス名を追加する ```html
...
``` Tailwind で同じことをするとこうなります: 1. HTML にクラス名を書く、以上。 ```html
...
``` Tailwind CSS は React Components と併用することで、UI を素早く構築するのに究極の威力を発揮します。 Tailwind CSS には美しい組込みのデザインシステムが備わっており、厳選されたカラーパレット、均一なデザインを実現する width/height や padding/margin などのスタイルのサイズのパターン、レスポンシブなレイアウトを作成するためのメディアブレークポイントなどがすぐに使える状態で提供されています。このデザインシステムはカスタマイズや拡張を行うことができ、プロジェクトが必要とするスタイルのツールボックスを正確に作成することができます。
Tru Narla better known as [mewtru](https://twitter.com/trunarla) gave an amazing talk on [building a design system using Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). [mewtru](https://twitter.com/trunarla)として知られる Tru Narla は、[Tailwind CSS を使ったデザインシステムの構築](https://www.youtube.com/watch?v=T-Zv73yZ_QI)について素晴らしい講演をしました。 ## 使用方法 Tailwind の書き味を向上させるために、Tailwind 用のエディタプラグインがインストールされていることを確認してください。 ### 拡張機能およびプラグイン - [VSCode 拡張](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains 拡張](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### 書式設定 Tailwind CSS のクラス名は、少し乱雑になりやすいので、クラス名のフォーマッタは必需品です。[Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) は、出力される css バンドルとクラス名が一致するように、クラス名を[推奨順序](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted)でソートします。CLI で Tailwind を選択すると、これをインストール・設定します。 ### 条件付きでクラスを適用する 三項演算子を使った条件分岐を伴うクラス名の追加は、非常に面倒で読みにくいものになります。以下のパッケージは、条件付きロジックを使用する際に、クラス名を整理するのに役立ちます。 - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## お役立ち情報 | リソース | リンク | | ------------------------------- | -------------------------------------------------------- | | Tailwind ドキュメント | https://tailwindcss.com/docs/editor-setup/ | | Tailwind チートシート | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind コミュニティ | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Tailwind Discord サーバー | https://tailwindcss.com/discord/ | | TailwindLabs Youtube チャンネル | https://www.youtube.com/tailwindlabs/ | | Tailwind プレイグラウンド | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/ja/usage/trpc.md ================================================ --- title: tRPC description: tRPCの使い方 layout: ../../../layouts/docs.astro lang: ja --- tRPC では、コード生成やランタイムの肥大化なしで、エンドツーエンドの型安全な API を書くことができます。TypeScript の優れた型推論機能を利用して API ルーターの型定義を推論し、フロントエンドから 完全に型安全かつ自動補完で API プロシージャを呼び出すことができます。tRPC を使用すると、フロントエンドとバックエンドがより近しいものに感じられ、極上の開発者体験を得ることができます。

私は、従来の API レイヤーの必要性を排除することによって、迅速なイテレーションを回してもアプリケーションが壊れないという自信を持ちつつ、人々がより速く動けるようにするために tRPC を開発しました。"

Avatar of @alexdotjs
Alex - tRPCの開発者 @alexdotjs
## tRPC の使い方
tRPC のコントリビューターである[trash_dev](https://twitter.com/trashh_dev)が tRPC について[Next.js Conf での決定的な講演](https://www.youtube.com/watch?v=2LYM8gf184U)を作成しました。未見の方はぜひご覧ください。 tRPC では、バックエンドで TypeScript の関数を書き、それをフロントエンドから呼び出します。単純な tRPC プロシージャは次のようなものです: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` これは tRPC プロシージャ(従来のバックエンドのルートハンドラに相当)で、まず Zod を使って入力の検証を行います(これは[環境変数の検証](./env-variables)に使っているのと同じ検証ライブラリです)。この場合、入力が文字列であることを確認しています。入力が文字列でない場合は、代わりに情報提供のためのエラー情報をレスポンスとして送信します。 入力の後、[query](https://trpc.io/docs/v10/react-queries)、[mutation](https://trpc.io/docs/v10/react-mutations)、[subscription](https://trpc.io/docs/v10/subscriptions)のいずれかであるリゾルバー関数を連鎖させます。この例では、リゾルバは[prisma](./prisma)クライアントを使用してデータベースを呼び出し、渡された`id`と一致するユーザを返します。 名前空間を共有する一連のプロシージャをまとめた`routers`を定義できます。例えば、`users`のルータ、`posts`のルータ、`messages`のルータがあるとして、これらのルータは、単一の集約された `appRouter` にマージすることができます: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` ルーターの型定義だけをエクスポートすれば良く、クライアントでサーバーサイドのコードをインポートすることは決してないことに注意してください。 さて、フロントエンドからプロシージャを呼び出してみましょう、tRPC は `@tanstack/react-query` のラッパーを提供しており、API 呼び出しが型付けされ推論されるという利点のもとで、TanStack Query が提供する Hooks のフルパワーを活用することができます。以下のように、フロントエンドからプロシージャを呼び出すことができます: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` 自動補完と型安全の優秀さがすぐに分かるはずです。`api.`と入力するや否や自動補完でルータ一覧が表示され、選択するとそのルータのプロシージャ一覧も表示されます。また、入力したものがバックエンドで定義したバリデータと一致しない場合は、TypeScript のエラーが表示されます。 ## エラーを推論する デフォルトで、`create-t3-app`はバックエンドで検証エラーが発生した場合に、Zod エラーを推論できるようにする[エラーフォーマッタ](https://trpc.io/docs/error-formatting)を設定します。 使用例: ```tsx function MyComponent() { const { mutate, error } = api.post.create.useMutation(); return ( { e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ title: formData.get('title') }); }}> {error?.data?.zodError?.fieldErrors.title && ( {/** `mutate` returned with an error on the `title` */} {error.data.zodError.fieldErrors.title} )} ... ); } ``` ## ファイル tRPC は、`create-t3-app`が設定するかなり多くのボイラープレートを必要とします。生成されるファイルを見ていきましょう: ### 📄 `pages/api/trpc/[trpc].ts`. これは API のエントリポイントであり、tRPC ルータを公開しています。通常、このファイルに触れることはあまりありませんが、たとえば CORS ミドルウェアなどを有効にする必要がある場合、エクスポートされた `createNextApiHandler` は [Next.js API ハンドラ](https://nextjs.org/docs/api-routes/introduction) であり、 [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) と [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) オブジェクトを受け取ることを知っていれば便利です。つまり、`createNextApiHandler`を任意のミドルウェアでラッピングすることができます。CORS を追加する[コードスニペットの例はこちら](#cors-の有効化)です。 ### 📄 `server/api/trpc.ts` このファイルは、コンテキスト作成と tRPC 初期化の 2 つに分かれています: 1. tRPC プロシージャに渡されるコンテキストを定義しています。コンテキストは、すべての tRPC プロシージャがアクセスできるデータであり、データベース接続、認証情報などが置かれるのに最適な場所です。create-t3-app では、リクエストオブジェクトにアクセスできない場合にコンテキストの一部を使用できるようにするために、2 つの関数を使用しています。 - `createInnerTRPCContext`:これは、リクエストに依存しないコンテキストを定義している場所です。例えば、データベース接続などです。この関数は、リクエストオブジェクトがない [統合テスト](#統合テストの例) や [ssg-helpers](https://trpc.io/docs/v10/ssg-helpers) で使用できます。 - `createTRPCContext`:これは、リクエストに依存するコンテキストを定義している場所です。例えば、ユーザのセッションです。`opts.req`オブジェクトを使ってセッションをリクエストし、そのセッションを `createInnerTRPCContext` 関数に渡して最終的なコンテキストを作成します。 2. tRPC を初期化し、再利用可能な[手続き](https://trpc.io/docs/v10/procedures)と[ミドルウェア](https://trpc.io/docs/v10/middlewares)を定義しています。慣例として、`t`オブジェクト全体をエクスポートするのではなく、再利用可能な手続きとミドルウェアを作成し、それらをエクスポートするようにしてください。 [データトランスフォーマー](https://trpc.io/docs/v10/data-transformers)として `superjson` を使用していることにお気づきでしょう。これにより、データがクライアントに到達したときにもデータ型が保持されるため、例えば`Date`オブジェクトを送信すると、クライアントは文字列ではなく`Date`を返します。他の API のほとんどではこうならないでしょう。 ### 📄 `server/api/routers/*.ts`. ここでは、API のルート群とプロシージャ群を定義します。慣例として、関連する一連のプロシージャごとに[個別のルータ](https://trpc.io/docs/v10/router)を作成します。 ### 📄 `server/api/root.ts`. ここでは、`routers/**`で定義されたすべてのサブルータを 1 つの app ルータに[マージ](https://trpc.io/docs/v10/merging-routers)しています。 ### 📄 `utils/api.ts` これは tRPC のフロントエンドのエントリポイントです。ここでルーターの**型定義**をインポートし、tRPC クライアントと react-query フックを作成します。バックエンドでデータトランスフォーマーとして `superjson` を有効にしたので、フロントエンドでも有効にする必要があります。これは、バックエンドからのシリアライズされたデータが、フロントエンドでデシリアライズされるようにするためです。 ここでは tRPC の[リンク](https://trpc.io/docs/v10/links)を定義し、クライアントからサーバーへのリクエストフローを決定します。ここでは、デフォルトとして [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink) が指定されており、[リクエストバッチ](https://cloud.google.com/compute/docs/api/how-tos/batch) が有効になっています。また、開発中に役立つリクエストログを出力する [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink) も使用しています。 最後に、フロントエンドで型を推論するために使える[ヘルパー型](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type)をエクスポートします。
Create T3 App のコントリビューターである[Christopher Ehrlich](https://twitter.com/ccccjjjjeeee)が[tRPC のデータフローに関するビデオ](https://www.youtube.com/watch?v=x4mu-jOiA0Q)を作成しました。このビデオは、tRPC を使ったことがあるけれども、まだ仕組みがよくわからないと感じる方におすすめです。 ## API を外部から呼び出せるようにするには 通常の API では、`curl`、`Postman`、`fetch`などの HTTP クライアントを使用してエンドポイントを呼び出したり、ブラウザから直接呼び出すことができます。しかし、tRPC の場合は少し違います。tRPC クライアントを使わずにプロシージャを呼び出したい場合、2 つの方法が推奨されます: ### 単一のプロシージャを外部に公開する 単一のプロシージャを外部に公開したい場合は、[サーバーサイドコール](https://trpc.io/docs/v10/server-side-calls)を探すことになります。これなら、通常の Next.js の API エンドポイントを作成しつつ、tRPC プロシージャのリゾルバ部分を再利用することができます。 ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### すべてのプロシージャを REST エンドポイントとして公開する もし、すべてのプロシージャを外部に公開したい場合は、コミュニティが作ったプラグイン [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master) をチェックアウトしてください。プロシージャにいくつかの特別なメタデータを追加することで、tRPC ルータから OpenAPI 準拠の REST API を生成することができます。 ### 単なる HTTP Requests として tRPC は HTTP で通信するため、通常の HTTP リクエストとして tRPC プロシージャを呼び出すこともできます。ただし、tRPC が使用している[RPC プロトコル](https://trpc.io/docs/v10/rpc)のため、構文が煩雑になる可能性があります。もし興味があれば、ブラウザのネットワークタブで tRPC のリクエストとレスポンスがどのように見えるかを確認することができます。ただ、これはあくまで教育的な練習として行い、上記の解決策のいずれかを採用することをお勧めします。 ## Next.js API エンドポイントとの比較 Next.js API エンドポイントと tRPC プロシージャを比較してみましょう。たとえば、データベースからユーザーオブジェクトを取得して、フロントエンドに返したいとします。Next.js API のエンドポイントは、次のように書きます: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` これを前掲した tRPC の例と比較すると、tRPC の利点がわかると思います: - 各ルートに url を指定する代わりに、ルーター全体が自動補完を持つオブジェクトになります。 - どの HTTP メソッドが使われたかを検証する必要はありません。 - リクエストのクエリやボディがプロシージャの中で正しいデータを含んでいるかどうかを検証する必要はありません、Zod がそれを行なっているからです。 - レスポンスを作成する代わりに、他の TypeScript 関数と同じように、エラーを投げたり、値やオブジェクトを返したりすることができます。 - フロントエンドでプロシージャを呼び出すと、自動補完と型安全性が提供されます。 ## 便利なスニペット 以下、便利そうなスニペットを紹介していきます。 ### CORS の有効化 React Native アプリを含むモノレポなどで、異なるドメインから API を呼び出す必要がある場合、CORS の有効化が必要かもしれません: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Enable cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### 楽観的な更新 楽観的な更新とは、API コールが終了する前に UI を更新することです。API コールの終了を待たずに UI に反映されるため、ユーザーにとってより良い体験になります。しかし、データの正確性を重視するアプリケーションでは、バックエンドの状態を「真に」反映していないため、楽観的な更新は避けるべきでしょう。詳しくは、[React Query ドキュメント](https://tanstack.com/query/v4/docs/guides/optimistic-updates)をご覧ください。 ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await utils.post.list.cancel(); // Get the data from the queryCache const prevData = utils.post.list.getData(); // Optimistically update the data with our new post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Return the previous data so we can revert if something goes wrong return { prevData }; }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sync with server once mutation has settled utils.post.list.invalidate(); }, }); }; ``` ### 統合テストの例 ここでは、[Vitest](https://vitest.dev)を使用して、tRPC ルーターが期待通りに動作しているか、入力パーサーが正しい型を推論しているか、返されたデータが期待通りの出力と一致しているかをチェックする統合テストのサンプルを紹介します。 ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` protected なプロシージャを呼び出す場合は、コンテキストを作成するときにモックした `session` オブジェクトを渡すことができます: ```ts test("protected example router", async () => { const ctx = await createInnerTRPCContext({ session: { user: { id: "123", name: "John Doe" }, expires: "1", }, }); const caller = appRouter.createCaller(ctx); // ... }); ``` ## お役立ち情報 | リソース | リンク | | ------------------------ | ------------------------------------------------------- | | tRPC ドキュメント | https://www.trpc.io | | tRPC のサンプル集 | https://github.com/trpc/trpc/tree/next/examples | | React Query ドキュメント | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/ja/usage/typescript.md ================================================ --- title: TypeScript description: TypeScriptの使用方法 layout: ../../../layouts/docs.astro lang: ja ---

ガードレールではなく、セーフティネットを作る

Avatar of @t3dotgg
Theo - creator of the T3 Stack @t3dotgg
新人開発者であれ、熟練した開発者であれ、TypeScript は必須の要素だと考えています。最初はとっつきにくいかもしれませんが、多くのツールがそうであるように、使い始めたら元に戻りたいと思うことはないでしょう。 TypeScript は、コードを書く際に、期待されるデータ型を定義することでリアルタイムなフィードバックを提供します。つまりコードエディタで便利な自動補完を提供したり、存在しないプロパティにアクセスしようとしたり、間違った型の値を渡そうとした場合に赤い波線で注意喚起を行います。これがなかったら、あなたは先の段階でその行のバグ取りを行う羽目になっていたでしょう。 あなたが今まさに書いているコードや、のめり込んで集中しているコードに対して、ドキュメントをエディター中で直接表示し、避けられないミスを犯したときに即座にフィードバックが得られるのですから、TypeScript はおそらく、開発者に最も生産性をもたらすツールです。 ## 型推論 多くの新しい TypeScript 開発者は、TypeScript の「_記述_」に関心がありますが、そのメリットの多くは実はコードを一切変更せずとも、型推論で得られるのです。型推論とは、何かが型付けされた場合、その型はアプリケーションのフロー全体を通じ、他の場所で再度型宣言することなしで、その型への適合が追従することを意味します。例えばある関数が受け取る引数の型を定義してしまえば、その関数の本体では通常、さらなる TypeScript 特有のコードを必要とせずに型安全になることを意味します。また、ライブラリの開発者は、ライブラリの型を維持するために多大な労力を費やしています。つまり、アプリケーション開発者は、型推論が提供する型と、ビルトインのドキュメントが提供する型の両方からエディタ上で恩恵を受けることができるのです。
[TypeScript の使い方が間違っているかもしれない](https://www.youtube.com/watch?v=RmGHnYUqQ4k)という Theo のビデオをチェックしてみてください。 ## 型推論の強力な活用法 ### Zod [Zod](https://github.com/colinhacks/zod)は、TypeScript の上に構築されたスキーマ検証ライブラリです。データの「唯一の真実の情報源」を表すスキーマを書くと、Zod は、ネットワーク境界や外部 API を越えても、アプリケーション全体でデータが有効であることを保証します。 ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) は、宣言的で、常にデータが最新化される自動管理されたクエリとミューテーションを提供し、開発者体験と利用者体験の両方を直接改善します。 ## お役立ち情報 | リソース | リンク | | -------------------------------------------------------------- | ----------------------------------------------------------------- | | TypeScript ハンドブック | https://www.typescriptlang.org/docs/handbook/ | | TypeScript 初心者向けチュートリアル | https://github.com/total-typescript/beginners-typescript-tutorial | | タイプチャレンジ | https://github.com/type-challenges/type-challenges | | TypeScript の Rodney Mullen (Matt Pocock)の Youtube チャンネル | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/ja/why.md ================================================ --- title: CT3A を選ぶ理由 description: なぜあなたは次のプロジェクトで Create T3 App を選ぶべきなのか layout: ../../layouts/docs.astro lang: ja --- 私たちが Create T3 App の開発を始めたのは、[Theo](https://twitter.com/t3dotgg)が自分の好みの技術のテンプレートを作ることを拒んだからです。create-next-app や[Astro's CLI](https://astro.build)、そして型安全性への全面的な愛に触発されて、Create T3 App チームは新しく T3 Stack プロジェクトを始めるための最高の出発点を作るために懸命に働きました。 Next.js を型安全な方法で使うことに興味があるなら、こここそが始めるべき場所です。私たちが選択した技術的選択について興味がある方は、引き続きお読みください :) ## なぜ TypeScript なのか? JavaScript は難しいですよね。なんだってこれ以上ルールを増やすのでしょうか? 私たちは、TypeScript が提供する体験が、より優れた開発者になるのに役立つと確信しています。TypeScript は、期待されるデータ型を定義することで、コードを書きながらのリアルタイムのフィードバックを提供し、存在しないプロパティにアクセスを試みたり、間違った型の値を渡そうとすると、エディタで役立つ自動補完を提供したり、赤い波線で警告を発してくれます。Web 開発の初心者であれ、熟練したプロであれ、TypeScript の「厳密さ」は、「バニラ JS」よりもフラストレーションが少なく、一貫した体験を提供してくれるでしょう。 型安全性はあなたをより速くします。もし納得できないなら、あなたは[TypeScript の使い方を間違っているかもしれません...](https://www.youtube.com/watch?v=RmGHnYUqQ4k)。 ## なぜ Next.js なのか? 私たちは React が大好きです。React はこれまで想像もしなかったような方法で UI 開発を身近なものにしてくれました。しかし、その反面、開発者を険しい道へ導くこともあります。 Next.js は、React を使ったアプリケーションを作成するための、思想に拘りすぎず、かつ強く最適化したアプローチを提供します。Next.js は、ルーティングから API 定義、画像レンダリングに至るまで、開発者が良い判断ができるように導いてくれると私たちは信じています。 ## なぜ tRPC/Prisma/Tailwind/etc なのか? 私たちは物事をできるだけシンプルに保つべきだと信じてやってきましたが、これらの構成要素は私たちが作ってきた、「アプリ」っぽいすべてのプロジェクトで使ってきたことがわかりました。`create-t3-app`は、必要とする構成要素を導入できるように、すばらしく良い仕事をしてくれます。 ### tRPC tRPC は GraphQL が実現してくれるはずのもの、「サーバーに対するシームレスで型安全なクライアント開発」を、定型文を一切使わずに実現するものです。これは TypeScript を巧みに利用したもので、極上の開発体験が得られます。 ### Prisma SQL に対する Prisma は 「JS に対する TypeScript」のようなものです。Prisma はこれまでにない開発者体験を作り出しました。[複数のデータベース](https://www.prisma.io/docs/concepts/database-connectors)と互換性のあるユーザー定義スキーマから型を生成することで、Prisma はデータベースからアプリケーションまでのエンドツーエンドの型安全性を保証します。 Prisma は、データベースの日常的な操作をより簡単にするための[ツール群](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows)を提供しています。特に Prisma Client は、クエリの実行と SQL の使用をほとんど意識しないで済むぐらい簡単にします。Prisma Studio は、データベースの便利な GUI で、コードを書くことなくデータを素早く表示したり操作したりすることができます。 ### Tailwind CSS Tailwind は「禅モードの CSS」みたいなものです。 Tailwind は、優れたデフォルトの色、間隔(spacing)、その他のプリミティブという形でビルディングブロックを提供することで、見栄えの良いアプリケーションを簡単に作成することができます。また、コンポーネントライブラリとは異なり、アプリケーションを次のレベルまで引き上げようとしたり、美しくユニークなものを作ろうとしたときに、足かせとなってにっちもさっちもいかなくなったりするようなことは Tailwind では起きません。 また、Tailwind ではインラインスタイルのような書きっぷりをするのですが、クラス名やファイルの編成など、「解決しようとしている問題には直接関係しないことがら」を気にせずにスタイリングできます。 ### NextAuth.js NextJS アプリケーションに認証システムを導入したいとき、NextAuth.js は、がんばって自分で構築する手間をかけずに、複雑なセキュリティ条件下に導入できる優れたソリューションです。NextAuth.js は、OAuth 認証をすばやく追加するための豊富なプロバイダーリストと、多くのデータベースや ORM のためのアダプターを提供しています。 ================================================ FILE: www/src/pages/no/deployment/docker.md ================================================ --- title: Docker description: Utrulling med Docker layout: ../../../layouts/docs.astro lang: no --- Stakken kan rulles ut med Docker. Enten som en enkel kontainer eller som en gruppe kontainere ved hjelp av `docker-compose`. Se [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) for et eksempel-_repo_ som er basert på denne dokumentasjonen. ## Docker-prosjektkonfigurasjon Vær klar over at Next.js krever forskjellig håndtering av variabler som er satt til "build time" (tilgjengelig i frontend, prefikset av `NEXT_PUBLIC`) og variabler som bare skal være tilgjengelige på serversiden under kjøring. I denne demonstrasjonen bruker vi to variabler. Så vær oppmerksom på rekkefølgen på kommandolinjeargumentene i `Dockerfile` og i `docker-compose.yml`-filen. - `DATABASE_URL` (brukes av serveren) - `NEXT_PUBLIC_CLIENTVAR` (brukes av klienten) ### 1. Next.js-konfigurasjon I [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), legg til _output_-alternativet `standalone` for å redusere størrelsen på Docker-_imaget_ ved å benytte ["Output File Tracing"](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Lag en dockerignore-fil
Klikk her og kopier innholdet til .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Lag Dockerfile > Siden vi ikke drar serverens miljøvariabler inn i kontaineren, vil [skjema for validering av miljøvariabler](/no/usage/env-variables) feile. For å forhindre dette må vi legge til `SKIP_ENV_VALIDATION=1` i byggkommandoen slik at miljøvariabelskjemaene ikke valideres ved "build time".
Klikk her og kopier innholdet til Dockerfile:
```docker ##### AVHENGIGHETER FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl WORKDIR /app # Installer Prisma-klienten - Fjern denne linjen hvis du ikke bruker Prisma COPY prisma ./ # Installer avhengigheter basert på foretrukket pakkebehandler COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Notater_** > > - _Emulering av `--platform=linux/amd64` er kanskje ikke lenger nødvendig dersom man bruker Node 18._ > - \_Se [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) for å forstå hvorfor `libc6-compat` kan være nødvendig. > - _Next.js samler inn [anonym bruksdata](https://nextjs.org/telemetry). I `Dockerfile` ovenfor er det allerede to kommenterte linjer med kommandoen `ENV NEXT_TELEMETRY_DISABLED 1`. Fjern kommentarer på den første linjen for å deaktivere datainnsamling under bygging. Den andre linjen deaktiverer datainnsamling under kjøring._
## Bygg og Kjør Bildet Lokalt Bygg og start opp bildet lokalt med følgende kommandoer: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_her" ct3a-docker ``` Åpne [localhost:3000](http://localhost:3000/) for å se programmet som kjøres. ## Docker Compose Du kan også bruke Docker Compose for å bygge bildet og kjøre det i kontaineren.
Følg trinn 1-4 ovenfor, klikk her og lim inn innholdet i docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_her ``` Kjør kommandoen `docker compose up`: ```bash docker compose up ``` Åpne [localhost:3000](http://localhost:3000/) for å se programmet som kjører.
## Rull ut til Railway Du kan bruke en PaaS slik som [Railways](https://railway.app) automatiserte [Dockerfile-utrullinger](https://docs.railway.app/deploy/dockerfiles) for å rulle ut applikasjonen din. Hvis du har installert [Railway CLI](https://docs.railway.app/develop/cli#install), kan du rulle ut applikasjonen din med følgende kommandoer: ```bash railway login railway init railway link railway up railway open ``` Gå til "Variables" og lim inn `DATABASE_URL`. Gå deretter til "Settings" og velg "Generate Domain". For å se et kjørende eksempel på Railway, besøk [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Nyttige Ressurser | Ressurser | Link | | -------------------------------------- | -------------------------------------------------------------------- | | Dockerfile-referanse | https://docs.docker.com/engine/reference/builder/ | | Compose file versjon 3-Referanse | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Docker CLI-referanse | https://docs.docker.com/engine/reference/commandline/docker/ | | Docker Compose CLI-referanse | https://docs.docker.com/compose/reference/ | | Rulle ut Next.js med Docker Image | https://nextjs.org/docs/deployment#docker-image | | Next.js i Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Next.js med Docker-Eksempel | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Lag et Docker-bilde fra en Next.js-app | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/no/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Utrulling", layout: "docs", description: "Lær hvordan man ruller ut T3-applikasjonen til produksjonsmiljø.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/no/deployment/netlify.md ================================================ --- title: Netlify description: Utrulling med Netlify layout: ../../../layouts/docs.astro --- Netlify er en alternativ distribusjonsleverandør på samme måte som Vercel. Se [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) for et eksempel-repo basert på dette dokumentet. ## Hvorfor hoste på Netlify Konvensjonell visdom tilsier at Vercel har en overlegen støtte for Next.js fordi Vercel utvikler Next.js. De har en egeninteresse i å sikre at plattformen er innstilt for optimal ytelse og best utvikleropplevelse med Next.js. For de fleste brukstilfeller vil dette være sant, og det vil ikke være fornuftig å avvike fra standardbanen. Det er også en felles oppfatning at mange Next.js-funksjoner kun støttes på Vercel. Selv om det er sant at nye Next.js-funksjoner vil bli testet og støttet på Vercel på utgivelsestidspunktet som standard, er det også slik at andre leverandører som Netlify raskt vil [implementere og gi ut støtte](https://www.netlify.com/blog/deploy-nextjs-13/) for [stabile Next.js-funksjoner](https://docs.netlify.com/integrations/frameworks/next-js/overview/). Det er noen fordeler og ulemper for alle distribusjonsleverandører siden ingen enkelt leverandør kan ha den beste støtten for alle brukstilfeller. For eksempel bygde Netlify sin egen [tilpassede Next.js kjøretid](https://github.com/netlify/next-runtime) for Netlifys Edge-funksjoner (som kjører på Deno Deploy) og [opprettholder unik mellomvare for å få tilgang til og endre HTTP-responser](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). > _MERK: For å følge statusen til ustabile Next 13-funksjoner, se [Bruk av Next 13 `app` -katalogen på Netlify](https://github.com/netlify/next-runtime/discussions/1724)._ ## Prosjektkonfigurasjon Det er mange måter å konfigurere byggeinstruksjonene på, inkludert direkte gjennom Netlify CLI- eller Netlify-dashbordet. Selv om det ikke er nødvendig, er det tilrådelig å opprette og inkludere en [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/) fil. Dette sikrer at forka og klonede versjoner av prosjektet blir enklere å rulle ut på en reproduserbar måte. ```toml [build] command = "next build" publish = ".next" ``` ## Bruk av Netlify-dashbordet 1. Push koden din til et GitHub-repo og registrer deg for [Netlify](https://app.netlify.com/signup). Etter at du har opprettet en konto, klikker du på **Add new site** og deretter **Import an existing project**. ![Nytt prosjekt på Netlify](/images/netlify-01-new-project.webp) 2. Koble til Git-leverandøren din. ![Importer repository](/images/netlify-02-connect-to-git-provider.webp) 3. Velg prosjektets repository. ![Velg prosjektets repository](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify vil oppdage om du har en `netlify.toml`-fil og automatisk konfigurere build-kommandoen og publiseringskatalogen. ![Nextjs byggeinnstillinger](/images/netlify-04-configure-build-settings.webp) 5. Klikk **Show advanced** og deretter **New variable** for å legge til miljøvariablene dine. ![Legg til miljøvariabler](/images/netlify-05-env-vars.webp) 6. Klikk på **Deploy site**, vent til bygget er fullført, og se det nye nettstedet ditt. ## Bruk av Netlify CLI For å rulle ut fra kommandolinjen må du først pushe prosjektet til et GitHub-repo og [installere Netlify CLI](https://docs.netlify.com/cli/get-started/). Du kan installere `netlify-cli` som en prosjektavhengighet eller installere den globalt på maskinen din med følgende kommando: ```bash npm i -g netlify-cli ``` For å teste prosjektet ditt lokalt, kjør [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) kommando og åpne [`localhost:8888`](http://localhost:8888/) for å se din lokalt kjørende Netlify-app: ```bash ntl dev ``` Kjør [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) kommando for å konfigurere prosjektet: ```bash ntl init ``` Importer prosjektets miljøvariabler fra `.env`-filen din med [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Rull ut prosjektet ditt med [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Du må ha med `--build`-flagget for å kjøre build-kommandoen før utrullingen og `--prod`-flagget for å rulle ut til nettstedets hoved-URL: ```bash ntl deploy --prod --build ``` For å se et kjørende eksempel på Netlify, besøk [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/no/deployment/vercel.md ================================================ --- title: Vercel description: Utrulling med Vercel layout: ../../../layouts/docs.astro lang: no --- Vi anbefaler at du ruller ut applikasjonen din til [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Det gjør det superenkelt å rulle ut Next.js-applikasjoner. ## Prosjektkonfigurasjon Vercel vil mest sannsynlig automatisk konfigurere byggekommandoen og publisere katalogen din. Du kan imidlertid også spesifisere dette, i tillegg til andre konfigurasjoner, i en fil som heter [`vercel.json`](https://vercel.com/docs/project-configuration) og inkludere følgende kommandoer: ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Bruk av Vercel Dashboard 1. Etter å ha pushet koden til et GitHub-repo, logger du på [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) med GitHub og klikker på **Add New Project**. ![Nytt prosjekt på Vercel](/images/vercel-new-project.webp) 2. Importer GitHub-repoet med prosjektet du vil rulle ut. ![Importer repository](/images/vercel-import-project.webp) 3. Legg til miljøvariablene dine. ![Legg til miljøvariabler](/images/vercel-env-vars.webp) 4. Klikk på **Deploy**. Hvis du nå _pusher_ repoet ditt vil Vercel automatisk rulle ut applikasjonen din på nytt! ## Bruke Vercel CLI For å rulle ut applikasjonen din fra kommandolinjen, må du først [installere](https://vercel.com/docs/cli#installing-vercel-cli) Vercel CLI globalt. ```bash npm i -g vercel ``` Kjør kommandoen [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) for å rulle ut prosjektet ditt. ```bash vercel ``` Legg til `--env DATABASE_URL=DIN_DATABASE_URL_HER` for miljøvariabler slik som strengen for databasetilkobling. Bruk `--yes` hvis du vil hoppe over spørsmålene rundt utrullingen og angi standardsvaret for hvert spørsmål. ```bash vercel --env DATABASE_URL=DIN_DATABASE_URL_HER --yes ``` Etter den første utrullingen vil denne kommandoen rulle ut til en forhåndsvisnings-_branch_. Du må legge til `--prod` for å _pushe_ endringer direkte til produksjonmiljøet. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/no/faq.md ================================================ --- title: FAQ description: Ofte stilte spørsmål om Create T3 App layout: ../../layouts/docs.astro lang: no --- Her er noen ofte stilte spørsmål om "create-t3-app". ## Hva nå? Hvordan lager jeg en app med dette? Vi prøver å holde dette prosjektet så enkelt som mulig, slik at du kan starte med bare det grunnleggende rammeverket vi har laget for deg. Du kan legge til flere ting senere etter hvert som de blir nødvendige. Hvis du ikke er kjent med de forskjellige teknologiene som brukes i dette prosjektet, vennligst se den relevante dokumentasjonen. Hvis du har flere spørsmål kan du bli med i vår [Discord](https://t3.gg/discord) og be om hjelp. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## Hvilke læringsressurser er tilgjengelige for øyeblikket? Selv om ressursene som er oppført nedenfor, er noen av de beste som finnes for T3-Stakken, anbefaler vårt samfunn (og [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) at du bare begynner å bruke stakken og bygger med den mens du lærer. Hvis du vurderer "create-t3-app", er sjansen stor for at du allerede har brukt noen av delene av stakken. Så hvorfor ikke bare hoppe inn i det og lære de andre delene mens du bygger noe? Nå har vi forståelse for at denne metoden ikke fungerer for alle. Så hvis du føler at du har prøvd anbefalingen og fortsatt vil ha noen ressurser, eller du bare ikke er sikker på hvordan å gjøre det selv og/eller føler deg overveldet av stakken, så sjekk ut disse fantastiske veiledningene på `create-t3-app `: ### Artikler - [Bygg en full stakk-app med create-t3-app](https://www.nexxel.dev/blog/ct3a-guestbook) - [En første titt på create-t3-app](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrering av T3-applikasjonen din til et Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Integrering av Stripe i T3-applikasjonen din](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Videoer - [Bygg en Twitter-klone med T3-stakken - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) – [Bygg en Blogg Med T3-stakken – tRPC, TypeScript, Next.js, Prisma og Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Bygg en Live Chat-applikasjon med T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [T3-stakken - Hvordan Vi Bygget Den](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [En oversikt over create-T3-app (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Hvorfor er det `.js`-filer i prosjektet? I henhold til [3. T3-Prinsipp](/no/introduction#typesafety-isnt-optional), anser vi _typesafety_ som en førsteklasses borger. Dessverre støtter ikke alle rammeverk og plugins TypeScript, noe som betyr at noen av konfigurasjonsfilene nødvendigvis må være `.js`-filer. Vi forsøker å understreke at disse filene er JavaScript for en grunn, ved å eksplisitt deklarere hver filtype (`cjs` eller `mjs`) avhengig av hva som støttes av biblioteket den brukes av. Dessuten er alle `js`-filene i dette prosjektet fortsatt typesjekket ved bruk av checkJs alternativet i kompilatoren (tsconfig). ## Jeg sliter med å legge til i18n i applikasjonen min. Er det noen referanse jeg kan bruke? Vi har bestemt oss for å ikke inkludere i18n som standard i `create-t3-app` fordi det er et emne omgitt av mange meninger og det er mange måter å implementere det på. Men hvis du sliter med å implementere det og ønsker å se et referanseprosjekt, så har vi et [referanserepo](https://github.com/juliusmarminge/t3-i18n) som viser hvordan du kan legge til i18n i en T3-app ved å bruke [next-i18next](https://github.com/i18next/next-i18next). ## Hvorfor bruker vi `/pages` og ikke `/app` fra Next.js 13? I henhold til [2. T3-Prinsipp](/no/introduction#bleed-responsibly), elsker vi ny teknologi, men verdsetter stabilitet. Det er vanskelig flytte hele ruteren og det er ikke en god idé å ta disse risikoene der, [se: Moderne og Ansvarsbevisst](https://youtu.be/mnwUbtieOuI?t=1662). Mens `/app` er [et glimt inn i fremtiden](https://youtu.be/rnsC-12PVlM?t=818), er det ikke klart for produksjon; API-et er i beta og forventes å ha _breaking changes_. For en liste over støttede, planlagte og funksjoner under arbeid i `/app`-katalogen, gå til [beta Next.js docs](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/no/folder-structure-pages.mdx ================================================ --- title: Mappestruktur (Pages) description: Mappestrukturen til en nylig opprettet T3 App layout: ../../layouts/docs.astro lang: no isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Velg pakkene dine for å se mappestrukturen til en app med disse valgene. Lenger ned finner du en beskrivelse av hver oppføring.
### `prisma` `prisma`-mappen inneholder `schema.prisma`-filen som brukes til å konfigurere databasetilkoblingen og databaseskjemaet. Det er også stedet for å lagre migreringsfiler og/eller _seedscript_, dersom de brukes. Se [Bruk av Prisma](/no/usage/prisma) for mer informasjon.
### `public` `public`-mappen inneholder statiske ressurser som betjenes av _webserveren_. `favicon.ico`-filen er et eksempel på en statisk ressurs.
### `src/env` Brukes for validering av miljøvariabler og typedefinisjoner - se [Miljøvariabler](usage/env-variables).
### `src/pages` `pages`-mappen inneholder alle sidene til Next.js-applikasjonen. `index.tsx`-filen i rotkatalogen til `/pages` er hjemmesiden til applikasjonen. Filen `_app.tsx` brukes til å pakke applikasjonen sammen med _providers_. Se [Next.js-dokumentasjon](https://nextjs.org/docs/basic-features/pages) for mer informasjon.
#### `src/pages/api` Mappen `api` inneholder alle API-rutene til Next.js-applikasjonen. Filen `examples.ts` inneholder et eksempel på en rute som bruker [Next.js API-rute](https://nextjs.org/docs/api-routes/introduction)-funksjonaliteten sammen med Prisma. `restricted.ts`-filen inneholder et eksempel på en rute som bruker [Next.js API-rute](https://nextjs.org/docs/api-routes/introduction)-funksjonaliteten, og er beskyttet av [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` `[...nextauth].ts`-filen håndterer autentiseringslug ruter for NextAuth.js. Den brukes til å håndtere autentiseringsforespørsler. Se [Bruk av NextAuth.js](usage/next-auth) for mer informasjon om NextAuth.js, og [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) for informasjon på catch-all/slug-ruter.
#### `src/pages/api/trpc/[trpc].ts` `[trpc].ts`-filen er inngangspunktet for tRPC-APIet. Den brukes til å håndtere tRPC-forespørsler. Se [Bruk av tRPC](usage/trpc#-pagesapitrpctrpcts) for mer informasjon om denne filen, og [Next.js Dynamic Routes Docs](https://nextjs.org/docs/routing/dynamic-routes) for informasjon om catch-all/slug-ruter.
### `src/server` `server`-mappen brukes til å tydelig skille serverside-kode fra klientside-kode.
#### `src/server/auth.ts` Hovedinngangspunktet for autentiseringslogikk på serversiden. Her setter vi opp NextAuth.js [konfigurasjonsalternativer](usage/next-auth), utfører [modulforstørrelse](usage/next-auth#inclusion-of-userid-on-the-session) samt gir utvikleropplevelsesverktøy for autentisering som for eksempel å hente brukerens økt på serversiden. Se [Bruk av NextAuth.js](usage/next-auth#usage-with-trpc) for mer informasjon.
#### `src/server/db.ts` `db.ts` filen brukes til å instansiere Prisma-klienten i globalt omfang. Se [Bruk av Prisma](usage/prisma#prisma-client) og [beste fremgangsmåter for bruk av Prisma med Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) for mer informasjon.
### `src/server/api` `api`-mappen inneholder tRPC serverside-koden.
#### `src/server/api/routers` `routers`-mappen inneholder alle dine tRPC-underrutere.
#### `src/server/api/routers/example.ts` `example.ts`-filen er et eksempel på en tRPC-ruter som bruker `publicProcedure`-hjelperen for å demonstrere hvordan du oppretter en offentlig tRPC-rute. Basert på dine valgte pakker inneholder denne ruteren flere eller færre ruter for best mulig å demonstrere bruken til dine behov.
#### `src/server/api/trpc.ts` `trpc.ts`-filen er hovedkonfigurasjonsfilen for din tRPC-backend. Her inne vi: 1. Definerer kontekst brukt i tRPC-forespørsler. Se [Bruk av tRPC](usage/trpc#-serverapitrpcts) for mer informasjon. 2. Eksporter prosedyre hjelpere. Se [Bruk av tRPC](usage/trpc#-serverapitrpcts) for mer informasjon.
#### `src/server/api/root.ts` `root.ts`-filen brukes til å slå sammen tRPC-rutere og eksportere dem som en enkelt ruter, så vel som ruterens typedefinisjon. Se [Bruk av tRPC](usage/trpc#-serverapirootts) for mer informasjon.
### `src/styles` `stiler`-mappen inneholder det globale stilsettet til applikasjonen.
### `src/utils` `utils`-mappen brukes til å lagre ofte gjenbrukte verktøyfunksjoner.
#### `src/utils/api.ts` `api.ts`-filen er frontendinngangspunktet til tRPC. Se [Bruk av tRPC](usage/trpc#-utilsapits) for mer informasjon.
### `.env` `.env`-filen brukes til å lagre miljøvariabler. Se [Miljøvariabler](usage/env-variables) for mer informasjon. Denne filen skal **ikke** _commites_ til git-historikk.
### `.env.example` `.env.example`-filen viser noen eksempler på miljøvariabler basert på valgte bibliotek. Denne filen skal _commites_ til git-historikk.
### `.eslintrc.json` `.eslintrc.json`-filen brukes til å konfigurere ESLint. Se [ESLint Docs](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) for mer informasjon.
### `next-env.d.ts` `next-env.d.ts`-filen sikrer at Next.js-typer blir plukket opp av TypeScript-kompilatoren. **Du bør ikke fjerne den eller redigere den, da den kan endres når som helst.** Se [Next.js Docs](https://nextjs.org/docs/basic-features/typescript#existing-projects) for mer informasjon.
### `next.config.mjs` `next.config.mjs`-filen brukes til å konfigurere Next.js. Se [Next.js Docs](https://nextjs.org/docs/api-reference/next.config.js/introduction) for mer informasjon. Merk: .mjs-utvidelsen brukes for å tillate ESM-import.
### `postcss.config.js` `postcss.config.js`-filen er for bruk av Tailwind PostCSS. Se [Tailwind PostCSS Docs](https://tailwindcss.com/docs/installation/using-postcss) for mer informasjon.
### `prettier.config.mjs` `prettier.config.mjs`-filen brukes til å konfigurere Prettier slik at prettier-plugin-tailwindcss inkluderes for formatering av Tailwind CSS-klasser. Se [Tailwind CSS-blogginnlegget](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) for mer informasjon.
### `tsconfig.json` Filen `tsconfig.json` brukes til å konfigurere TypeScript. Noen ikke-standardinnstillinger, som for eksempel `strict mode`, har blitt aktivert for å sikre best mulig bruk av TypeScript i create-t3-app og tilhørende biblioteker. Se [TypeScript Docs](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) eller [TypeScript Usage](usage/typescript) for mer informasjon.
================================================ FILE: www/src/pages/no/installation.mdx ================================================ --- title: Installasjon description: Installasjonsveiledning for Create T3 App layout: ../../layouts/docs.astro lang: no isMdx: true --- import Callout from "../../components/docs/callout.tsx"; For å lage en app med `create-t3-app`, kjør en av følgende tre kommandoer og svar på spørsmålene i veiviseren: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` Etter at applikasjonen din har blitt opprettet, sjekk ut [første steg](/no/usage/first-steps) for å begynne å utvikle den nye applikasjonen. ## Avansert bruk | Alternativ/Flagg | Beskrivelse | | ----------------- | -------------------------------------------------------------------------------------- | | `[dir]` | Inkluder et mappeargument med navnet på prosjektet | | `--noGit` | Eksplisitt be CLI om ikke å initialisere et nytt git-repo i prosjektet | | `-y`, `--default` | CLI vil bli hoppet over og en ny t3-app vil bli opprettet med alle alternativene valgt | | `--noInstall` | Bygger prosjektet uten å installere avhengigheter | ## Eksperimentell bruk For vår CI (Kontinuerlig Integrasjon) har vi noen eksperimentelle flagg som lar deg opprette skjelett for hvilken som helst app uten noen spørsmål. Hvis denne _use casen_ gjelder deg, kan du bruke disse flaggene. Vær oppmerksom på at disse flaggene er eksperimentelle og kan bli endret i fremtiden uten å følge noen semver-versjonering. | Flagg | Beskrivelse | | ------------ | ---------------------------------- | | `--CI` | La CLI vite at du er i CI-modus | | `--trpc` | Inkluder tRPC i prosjektet | | `--prisma` | Inkluder Prisma i prosjektet | | `--nextAuth` | Inkluder NextAuth.js i prosjektet | | `--tailwind` | Inkluder Tailwind CSS i prosjektet | Hvis du utelater CI-flagget har resten flaggene ingen effekt. Du trenger ikke å eksplisitt velge bort pakkene du ikke vil ha. Men hvis du vil være eksplisitt, kan du sende `false`, f.eks. `--nextAuth false`. ### Eksempel Følgende vil lage en T3-app med både tRPC og Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/no/introduction.md ================================================ --- title: Introduksjon description: Introduksjon til T3-stakken layout: ../../layouts/docs.astro lang: no ---
## T3-Stakken _"T3-Stakken"_ er en webutviklingsstakk inspirert av [Theo](https://twitter.com/t3dotgg) som fokuserer på enkelhet, modularitet og full typesikkerhet. I kjernen består den av [**Next.js**](https://nextjs.org/) og [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) brukes også nesten alltid. Dersom backend-funksjonalitet er nødvendig så er [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/) og [**NextAuth.js**](https://next-auth.js.org/) også gode tillegg. Du har kanskje lagt merke til at T3-stakken har mange komponenter. Det er med vilje. Bytt ut delene hvis du trenger dem - stakken er modulær i kjernen :) ## Så... hva er create-t3-app? En mal? På en måte? `create-t3-app` er en CLI utviklet av erfarne T3-stakk-utviklere som forenkler oppsett av en modulær T3-stakk-app. Dette betyr at hver komponent er valgfri og "malen" lages basert på dine spesifikke krav. Etter å ha jobbet med mange prosjekter og mange år med denne teknologien, har vi mange meninger og mye innsikt. Vi har gjort vårt beste for inkludere dette i CLI-et. Dette er **IKKE** en altomfattende mal. Vi **forventer** at du tar inn dine egne biblioteker som oppfyller behovene til **DIN** applikasjon. Selv om vi ikke ønsker å foreskrive løsninger for mer spesifikke problemer som _state_-administrasjon og utrulling, har vi [laget en liste over noen anbefalinger her](/no/other-recs). ## T3-Prinsipper Vi skal være ærlige - Dette prosjektet ble designet i henhold til _våre ideer_. Vi deler et sett med oppfatninger om å bygge webapplikasjoner og bruker disse som grunnlag for våre beslutninger. ### Løs Problemer Det er lett å gå i fellen med "å legg til alt" – det ønsker vi spesifikt _ikke_ å gjøre. Alt som legges til 'create-t3-app' bør løse et spesifikt problem som finnes innenfor de inkluderte kjerneteknologiene. Dette betyr at vi ikke vil legge til ting som state-biblioteker (`zustand`, `redux`). Vi vil imidlertid integrere ting som NextAuth.js, Prisma og tRPC. ### Moderne og Ansvarsbevisst Vi elsker moderne teknologi. Arbeidshastigheten og, ærlig talt, hvor moro det er å jobbe med disse teknologiene er fantastisk. Imidlertid synes vi det er viktig å være ansvarsbevisst når det gjelder å bruke noe risikofylte teknologier i de mindre risikofylte delene av stakken. Dette betyr at vi ikke ⛔️ stoler på risikable nye databaseteknologier (SQL er flott!). Men vi liker å ✅ stole på tRPC da det kun gjelder funksjonalitet som er trivielt å endre. ### Typesikkerhet er ikke valgfritt Det uttalte målet med `create-t3-app` er å gi den raskeste ruten til en ny, **typesikker** fullstakk-webapplikasjon. Vi tar typesikkerhet på alvor fordi det forbedrer produktiviteten vår og hjelper oss å sende færre feil ut i produksjonsmiljø. Enhver beslutning som svekker typesikkerheten til `create-t3-app` er en beslutning som bør tas i et annet prosjekt. ================================================ FILE: www/src/pages/no/other-recs.md ================================================ --- title: Flere anbefalinger description: Biblioteker og tjenester som vi anbefaler til mange prosjekter layout: ../../layouts/docs.astro lang: no --- Vi er klar over at bibliotekene som er inkludert i `create-t3-app` ikke løser alle problemer. Selv om vi oppfordrer deg til å starte prosjektet med tingene vi tilbyr, vil det garantert komme en tid da du må inkludere andre pakker. Bare du kan vite hva prosjektet ditt trenger, men her er noen ting vi ofte anbefaler å bruke. Disse anbefalingene er gitt av individuelle create-t3-app-bidragsytere og skal ikke tas som "offisielle" godkjenninger gitt av create-t3-app-teamet eller T3-OSS. _**Vennligst gjør din egen undersøkelse, spesielt før du velger betaltingstjenester**_. ## Statehåndtering _**Merk**_: Statehåndteringsbiblioteker kan være flotte, men er ofte ikke nødvendige. tRPCs React Query Hooks skal kunne håndtere serverstaten din. For state på klientsiden bør du starte med Reacts `useState` og vurdere ett av disse alternativene hvis du trenger noe mer omfattende. ### Zustand **For aldri å måtte bruke Redux igjen** Den "moderne, enkle Reduxen" du ikke visste du trengte. [Poimandres](https://github.com/pmndrs) kan du alltid stole på. Med dette lille biblioteket kan du bygge alt fra videokonferanseapper til spill og servere. - [Zustand Hjemmeside](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **For aldri å måtte bruke Context igjen** Jotai er vanskelig å slå når en atomær tilnærming er foretrukket. Også av [Poimandres](https://github.com/pmndrs). Jotai lar deg definere _singletons_ som føles som global `useState`. Et flott alternativ for stateful adfærd som ennå ikke trenger en statemaskin. - [Jotai Hjemmeside](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## Komponentbibliotek De fleste applikasjoner krever den samme håndfullen komponenter – toggleknapper, dropdownmenyer, modaler osv. Disse bibliotekene gir flotte, tilgjengelige komponenter som du kan bruke og tilpasse etter dine egne behov. ### Ustilte Komponentbiblioteker Slike biblioteker er også kjent som hodeløse biblioteker. De tilbyr flotte, ustilte og tilgjengelige komponenter som du kan tilpasse etter din smak. Her er noen anbefalinger. - [Radix UI](https://www.radix-ui.com/) tilbyr deg et kraftig sett med praktiske og tilgjengelige primitive komponenter som du kan style med vanilla CSS eller Tailwind CSS. – [Headless UI](https://headlessui.com/) ble opprettet av Tailwind CSS-teamet og tilbyr også ustilte, tilgjengelige komponenter som enkelt kan lages med Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) en stor samling av React-hooks for å lage tilgjengelige komponenter. Datovelgeren deres er aldeles toppnivå. ### Stylede Komponentbiblioteker **Hvis du bare vil ha en app som ser helt OK ut** Noen ganger vil du lage et prosjekt der brukergrensesnittet ser helt ordinært ut. Dette kan være tilfelle med f.eks. admin dashboards eller lignende prosjekter. For slike prosjekter er alle disse komponentbibliotekene gode valg. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) ### Class Variance Authority **For å lage UI-biblioteker** Lar deg deklarativt lage et brukergrensesnittbibliotek med forskjellige varianter av farger, størrelser osv. Hvis prosjektet ditt har nådd en størrelse der du vil ha et standardisert sett med UI-komponenter med flere varianter enn ved å bruke Tailwind CSS, er CVA et flott verktøy. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Animasjoner Her er våre anbefalinger hvis du trenger animasjoner i applikasjon din. ### AutoAnimate **For animasjoner med bare én kodelinje** De fleste animasjonsbiblioteker forsøker å oppfylle alle mulige brukstilfeller og blir dermed uhåndterlig. AutoAnimate er et nullkonfigurasjonsverktøy som gir deg betydelige UX-forbedringer uten ytterligere utviklerinnsats. - [AutoAnimate Hjemmeside](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [AutoAnimate Komponentkodebit](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **For komplekse animasjoner med deklarativ kode** Framer Motion tilbyr en enkel, deklarativ syntaks og lar deg lage komplekse animasjoner og til og med gestikuleringer med bare noen få linjer kode. - [Framer Motion Hjemmeside](https://framer.com/motion) - [Framer Motion Dokumentasjon](https://www.framer.com/docs/) ## Implementeringer, Infrastruktur, Databaser og CI ### Vercel **For å hoste applikasjonen din** Vercel har gjort web-app-hosting til en lek. Vi har skalert applikasjonen vår til hundretusenvis av brukere, og vi har aldri støtt på noen problemer. Vercel bruker AWS-teknologi, og tilbyr et mye bedre brukergrensesnitt enn mange andre tjenester. - [Vercels Hjemmeside](https://vercel.com/) - [Create T3 App Vercel-utrullingsveiledning](/no/deployment/vercel) ### PlanetScale **For databaser uten å måtte bekymre deg** PlanetScale er den beste "serverløse databaseplattformen" vi har brukt så langt. Vanvittig skalerbarhet, meget utviklervennlig og fantastiske priser. Hvis du bruker SQL (og forhåpentligvis Prisma) er dette vanskelig å slå. - [PlanetScale Hjemmeside](https://planetscale.com/) ### Railway **For å hoste infrastrukturen din** "Den moderne Heroku". Den enkleste måten å starte en ekte server på. Hvis Vercel og PlanetScale ikke er nok, er Railway sannsynligvis det beste alternativet. Bare pek på et GitHub-repo og kom i gang. - [Railway Homepage](https://railway.app/) ### Upstash **For serverløse Redis** Vi elsker Prisma og PlanetScale, men enkelte prosjekter tremger noen ganger en mer effektiv løsning. Upstash lar deg bruke minneytelsen til Redis i ditt serverløse prosjekt uten å måtte bekymre deg for infrastruktur og skalering. - [Upstash hjemmeside](https://upstash.com/) ### Pusher **For serverløse WebSockets** Hvis WebSockets er hovedfokuset for prosjektet ditt, kan det være lurt å vurdere en mer tradisjonell backend-løsning som [Fastify](https://www.fastify.io/) (som [også fungerer med tRPC!](https://trpc.io/docs/v10/fastify)). For raskt å legge til WebSockets i en T3-app er Pusher et utmerket valg. - [Pusher Hjemmeside](https://pusher.com/) ### Soketi Soketi er et selvhostende, enkelt og raskt alternativ til Pusher. Det er helt kompatibel med Pusher SDK, som du kan bruke til å koble til serveren. Soketi serverless er fortsatt i beta. - [Soketi Hjemmeside](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Analytics Brukerdata er svært verdifullt når du skal utvikle en app. Her er noen analyseleverandører vi anbefaler. ### Plausible Trenger du analyser? Plausibel er en av de raskeste måtene. Superminimal. Og den har til og med en [enkel plugin for Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Plausible Hjemmeside](https://plausible.io/) ### Umami Umami er et åpen kilde, selvhostende, enkelt, raskt og personvernvennlig alternativ til Google Analytics. Du kan hoste det veldig enkelt på Vercel, Railway, osv. med PlanetScale som database. - [Umami Hjemmeside](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) ## Annet ### Neste Bundle Analyzer Noen ganger er det vanskelig å finne ut hvilken kode som inkluderes i byggeutdataen til applikasjonen din. Next Bundle Analyzer er en enkel måte å visualisere og analysere JavaScript-bundler på. - [@next/bundle-analyzer på npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/no/t3-collection.mdx ================================================ --- title: T3-Kolleksjon description: Kule open source-prosjekter og selskaper som bruker T3-stakken layout: ../../layouts/docs.astro lang: no isMdx: true --- Har du laget et prosjekt med T3-stakken og ønsker å dele det? Legg det til lista! ## Open source-prosjekter bygget med T3-stakken import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; ## Selskaper som bruker T3-stakken Vi vil gjerne vite hvilke selskaper som bruker T3-stakken for applikasjonene sine. Bruker du T3-stakken og vil dele den? Legg den til lista! _Har du et kult prosjekt som bruker T3-stakken? Opprett en [Pull Request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) og legg den til her!_ ================================================ FILE: www/src/pages/no/usage/env-variables.md ================================================ --- title: Miljøvariabler description: Introduksjon til create-t3-app layout: ../../../layouts/docs.astro lang: no --- `create-t3-app` bruker [Zod](https://github.com/colinhacks/zod) for å validere miljøvariablene dine ved kjøretid _og_ ved byggetidspunkt. Ytterligere filer er angitt i `env`-katalogen for dette formålet: 📁 src/env ┣ 📄 client.mjs ┣ 📄 schema.mjs ┣ 📄 server.mjs Innholdet i disse filene kan virke skummelt til å begynne med, men ikke til bekymring, det er ikke så komplisert som det ser ut til. La oss se på disse en etter en og på hvordan du legger til flere miljøvariabler. _TLDR; Hvis du vil legge til en ny miljøvariabel, må du definere den i både `.env` og `env/schema.mjs`._ ## schema.mjs Endringene skjer i denne filen. Den inneholder to skjemaer, ett for servermiljøvariabler og ett for klientmiljøvariabler, og et `clientEnv`-objekt. ```ts:env/schema.mjs export const serverSchema = z.object({ // DATABASE_URL: z.string().url(), }); export const serverEnv = { // DATABASE_URL: process.env.DATABASE_URL, }; export const clientSchema = z.object({ // NEXT_PUBLIC_WS_KEY: z.string(), }); export const clientEnv = { // NEXT_PUBLIC_WS_KEY: process.env.NEXT_PUBLIC_WS_KEY, }; ``` ### Oppsett av Serverskjema Definer skjemaet for servermiljøvariablene her. Pass på at du _ikke_ bruker nøkler med prefikset `NEXT_PUBLIC` her. Validering vil mislykkes hvis du gjør dette for å hjelpe deg med å oppdage en ugyldig konfigurasjon. ### Oppsett av Klientskjema Definer ditt skjema for klientmiljøvariabeler her. For å gjøre dem tilgjengelige for klienten, _må_ du prefiksere dem med `NEXT_PUBLIC`. Validering vil mislykkes hvis du ikke gjør det, for å hjelpe deg med å oppdage en ugyldig konfigurasjon. ### clientEnv-Objektet I denne filen må vi få tilgang til verdiene fra `process.env`-objektet. Vi trenger et JavaScript-objekt som vi kan analysere gjennom Zod-skjemaene og på grunn av måten Next.js håndterer miljøvariabler kan vi ikke destrukturere `process.env`-objektet som et normalt objekt. Derfor må vi gjøre det manuelt. TypeScript vil hjelpe deg med å sørge for at du legger nøklene i både `clientEnv` og `clientSchema`. ```ts // ❌ Dette fungerer ikke. Vi må destrukturere den manuelt. const schema = z.object({ NEXT_PUBLIC_WS_KEY: z.string(), }); const validated = schema.parse(process.env); ``` ## server.mjs & client.mjs Her foregår valideringen og de validerte objektene eksporteres. Du bør ikke redigere disse filene. ## Bruk Miljøvariabler Hvis du vil bruke miljøvariablene dine, kan du importere dem fra `env/client.mjs` eller `env/server.mjs` avhengig av hvor du vil bruke dem: ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` er helt typesikker og tillater autofullføring const dbUrl = env.DATABASE_URL; ``` ## .env.example Siden standard `.env`-filen ikke er versjonert, har vi også inkludert en `.env.example`-fil der du eventuelt kan lagre en kopi av `.env`-filen din med eventuelle hemmeligheter fjernet. Dette er ikke nødvendig, men vi anbefaler å holde eksempelfilen oppdatert for å gjøre det så enkelt som mulig for bidragsytere å få miljøet sitt i gang. ## Legg til Miljøvariabler For å sikre at _builden_ aldri fullføres uten miljøvariablene som prosjektet trenger, må du legge til nye miljøvariabler **to** steder: 📄 `.env`: Skriv miljøvariabelen din her slik du vanligvis ville gjort i en `.env`-fil, f.eks. `KEY=VALUE` 📄 `schema.mjs`: Legg til riktig valideringslogikk for miljøvariabelen ved å definere et Zod-skjema, f.eks. `KEY: z.string()` I tillegg kan du også oppdatere `.env.example`: 📄 `.env.example`: Legg til miljøvariabelen din, men ikke glem å fjerne verdien hvis den er hemmelig, for eksempel `KEY=VALUE` eller `KEY=` ### Eksempel _Jeg vil legge til min Twitter API-token som en servermiljøvariabel_ 1. Legg til miljøvariabelen i filen ".env": ``` TWITTER_API_TOKEN=1234567890 ``` 2. Legg til miljøvariabelen i `schema.mjs`: ```ts export const serverSchema = z.object({ // ... TWITTER_API_TOKEN: z.string(), }); export const serverEnv = { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }; ``` 3. Valgfritt: Inkluder miljøvariabelen i `.env.example`, men ikke glem å fjerne verdien ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/no/usage/first-steps.md ================================================ --- title: Komme i gang description: De første stegene med din nye T3-app layout: ../../../layouts/docs.astro lang: no --- Du har nettopp opprettet en ny T3-app, og du er klar til å sette i gang. Her er det minste du må gjøre for å få applikasjonen din til å kjøre. ## Database Hvis applikasjonen din bruker Prisma, må du kjøre `npx prisma db push` fra rotmappen av applikasjonen. Denne kommandoen synkroniserer Prisma-skjemaet til databasen og genererer TypeScript-typene for Prisma-klienten basert på skjemaet ditt. Merk at du må [starte TypeScript-serveren på nytt](https://tinytip.co/tips/vscode-restart-ts/) etter denne handlingen for at de genererte typene skal gjenkjennes. ## Autentisering Hvis applikasjonen din bruker NextAuth.js, starter vi med `DiscordProvider`. Dette er en av de enkleste leverandørene som NextAuth.js tilbyr, men det krever fortsatt litt oppsett fra din side. Hvis du foretrekker en annen autentiseringsleverandør, kan du også bruke en av de [mange leverandørene](https://next-auth.js.org/providers/) som NextAuth.js tilbyr. 1. Du trenger en Discord-konto. Meld deg på hvis du ikke har en ennå. 2. Naviger til https://discord.com/developers/applications og klikk "New Application" øverst til høyre. Gi applikasjonen din et navn og godta vilkårene for bruk. 3. Når applikasjonen din er opprettet, naviger til "Settings → OAuth2 → General". 4. Kopier "Client ID" og lim den inn i `.env` som `AUTH_DISCORD_ID`. 5. Klikk "Reset Secret", kopier den nye hemmeligheten og lim inn verdien i `.env` som `AUTH_DISCORD_SECRET`. 6. Klikk "Add Redirect" og skriv inn `http://localhost:3000/api/auth/callback/discord`. - For utrulling i produksjonsmiljø må de foregående trinnene følges på nytt for å lage en annen Discord-applikasjon. Denne gangen erstatt `http://localhost:3000` med URL-en du publiserer til. 7. Lagre endringene. Du skal nå kunne logge på. ## Editor Setup Følgende utvidelser anbefales for en optimal utvikleropplevelse. Linkene nedenfor gir støtte for redigeringsprogram utvidelser. - [Prisma Extension](https://www.prisma.io/docs/guides/development-environment/editor-setup) - [Tailwind CSS IntelliSense Extension](https://tailwindcss.com/docs/editor-setup) - [Prettier Extension](https://prettier.io/docs/en/editors.html) ## Neste Steg - Hvis applikasjonen din bruker tRPC, ta en titt på `src/pages/index.tsx` og `src/server/api/routers/post.ts` for å se hvordan tRPC-spørringer fungerer. - Ta en titt på dokumentasjonen for `create-t3-app` samt dokumentasjonen for pakkene applikasjonen din inkluderer. - Bli med i [Discord](https://t3.gg/discord) og gi oss en stjerne på [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/no/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Bruk", layout: "docs", description: "Lær hvordan man bruker de forskjellige teknologiene i T3-stakken.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/no/usage/next-auth.md ================================================ --- title: NextAuth.js description: Bruk av NextAuth.js layout: ../../../layouts/docs.astro lang: no --- Hvis du vil ha et autentiseringssystem i Next.js-applikasjonen din, er NextAuth.js en utmerket løsning for å unngå kompleksiteten med å bygge det selv. Den kommer med en omfattende liste leverandører for raskt å legge til OAuth-autentisering og tilbyr adaptere for mange databaser og ORM-er. ## Kontekstleverandør I applikasjonens inngangspunkt vil du se at applikasjonen er pakket inn av en [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider). ```tsx:pages/_app.tsx ``` Denne kontekstleverandøren lar applikasjonen din få tilgang til din `session`-data fra hvor som helst i applikasjonen din uten å måtte sende dem som _props_: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Håndter uautentisert state f.eks. ved å vise en påloggingskomponent return ; } return

Welcome {session.user.name}!

; }; ``` ## Henting av session på serversiden Noen ganger vil du kanskje be om _session_ på serveren. For å gjøre dette, prefetch'er du session ved å bruke `getServerAuthSession`-hjelperfunksjonen som `create-t3-app` gir, sender den videre til klienten ved å bruke `getServerSideProps`: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import type { GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // MERK: `session` vil ikke ha en lastestatus siden den allerede er prefetched på serveren ... } ``` ## Inkluder `user.id` i din Session `create-t3-app` er konfigurert til å bruke [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) i NextAuth.js-konfigurasjonen for å inkludere bruker-ID i 'session'-objektet. ```ts:server/auth.ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Dette er kombinert med en typedeklarasjonsfil for å sikre at `user.id` er riktig typet når du får tilgang til `session`-objektet. Les mer om [`"Module Augmentation"`](https://next-auth.js.org/getting-started/typescript#module-augmentation) i NextAuth.js-dokumentasjonen. ```ts:server/auth.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` Det samme mønsteret kan brukes til å legge til flere data til `session`-objektet, som f.eks et `role`-felt, men **skal ikke misbrukes til å lagre sensitive data på klienten**. ## Bruk med tRPC Hvis du bruker NextAuth.js med tRPC, kan du opprette gjenbrukbare beskyttede prosedyrer med [middlewares](https://trpc.io/docs/v10/middlewares). Dette lar deg lage prosedyrer som bare er tilgjengelige for autentiserte brukere. `create-t3-app` gir deg allerede dette, slik at du enkelt kan få tilgang til session-objektet i autentiserte prosedyrer. Dette skjer i to trinn: 1. Få tilgang til sesjonen fra _request-headerne_ ved å bruke funksjonen [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). Fordelen med `getServerSession` sammenlignet med `getSession` er at det er en funksjon på serversiden og medfører ikke unødvendige kall. `create-t3-app` lager en hjelpefunksjon som abstraherer dette særegne API-et, slik at du ikke trenger å importere både NextAuth.js-alternativene dine så vel som `getServerSession`-funksjonen hver gang du trenger tilgang til sesssion. ```ts:server/auth.ts export const getServerAuthSession = (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); }; ``` Med denne hjelpefunksjonen kan vi hente sesjonen og sende den videre til tRPC-konteksten: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Lag en tRPC-middleware som sjekker om brukeren er autentisert. Vi bruker deretter middlewaren i en `protectedProcedure`. Hvert kall av disse prosedyrene må autentiseres, ellers kastes en feilmelding, som kan håndteres av klienten. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // inferer `session` som ikke-nullbar session: { ...ctx.session, user: ctx.session.user }, }, }); })); ``` `Session`-objektet er en minimal representasjon av brukeren og inneholder bare noen få felt. Hvis du bruker `protectedProcedures`, har du tilgang til brukerens ID, som kan brukes til å hente ut mer data fra databasen. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Bruk med Prisma Mye [førstegangsoppsett](https://authjs.dev/reference/adapter/prisma/) kreves for å bruke NextAuth.js med Prisma. `create-t3-app` vil gjøre dette for deg, og hvis du velger både Prisma og NextAuth.js, får du et fullt funksjonelt autentiseringssystem med alle nødvendige modeller forhåndskonfigurert. Vi oppretter applikasjonen din med en forhåndskonfigurert Discord OAuth-leverandør, som vi valgte siden den er en av de enkleste leverandørene å komme i gang med – du trenger bare å legge inn tokens i `.env`-filen og du er i gang. Du kan imidlertid enkelt legge til flere leverandører ved å følge [NextAuth.js-dokumentasjonen](https://next-auth.js.org/providers/). Vær oppmerksom på at enkelte leverandører krever at du legger til flere felt i enkelte modeller. Vi anbefaler å lese dokumentasjonen for leverandøren du planlegger å bruke for å sikre at alle obligatoriske felt er til stede. ### Legge til nye felt i modellene dine Hvis du legger til nye felt i noen av modellene `User`, `Account`, `Session` eller `VerificationToken` (du trenger sannsynligvis bare å justere `User`-modellen), må du huske på at [Prisma-adapteren](https://next-auth.js.org/adapters/prisma) automatisk legger til felt i disse modellene når nye brukere registrerer seg og logger på. Så når du legger til nye felt i disse modellene, må du oppgi standardverdier for dem fordi adapteren ikke vet om disse feltene. Hvis du for eksempel vil legge til et `role`-felt i `User`-modellen, må du angi en standardverdi for feltet. Dette oppnås ved å legge til en `@default`-verdi i `role`-feltet i `User`-modellen: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Bruk med Next.js middleware Bruk av NextAuth.js med Next.js middleware [krever bruk av "JWT session strategy"](https://next-auth.js.org/configuration/nextjs#caveats) for autentisering. Dette er fordi middlewaren bare kan få tilgang til session-informasjonskapslene når den er en JWT. Som standard er `create-t3-app` konfigurert til å bruke **default**-databasestrategien, i kombinasjon med Prisma som databaseadapter. ## Oppsett av DiscordProvider (standard) 1. Naviger til [Applications-delen i Discord Developer Portal](https://discord.com/developers/applications) og klikk på "New Application" 2. Bytt til "OAuth2 => Generelt" i settings-menyen - Kopier klient-ID-en og lim den inn i `AUTH_DISCORD_ID` i `.env`. - Under Client Secret, klikk på "Reset Secret" og kopier denne strengen til `AUTH_DISCORD_SECRET` i `.env`. Vær forsiktig siden du ikke lenger vil kunne se denne hemmeligheten og tilbakestilling av den vil føre til at den eksisterende hemmeligheten utløper. - Klikk på "Add Redirect" og lim inn `/api/auth/callback/discord` (eksempel for utvikling i lokal miljø: http://localhost:3000/api/auth/callback/discord) - Lagre endringene dine - Det er mulig, men ikke anbefalt, å bruke samme Discord-applikasjon for utvikling og produksjon. Du kan også vurdere å [Mocke leverandøren](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) under utviklingen. ## Nyttige Ressurser | Ressurser | Link | | -------------------------------- | --------------------------------------- | | NextAuth.js Dokumentasjon | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - med NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/no/usage/next-js.md ================================================ --- title: Next.js description: Bruk av Next.js layout: ../../../layouts/docs.astro lang: no --- Next.js er et backend-rammeverk for dine React-applikasjoner.
Se [Theos Next.js Conf-prat](https://www.youtube.com/watch?v=W4UhNo3HAMw) for å bedre forstå hva Next.js er og hvordan det fungerer. ## Hvorfor skal jeg bruke det? Vi elsker React. Det har gjort UI-utvikling tilgjengelig på måter vi aldri kunne ha forestilt oss før. Det kan også lede utviklere ned noen vanskelige veier. De som utvikler Next.js har noen klare meninger om hvordan de ønsker at ting skal gjøres, samtidig som de tilbyr en svært optimalisert tilnærming til hvordan å bygge React-applikasjoner. Fra ruting til API-definisjoner til bilde-rendering stoler vi på at gjenge i Next.js dirigerer oss utviklere til gode beslutninger. Next.js kombinert med [Vercel](https://vercel.com/) gjør utvikling og utrulling av webapplikasjoner enklere enn noen gang. Deres ekstremt sjenerøse free-tier og superintuitive grensesnitt lar deg publisere nettstedet ditt i løpet av noen få klikk. (Vi ❤️ Vercel) ## Get Static/Server Props En nøkkelfunksjon i Next.js er hvordan man henter data. Vi anbefaler på det sterkeste å lese den [offisielle dokumentasjonen](https://nextjs.org/docs/basic-features/data-fetching) for å forstå hvordan disse funksjonene fungerer og hvordan de er forskjellige. `getServerSideProps` anbefales vanligvis bare når det er en god grunn til å bruke det, da det er et blokkerende anrop og fordi det gjør siden tregere. [Inkrementell statisk regenerering (ISR)](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) er et godt alternativ til `getServerSideProps` når dataen er dynamiske og kan hentes trinnvis. ## Nyttige Ressurser | Ressurser | Link | | ---------------------------- | ---------------------------------- | | Next.js Dokumentasjon | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js Blogg | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Vercel/Next.js YouTube-kanal | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/no/usage/prisma.md ================================================ --- title: Prisma description: Bruk av Prisma layout: ../../../layouts/docs.astro lang: no --- Prisma er en ORM (Object Relational Mapper) for TypeScript, som gir deg muligheten til å definere databasens skjema og modeller i en schema.prisma-fil. Deretter genereres en typesikker klient som kan brukes til å kommunisere med databasen din fra backend. Dette gir en enklere og mer sikker måte å utvikle databaserelaterte applikasjoner med TypeScript på. ## Prisma Client Prisma-klienten blir instansiert i `/server/db.ts` og eksporteres som en global variabel. Som anbefalt av Prisma-teamet som [beste praksis](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem). Vi angir, som standard, Prisma-klienten i [context](/no/usage/trpc#-serverapitrpcts). Vi anbefaler å bruke klienten via konteksten i stedet for å importere klienten i hver fil. ## Skjema Prisma-skjemafilen finner du i `/prisma/schema.prisma`. I denne filen definerer du databasens skjema og modeller. Denne filen brukes til å generere Prisma-klienten. ### Med NextAuth.js Hvis du velger å bruke NextAuth.js sammen med Prisma, vil skjemafilen bli generert for deg og satt opp med de anbefalte verdiene for modellene `User`, `Session`, `Account` og `VerificationToken` i henhold til [NextAuth. js-dokumentasjonen](https://next-auth.js.org/adapters/prisma). ## Standard Database Standarddatabasen er en SQLite-database, som er meget godt egnet for utvikling og rask oppretting av et proof-of-concept. Imidlertid anbefaler vi ikke å bruke SQLite i produksjonsmiljøer. Du kan endre databasen ved å endre `provider` i `datasource`-blokken til `postgresql` eller `mysql` og deretter justere URL for database i miljøvariablene slik at den peker på databasen din. ## Fyll Databasen ("Seeding") ["Seeding"](https://www.prisma.io/docs/guides/database/seed-database) er en god måte å fylle databasen med testdata. For å konfigurere fyllingen må du lage en `seed.ts`-fil i `/prisma`-katalogen og deretter legge inn et `seed`-skript i `package.json`-filen. Du trenger også en TypeScript runner som kan kjøre startskriptet. Vi anbefaler [tsx](https://github.com/esbuild-kit/tsx) som er en meget effektiv TypeScript-runner som bruker esbuild og ikke trenger noen ESM-konfigurasjon. `ts-node` eller andre runners vil også fungere. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db/client"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Deretter kan du kjøre `pnpm db-seed` (eller `npm`/`yarn`) for å fylle inn databasen. ## Nyttige Ressurser | Ressurser | Link | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Prisma Dokumentasjon | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | Prisma Migrate Playground | https://playground.prisma.io/guides | | NextAuth.JS Prisma Adapter | https://next-auth.js.org/adapters/prisma | | PlanetScale Tilkoblingsveiledning | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/no/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Bruk av TailwindCSS layout: ../../../layouts/docs.astro lang: no --- ## Hva er Tailwind CSS? Tailwind CSS er et lite, [utility first](https://tailwindcss.com/docs/utility-first) CSS-rammeverk for å lage tilpassede temaer uten å måtte bytte mellom ulike kontekster. Det er et rent CSS-rammeverk og tilbyr ingen forhåndsbygde komponenter eller logikk. Det tilbyr også [et helt annet sett med fordeler](https://www.youtube.com/watch?v=CQuTF-bkOgc) sammenlignet med et komponentbibliotek som Material UI. Det gjør det utrolig enkelt og raskt å skrive CSS, som vist i eksemplet nedenfor: Gammel CSS: 1. Skriv CSS (ofte i en egen fil) ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Importer CSS til komponenten din ```jsx import "./my-class.css"; ``` 3. Legg til klassen i HTML-koden din ```html
...
``` Tilsvarende i Tailwind: 1. Bare skriv klassen i HTML-koden din ```html
...
``` React-komponenter er en sterk kombinasjon for å raskt lage brukergrensesnitt. Tailwind CSS har et veldig fint innebygd temasystem som kommer med en nøye utvalgt fargepalett, dimensjonsstiler for bredde/høyde og padding/margin, og mediabrytepunkter for responsive layouter. Dette designsystemet kan tilpasses og utvides for å lage nøyaktig det settet med stiler som prosjektet ditt trenger.
Tru Narla, bedre kjent som [mewtru](https://twitter.com/trunarla), har en flott presentasjon om hvordan å [bygge et temasystem med Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Bruk Sørg for at du har installert editor-plugins for Tailwind for å forbedre utvikleropplevelsen. ### Utvidelser og plugins - [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains-integrasjon](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Formatering Tailwind CSS-klasser kan fort bli forvirrende, så en klasseformaterer er et must. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) sorterer klassene i [anbefalt rekkefølge](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) slik at klassene samsvarer med utdata-CSS-bundlen. Hvis du velger Tailwind i CLI, vil vi installere og konfigurere det for deg. ### Betinget Bruk av Klasser Å legge til klasser under forhold med "ternærer" kan bli veldig rotete og vanskelig å lese. Disse pakkene hjelper deg med å organisere klassene dine når du bruker betinget logikk. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Nyttige Ressurser | Ressurser | Link | | -------------------------- | -------------------------------------------------------- | | Tailwind Dokumentasjon | https://tailwindcss.com/docs/editor-setup/ | | Tailwind Cheat Sheet | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Tailwind Discord-server | https://tailwindcss.com/discord/ | | TailwindLabs Youtube-kanal | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/no/usage/trpc.md ================================================ --- title: tRPC description: Bruk av tRPC layout: ../../../layouts/docs.astro lang: no --- tRPC lar oss skrive ende-til-ende typesikre APIer, helt uten kodegenerering eller runtime-bloat. tRPC bruker TypeScripts _inferens_ for å automatisk utlede API-ruterens typedefinisjoner og lar deg kalle API-prosedyrene dine fra frontend med full typesikkerhet og autofullføring. Når du bruker tRPC, føles frontend og backend nærmere enn noen gang, noe som resulterer i en enestående utvikleropplevelse.

"Jeg bygde tRPC for å forbedre hastigheten på utviklingen av applikasjoner ved å fjerne behovet for et tradisjonelt API-lag. Samtidig kan vi fortsatt stole på at de vil være stabile når man itererer raskt.

Avatar of @alexdotjs
Alex - creator of tRPC @alexdotjs
## Hvordan bruker jeg tRPC?
tRPC-bidragsyter [trashh_dev](https://twitter.com/trashh_dev) holdt [en flott tale på Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) om tRPC. Vi anbefaler deg å se den hvis du ikke allerede har gjort det. Med tRPC skriver du TypeScript-funksjoner i backend, og kaller dem deretter fra frontend. En enkel tRPC-prosedyre kan se slik ut: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Dette er tRPC-prosedyre (tilsvarer en rutebehandler i en tradisjonell backend) som først validerer inndataene ved å bruke Zod (som er det samme valideringsbiblioteket vi bruker for [miljøvariablene](./env-variables)) - i dette tilfellet forsikres det at _input_ er en streng. Hvis input ikke er en streng, returneres en detaljert feil. Etter input følger en resolver-funksjon som enten utfører en [query](https://trpc.io/docs/v10/react-queries), [mutasjon](https://trpc.io/docs/v10/react-mutations) eller en [subscription](https://trpc.io/docs/v10/subscriptions). I vårt eksempel kaller resolver-funksjonen vår database med vår [prisma](./prisma)-klient og returnerer brukeren hvis `id` samsvarer med den vi sendte inn. Du definerer prosedyrene dine i `rutere` som er en samling av relaterte prosedyrer innenfor et felles _namespace_. Du kan ha en ruter for `users`, en for `posts` og en for `messages`. Disse ruterne kan deretter slås sammen til en enkelt, sentral `appRouter`: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Merk at vi bare trenger å eksportere vår ruters typedefinisjoner, noe som betyr at vi aldri importerer noen serverkode i klienten vår. La oss nå påkalle prosedyren i frontenden vår. tRPC tilbyr en _wrapper_ for `@tanstack/react-query` hvor det er definert noen hooks som gjør at du kan påkalle ditt API med definerte typer som er "inferred", det vil at TypeScript-kompilatoren automatisk har utledet hvilken type API-kallene dine har. Vi kan kalle prosedyrene våre fra vår frontend slik: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Du vil umiddelbart legge merke til hvor god autofullføringen og typesikkerheten er. Så snart du skriver inn `api.` vil ruterne dine automatisk bli foreslått. Hvis du nå velger en ruter, vil prosedyrene også vises. Hvis inndataene dine ikke samsvarer med validatoren du definerte i backend, får du en TypeScript-feil. ## Filer tRPC krever mye _boilerplate_, som `create-t3-app` setter opp for deg. La oss gå gjennom filene som vil bli opprettet: ### 📄 `pages/api/trpc/[trpc].ts` Dette er inngangspunktet for API-et ditt og eksponerer tRPC-ruteren. Normalt vil du ikke være borti denne filen så ofte. Men hvis du f.eks. trenger en _middleware_ for CORS eller lignende, er det nyttig å vite at den eksporterte funksjonen `createNextApiHandler` er en [Next.js API-Handler](https://nextjs.org/docs/api-routes/introduction) som mottar et [request-](https://developer.mozilla.org/en-US/docs/Web/API/Request) og et [response](https://developer.mozilla.org/en-US/docs/Web/API/Response)-objekt. Dette betyr at du kan _wrappe_ `createNextApiHandler` med hvilken som helst middleware. Se under for et [eksempel](#aktivering-av-cors) for å legge til CORS. ### 📄 `server/api/trpc.ts` Denne filen er delt opp i to deler, kontekstoppretting og tRPC-initialisering: 1. Vi definerer konteksten som videresendes til tRPC-prosedyrene dine. Kontekst er data som alle dine tRPC-prosedyrer vil ha tilgang til, og er et flott sted å plassere ting som databaseforbindelser, autentiseringsinformasjon, osv. I create-t3-app bruker vi to funksjoner, for å muliggjøre bruk av en undergruppe av konteksten når vi ikke har tilgang til forespørselsobjektet. - `createInnerTRPCContext`: Det er her du definerer konteksten som ikke avhenger av forespørselen, f.eks. databasetilkoblingen din. Du kan bruke denne funksjonen til [integrasjonstesting](#sample-integration-test) eller [ssg-hjelpere](https://trpc.io/docs/v10/ssg-helpers) der du ikke har et forespørselsobjekt. - `createTRPCContext`: Det er her du definerer konteksten som avhenger av forespørselen, f.eks. brukerens økt. Du ber om økten ved å bruke `opts.req`-objektet, og sender deretter økten ned til `createInnerTRPCContext`-funksjonen for å lage den endelige konteksten. 2. Vi initialiserer tRPC og definerer gjenbrukbare [prosedyrer](https://trpc.io/docs/v10/procedures) og [middlewares](https://trpc.io/docs/v10/middlewares). Av konvensjon bør du ikke eksponere hele `t`-objektet, men i stedet lage gjenbrukbare prosedyrer og middleware og eksportere de. Du har sikkert lagt merke til at vi bruker `superjson` som [datatransformator](https://trpc.io/docs/v10/data-transformers). Dette sikrer at datatypene dine blir bevart når de når klienten, så hvis du for eksempel sender et `Date`-objekt til klienten så returneres et `Date`-objekt og ikke en streng slik de fleste API gjør. ### 📄 `server/api/routers/*.ts` Det er her du definerer ruterne og prosedyrene for API-et din. Konvensjon tilsier at du bør [opprette separate rutere](https://trpc.io/docs/v10/router) for relaterte prosedyrer. ### 📄 `server/api/root.ts` Her slår vi sammen alle underruterne definert i `routers/**` [merge](https://trpc.io/docs/v10/merging-routers) til et enkelt app-ruter. ### 📄 `utils/api.ts` Dette er inngangspunktet for tRPC på klientsiden. Her importerer du ruterens **typedefinisjonen** og oppretter tRPC-klienten, samt hooks for react-query. Ettersom vi har aktivert `superjson` som vår datatransformator på serversiden, må vi aktivere den på klientsiden også. Dette er fordi serialisert data fra _backend_ blir deserialisert på _frontend_. Du definerer tRPC [lenker](https://trpc.io/docs/v10/links) her, som kartlegger request-flyten fra klienten til serveren. Vi bruker "standard" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink) som muliggjør [request batching](https://cloud.google.com/compute/docs/api/how-tos/batch), samt en [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink) som gir ut request-logger som kan være nyttige under utviklingsprosessen. Til slutt eksporterer vi en [hjelpertype](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type) som du kan bruke til å utlede typene dine på klientsiden. ## Hvordan påkaller jeg API-et mitt eksternt? Med vanlige API-er kan du bruke hvilken som helst HTTP-klient som `curl`, `Postman`, `fetch` eller bare nettleseren din. Med tRPC er det litt annerledes. Hvis du vil kalle opp prosedyrene dine uten tRPC-klienten, er det to anbefalte måter: ### Gjør en enkelt prosedyre tilgjengelig eksternt Hvis du ønsker å eksponere en enkel prosedyre eksternt, er du avhengig av [server-side-kall](https://trpc.io/docs/v10/server-side-calls). Dette vil tillate deg å opprette et normalt Next.js API-endepunkt, men gjenbruke resolver-delen av tRPC-prosedyren. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Vis alle prosedyrer som et REST-endepunkt Hvis du vil gjøre hver enkelt prosedyre tilgjengelig eksternt, sjekk ut den community-skapte plugin-modulen [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Den lar deg generere et OpenAPI-kompatibelt REST API fra tRPC-ruteren din, ved å legge til ytterligere metadata i prosedyrene dine. ### Dette er kun HTTP-requests tRPC kommuniserer via HTTP, så det er også mulig å starte tRPC-prosedyrene dine med "normale" HTTP-requests. Syntaksen kan imidlertid være vanskelig på grunn av [RPC-protokollen](https://trpc.io/docs/v10/rpc) som tRPC bruker. Hvis du er nysgjerrig, sjekk nettleserens nettverksfane for å se hvordan tRPC-requestene og -responsene ser ut. Vi anbefaler imidlertid dette kun for pedagogiske formål og vil råde deg til generelt å bruke en av løsningene ovenfor. ## Sammenligning med et Next.js API-endepunkt La oss sammenligne et Next.js API-endepunkt med en tRPC-prosedyre. Anta at vi ønsker å hente et brukerobjekt fra databasen vår og returnere det til frontend. Vi kan skrive et Next.js API-endepunkt slik: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Hvis vi nå sammenligner dette med tRPC-eksemplet fra lenger opp i dokumentasjonen, kan følgende fordeler med tRPC sees: - I stedet for å spesifisere en URL for hver rute, som kan forårsake feil ved endring av prosjektets struktur, er hele ruteren et objekt med autofullføring. - Du trenger ikke å validere hvilken HTTP-metode som ble brukt. - Du trenger ikke å validere at request eller _body_ inneholder riktige data i prosedyren, fordi Zod tar seg av dette. - I stedet for å opprette en response, kan du kaste en error og returnere en verdi eller et objekt som du ville gjort i en hvilken som helst annen TypeScript-funksjon. - Å kalle prosedyren på frontend gir autofullføring og typesikkerhet. ## Nyttige Kodeutdrag Her er noen kodesnutter som kan hjelpe deg. ### Aktivering av CORS Hvis du trenger å konsumere API-et ditt fra et annet domene, for eksempel i en monorepo som inneholder en React Native-app, må du antageligvis aktivere CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Aktiver cors await cors(req, res); // Oprett og påkall tRPC-handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### Optimistiske oppdateringer Optimistiske oppdateringer er oppdateringer vi gjør før API-forespørselen fullføres. Dette gir en bedre opplevelse for brukeren siden de ikke trenger å vente på at API-forespørselen skal fullføres før brukergrensesnittet reflekterer resultatet av handlingen deres. Imidlertid bør applikasjoner som verdsetter riktigheten av dataen unngå optimistiske oppdateringer, da de ikke gjenspeiler de "sanne" dataene til backend. Du kan lese mer om det i [React Query-dokumentasjonen](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Avbryt utgående henting (slik at de ikke overskriver vår optimistiske oppdatering) vent utils.post.list.cancel(); // Få dataene fra queryCache const prevData = utils.post.list.getData(); // Oppdater dataene optimistisk med vårt nye innlegg utils.post.list.setData(udefinert, (gammel) => [...gammel, nyinnlegg]); // Returner forrige data slik at vi kan gå tilbake hvis noe går galt return { prevData }; }, onError(err, newPost, ctx) { // Hvis mutasjonen mislykkes, bruk kontekstverdien fra onMutate utils.post.list.setData(udefinert, ctx.prevData); }, onSettled() { // Synkroniser med server når mutasjonen er fullført utils.post.list.invalidate(); }, }); }; ``` ### Eksempel på Integrasjonstest Her er et eksempel på en integrasjonstest som bruker [Vitest](https://vitest.dev) for å bekrefte at tRPC-ruteren din fungerer som forventet, at input-parseren _inferrer_ riktig type, og at returnert data samsvarer med forventet output. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` ## Nyttige Ressurser | Ressurser | Link | | ------------------------- | ------------------------------------------------------- | | tRPC Dokumentasjon | https://www.trpc.io | | Noen tRPC-eksempler | https://github.com/trpc/trpc/tree/next/examples | | React Query Dokumentasjon | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/no/usage/typescript.md ================================================ --- title: TypeScript description: Bruk av TypeScript layout: ../../../layouts/docs.astro lang: no ---

Bygg sikkerhetsnett, ikke rekkverk

Avatar av @t3dotgg
Theo – skaperen av T3-stakken @t3dotgg
Enten du er en ny eller erfaren utvikler, synes vi at TypeScript er et must. Det kan virke skremmende i begynnelsen, men som mange verktøy, når du først begynner å bruke det, er det noe du ikke vil være foruten. Det tilbyr live tilbakemeldinger mens du skriver koden din. De forventede datatypene er definert, og vi får nyttig autofullføring i koderedigeringsprogrammet vårt. Dersom det er et problem får man rød understrek, for eksempel når vi prøver å få tilgang til en attributt som ikke eksisterer eller når vi prøver å angi en verdi av feil type. Dette lar oss identifisere og fikse feil tidlig uten å måtte feilsøke etterpå. Det er uten tvil verktøyet i stakken som mest øker utvikleres produktivitet; man får dokumentasjon på at koden man skriver er riktig direkte i editoren og man får umiddelbar tilbakemelding når man uunngåelig gjør noen feil. Dette er helt uvurderlig. ## Typeinferens Mange nye TypeScript-utviklere blir veldig opptatt av å _skrive_ TypeScript, men mange av fordelene med TypeScript kommer av at man ikke trenger å endre koden sin i det hele tatt. Spesielt vha. typeinferens. Typeinferens betyr at når noe er definert med type, så vil den typen følge med gjennom hele applikasjonen uten å måtte redeklareres andre steder. Dette betyr at, for eksempel, når du har definert typene av en funksjons argumenter, er resten av funksjonen typesikker, uten ekstra TypeScript-spesifikk kode. Bibliotekutviklere legger mye arbeid i å vedlikeholde typene eksponert i bibliotekene deres, noe som betyr at vi som brukere kan dra nytte av inferens og den innebygde dokumentasjonen i koderedigeringsprogrammet som tilbys av disse typene.
Se videoen av Theo, hvorfor [det kan hende at du bruker TypeScript feil](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Gode bruksområder for typeinferens ### Zod [Zod](https://github.com/colinhacks/zod) er et skjemavalideringsbibliotek bygd på TypeScript. Skriv et skjema som representerer den eneste kilden til sannhet ("single source of truth") for din data, og Zod vil sørge for at din data er gyldig gjennom hele applikasjonenen. Selv på tvers av nettverksgrenser og eksterne API. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) gir deg deklarative, alltid oppdaterte, automatisk administrerte `queries` og `mutations` som direkte forbedrer utvikler- og brukeropplevelsen. ## Nyttige Ressurser | Ressurser | Link | | -------------------------------------------------------- | ----------------------------------------------------------------- | | TypeScript håndbok | https://www.typescriptlang.org/docs/handbook/ | | TypeScript opplæring for nybegynnere | https://github.com/total-typescript/beginners-typescript-tutorial | | Type-utfordringer | https://github.com/type-challenges/type-challenges | | Rodney Mullen fra TypeScript (Matt Pocock) Youtube-kanal | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/no/why.md ================================================ --- title: Hvorfor CT3A? description: Hvorfor du bør velge Create T3 App for ditt neste prosjekt layout: ../../layouts/docs.astro lang: no --- Vi startet create-t3-app fordi [Theo](https://twitter.com/t3dotgg) ikke ønsket å lage en mal for favoritteknologiene sine. Inspirert av create-next-app, [Astro's CLI](https://astro.build) og en forkjærlighet for typesikkerhet, har create-t3-app-teamet jobbet hardt for å skape et best mulig utgangspunkt for nye T3-stakkprosjekter. Hvis du er interessert i å bruke Next.js på en typesikker måte, er dette det rette stedet å starte. Hvis du er interessert i hvorfor vi tok visse spesifikke teknologivalg, les videre :) ## Hvorfor TypeScript? JavaScript er vanskelig. Hvorfor legge til enda flere regler? Vi er overbevist om at opplevelsen som TypeScript gir vil gjøre deg til en bedre utvikler. Man mottar umiddelbar tilbakemelding når man skriver koden sin. De forventede datatypene er definert, og enten får vi nyttig autofullføring i kodeeditoren, eller røde understrekinger som varsler oss om et problem, for eksempel når vi prøver å få tilgang til en attributt som ikke eksisterer eller når vi prøver å angi en verdi av feil type. Dette lar oss identifisere og fikse feil tidlig uten å måtte feilsøke etterpå. Enten du er helt ny på webutvikling eller en erfaren proff, vil TypeScripts "strenghet" gi deg en mindre frustrerende, mer konsekvent opplevelse enn vanilla JS. Typesikkerhet gjør deg raskere. Hvis du ikke er overbevist, [kan det hende du bruker TypeScript feil...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## Hvorfor Next.js? Vi elsker React. Det har gjort UI-utvikling tilgjengelig på måter vi aldri kunne ha forestilt oss før. Det kan også lede utviklere ned noen vanskelige veier. Next.js, som har noen enkle, tydelige meninger, tilbyr en svært optimalisert tilnærming til å bygge applikasjoner med React. Fra ruting til API-definisjoner til bilde-rendering stoler vi på at Next.js dirigerer utviklere til gode beslutninger. ## Hvorfor tRPC/Prisma/Tailwind/osv? Selv om vi ønsker å holde ting så enkelt som mulig, så opplever vi at disse delene kan brukes i så og si alle applikasjonene vi lager. `create-t3-app` gjør det enkelt for deg å legge til delene du trenger. ### tRPC tRPC bygger på GraphQLs løfte om sømløs klientsideutvikling opp mot en typesikker server uten å generere mye _boilerplate_. Det er en smart utnyttelse av TypeScript som tilbyr en utrolig utvikleropplevelse. ### Prisma Prisma er for SQL det TypeScript er for JS. Det skaper en utvikleropplevelse som ikke har eksistert før. Prisma garanterer ende-til-ende typesikkerhet fra databasen til applikasjonen din ved å generere typer fra et brukerdefinert skjema som er kompatible med [forskjellige databaser](https://www.prisma.io/docs/concepts/database-connectors). Prisma tilbyr et helt [sett med verktøy](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) som gjør det daglige arbeidet med databasen enklere. Spesielt å merke seg er Prisma-klienten, som utfører spørringer og gjør SQL så enkelt at du knapt vil legge merke til at du bruker det. Prisma Studio er et nyttig GUI for databasen din som lar deg raskt lese og manipulere dataene dine uten å måtte skrive noen kode. ### Tailwind CSS Tailwind føles som "zen-modus CSS". Tailwind lar deg lage en tydelig beskrevet applikasjon ved å gi deg grunnleggende byggeklosser i form av gode standardfarger, mellomrom og andre primitiver. I motsetning til komponentbiblioteker, blir du ikke holdt tilbake hvis du ønsker å ta applikasjonen din til det neste nivået og skape noe ekstraordinært og unikt. I tillegg betyr Tailwinds "inline"-tilnærming at du ikke trenger å bekymre deg for klassenavn, mappestruktur eller andre problemer som ikke er direkte relatert til problemet du ønsker å løse. ### NextAuth.js Hvis du vil ha et autentiseringssystem i Next.js-applikasjonen din, er NextAuth.js en utmerket løsning for å unngå kompleksiteten innebært i å bygge det selv. Den kommer med en omfattende liste over _providers_ for å raskt legge til OAuth-autentisering og tilbyr adaptere for mange databaser og ORM-er. ================================================ FILE: www/src/pages/og.ts ================================================ import { Resvg } from "@resvg/resvg-js"; import { type APIRoute } from "astro"; import satori from "satori"; import OpenGraph from "../components/openGraph"; import { SITE } from "../config"; import { getIsRtlFromLangCode, getLanguageFromURL } from "../languages"; import { getFont } from "../utils/ogFont"; import { SITE_URL } from "../utils/siteUrl"; const removeEndingSlash = (str: string) => str.replace(/\/$/, ""); export const get: APIRoute = async (request) => { const params = request.url.searchParams; const title = params.get("title") ?? SITE.title; const description = params.get("description") ?? SITE.description; const pagePath = params.get("pagePath") ?? ""; // Used for most languages const inter = await getFont({ family: "Inter", weights: [400, 700] as const, }); // Used for arabic text const bonaNova = await getFont({ family: "Bona Nova", weights: [400, 700] as const, }); // Used for chinese const notoSans = await getFont({ family: "Noto Sans SC", weights: [400, 700] as const, }); const hostname = request.site?.hostname.replace(/^https?:\/\//, ""); const pageLang = getLanguageFromURL(pagePath); const svg = await satori( OpenGraph({ title, description, imageBase: SITE_URL, pageUrl: `${hostname}${removeEndingSlash(pagePath)}`, rtl: getIsRtlFromLangCode(pageLang), }), { width: 1200, height: 630, fonts: [ { name: "Inter", data: inter[400], weight: 400 }, { name: "Inter", data: inter[700], weight: 700 }, { name: "Noto Sans SC", data: notoSans[400], weight: 400 }, { name: "Noto Sans SC", data: notoSans[700], weight: 700 }, { name: "Bona Nova", data: bonaNova[400], weight: 400 }, { name: "Bona Nova", data: bonaNova[700], weight: 700 }, ], debug: import.meta.env.DEBUG_OG === "true", }, ); const resvg = new Resvg(svg, {}); const pngData = resvg.render(); const pngBuffer = pngData.asPng(); return new Response(pngBuffer, { headers: { "Content-Type": "image/png", "cache-control": "public, max-age=31536000, immutable", }, }); }; ================================================ FILE: www/src/pages/pl/deployment/docker.md ================================================ --- title: Docker description: Deployment with Docker layout: ../../../layouts/docs.astro lang: pl --- Stack ten możesz skonteneryzować i zdeploy'ować jako pojedynczy kontener korzystając z Dockera, czy też jako część grupy kontenerów korzystając z docker-compose. Po przykładowe repozytorium bazowane na tym dokumencie, zobacz [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker). ## Konfiguracja Projektu z Dockerem Należy pamiętać, że Next.js wymaga innego procesu na czas budowania (dostępnego na frontendzie, z prefiksem `NEXT_PUBLIC`) a innego dla środowiska na czas działania, tylko po stronie serwera, oraz zmiennych. W poniższym demo korzystamy z dwóch zmiennych, zwróć uwagę na ich umiejscowienie w pliku `Dockerfile`, argumenty w konsoli i plik `docker-compose.yml`: - `DATABASE_URL` (używany przez serwer) - `NEXT_PUBLIC_CLIENTVAR` (używany przez klienta) ### 1. Konfiguracja Next.js W swoim pliku [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), dodaj opcję konfiguracji outputu `standalone` aby [zredukować rozmiar zdjęć poprzez automatyczne wykorzystanie danych wyjściowych](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Stwórz plik dockerignore
Kliknij tutaj i dodaj poniższą zawartość do pliku .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Stwórz plik Dockerfile > Jako iż nie pobieramy serwerowych zmiennych środowiskowych do naszego kontenera, [walidacja zmiennych środowiskowych](/pl/usage/env-variables) wyrzuci błąd. Aby mu zapobiec, musimy dodać flagę `SKUP_ENV_VALIDATION=1` do komendy budowania projektu - schemat zmiennych środ. nie zostanie wtedy sprawdzony podczas budowania.
Kliknij tutaj i dodaj poniższą zawartość do pliku Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Install Prisma Client - remove if not using Prisma COPY prisma ./ # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Uwagi_** > > - _Emulacja platformy z flagą `--platform=linux/amd64` może nie być potrzebna podczas korzystania z Node'a w wersji 18._ > - _Aby zrozumieć, dlaczego `libc6-compat` może być potrzebny, zobacz [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine)._ > - _Korzystanie z obrazów bazujących na Alpine 3.17 [może powodować problemy z Prismą](https://github.com/t3-oss/create-t3-app/issues/975). Ustawienie `engineType = "binary"` rozwiązuje ten problem, [niestety kosztem wydajności](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ > - _Next.js zbiera [anonimowe dane telemetryczne o ogólnym użyciu](https://nextjs.org/telemetry). Odkomentuj pierwsze wystąpienie `ENV NEXT_TELEMETRY_DISABLED 1` aby wyłączyć telemetrię podczas budowania. Odkomentuj drugie wystąpienie, aby wyłączyć telemetrię w produkcie końcowym._
## Zbuduj oraz Uruchom Obraz Lokalnie Zbuduj i uruchom ten obraz lokalnie korzystając z następujących komend: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Otwórz [localhost:3000](http://localhost:3000/) aby zobaczyć uruchomioną aplikację. ## Docker Compose Możesz także skorzystać z narzędzia Docker Compose, aby zbudować obraz i uruchomić kontener.
Podążaj za krokami 1-4 powyżej, kliknij tutaj i dodaj poniższą zawartość do pliku docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Uruchom aplikację za pomocą komendy `docker compose up`: ```bash docker compose up ``` Otwórz [localhost:3000](http://localhost:3000/) aby zobaczyć uruchomioną aplikację.
## Deployment na Railway'a Możesz użyć usługi PaaS ("platforma jako usługa"), takiej jak [zautomatyzowany deployment Dockerfile'a](https://docs.railway.app/deploy/dockerfiles) od [Railway'a](https://railway.app), aby zdeploy'ować swoją aplikację. Jeżeli zainstalowałeś [CLI Railway'a](https://docs.railway.app/develop/cli#install), możesz zdeploy'ować aplikację korzystając z poniższych komend. ```bash railway login railway init railway link railway up railway open ``` Przejdź do zakładki "Zmienne" ("Variables") i dodaj tam swój `DATABASE_URL`. Następnie przejdź do ustawień ("Settings") i wybierz opcję "Wygeneruj Domenę." ("Generate Domain."). Aby zobaczyć działający przykład na Railway, odwiedź stronę [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Przydatne Zasoby | Nazwa odnośnika | Link | | --------------------------------------------- | -------------------------------------------------------------------- | | Odnośnik do pików Dockerfile | https://docs.docker.com/engine/reference/builder/ | | Odnośnik do plików "Compose file" w wersji 3 | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Odnośnik do Docker CLI | https://docs.docker.com/engine/reference/commandline/docker/ | | Odnośnik do Docker Compose CLI | https://docs.docker.com/compose/reference/ | | Wdrażanie aplikacji Next.js z obrazem Dockera | https://nextjs.org/docs/deployment#docker-image | | Next.js z Dockerem | https://benmarte.com/blog/nextjs-in-docker/ | | Przykład Next.js z Dockerem | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Stwórz obraz Dockera aplikacji Next.js | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/pl/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Deployment", layout: "docs", description: "Naucz się deploy'ować produkcyjną wersję swojej aplikacji T3.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/pl/deployment/netlify.md ================================================ --- title: Netlify description: Deployment na platformie Netlify layout: ../../../layouts/docs.astro lang: pl --- Netlify, to alternatywny provider do deploymentu z podobnym założeniem do Vercela. Po przykładowe repozytorium bazujące na tej stronie, zobacz [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify). ## Dlaczego hostować na Netlify'u? Racjonalizm podpowiadać może, iż Vercel ma znakomite wsparcie dla Next.js, ponieważ to właśnie on jest jego twórcą. Vercel ma duży biznes w zapewnianiu optymalnego doświadczenia i wydajności z Next.js na swojej platformie. W większości przypadków będzie to prawda i zmiana ścieżki z Vercelem nie będzie miała sensu. Często powtarzane jest także to, iż wiele funkcji z Next.js ma wsparcie jedynie na Vercelu. Chociaż prawdą jest to, iż nowe funkcje Next.js testowane i wspierane będą na Vercelu od razu po wydaniu, warto pamiętać, iż inni dostawcy, tacy jak Netlify, [szybko implementują i wdrażają wsparcie](https://www.netlify.com/blog/deploy-nextjs-13/) dla [stabilnych funkcji Next.js](https://docs.netlify.com/integrations/frameworks/next-js/overview/). Zależnie od tego, co dana aplikacja potrzebuje, każda strona od deploymentu posiada swoje wady i zalety - żadna strona nie jest w stanie rozwiązać wszystkich problemów. Przykładowo, Netlify zbudował swój własny [runtime Next.js](https://github.com/netlify/next-runtime) dla własnych Edge Functions (które działają na Deno Deploy) i [utrzymuje unikalny middleware pozwalający na dostęp i modyfikację odpowiedzi HTTP](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). > _UWAGA: Aby obserwować status niestabilnych funkcji Next.js, zobacz [Korzystanie z folderu `app` od Next 13 na Netlify](https://github.com/netlify/next-runtime/discussions/1724)._ ## Konfiguracja Projektu Jest wiele sposobów na konfigurację sposobu budowania aplikacji bezpośrednio poprzez narzędzie CLI od Netlify, czy też dashboard Netlify. Choć nie jest to wymagane, polecamy stworzyć i dołączyć plik [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Zapewnia to, iż system budowania sklonowanych i zforkowanych wersji twojego projektu będzie łatwiejszy w odwzorowaniu. ```toml [build] command = "next build" publish = ".next" ``` ## Korzystanie z Dashboardu Netlify 1. Wrzuć swój kod do repozytorium GitHuba i zarejestruj się na stronie [Netlify](https://app.netlify.com/signup). Po stworzeniu konta, kliknij na **Add new site** (dodaj nową stronę) a następnie na **Import an existing project** (zaimportuj istniejący projekt). ![Nowy projekt na Netlify](/images/netlify-01-new-project.webp) 2. Połącz swojego dostawcę Gita. ![Importowanie repozytorium](/images/netlify-02-connect-to-git-provider.webp) 3. Wybierz repozytorium swojego projektu. ![Wybierz repozytorium swojego projektu](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify wykryje, czy posiadasz plik `netlify.toml` i automatycznie skonfiguruje polecenia budowania i publikowany folder. ![Ustawienia budowania Next.js](/images/netlify-04-configure-build-settings.webp) 5. Kliknij na **Show advanced** (pokaż zaawansowane) a następnie na **New variable** (nowa zmienna), aby dodać swoje zmienne środowiskowe. ![Dodaj zmienne środowiskowe](/images/netlify-05-env-vars.webp) 6. Kliknij na **Deploy site** (opublikuj stronę), poczekaj aż budowanie się zakończy i otwórz swoją nową stronę. ## Korzystanie z nadzędzia CLI Netlify Aby wrzucić stronę z konsoli, musisz najpierw opublikować swój projekt do repozytorium na GitHubie i [zainstalować narzędzie CLI od Netlify](https://docs.netlify.com/cli/get-started/). Możesz zainstalować `netlify-cli` jako zależność projektu, lub globalnie na swoim urządzeniu korzystając z polecenia: ```bash npm i -g netlify-cli ``` Aby przetestować lokalnie swój projekt, uruchom polecenie [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) i otwórz [`localhost:8888`](http://localhost:8888/), aby zobaczyć lokalnie odpaloną aplikację Netlify. ```bash ntl dev ``` Uruchom polecenie [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) aby skonfgurować swój projekt: ```bash ntl init ``` Zaimportuj zmienne środowiskowe swojego projektu z pliku `.env` poleceniem [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Wrzuć do sieci swój projekt korzystając z polecenia [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Aby zbudować projekt przed jego publikacją, będziesz musiał dodać flagę `--build`. Aby wrzucić projekt pod główny URL aplikacji, dodaj flagę `--prod`: ```bash ntl deploy --prod --build ``` Aby zobaczyć działający przykład na Netlify, odwiedź stronę [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/pl/deployment/vercel.md ================================================ --- title: Vercel description: Deployment na platformie Vercel layout: ../../../layouts/docs.astro lang: pl --- Polecamy wykonywać deployment twojej aplikacji na platformę [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Pozwala ona w bardzo prosty sposób wrzucać do sieci aplikacje Next.js. ## Konfiguracja Projektu Vercel prawdopodobnie automatycznie skonfiguruje za Ciebie komendy budowania i folder publikacji aplikacji. Jeśli jednak chcesz, możesz skonfigurować projekt tworząc plik [`vercel.json`](https://vercel.com/docs/project-configuration) zawierający poniższą treść. **Nie jest to wymagane przy większości projektów.** ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Korzystanie z Dashboardu Vercela 1. Po wrzuceniu kodu do repozytorium GitHuba, zarejestruj się na stronie [Vercela](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) z tym kontem GitHub i kliknij na **Dodaj Nowy Projekt**. ![Nowy projekt na stronie Vercel](/images/vercel-new-project.webp) 2. Importuj repozytorium z GitHuba z projektem. ![Importuj repozytorium](/images/vercel-import-project.webp) 3. Dodaj zmienne środowiskowe. ![Dodaj zmienne środowiskowe](/images/vercel-env-vars.webp) 4. Kliknij **Deploy**. Od teraz, kiedykolwiek wrzucisz zmianę w kodzie w repozytorium, Vercel automatycznie opublikuje twoją aplikację! ## Korzystanie z Vercel CLI Aby wykonać deploy z konsoli, musisz najpierw [zainstalować Vercel CLI globalnie](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Uruchom komendę [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) aby opublikować swój projekt. ```bash vercel ``` Dołącz `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` aby dodać zmienne środowiskowe, jakie jak link do bazy danych. Użyj `--yes`, jeżeli chcesz pominąć pytania podczas publikowania projektu i pozostawić ustawienia domyślne dla każdego z nich. ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` Po pierwszym opublikowaniu, komenta ta publikować będzie do gałęzi poglądowej. Będziesz musiał dołączyć `--prod`, aby wrzucić zmiany bezpośrednio do aktywnej strony. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/pl/examples.mdx ================================================ --- title: Przykłady description: Przykłady różnych aplikacji na żywo layout: ../../layouts/docs.astro lang: pl isMdx: true --- import Callout from "../../components/docs/callout.tsx"; import Form from "../../components/docs/exampleOptionForm.astro"; Możesz wypróbować różne kombinacje technologii, które oferuje create-t3-app. Nie można wybrać `prisma` i `drizzle` jednocześnie. Niektóre funkcje mogą nie działać, chyba że utworzysz plik env ================================================ FILE: www/src/pages/pl/faq.md ================================================ --- title: FAQ description: Najczęściej zadawane pytania dotyczące Create T3 App layout: ../../layouts/docs.astro lang: pl --- Tu znajdziesz najczęściej zadawane pytania dotyczące Create T3 App. ## Co dalej? Jak mam napisać aplikację? Staramy się, aby projekt ten był jak najprostszy - możesz zacząć już korzystać z zawartego w nim szablonu a następnie stopniowo dodawać potrzebne Ci rzeczy. Jeżeli nie znasz poszczególnych technologi użytych w projekcie, skorzystaj z odnośników do odpowiednich stron z dokumentacjami. Jeżeli dalej nie jesteś co do nich pewien, możesz dołączyć do naszego [serwera Discord](https://t3.gg/discord) i poprosić o pomoc. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) - [Drizzle](https://orm.drizzle.team/docs/overview) ## Jak sprawić, by aplikacja była ciągle aktualna? Create T3 App to narzędzie do tworzenia szablonu, a nie framework. Oznacza to, że po inicjalizacji aplikacji jest ona już twoja. Nie istnieje żadne narzędzie CLI do wykorzystania po instalacji aplikacji, które utrzymałoby twoją aplikację aktualną. Jeżeli chcesz być na bieżąco z wprowadzanymi przez nas zmianami do szablonu, możesz [włączyć powiadomienia nowych wersji](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository) dla naszego repozytorium. Pamiętaj jednak, że wprowadzanie każdej zmiany z naszego szablonu nie jest wcale wymagane. ## Jakie zasoby do nauki są dostępne? Poniższe elementy to jedne z najlepszych zasobów dla stacka T3, jednak społeczność oraz [Theo](https://youtu.be/rzwaaWH0ksk?t=1436) polecają Ci zacząć po prostu z niego korzystać. W ten sposób podczas pisania aplikacji zdobędziesz potrzebną wiedzę. Jeżeli zastanawiasz się nad korzystaniem z Create T3 App, mogłeś już używać poszczególnych jego składowych w przeszłości. W takim przypadku spróbuj wskoczyć na głęboką wodę i nauczyć się innych jego części po drodze! Zdajemy sobie sprawę z tego, iż opisany wyżej sposób nie jest dla każdego najlepszy. Jeżeli więc w dalszym ciągu czujesz potrzebę skorzystania z innych źródeł (lub jeżeli nie jesteś wystarczająco pewny siebie / czujesz się przytłoczony stackiem), sprawdź te poradniki dotyczące Create T3 App (pamiętaj, że są one w języku angielskim): ### Artykuły - [Zbuduj aplikację full stack z Create T3 App](https://www.nexxel.dev/blog/ct3a-guestbook) - [Pierwsze spojrzenie na Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrowanie aplikacji T3 do Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Integrowanie Stripe'a z Aplikacją T3](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Filmy - [T3 Stack Tutorial - FROM 0 TO PROD FOR $0 (Next.js, tRPC, TypeScript, Tailwind, Prisma & More)](https://www.youtube.com/watch?v=YkOSUVzOAA4) **(recommended)** - [Jack Herrington - Zbuduj aplikację do sporządzania notatek z T3 Stackiem](https://www.youtube.com/watch?v=J1gzN1SAhyM) - [Zbuduj klon Twittera z T3 Stackiem - tRPC, Next.js, Prisma, Tailwind oraz Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [Zbuduj blog z T3 Stackiem - tRPC, TypeScript, Next.js, Prisma i Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Zbuduj live chat z T3 Stackiem - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [T3 Stack - Jak go zbudowaliśmy](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [Przegląd Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Dlaczego w projekcie są pliki `.js`? Tak jak opisano w [aksjomacie #3](/pl/introduction#typesafety-nie-jest-opcjonalne), traktujemy typesafety za pierwszorzędną rzecz. Niestety nie wszystkie frameworki i pluginy posiadają wsparcie do TypeScripta, dlatego też niektóre pliki konfiguracyjne muszą mieć powyższe rozszerzenie. Staramy się podkreślić, iż pliki te korzystają z TypeScripta nie bez powodu. Wyraźnie określamy rozszerzenia plików jako `cjs` lub `mjs`, zależnie od wsparcia przez daną bibliotekę. Dodatkowo, wszystkie pliki `.js` w naszym projekcie są w dalszym ciągu sprawdzane pod kątem poprawności typów - korzystamy do tego opcji `checkJs` w kompilatorze (tsconfig). ## Mam problem z dodaniem i18n do aplikacji. Czy istnieje jakiś projekt, do którego mógłbym się odnieść przy jej budowaniu? Zdecydowaliśmy się nie umieszczać i18n w `create-t3-app`, ponieważ jest to bardzo kontrowersyjny temat i istnieje wiele sposobów, aby element ten zaimplementować. Jeżeli jednak nie wiesz jak wygląda poprawna interpretacja internacjonalizacji i chciałbyś zobaczyć przykładowy projekt, sprawdź [to repozytorium](https://github.com/juliusmarminge/t3-i18n) - ukazuje ono jak możesz dodać i18n do aplikacji T3 korzystając przy tym z [next-i18next](https://github.com/i18next/next-i18next). ## Dlaczego korzystamy z folderu `/pages` a nie `/app` z Next.js 13? Tak jak opisano w [aksjomacie #2](/pl/introduction#bleed-responsibly-korzystaj-rozważnie-z-nowych-technologii), kochamy technologie "bleeding edge", jednak ważna jest dla nas stabilność. Ciężko jest zmienić cały `router` w aplikacji ([not a great place to bleed](https://youtu.be/mnwUbtieOuI?t=1662)). Folder `/app` to [wgląd na przyszłość](https://youtu.be/rnsC-12PVlM?t=818), jednak nie jest on gotowy na wersję produkcyjną. API jest w becie i oczekiwać można zmian wpływających na działanie całej aplikacji (niekoniecznie pozytywnie!). Żeby poznać listę wspieranych, zaplanowanych i tych, nad którymi prace trwają funkcji, odwiedź [dokumentację Next.js beta](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/pl/folder-structure-pages.mdx ================================================ --- title: Struktura projektu (Pages) description: Struktura projektu nowej aplikacji T3 layout: ../../layouts/docs.astro lang: pl isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Wybierz paczkę, aby zobaczyć strukturę folderów nowo utworzonej z nią aplikacji. Poniżej znajdziesz opis każdego elementu.
### `prisma` Folder `prisma` zawiera plik `schema.prisma`, który używany jest do konfiguracji połączenia z bazą danych i struktury tej bazy. Jest to także miejsce, w którym przechowywać można pliki migracji i/lub skrypty "seedowania" (jeżeli z nich korzystasz). Po więcej informacji, zobacz [korzystanie z Prismy](/pl/usage/prisma).
### `public` Folder `public` zawiera zasoby statyczne, które "serwowane" są przez serwer webowy. Plik `favicon.ico` jest przykładem jednego z takich zasobów.
### `src/env` Używany do walidacji zmiennych środowiskowych i definiowania ich typów - zobacz [zmienne środowiskowe](/pl/usage/env-variables).
### `src/pages` Folder `pages` zawiera wszystkie strony aplikacji Next.js. Plik `index.tsx` w głównym folderze zawierającym folder `/pages` to strona główna twojej aplikacji. Plik `_app.tsx` dostarcza aplikacji odpowiednie "providery". Po więcej informacji, zobacz [dokumentację Next.js](https://nextjs.org/docs/basic-features/pages).
#### `src/pages/api` Folder `api` zawiera wszystkie route'y twojej aplikacji Next.js. Po więcej informacji o api route'ach, zobacz [ich dokumentację od Next.js](https://nextjs.org/docs/api-routes/introduction).
#### `src/pages/api/auth/[...nextauth].ts` Plik `[...nextauth].ts` jest slug routem używanym do uwierzytelniania ("authentication") poprzez NextAuth.js. Pomaga on przetwarzać zapytania o uwierzytelnianie. Po więcej informacji o kolejno NextAuth.js i slug route'ach, zobacz [korzystanie z NextAuth.js](/pl/usage/next-auth) oraz [dokumentację dynamicznych route'ów Next.js](https://nextjs.org/docs/routing/dynamic-routes).
#### `src/pages/api/trpc/[trpc].ts` Plik `[trpc].ts` to punkt wejścia dla API (tRPC). Pomaga on przetwarzać zapytania wykonane z tRPC. Po więcej informacji o kolejno tRPC i slug route'ach, zobacz [korzystanie z tRPC](/pl/usage/trpc#-pagesapitrpctrpcts) oraz [dokumentację dynamicznych route'ów Next.js](https://nextjs.org/docs/routing/dynamic-routes).
### `src/server` Folder `server` używany jest do stworzenia wyraźnego podziału między kodem działającym na serwerze, a tym po stronie klienta.
### `src/server/auth.ts` Jest to główny punkt wejścia dla systemu uwierzytelniania po stronie serwera. Ustawiamy tutaj [opcje konfiguracji](/pl/usage/next-auth), [dostosowujemy model użytkownika](/pl/usage/next-auth#inclusion-of-userid-on-the-session) oraz zapewniamy narzędzia DX dla uwierzytelniania, takie jak funkcja pobierania sesji użytkownika po stronie serwera. Po więcej informacji, zobacz [korzystanie z NextAuth.js](/pl/usage/next-auth#korzystanie-z-trpc).
#### `src/server/db.ts` Plik `db.ts` jest używany do tworzenia instancji klienta Prismy z globalnym zakresie. Po więcej informacji, zobacz [korzystanie z Prismy](/pl/usage/prisma#prisma-client) oraz [polecane korzystanie z Prismy wraz z Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices).
### `src/server/api` Folder `api` zawiera kod tRPC po stronie serwera.
#### `src/server/api/routers` Folder `routers` zawiera wszystkie "sub-routery" tRPC.
#### `src/server/api/routers/example.ts` Plik `example.ts` zawiera przykład routera tRPC wykorzystującego funkcję pomocniczą `publicProcedure` do zaprezentowania, jak stworzyć publiczny route tRPC. Zależnie od wybranych paczek, router ten zawiera więcej lub mniej route'ów, aby najlepiej zaprezentować jego sposób użycia.
#### `src/server/api/trpc.ts` Plik `trpc.ts` jest głównym plikiem konfiguracyjnym dla tRPC po stronie serwera. W pliku tym: 1. Definiujemy kontekst używany w zapytaniach tRPC. Po więcej informacji, zobacz [korzystanie z tRPC](usage/trpc#-serverapitrpcts). 2. Eksportujemy funkcje pomocnicze helperów. Po więcej informacji, zobacz [korzystanie z tRPC](usage/trpc#-serverapitrpcts).
#### `src/server/api/root.ts` Plik `root.ts` jest używany do scalenia routerów tRPC i eksportowania ich jako pojedynczego routera, a także eksportowania jego definicji typów. Po więcej informacji, zobacz [korzystanie z tRPC](/pl/usage/trpc#-serverapirootts).
### `src/styles` Folder `styles` zawiera globalne style aplikacji.
### `src/utils` Folder `utils` jest używany do przechowywania często powielanych funkcji pomocniczych.
#### `src/utils/api.ts` Plik `api.ts` to plik wejścia tRPC od strony klienta. Po więcej informacji, zobacz [korzystanie z tRPC](/pl/usage/trpc#-utilsapits).
### `.env` Plik `.env` jest używany do przechowywania zmiennych środowiskowych. Po więcej informacji, zobacz [Zmienne Środowiskowe](usage/env-variables). Plik ten **nie** powinien być commitowany do historii gita.
### `.env.example` Plik `.env.example` pokazuje przykładowe zmienne środowiskowe bazując na wybranych bibliotekach. Plik ten powinien być commitowany do historii gita.
### `.eslintrc.cjs` Plik `.eslintrc.cjs` jest używany do konfigurowania ESLinta. Po więcej informacji, zobacz [dokumentację ESLinta](https://eslint.org/docs/latest/user-guide/configuring/configuration-files).
### `next-env.d.ts` Plik `next-env.d.ts` gwarantuje korzystanie z typów Next.jsa przez kompilator TypeScripta. \*\*Nie powinieneś go usuwać ani modyfikować, ponieważ może on zmienić się w każdym momencie. Po więcej informacji, zobacz [dokumentację Next.js](https://nextjs.org/docs/basic-features/typescript#existing-projects).
### `next.config.mjs` Plik `next.config.mjs` jest używany do konfigurowania Next.js. Po więcej informacji, zobacz [dokumentację Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction). Uwaga: Rozszerzenie .mjs pozwala korzystać z importów ESM.
### `postcss.config.js` Plik `postcss.config.js` jest używany przez Tailwind PostCSS. Po więcej informacji, zobacz [dokumentację Tailwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss).
### `prettier.config.mjs` Plik `prettier.config.mjs` jest używany do konfigurowania Prettiera. Dołącza on plugin `prettier-plugin-tailwindcss` formatujący klasy CSS Tailwinda. Po więcej informacji, zobacz [post na blogu Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier).
### `tsconfig.json` Plik `tsconfig.json` jest używany do konfigurowania TypeScripta. Niektóre ustawienia zostały włączone (takie jak `strict mode`), aby zapewnić najlepsze użycie TypeScripta do Create T3 App i jej bibliotek. Po więcej informacji, zobacz [dokumentację TypeScripta](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) albo [korzystanie z TypeScripta](/pl/usage/typescript).
### `drizzle.config.ts` Plik `drizzle.config.ts` jest używany do konfigurowania Drizzle kit. Po więcej informacji przeczytaj [ich dokumentację](https://orm.drizzle.team/kit-docs/config-reference)
================================================ FILE: www/src/pages/pl/installation.mdx ================================================ --- title: Instalacja description: Instrukcje instalacji dla Create T3 App. layout: ../../layouts/docs.astro lang: pl isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Aby zacząć używać szablonu `create-t3-app`, uruchom którąkolwiek z poniższych trzech komend i odpowiedz na pytania w konsoli: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` Po tym, jak szablon aplikacji zostanie utworzony, sprawdź [pierwsze kroki](/pl/usage/first-steps) aby zacząć budować swoją nową aplikację. ## Zaawansowane użycie | Opcja/Flaga | Opis | | ----------------- | ------------------------------------------------------------------------------------ | | `[dir]` | Argument z nazwą dla projektu | | `--noGit` | Wyraźnie poinformuj narzędzie, aby nie inicjalizować nowego repozytorium w projekcie | | `-y`, `--default` | Pomiń CLI i stwórz nową aplikację z wszystkimi wybranymi opcjami | | `--noInstall` | Wygeneruj projekt bez instalowania zależności (npm) | ## Eksperymentalne użycie Z powodu naszej integracji CI, posiadamy eksperymentalne flagi pozwalające skorzystać z szablonu bez żadnych informacji w konsoli. Jeżeli przemawia do Ciebie któraś z poniższych opcji, możesz z jakiejś flagi skorzystać. Pamiętaj, że są to opcje eksperymentalne - mogą się one zmienić bez względu na system wersji semver. | Flaga | Opis | | ------------ | ------------------------------------------- | | `--CI` | Poinformuj narzędzie, że jesteś w trybie CI | | `--trpc` | Zawrzyj tRPC w projekcie | | `--prisma` | Zawrzyj Prisma w projekcie | | `--nextAuth` | Zawrzyj NextAuth.js w projekcie | | `--tailwind` | Zawrzyj Tailwind CSS w projekcie | Jeżeli nie podasz flagi `CI`, pozostałe flagi nie zostaną użyte. Nie musisz wyraźnie wskazywać paczek, z których nie chcesz korzystać. Jeżeli jednak wolisz jasno wskazać czego używasz, możesz podać wartość `false` - np. `--nextAuth false`. ### Przykład Poniższy przykład stworzy aplikację T3 z bibliotekami tRPC oraz Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/pl/introduction.md ================================================ --- title: Wstęp description: Wstęp do stacka T3 layout: ../../layouts/docs.astro lang: pl ---
## T3 Stack _"T3 Stack"_, to stack do web developmentu stworzony przez [Theo](https://twitter.com/t3dotgg) i mający na celu prostotę, modularność oraz typesafety całego projektu. Jego głównymi elementami są [**Next.js**](https://nextjs.org/) oraz [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) również jest prawie zawsze dołączony. Jeżeli budujesz cokolwiek wspólnego z backendem, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/) oraz [**NextAuth.js**](https://next-auth.js.org/) mogą być dobrymi dodatkami. Mogłeś dostrzec już, iż jest tu... dużo elementów. Jest to celowe. Zmieniaj elementy jak tylko chcesz - stack ten jest tak modularny, jak to tylko możliwe. ## A więc... co to Create T3 App? Jakiś szablon? Może? Create T3 App to narzędzie CLI zbudowane przez doświadczonych deweloperów stacka T3 aby usprawnić zakładanie w nim nowych, modularnych projektów. Oznacza to, iż każdy element jest opcjonalny a "szablon" generowany jest bazując na twoich szczegółowych potrzebach. Po niezliczonych projektach i wielu latach w tej technologii, nabyliśmy wiele opinii i wewnętrznych doświadczeń. Zrobiliśmy, co tylko się dało, aby zawrzeć je w naszym CLI. **NIE** jest to szablon all-inclusive. **Oczekujemy** od Ciebie trochę wkładu, dodając biblioteki rozwiązujące problemy, które posiada **TWOJA** aplikacja. Nie chcemy zapisywać rozwiązań na bardziej specyficzne problemy, takie jak state management lub deployment. [Mamy jednak tutaj parę rekomendacji](/pl/other-recs). ## Aksjomaty T3 Będziemy z tobą szczerzy - jest to _kontrowersyjny_ projekt. Posiadamy garść założeń dotyczących budowania aplikacji i traktujemy je jako bazę naszych decyzji. ### Rozwiązuj Problemy Łatwo jest wpaść w pułapkę "dodawania wszystkiego" - wyraźnie nie chcemy tego robić. Wszystko dodane do Create T3 App powinno rozwiązywać konkretny problem który istnieje w dołączonych technologiach. Znaczy to, że nie dodamy rzeczy, takich jak biblioteki do state managementu (`zustand`, `redux`) ale dodamy elementy takie jak NextAuth.js, Prisma i tRPC. ### "Bleed Responsibly" (Korzystaj Rozważnie z Nowych Technologii) Kochamy technologie "bleeding edge". Uzyskana prędkość oraz szczerze mówiąc, zabawa, jaka wynika z ich zastosowania - jest naprawdę super. Uważamy jednak za ważne odpowiedzialne korzystanie z powyższych elementów. Oznacza to, iż nie ⛔️ postawimy na nowy rodzaj bazy danych (SQL jest dobry!). Z radością ✅ stawiamy jednak na tRPC - są to tylko funkcje, które łatwo można zamienić na coś innego. ### Typesafety NIE Jest Opcjonalne Celem Create T3 App jest zapewnienie jak najszybszego sposobu na stworzenie nowego projektu full-stack z pełnym **typesafety**. Typesafety traktujemy poważnie - pozwala ono poprawić naszą produktywność i pomaga nam w dostarczaniu mniejszej ilości bugów. Jakakolwiek decyzja, która naraża naturę typesafety jest decyzją, która powinna zostać podjęta w innym projekcie. ================================================ FILE: www/src/pages/pl/other-recs.md ================================================ --- title: Inne Rekomendacje description: Biblioteki i usługi, które polecamy dla wielu projektów layout: ../../layouts/docs.astro lang: pl --- Zdajemy sobie sprawę z tego, że biblioteki które zawiera Create T3 App nie są lekarstwem na wszystko. Polecamy zaczynać projekty z rzeczami, które są już dołączone - przyjdzie jednak czas, kiedy będziesz musiał dodać swoje paczki. Tylko ty wiesz, czego twój projekt potrzebuje. Polecamy przejrzeć jednak poniższą listę naszych rekomendacji. Są to rekomendacje pojedynczych kontrybutorów i nie powinny być postrzegane jako "oficjalne" sposoby na rowiązywanie danych problemów od zespołu Create T3 App czy też T3-OSS. _**Zrób swój research, szczególnie zanim skorzystasz z płatnych usług**_. ## State Management _**Informacja**_: Biblioteki do state managementu mogą być niezłe, jednak często nie są one potrzebne. Hooki od tRPC (a dokładniej od React Query) powinny być w stanie zarządzać statem od strony serwera. W przypadku state'a klienta, zacznij od hooka `useState` oferowanego przez Reacta i skorzystaj z poniższych opcji jeśli będziesz potrzebować czegoś więcej. ### Zustand **Aby nigdy więcej nie używać Reduxa** "Nowoczesny, prosty Redux", o którym nie wiedziałeś, że go potrzebujesz. [Poimandresowi](https://github.com/pmndrs) zawsze można zaufać. Zbudować możesz wszystko, począwszy od aplikacji do rozmów wideo a skończywszy na grach czy też serwerach. - [Strona główna Zustanda](https://zustand-demo.pmnd.rs/) - [GitHub Zustanda](https://github.com/pmndrs/zustand) ### Jotai **Aby nigdy więcej nie używać kontekstu** Przy bardziej "niepodzielnym" podejściu, ciężko jest pobić Jotai. Także wykonana przez [Poimandres](https://github.com/pmndrs), Jotai pozwala ci definiować singletony które sprawiają wrażenie, jakby były globalnym hookiem `useState`. Świetna opcja do zachowań wymagających zachowania ze statem i takich, które jeszcze nie potrzebują maszyny state'a. - [Strona główna Jotai](https://jotai.org/) - [GitHub Jotai](https://github.com/pmndrs/jotai) ## Biblioteki Komponentów Wiele aplikacji wymaga tej samej garści komponentów - przełączników, dropdownów czy też modali. Biblioteki te zaopatrzą cię w znakomite i przystępne komponenty, z których możesz korzystać, i które możesz dostosowywać jak tylko chcesz. ### Biblioteki Komponentów Bez Narzuconych Stylów Znane także jako biblioteki "headless", zapoatrzą cię w znakomite, przystępne komponenty bez narzuconych stylów, które będziesz mógł dostosować jak tylko chcesz. Tu znajdziesz kilka rekomendacji. - [Radix UI](https://www.radix-ui.com/) zaopatrzy cię w olbrzymi zestaw praktycznych i przystępnych prymitywów, które będziesz mógł dostosować do swoich potrzeb z czystym CSSem lub Tailwind CSS. - [Headless UI](https://headlessui.com/) wykonane przez zespół stojący za Tailwind CSS, przekazuje ci przystępne komponenty bez narzuconych stylów, które bezproblemowo integrują się z Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) zaopatrzy cię w przystępne prymitywy do UI dla twojego design systemu. ### Biblioteki Komponentów Z Gotowymi Stylami **W przypadku, kiedy chcesz żeby twoja aplikacja wyglądała OK** Czasem budując projekt, chcesz aby Ui wyglądało po prostu przyzwoicie. Do projektów takich jak panele administratora i podobnych, każda z tych bibliotek da sobie doskonale radę. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **Gdy budujesz własną bibliotekę UI** Zbuduj bibliotekę UI z różnymi kolorami, rozmiarami, wariantami itp. Gdy twój projekt osiągnie skalę na tyle dużą, że będziesz musiał skorzystać z ustandaryzowanego zestawu komponentów z wieloma wariantami (korzystając przy tym z Tailwind CSS), CVA stanie się doskonałym narzędziem. - [GitHub Class Variance Authority](https://github.com/joe-bell/cva) ## Animacje Rekomendacje na sytuacje, kiedy w aplikacji potrzebować będziesz animacji :) ### AutoAnimate **Aby animacje były pojedynczą linią kodu** Wiele bibliotek od animacji stara się rozwiązać każdy możliwy problem, stając się przy tym uciężałe i niezbyt praktyczne. AutoAnimate to narzędzie niewymagające konfiguracji, które pozwoli ci uzyskać znaczącą poprawę w UX twojej aplikacji bez dodatkowego wysiłku. - [Strona główna AutoAnimate](https://auto-animate.formkit.com/) - [GitHub AutoAnimate](https://github.com/formkit/auto-animate) - [Snippet komponentu AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **Złożone animacje z deklaratywnym kodem** Framer Motion pozwala Ci w prosty sposób i z deklaratywną składnią napisać mało kodu, a w zamian zaopatrzy twoją aplikację w złożone animacje, czy nawet reakcje na gesty. - [Strona główna Framer Motion](https://framer.com/motion) - [Dokumentacja Framer Motion](https://www.framer.com/docs/) ## Wdrażanie, Infrastruktura, Bazy Danych i CI ### Vercel **Aby zhostować swoją aplikację** Vercel zamienił prawdziwe piekło deploymentu aplikacji w łatwą integrację z GitHubem. Przeskalowaliśmy się do setek tysięcy użytkowników bez problemu. Napędzany przez AWS, o wiele lepszy interfejs :) - [Strona główna Vercel](https://vercel.com/) - [Poradnik deploymentu aplikacji T3 z Vercelem](/pl/deployment/vercel) ### PlanetScale **Bazy danych bez obaw** PlanetScale to najlepsza platforma "serverless" do baz danych, z której kiedykolwiek korzystaliśmy. Znakomicie się skaluje, dostarcza świetny DX, zawiera doskonałe ceny. Jeżeli korzystasz z SQLa (i miejmy nadzieję z Prismy), ciężko jest tą usługę pobić. - [Strona główna PlanetScale](https://planetscale.com/) ### Railway **Aby hostować swoją infrastrukturę** "Nowoczesne Heroku". Najłatwiejszy sposób aby uzyskać prawdziwy działający serwer. Jeżeli Vercel i PlanetScale nie są dla Ciebie wystarczające, prawdopodobnie Railway będzie. Połącz go z repozytorium GitHuba i gotowe. - [Strona główna Railway](https://railway.app/) ### Upstash **Redis dla aplikacji "serverless"** Kochamy Prismę i PlanetScale, ale niektóre projekty wymagają bardziej wydajnego rozwiązania. Upstash pozwala na prędkość bliską tej przy bezpośrednim odczycie z pamięci w twoim projekcie "serverless", przy tym nie obciążając Cię zarządzaniem infrastruktury i skalowania. - [Strona główna Upstash](https://upstash.com/) ### Pusher **WebSockety "serverless"** Jeżeli WebSockety stanowią główną część projektu, możesz chcieć rozważyć bardziej tradycyjny backend taki jak [Fastify](https://www.fastify.io/) ([który również działa z tRPC!](https://trpc.io/docs/v10/fastify)). Jesli jednak chcesz szybko dodać WebSockety do aplikacji T3, Pusher to doskonały wybór. - [Strona głowna Pushera](https://pusher.com/) ### Soketi Soketi to prosta w użyciu alternatywa do Pushera, którą hostuje się samemu. Jest w pełni kompatybilna z SDK Pushera, z którego możesz korzystać, aby połączyć się z serwerem. Wersja "serverless" Soketi jest w becie. - [Strona główna Soketi](https://soketi.app) - [GitHub Soketi](https://github.com/soketi/soketi) ## Statystyki Stron (Analytics) Budując aplikację, dane użytkowników są bardzo cenne. Znajdziesz tu parę serwisów do ich zbierania, które polecamy. ### Plausible Potrzebujesz zbierać statystyki? Plausible to jedna z najszybszych opcji aby to zrobić. Bardzo minimalna. Posiada nawet [prosty plugin dla Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Strona główna Plausible](https://plausible.io/) ### Umami Umami to prosta w użyciu, open-source, szybka i skupiająca się na prywatności alternatywa dla Google Analytics, którą hostuje się samemu. Możesz go zdeploy'ować dzięki serwisom, takim jak Vercel, Railway itp., korzystając przy tym z PlanetScale jako bazy danych. - [Strona główna Umami](https://umami.is/) - [GitHub Umami](https://github.com/umami-software/umami) ## Inne ### Next Bundle Analyzer Określenie co znajdzie się w finalnym buildzie aplikacji może czasem stanowić problem. Next Bundle Analyzer to łatwy sposób na wizualizację i analizę kodu JavaScript, który zostaje wygenerowany. - [@next/bundle-analyzer na npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/pl/t3-collection.mdx ================================================ --- title: Kolekcja T3 description: Ciekawe projekty open source oraz firmy korzystające ze stacka T3 layout: ../../layouts/docs.astro lang: pl isMdx: true --- Zrobiłeś projekt korzystając z T3 stacka i chcesz się nim podzielić? Dodaj go do listy! ## Aplikacje open source wykonane z T3 Stackiem import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; ## Firmy korzystające ze stacka T3 Bardzo chcielibyśmy poznać firmy korzystające ze stacka T3 do budowania swoich aplikacji. Czy twoja firma korzysta ze stacka T3 i chciałaby się tym podzielić? Dodaj tą informację do listy poniżej! _Posiadasz ciekawy projekt korzystający ze stacka T3? Zrób [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) i dodaj go tutaj!_ ================================================ FILE: www/src/pages/pl/usage/env-variables.md ================================================ --- title: Zmienne Środowiskowe description: Jak zacząć z Create T3 App layout: ../../../layouts/docs.astro lang: pl --- Create T3 App korzysta z paczki [Zod](https://github.com/colinhacks/zod) w celu walidacji twoich zmiennych środowiskowych podczas runtime'u _oraz_ budowania aplikacji. Dołączane są z tego powodu dodatkowe narzędzia w pliku `src/env.js`. ## env.js _TLDR; Jeżeli chcesz dodać nową zmienną środowiskową, musisz dodać ją zarówno do pliku `.env`, jak i zdefiniować jej walidator w pliku `src/env.js`._ Plik ten podzielony jest na dwie części - schemat zmiennych i wykorzystywanie obiektu `process.env`, jak i logika walidacji. Logika ta nie powinna być zmieniana. ```ts:env.js const server = z.object({ NODE_ENV: z.enum(["development", "test", "production"]), }); const client = z.object({ // NEXT_PUBLIC_CLIENTVAR: z.string(), }); const processEnv = { NODE_ENV: process.env.NODE_ENV, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }; ``` ### Schemat Dla Serwera Zdefiniuj tutaj zmienne środowiskowe dla serwera. Koniecznie **nie** prefixuj tutejszych kluczy `NEXT_PUBLIC_`, aby przypadkiem nie ujawnić ich do klienta. ### Schemat Dla Klienta Zdefiniuj tutaj zmienne środowiskowe dla klienta. Aby ujawnić zmienne dla klienta dodaj prefix `NEXT_PUBLIC`. Jeżeli tego nie zrobisz, walidacja nie zadziała, pomagając ci w wykryciu niewłaściwej konfiguracji. ### Obiekt `processEnv` Wykorzystaj destrukturyzację obiektu `process.env`. Potrzebny jest nam obiekt, który parse'ować możemy z naszymi schematami Zoda, a z powodu sposobu w jaki Next.js przetwarza zmienne środowiskowe, nie możesz destrukturyzować obiektu `process.env` tak jak zwykłego obiektu - trzeba to zrobić manualnie. TypeScript zapewni poprawność destrukturyzacji obiektu i zapobiegnie sytuacji, w której zapomnisz o jakimś kluczu. ```ts // ❌ To nie zadziała, musimy ręcznie "rozbić" `process.env` const schema = z.object({ NEXT_PUBLIC_WS_KEY: z.string(), }); const validated = schema.parse(process.env); ``` ### Logika Walidacji _Dla zainteresowanego czytelnika:_
Zaawansowane: Logika walidacji W zależności od środowiska (serwer lub klient) walidujemy albo oba schematy, albo tylko schemat klienta. Oznacza to, iż nawet jeśli zmienne środowiskowe serwera nie będą zdefiniowane, nie zostanie wyrzucony błąd walidacji - możemy więc mieć jeden punkt odniesienia do naszych zmiennych. ```ts:env.js const isServer = typeof window === "undefined"; const merged = server.merge(client); const parsed = isServer ? merged.safeParse(processEnv) // <-- na serwerze, sprawdź oba schematy : client.safeParse(processEnv); // <-- na kliencie, sprawdź tylko zmienne klienta if (parsed.success === false) { console.error( "❌ Invalid environment variables:\n", ...formatErrors(parsed.error.format()), ); throw new Error("Invalid environment variables"); } ``` Następnie korzystamy z obiektu proxy, aby wyrzucać błędy, jeśli chcesz skorzystać z serwerowych zmiennych środowiskowych na kliencie. ```ts:env.js // proxy pozwala na zmianę gettera export const env = new Proxy(parsed.data, { get(target, prop) { if (typeof prop !== "string") return undefined; // na kliencie pozwalamy jedynie na zmienne NEXT_PUBLIC_ if (!isServer && !prop.startsWith("NEXT_PUBLIC_")) throw new Error( "❌ Attempted to access serverside environment variable on the client", ); return target[prop]; // <-- w przeciwnym razie, zwróć wartość }, }); ```
## Korzystanie Ze Zmiennych Środowiskowych Jeżeli chcesz skorzystać ze swoich zmiennych środowiskowych, możesz zaimportować je z pliku `env.js` i skorzystać z nich tak, jak normalnie byłoby to możliwe. Jeżeli zaimportujesz obiekt ten na kliencie i spróbujesz skorzystać ze zmiennych serwera, wystąpi błąd runtime. ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` jest w pełni typesafe i zapewnia autouzupełnianie const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ Wyrzuci to błąd runtime const dbUrl = env.DATABASE_URL; // ✅ To jest ok const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example Ponieważ plik `.env` nie jest wrzucany na system kontroli wersji, dołączamy także plik `.env.example`, w którym - jesli chcesz - możesz zawrzeć kopię pliku `.env` z usuniętymi secretami. Nie jest to wymagane, jednak polecamy trzymać aktualną kopię przykładowego pliku, aby ułatwić potencjalnym kontrybutorom rozpoczęcie pracy w ich środowisku. Niektóre frameworki i narzędzia do budowania, takie jak Next.js, zalecają przechowywanie sekretnych wartości w pliku `.env.local` i commitowanie plików `.env` do projektu. Nie jest to przez nas jednak rekomendowane, ponieważ może to łatwo prowadzić do przypadkowego ujawnienia tych wartości. Polecamy natomiast przechowywanie sekretnych wartości w pliku `.env`, trzymanie pliku tego w `.gitignore` i commitowanie jedynie plików `.env.example`. ## Dodawanie Zmiennych Środowiskowych Aby upewnić się, że twój projekt nie zbuduje się bez wymaganych zmiennych środowiskowych, będziesz musiał dodać nową zmienną w **dwóch** miejscach: 📄 `.env`: Wprowadź swoją zmienną środ. tak, jak to zwykle robisz (np. `KLUCZ=WARTOŚĆ`) 📄 `env.js`: Dodaj odpowiadającą jej logikę walidacji definiując schemat Zod, np. `KLUCZ: z.string()`. Następnie wykorzystaj obiekt `process.env` w `processEnv`, np. `KEY: process.env.KEY`. Opcjonalnie możesz zaktualizować plik `.env.example`: 📄 `.env.example`: Wprowadź swoją zmienną środowiskową, upewnij się jednak że nie nie posiada ona wartości, która jest sekretna, np. `KLUCZ=WARTOŚĆ` lub `KLUCZ=` ### Przykład _Chcę dodać mój token do API Twittera jako zmienną środowiskową po stronie serwera_ 1. Dodaj zmienną środ. do pliku `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Dodaj zmienną środowiskową do pliku `env.js`: ```ts export const server = z.object({ // ... TWITTER_API_TOKEN: z.string(), }); export const processEnv = { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }; ``` 3. opcjonalnie: Dodaj zmienną środowiskową do `.env.example`. Usuń jednak token. ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/pl/usage/first-steps.md ================================================ --- title: Pierwsze Kroki description: Jak zacząć pracę z nową aplikacją T3 layout: ../../../layouts/docs.astro lang: pl --- Skorzytałeś właśnie z szablonu aplikacji T3 i jesteś gotowy, aby zacząć z nim pracę. Tu znajdziesz minimalny setup, jaki powinieneś wykonać, aby uruchomić aplikację. ## Baza Danych ### Prisma Jeżeli twoja aplikacja zawiera Prismę, koniecznie uruchom `npx prisma db push` z głównego folderu projektu. Komenda ta zsynchronizuje twój schemat Prismy z bazą danych i wygeneruje typy TypeScripta dla "Prisma Client" bazując na tym schemacie. Uwaga: po wygenerowaniu typów Prismy prawdopodobnie będziesz musiał [zrestartować serwer TypeScripta](https://tinytip.co/tips/vscode-restart-ts/), aby był on w stanie je wykryć. ### Drizzle Jeżeli twoja aplikacja zawiera Drizzle, sprawdź plik `.env` po instrukcje jak stworzyć swój `DATABASE_URL`. Po dodaniu `DATABASE_URL` do pliku `.env` uruchom `pnpm db:push` ( lub odpowiednik dla innych menedżerów pakietów) - dokonana zostanie synchronizacja schematów twojej bazy danych. ## Uwierzytelnianie Jeżeli twoja aplikacja zawiera NextAuth.js, posiadasz przygotowany już przez nas `DiscordProvider`. Jest to jeden z najprostszych providerów oferowanych przez NextAuth.js, w dalszym ciągu wymaga on jednak trochę setupu po twojej części. Oczywiście, jeżeli wolisz korzystać z innego, możesz użyć jednego z [wielu providerów](https://next-auth.js.org/providers/) oferowanych przez NextAuth.js. 1. Potrzebować będziesz konta Discord, więc utwórz je, jeśli jeszcze tego nie zrobiłeś. 2. Przejdź do strony https://discord.com/developers/applications i kliknij "New Appliction" w prawym górnym rogu. Nazwij ją i wyraź zgodę na warunki korzystania z serwisu. 3. Po stworzeniu aplikacji, przejdź do "Settings → OAuth2 → General". 4. Skopiuj "Client ID" i dodaj go do pliku `.env` pod kluczem `AUTH_DISCORD_ID`. 5. Kliknij "Reset Secret", skopiuj nowy secret i dodaj go do pliku `.env` pod kluczem `AUTH_DISCORD_SECRET`. 6. Kliknij "Add Redirect" i wpisz `http://localhost:3000/api/auth/callback/discord`. - Dla deploymentu w wersji produkcyjnej, podążaj za powyższymi krokami aby stworzyć nową aplikację Discord, ale tym razem zamień `http://localhost:3000` na URL, na który wrzucasz swój projekt. 1. Zapisz zmiany 2. Ustaw `AUTH_SECRET` w pliku `.env`. W wersji rozwojowej zadziała byle co, w wersji produkcyjnej zobacz uwagę w pliku `.env`, która mówi, jak wygenerować bezpieczny secret. Powinieneś być w stanie się zalogować. ## Konfiguracja Edytora Następujące rozszerzenia są przez nas polecane dla optymalnego DX'u. Poniższe linki dostarczają pluginy dla odpowiednich edytorów. - [Prisma Extension](https://www.prisma.io/docs/guides/development-environment/editor-setup) - [Tailwind CSS IntelliSense Extension](https://tailwindcss.com/docs/editor-setup) - [Prettier Extension](https://prettier.io/docs/en/editors.html) ## Następne Kroki - Jeżeli twoja aplikacja zawiera tRPC, sprawdź pliki `src/pages/index.tsx` i `src/server/api/routers/post.ts` aby dowiedzieć się, jak działają zapytania wykonywane przez tRPC. - Rozejrzyj się po dokumentacji Create T3 App oraz dokumentacji paczek, z których korzysta twój projekt. - Dołącz do naszego serwera [Discord](https://t3.gg/discord) i dodaj gwiazdkę na repozytorium [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/pl/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Korzystanie z technologii", layout: "docs", description: "Naucz się, jak korzystać z poszczególnych technologii w T3 Stacku.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/pl/usage/next-auth.md ================================================ --- title: NextAuth.js description: Korzystanie z NextAuth.js layout: ../../../layouts/docs.astro lang: pl --- Kiedy chcesz dodać system kont do swojej aplikacji Next.js, NextAuth.js to znakomite rozwiązanie. Pozwala ono wdrożyć złożone systemy bezpieczeństwa nie zmuszając Cię przy tym do pisania ich własnoręcznie. NextAuth.js zawiera rozległą listę providerów, które zapewnią Ci szybki sposób na dodanie OAutha. Paczka ta posiada również wiele adapterów dla baz danych i ORMów. ## Context Provider W pliku `pages/_app.tsx` zobaczyć możesz, iż twoja aplikacja znajduje się w [SessionProviderze](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx ``` Ten oto provider kontekstu pozwala twojej aplikacji na dostęp do danych sesji z każdego miejsca, bez potrzeby przesyłania ich po "propsach": ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Obsłuż status nie bycia zalogowanym, np. wyświetl komponent `SignIn` return ; } return

Welcome {session.user.name}!

; }; ``` ## Otrzymywanie sesji po stronie serwera Czasem możesz chcieć otrzymać sesję na serwerze. Aby to zrobić, pobierz sesję korzystając z funkcji pomocniczej `getServerAuthSession` dostarczanej przez Create T3 App a następnie prześlij ją do klienta korzystając z `getServerSideProps`: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // UWAGA: obiekt `session` nie będzie miał stanu ładowania, ponieważ jest już pobrany na serwerze ... } ``` ## Dołączanie `user.id` do sesji Create T3 App jest skonfigurowany tak, aby wykorzystać [callback `session`](https://next-auth.js.org/configuration/callbacks#session-callback) w konfiguracji NextAuth.js do dodania ID użytkownika do obiektu `session`. ```ts:server/auth.ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Łączy się to z plikiem deklaracji typów, aby zapewnić odpowiednie typy obiektu `session` - musi on zawierać powyższe pole `user.id`. Więcej o zmianie typów poczytać możesz w rozdziale [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) dokumentacji NextAuth.js. ```ts:server/auth.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` Ten sam sposób wykorzystany może zostać do dodania większej ilości danych na obiekcie `session`, takich jak pole `role` (rola). **Nie powinien być on jednak wykorzystany do zapisywania wrażliwych danych** dla klienta. ## Korzystanie wraz z tRPC Jeżeli używasz NextAuth.js oraz tRPC, stworzyć można zabezpieczone procedury (z możliwością wielokrotnego użycia) korzystając z [middleware'ów](https://trpc.io/docs/v10/middlewares). Pozwala to na umieszczenie procedur, które zainicjowane być mogą jedynie przez autoryzowanych użytkowników. `create-t3-app` wszystko to dla Ciebie konfiguruje, dając ci możliwość łatwego dostępu do obiektu sesji wśród autoryzowanych procedur. Konfiguracja ta zachodzi w dwóch krokach: 1. Pobierz sesję z headerów zapytania korzystając z funkcji [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). Zaletą korzystania z `getServerSession` zamiast `getSession` jest fakt, iż jest to funkcja wywoływana jedynie po stronie serwera i nie inicjuje ona żadnych niepotrzebnych zapytań. `create-t3-app` tworzy funkcję pomocniczą, która ułatwia korzystanie z `getServerSession` - nie musisz więc importować zarówno opcji NextAuth.js jak i funkcji `getServerSession` za każdym razem, kiedy chcesz otrzymać sesję. ```ts:server/auth.ts export const getServerAuthSession = (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); }; ``` Korzystając z tej funkcji pomocniczej, możemy otrzymać sesję i przesłać ją do kontekstu tRPC: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../common/get-server-auth-session"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Stwórz middleware tRPC, który sprawdza, czy użytkownik jest autoryzowany. Wykorzystamy następnie stworzony middleware w `protectedProcedure` - specjalnej, zabezpieczonej procedurze. Każda osoba wywołująca ją będzie musiała spełniać warunki autoryzacji - w przeciwnym razie wystąpi błąd, który w odpowiedni sposób będzie mógł zostać obsłużony po stronie klienta. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // inferer `session` som ikke-nullbar session: { ...ctx.session, user: ctx.session.user }, }, }); })); ``` Obiekt `session` to minimalna i lekka reprezentacja użytkownika zawierająca jedynie parę pól. Jeśli korzystasz z procedur `protectedProcedure`, masz dostęp do ID użytkownika, które może zostać wykorzystane do pobrania większej ilości danych z bazy. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Korzystanie wraz z Prismą Przygotowanie NextAuth.js do pracy z Prismą wymaga wiele [wstępnej konfiguracji](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` zajmuje się nią za Ciebie, a jeśli wybierzesz zarówno Prismę i NextAuth.js, otrzymasz w pełni działający system uwierzytelniania, wraz z wszystkimi potrzebnymi modelami bazy danych. Szablon aplikacji, który dostarczamy, zawiera domyślnie provider OAuth Discorda, który wybraliśmy z powodu bycia jednym z łatwiejszych do obsługiwania - jedyne, co musiz zrobić, to podać tokeny w pliku `.env`... i gotowe! Jeżeli jednak chcesz skorzystać z czegoś innego, możesz w łatwy sposób dołączyć inne providery opierając się na [dokumentacji NextAuth.js](https://next-auth.js.org/providers/). Uwaga - niektóre z providerów wymagają większej ilości pól na modelach w bazie danych. Polecamy Ci przeczytać dokumentację providera, z którego chciałbyś skorzystać aby upewnić się, iż posiadasz wszystko, czego potrzebujesz. ### Dodawanie nowych pól do modeli Kiedy dodajesz nowe pola do któregokolwiek z modeli - `User`, `Account`, `Session` lub `VerificationToken` (prawdopodobnie będziesz chciał zmieniać jedynie model `User`) - pamiętać musisz, iż [adapter dla Prismy](https://next-auth.js.org/adapters/prisma) automatycznie tworzy pola na powyższych modelach w przypadku rejestracji kont i logowania na nie. Z tego też powodu, dodając nowe pola do tychże modeli, zaopatrzyć musisz je w wartości domyślne - adapter nie jest ich świadomy. Jeżeli przykładowo chcesz dodać pole `role` (roli) do modelu `User` (użytkownika), musisz pamiętać o dołączeniu wartości domyślnej tego pola. Zrobisz to za pomocą wartości `@default` w modelu `User`: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Korzystanie wraz z middlewarem Next.js Wykorzystanie middleware'a Next.js [wymaga od Ciebie skorzystania ze strategii JWT](https://next-auth.js.org/configuration/nextjs#caveats) do autoryzacji. Dzieje się tak, ponieważ middleware jest w stanie pobierać ciasteczko sesji jedynie, gdy jest ono JWT. Create T3 App jest skonfigurowany tak, by wykorzystać **domyślną** strategię bazy danych, wraz z Prismą jako jej adapterem. ## Konfigurowanie domyślnego providera Discord (`DiscordProvider`) 1. Przejdź do [sekcji Aplikacje w Panelu Discord Developer Portal](https://discord.com/developers/applications), a następnie kliknij na "New Application" 2. W menu ustawień, przejdź do "OAuth2 => General" - Skopiuj Client ID i wklej go do pliku `.env` pod kluczem `AUTH_DISCORD_ID`. - Pod Client Secret, kliknij "Reset Secret" i skopiuj podany tekst do pliku `.env` pod kluczem `AUTH_DISCORD_SECRET`. Uważaj - nie będziesz mógł ponownie zobaczyć tego klucza, a jego reset spowoduje wygaśnięcie aktualnego. - Dodaj "Add Redirect" i wklej tam `/api/auth/callback/discord` (przykładowo dla lokalnej aplikacji: http://localhost:3000/api/auth/callback/discord) - Zapisz zmiany - Jest możliwość (nie jest ona jednak polecana), aby wykorzystać tą samą aplikację Discorda dla zarówno aplikacji lokalnej i tej w wersji produkcyjnej. Możesz także wykorzystać [mockowanie providera](https://github.com/trpc/trpc/blob/next/examples/next-prisma-starter-websockets/src/pages/api/auth/%5B...nextauth%5D.ts) podczas rozwoju aplikacji. ## Przydatne Zasoby | Zasób | Link | | ----------------------------------- | --------------------------------------- | | Dokumentacja NextAuth.js | https://next-auth.js.org/ | | GitHub NextAuth.js | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - wraz z NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/pl/usage/next-js.md ================================================ --- title: Next.js description: Korzystanie z Next.js layout: ../../../layouts/docs.astro lang: pl --- Next.js to backend framework dla aplikacji React.
Zapoznaj się z [rozmową Theo na konferencji Next.js](https://www.youtube.com/watch?v=W4UhNo3HAMw) aby lepiej zrozumieć, czym jest i jak działa Next.js.

## Dlaczego powinienem go używać? Kochamy Reacta. Zmienił on sposób tworzenia interfejsów na niewyobrażalnie bardziej przystępny. Może on także prowadzić dewelopera za rękę w miejscach, mogących sprawiać problemy. Next.js oferuje lekko kontrowersyjne, bardzo zoptymalizowane podejście do tworzenia aplikacji z pomocą Reacta. Od routingu, przez API, do renderowania zdjęć, ufamy Next.js i wierzymy, że prowadzi nas do dobrych decyzji. Połączenie Next.js z [Vercelem](https://vercel.com/) sprawia, iż pisanie i deploy applikacji staje się łatwieszy niż kiedykolwiek. Ich bardzo hojny darmowy tier i bardzo intuicyjny interfejs zaopatruje Cię w rozwiązanie "point and click", dzięki któremu z łatwością opublikujesz swoją stronę (Kochamy ❤️ Vercela). ## Get Static/Server Props Kluczową funkcją Next.jsa są jego możliwości fetchowania danych. Bardzo polecamy przejrzenie jego [oficjalnej dokumentacji](https://nextjs.org/docs/basic-features/data-fetching), aby zrozumieć jak korzystać z każdej z tych metod i poznać ich różnice. `getServerSideProps` jest ogólnie odradzane (chyba, że jest dla metody tej BARDZO dobry argument za), z powodu blokowania renderu strony - i przez to jej spowalniania. [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) to dobra anternatywa dla `getServerSideProps`, jeżeli dane są dynamiczne i mogą być pobierane "przyrostowo". ## Przydatne Zasoby | Zasób | Link | | ----------------------------- | ---------------------------------- | | Dokumentacja Next.js | https://nextjs.org/docs | | GitHub Next.js | https://github.com/vercel/next.js | | Blog Next.js | https://nextjs.org/blog | | Discord Next.js | https://nextjs.org/discord | | Twitter Next.js | https://twitter.com/nextjs | | Kanał YouTube Vercela/Next.js | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/pl/usage/prisma.md ================================================ --- title: Prisma description: Korzystanie z Prismy layout: ../../../layouts/docs.astro lang: pl --- Prisma to ORM dla TypeScripta, który pozwala na definiowanie schematu bazy danych i modeli w pliku `schema.prisma`, a następnie generuje klikenta będącego typesafe, którego wykorzystasz w interakcjach z bazą danych ze swojego backendu. ## Prisma Client Zlokalizowany w pliku `src/server/db.ts`, Prisma Client to globalna zmienna (tak jak rekomendowane na stronie z [najlepszymi sposobami](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) przez zespół Prismy) wyeksportowana i gotowa do użycia w twoich route'ach API. Prisma Client dołączany jest przez nas w [kontekście tRPC](/en/usage/trpc#-serverapitrpcts). Polecamy tą metodę, zamiast importowania go osobno w każdym pliku. ## Schema (Schemat) Znajdziesz schemat Prismy w pliku `/prisma/schema.prisma`. Plik ten to miejsce, gdzie definiuje się schemat bazy danych i modele. Jest on także wykorzystywany podczas generowania Prisma Clienta. ### Z NextAuth.js Jeżeli wybierzesz NextAuth.js w połączeniu z Prismą, plik shcematu generowany jest wraz z rekomendowanymi wartościami dla modeli `User`, `Session`, `Account`, czy też `VerificationToken` - tak jak opisano to w [dokumentacji NextAuth.js](https://next-auth.js.org/adapters/prisma). ## Domyślna Baza Danych Domyślna baza danych to baza SQLite, która jest znakomita do szybkiego pisania aplikacji, takich jak "weryfikacja konceptu" ("proof-of-concept"), lecz nie jest ona polecana do pisania produkcyjnych wersji projektów. Typ bazy danych zmienić możesz poprzez edycję pola `provider` w bloku `database`, na `postgresql` lub `mysql`. Następnie, aktualizując string połączenia z bazą w pliku `.env` na taki, który skieruje Prismę do twojej bazy danych. ## Seedowanie Bazy Danych [Seedowanie bazy danych](https://www.prisma.io/docs/guides/database/seed-database) to dobry sposób na szybkie uzupełnienie bazy danych testową zawartością. Aby rozpocząć seedowanie, będziesz musiał stworzyć plik `seed.ts` w folderze `/prisma`, a następnie dodać skrypt `seed` do pliku `package.json`. Będziesz musiał także skorzystać z jakiegoś środowiska uruchomieniowego dla TypeScripta. Polecamy [tsx](https://github.com/esbuild-kit/tsx), który jest bardzo szybkim środowiskiem korzystającym z esbuilda i niewymagającym żadnej konfiguracji ESM. Runnery, takie jak `ts-node`, także zadziałają. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db/client"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Następnie uruchom po prostu `pnpm db-seed` (lub `npm`/`yarn`) aby wykonać seedowanie bazy danych. ## Przydatne Zasoby | Zasób | Link | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Dokumentacja Prismy | https://www.prisma.io/docs/ | | GitHub Prismy | https://github.com/prisma/prisma | | Prisma Migrate Playground | https://playground.prisma.io/guides | | Adapter NextAuth.JS dla Prismy | https://next-auth.js.org/adapters/prisma | | Poradnik Połączenia z PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/pl/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Korzystanie z Tailwind CSS layout: ../../../layouts/docs.astro lang: pl --- ## Co to Tailwind CSS? TailwindCSS to mały, ["utility first"](https://tailwindcss.com/docs/utility-first) framework CSS służący do budowania własnych designów, nie wymagając przy tym zmiany kontekstu, tak jak to robi zwykły CSS. Jest to tylko i wyłącznie framework CSS, który nie oferuje gotowych komponentów czy też logiki. Zawiera on natomiast [wiele różnych benefitów](https://www.youtube.com/watch?v=CQuTF-bkOgc) w porównaniu do biblioteki komponentów, takiej jak Material UI. Sprawia on, iż pisanie CSSa staje się wyjątkowo łatwe i szybkie, jak widać z resztą na poniższym przykładzie: Stary CSS: 1. Napisz kod CSSa, często w osobnym pliku ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Zaimportuj CSSa do swojego komponentu ```jsx import "./my-class.css"; ``` 3. Dodaj klasę do swojego kodu HTMLa ```html
...
``` Odpowiednik z Tailwindem: 1. Po prostu dodaj klasy do HTMLa ```html
...
``` Kiedy korzystasz z Tailwinda w połączeniu z komponentami Reacta, staje się on niezwykle silnym sposobem na szybkie pisanie interfejsów użytkownika (UI). Tailwind CSS posiada piękny, wbudowany design system, który zawiera uważnie dobraną paletę kolorów, wzorce rozmiarów dla stylów, takich jak wysokość i szerokość czy też padding i margines. Zawiera też breakpointy pomocne przy tworzeniu responsywnego layoutu. Design system może być dostosowywany i poszerzany, aby stworzyć dokładnie ten zestaw narzędzi i stylów, którego twój projekt potrzebuje.
Tru Narla, lepiej znana jako [mewtru](https://twitter.com/trunarla), wygłosiła niezwykłą rozmowę na temat [budowania design systemu korzystając z Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Korzystanie z Tailwinda Upewnij się, że zainstalowałeś pluginy dla Tailwinda. Zwięszą one komfort pisania kodu korzystając z tego narzędzia. ### Rozszerzenia i Pluginy - [Rozszerzenie dla VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Formatowanie Klasy Tailwinda mogą łatwo stać się nieczytelne, więc formater to "must-have". [Prettier Plugin dla Tailwind CSS](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) sortuje klasy używając [rekomendowanej kolejności](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) tak, że kolejność klas jest zgodna z ich kolejnością w zbudowanej aplikacji. Wybierając Tailwind w naszym CLI, automatycznie zainstalujemy i skonfigurujemy plugin za Ciebie. ### Warunkowe Dodawanie Klas Warunkowe dodawanie klas korzystając z "ternary operators" może stać się nieczytelne i niechlujne. Paczki te pomogą w organizowaniu twoich klas korzystając z logiki warunkowej. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Przydatne Zasoby | Zasób | Link | | -------------------------- | -------------------------------------------------------- | | Dokumentacja Tailwinda | https://tailwindcss.com/docs/editor-setup/ | | Tailwind Cheat Sheet | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Serwer Discrod Tailwinda | https://tailwindcss.com/discord/ | | Kanał Youtube TailwindLabs | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/pl/usage/trpc.md ================================================ --- title: tRPC description: Korzystanie z tRPC layout: ../../../layouts/docs.astro lang: pl --- tRPC pozwala nam pisanie API będących w pełni typesafe bez żadnego generowania kodu czy też zaśmiecania runtime'u. Korzysta on ze świetnego type inference od Typecripta aby przekazywać definicje routerów oraz pozwala Ci na korzystanie z procedur API na frontendzie z pełnym tyepsafety i autouzupełnianiem. Jeśli korzystasz z tRPC, twój frontend i backend będą sprawiały wrażenie bycia bardziej połączonymi niż kiedykolwiek, pozwalając na niespotykany DX (developer experience).

Zbudowałem tRPC aby umożliwić każdemu szybsze robienie postępów, usuwając przy tym potrzebę korzystania z tradycyjnej wartswy API oraz zachowując pewność, iż nasze aplikacje nie zepsują się nadążając za własnym rozwojem.
Oryginał: I built tRPC to allow people to move faster by removing the need of a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.

Avatar of @alexdotjs
Alex - twórca tRPC @alexdotjs
## Jak korzystać z tRPC?
Kontrybutor tRPC [trashh_dev](https://twitter.com/trashh_dev) zrobił [znakomity występ na Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) właśnie o tRPC. Jeżeli jeszcze się z nim nie zapoznałeś, bardzo polecamy Ci to zrobić. Z tRPC, piszesz funkcje w TypeScript'cie na backendzie a następnie wywołujesz je z frontendu. Prosta procedura tRPC wyglądać może tak: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Jest to procedura (odpowiednik handlera route'a w tradycyjnym API), która najpierw waliduje wejście/input korzystając z biblioteki Zod (jest to ta sama biblioteka, z której korzystamy podczas sprawdzania [zmiennych środowiskowych](./env-variables)) - w tym przypadku zapewnia ona, iż dane przesłane do API są w formie tekstu (stringa). Jeżeli jednak nie jest to prawda, API wyśle informatywny błąd. Po sprawdzeniu wejścia, dołączamy funkcję, która może być albo [query](https://trpc.io/docs/v10/react-queries), albo [mutacją](https://trpc.io/docs/v10/react-mutations), albo [subscrypcją](https://trpc.io/docs/v10/subscriptions). W naszym przykładzie, funkcja ta (zwana "resolverem") wysyła zapytanie do bazy danych korzystając z naszego klienta [prisma](./prisma) i zwraca użytkownika z pasującym do wysłanego `id`. Swoje procedury definiujesz w folderze `routers`, który reprezentuje kolekcję pasujących procedur ze wspólnej przestrzeni. Możesz mieć router `users`, router `posts` i router `messages`. Routery te mogą zostać następnie połączone w jeden, scentralizowany `appRouter`: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Zwróć uwagę na to, iż musimy eksportować jedynie definicje typów tego routera - oznacza to, iż nigdy nie importujemy kodu serwera po stronie klienta. Wywołajmy teraz procedurę na naszym frontendzie. tRPC dostarcza nam wrapper dla paczki `@tanstack/react-query`, który pozwala ci wykorzystać pełną moc hooków. Dodatkowo, zapytania API dostajesz w pełni "otypowane". Zapytanie do naszych procedur możemy wykonać w następujący sposób: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Natychmiast zauważysz, jak dobrze działa type-safety i autouzupełnianie. Jak tylko napiszesz `trpc.`, twoje routery automatycznie pojawią się w opcjach autopodpowiedzi a kiedy tylko wybierzesz router, również znajdą się tam jego procedury. Otrzymasz także błąd TypeScripta, jeżeli wejście (input) nie będzie zgadzać się z tym, podanym do systemu walidacji na backendzie. ## Korzystanie z błędów biblioteki Zod Domyślnie `create-t3-app` konfiguruje [error formatter](https://trpc.io/docs/error-formatting), który pozwala pobierać błędy z biblioteki Zod, jeśli na backendzie wystąpią błędy walidacji. Przykładowe użycie: ```tsx function MyComponent() { const { mutate, error } = api.post.create.useMutation(); return ( { e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ title: formData.get('title') }); }}> {error?.data?.zodError?.fieldErrors.title && ( {/** `mutate` returned with an error on the `title` */} {error.data.zodError.fieldErrors.title} )} ... ); } ``` ## Pliki tRPC wymaga dużo boilerplate'u, który `create-t3-app` przygotowuje za Ciebie. Przejdźmy więc po kolei po plikach, które są generowane: ### 📄 `pages/api/trpc/[trpc].ts` Jest to właściwy punkt początkowy dla twojego API - to on ujawnia dla reszty aplikacji twój router od tRPC. Prawdopodobnie nie będziesz musiał edytować tego pliku, ale jeżeli zajdzie taka potrzeba (np. do włączenia CORSa), warto wiedzieć o tym, iż eksportowany `createNextApiHandler` to [Next.js API handler](https://nextjs.org/docs/api-routes/introduction), który pobiera obiekt [zapytania](https://developer.mozilla.org/en-US/docs/Web/API/Request) i [odpowiedzi](https://developer.mozilla.org/en-US/docs/Web/API/Response) serwera. Oznacza to, iż możesz zawrzeć `createNextApiHandler` w middleware, w jakim tylko chcesz. Poniżej znajdziesz [przykładowy kod](#aktywacja-cors), dzięki któremu dodasz CORS. ### 📄 `server/api/trpc.ts` Plik ten podzielony jest na dwie części - tworzenie kontekstu oraz inicjalizacji tRPC: 1. Definiujemy kontekst przesyłany do procedur tRPC. Kontekst, to dane do których dostęp mają wszystkie twoje procedury tRPC. Jest to doskonałe miejsce do umieszczenia rzeczy, takich jak połączenia z bazą danych, informacje o uwierzytelnianiu, itp. W Create T3 App korzystamy z dwóch funkcji, aby umożliwić korzystanie z części kontekstu bez dostępu do obiektu zapytania. - `createInnerTRPCContext`: Tutaj definiujesz kontekst, który nie zależy od obiektu zapytania, np. połączenie z bazą danych. Możesz wykorzystać tą funkcję do [testów integracji](#przykładowy-test-integracji) oraz [funkcji pomocniczych SSG](https://trpc.io/docs/v10/ssg-helpers), gdzie nie posiadasz obiektu zapytania. - `createTRPCContext`: Tutaj definiujesz kontekst, który zależny jest od zapytania, np. sesja użytkownika. Otrzymujesz sesję korzystając z obiektu `opts.req`, a następnie posyłasz ją do funkcji `createInnerTRPCContext` w celu utworzenia finalnego kontekstu. 2. Inicjalizujemy tRPC i definiujemy [procedury](https://trpc.io/docs/v10/procedures) oraz [middleware](https://trpc.io/docs/v10/middlewares). Umownie, nie powinieneś eksportować całego obiektu `t` a jedynie poszczególne procedury i middleware. Zwróć uwagę, iż korzystamy z paczki `superjson` jako [transformera danych](https://trpc.io/docs/v10/data-transformers). Umożliwia on na zachowanie typów danych, które otrzymuje klient - przykładowo, posyłając obiekt `Date`, klient również otrzyma obiekt `Date` - a nie tekst, w przeciwieństwie do wielu innych API. ### 📄 `server/api/routers/*.ts` Tutaj definiujesz routery i procedury swojego API. Umownie, powinieneś tworzyć [osobne routery](https://trpc.io/docs/v10/router) dla odpowiadających im procedur. ### 📄 `server/api/root.ts` Tutaj [łączymy](https://trpc.io/docs/v10/merging-routers) wszystkie "sub-routery" zdefiniowane w folderze `routers/**` w jeden router aplikacji. ### 📄 `utils/api.ts` Jest to punkt startowy tRPC po stronie frontendu. To tutaj importować będziesz wszystkie **definicje typów** i tworzyć będziesz swój client tRPC razem z hookami od react-query. Ponieważ korzystamy z paczki `superjson` jako transformera danych na backendzie, musimy go uruchomić również na frontendzie. Dzieje się tak, ponieważ dane serializowane w API muszą być dekodowane właśnie na frontendzie. Zdefiniujesz tu także [linki](https://trpc.io/docs/v10/links) tRPC, które decydują o całym flow zapytania - od klienta do serwera. My korzystamy z "domyślnego" linku [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink), który umożliwia ["request batching"](https://cloud.google.com/compute/docs/api/how-tos/batch). Korzystamy też z linku [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink), pozwalającego na wyświetlanie przydatnych podczas pisania aplikacji logów. Na koniec eksportujemy [pomocniczy typ](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type), którego użyć możesz do dziedziczenia typów na frontendzie. ## Jak wykonać zewnętrzne zapytania do mojego API? Korzystając z regularnego API, zapytania takie możesz wykonać korzystając z klientów HTTP takich jak `curl`, `Postman`, `fetch`, czy tez bezpośrednio z przeglądarki. Z tRPC sprawa wygląda jednak inaczej. Jeżeli chcesz wykonać takie zapytania bez klienta tRPC, możesz skorzystać z jedngo z dwóch polecanych na to sposobów: ### Ujawnianie zewnętrznie pojedynczej procedury tRPC Jeżeli chcesz ujawnić zewnętrznie pojedynczą procedurę, powinieneś skorzystać z [zapytań po stronie serwera](https://trpc.io/docs/v10/server-side-calls). Pozwoli Ci to na wykonanie standardowego endpointa Next.js, ale użyje części "resolvera" twojej procedury tRPC. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Ujawnianie wszystkich procedur tRPC jako endpointów REST Jeżeli chcesz ujawnić zewnętrznie wszystkie procedury tRPC, sprawdź rozszerzenie stworzone przez społeczność - [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Dostarczając dodatkowych metadanych do twoich procedur, wygenerować możesz REST API zgodne z OpenAPI ze swoich routerów tRPC. ### To tylko zapytania HTTP tRPC komunikuje się za pomocą HTTP, więc masz także możliwość wykonywania zapytań do swoich procedur korzystając właśnie z "regularnych" zapytań HTTP. Składnia może wydawać się jednak nieporęczna z powodu wykorzystywanego przez tRPC [protokołu RPC](https://trpc.io/docs/v10/rpc). Jeżeli jesteś ciekawy jak on działa, możesz zobaczyć jak wyglądają zapytania tRPC w zakładce "sieć" w swojej przeglądarce - polecamy robić to jednak tylko w celach edukacyjnych i skorzystać z jednego z rozwiązań przedstawionych powyżej. ## Porównanie do endpointu API Next.js Porównajmy endpoint API Next.js z procedurą tRPC. Powiedzmy, że chcemy pobrać ubiekt użytkownika z naszej bazy danych i zwrócić go na frontend. Endpoint API Next.js napisać moglibyśmy w następujący sposób>+: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Porównaj to do powyższego przykładu z tRPC - zobaczysz zalety korzystanie właśnie z tego sposobu: - Zamiast precyzować url dla każdego route'a (co może stać się uciążliwe do debugowania, jeśli coś przeniesiesz), twój cały router jest obiektem z autouzupełnianiem. - Nie musisz walidować użytej metody HTTP. - Nie musisz walidować zawartości zapytania pod kątem pooprawności zawartych danych - zajmuje się tym Zod. - Zamiast tworzyć obiekt "response", możesz wyrzucać błędy i zwracać wartości lub obiekty tak, jak robiłbyś to w zwykłej funkcji TypeScripta. - Wywoływanie procedury na frontendzie dostarcza Ci autouzupełniania i type-safety. ## Przydatne fragmenty Znajdziesz tutaj fragmenty kodu, które mogą Ci się przydać. ### Aktywacja CORS Jeżeli chcesz korzystać z API z różnych domen, np. w monorepo zawierającym aplikację React Native, możesz chcieć włączyć CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Włącz cors await cors(req, res); // Stwórz i wywołaj handler tRPC return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### "Optimistic updates" Aktualizacje danych zwane "Optimistic updates" zachodzą wtedy, kiedy aktualizujemy UI, zanim zapytanie API zostanie ukończone. Dostarcza to lepsze doświadczenie użytkownika, ponieważ nie musi on czekać na ukończenie zapytania API, aby zobaczyć odzwierciedlenie zmian w interfejsie aplikacji. Pamiętaj jednak, że aplikacje, które cenią sobie poprawność danych, powinny za wszelką cenę unikać aktualizacji "optimisic updates" - nie są one "poprawną" reprezentacją stanu backendu. Więcej na ich temat możesz poczytać w [dokumentacji React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Anuluj wychodzące zapytania (aby nie nadpisały one "optimistic update'u") await utils.post.list.cancel(); // Otrzymaj dane z queryCache const prevData = utils.post.list.getData(); // Zaktualizuj dane z naszego nowego postu utils.post.list.setData(undefined, (old) => [...old, newPost]); // Zwróć poprzednie dane, aby w razie błędu można było z nich przywrócić stan aplikacji return { prevData }; }, onError(err, newPost, ctx) { // Jeżeli mutacja wyrzuci błąd, skorzystaj z wartości kontekstu z onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Zsynchronizuj z serwerem po ukończonej mutacji utils.post.list.invalidate(); }, }); }; ``` ### Przykładowy Test Integracji Tu znajdziesz przykładowy test integracji korzystający z paczki [Vitest](https://vitest.dev), aby sprawdzić, czy router tRPC działa poprawnie, czy parser danych wejściowych dziedziczy odpowiedni typ, oraz czy zwracane dane pasują do oczekiwanego outputu. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` Jeżeli twoja procedura jest chroniona, możesz przesłać stworzony obiekt `session` tworząc kontekst: ```ts test("protected example router", async () => { const ctx = await createInnerTRPCContext({ session: { user: { id: "123", name: "John Doe" }, expires: "1", }, }); const caller = appRouter.createCaller(ctx); // ... }); ``` ## Przydatne Zasoby | Zasób | Link | | ------------------------ | ------------------------------------------------------- | | Dokumentacja tRPC | https://www.trpc.io | | Parę przykładów z tRPC | https://github.com/trpc/trpc/tree/next/examples | | Dokumentacja React Query | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/pl/usage/typescript.md ================================================ --- title: TypeScript description: Korzystanie z TypeScripta layout: ../../../layouts/docs.astro lang: pl ---

Buduj siatki bezpieczeństwa, nie barierki
Oryginał: Build safety nets, not guard rails

Avatar of @alexdotjs
Theo - twórca stacka T3 @t3dotgg
Bez względu na to, czy jesteś nowym deweloperem, czy doświadczonym programistą, uważamy, iż TypeScript jest w tych czasach obowiązkiem. Początkowo może wyglądać strasznie, ale tak jak z większością narzędzi, z czasem nie będziesz chciał już z niego z rezygnować. Zaopatruje Cię on w feedback na żywo definiując oczekiwane typy danych. Dostarcza także pomocnego autouzupełniania w edytorze lub krzyczy na ciebie czerwonymi podkreśleniami, jeżeli próbujesz uzyskać dostęp do właściwości, która nie istnieje, lub jesli próbujesz przesłać wartość złego typu - co skutkowałoby dłuższym debugowaniem w razie błędu. Jest to prawdopodobnie narzędzie, które pozwala na największy wzrost produktywności dewelopera; dostarczanie dokumentacji kodu który piszesz, czy też z którego bezpośrednio korzystasz, a także posiadanie nagłego feedbacku w razie nieuniknionych błędów jest bezcenne. ## Type Inference Podczas gdy wielu nowych deweloperów korzystających z TypeScripta martwi się o mus jego _pisania_, wiele benefitów wynikających z korzystania z tego języka, nie wymaga od ciebie praktycznie żadnej zmiany w kodzie - jednym z tych benefitów jest tzw. "inference". Oznacza to, iż jeśli coś posiada własny typ, typ ten przechodzić będzie przez całe flow aplikacji bez potrzeby jego ponownego definiowania w innych jej miejscach. Jeżeli na przykład zdefiniujesz w jednym miejscu typy argumentów przyjmowanych przez daną funkcję, reszta funkcji będzie typesafe bez potrzeby pisania kodu specyficznego dla TypeScripta. Deweloperzy tworzący biblioteki wkładają dużo wysiłku w utrzymywanie typów dla swoich projektów, co skutkuje tym, iż twórcy aplikacji mogą benefitować zarówno z type inference, jak i z wbudowanej dokumentacji w edytorze kodu, którą typy te dostarczają.
Sprawdź film od Theo - [korzystasz z TypeScripta źle...](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Wykorzystanie mocnych stron type inference w projektach ### Zod [Zod](https://github.com/colinhacks/zod) to biblioteka walidacji bazująca na schematach, zbudowana z pomocą TypeScripta. Napisz schemat reprezentujący pojedyncze źródło prawdy, a Zod zapewni poprawność danych w całej aplikacji - nawet wśród zapytań do zewnętrznych API. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) dostarcza deklaratywne, zawsze aktualne automatycznie zarządzane zapytania i mutacje, które bezpośrednio wspomagają zarówno Ciebie jako dewelopera i UX. ## Przydatne Zasoby | Zasób | Link | | --------------------------------------------------------- | ----------------------------------------------------------------- | | TypeScript Handbook | https://www.typescriptlang.org/docs/handbook/ | | Poradnik TypeScripta dla Początkujących | https://github.com/total-typescript/beginners-typescript-tutorial | | Type Challenges | https://github.com/type-challenges/type-challenges | | Kanał YouTube - Rodney Mullen of TypeScript (Matt Pocock) | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/pl/why.md ================================================ --- title: Dlaczego CT3A? description: Dlaczego powinieneś wybrać Create T3 App dla swojego następnego projektu layout: ../../layouts/docs.astro lang: pl --- Stworzyliśmy Create T3 App, ponieważ [Theo](https://twitter.com/t3dotgg) nie chciał przygotować szablonu składającego się ze swoich ulubionych technologii. Zainspirowany przez create-next-app, [CLI od Astro](https://astro.build) oraz ogólne zamiłowanie do typesafety, zespół Create T3 App ciężko pracował budując najlepszy możliwy punkt startowy dla nowych projektów korzystających ze stacka T3. Jeżeli jesteś zainteresowany korzystaniem z Next.jsa w sposób, w który nie stracisz na typesafety, jest to znakomite miejsce, aby zacząć. Jeśli jesteś ciekawy co do poszczególnych technologii wybranych przez nas dla tego narzędzia, czytaj dalej :) ## Dlaczego TypeScript? JavaScript jest ciężki. Dlaczego dodajemy więcej zasad? Stanowczo wierzymy, że TypeScript pomoże ci być lepszym deweloperem. Zaopatruje Cię on w feedback na żywo definiując oczekiwane typy danych. Dostarcza także pomocnego autouzupełniania w edytorze lub krzyczy na ciebie czerwonymi podkreśleniami, jeżeli próbujesz uzyskać dostęp do właściwości, która nie istnieje, lub jesli próbujesz przesłać wartość złego typu - co skutkowałoby dłuższym debugowaniem w razie błędu. Bez względu na to, czy jesteś nowy w świecie web developmentu, czy też doświadczonym programistą, "surowość" jaką daje ci TypeScript pozwala na mniej frustrujące i bardziej konsekwentne doświadczenie niż czysty JS. Typesafety pozwala ci być szybszym. Jeżeli dalej nie jesteś przekonany, może się okazać, że [korzystasz z TypeScripta źle...](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Dlaczego Next.js? Kochamy Reacta. Zmienił on sposób tworzenia interfejsów na niewyobrażalnie bardziej przystępny. Może on także prowadzić dewelopera za rękę z miejscach, mogących sprawiać problemy. Next.js oferuje lekko kontrowersyjne, bardzo zoptymalizowane podejście do tworzenia aplikacji z pomocą Reacta. Od routingu przez API do renderowania zdjęć, ufamy Next.jsowi i wierzymy, że prowadzi on nas do dobrych decyzji. ## Dlaczego tRPC/Prisma/Tailwind/itp.? Staramy się utrzymać projekty w tak prosty sposób, jak tylko się da. Okazuje się jednak, że korzystamy z tych narzędzi w każdym "projekcie-aplikacji" jaki tylko budujemy. `create-t3-app` robi znakomitą robotę pozwalając ci zaadoptować te części, których potrzebujesz. ### tRPC tRPC spełnia obietnice GraphQLa o bezproblemowej integracji klienta z serwerem bez niepotrzebnego boilerplate'a. Jest to przemyślane wykorzystanie TypeScripta które zaopatrzy Cię w niespotykane doświadczenie przy programowaniu. ### Prisma Prisma dla SQLa to jak TypeScript dla JSa. Pozwala na DX jaki wcześniej nie istniał. Generując typy ze schematu zdefiniowanego przez użytkownika i kompatybilnego z [wieloma bazami danych](https://www.prisma.io/docs/concepts/database-connectors), Prisma gwarantuje typesafety na każdym kroku od bazy danych do twojej aplikacji. Prisma oddaje ci w ręce cały [zestaw narzędzi](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) ułatwiając codzienne integracje z bazą danych. Prisma Client odpowiedzialny za wykonywanie zapytań do bazy danych jest tak łatwy w użyciu, że nawet nie zorientujesz się, kiedy będziesz z niego korzystać. Prisma Studio natomiast to przystępny interfejs graficzny dla twojej bazy danych, który pozwala ci odczytywać z niej dane i manipulować nimi bez potrzeby pisania kodu. ### Tailwind CSS Tailwind sprawia wrażenie "CSSa w trybie zen". Zaopatrując Cię w narzędzia do budowania, takie jak świetne domyślne kolory, spacingi oraz inne prymitywy, Tailwind w znaczny sposób ułatwia stworzenie dobrze wyglądającej aplikacji. W przeciwieństwie do bibliotek komponentów, nie sprawia niepotrzebnych problemów, kiedy chcesz stworzyć coś unikalnego i pięknego. Dodatkowo, z podejściem "inline", Tailwind zachęca cię do stylizowania aplikacji bez martwienia się o nazywanie klas, organizowanie struktury plików czy rozwiązywanie innych problemów niekoniecznie mających coś wspólnego z tym, co chcesz właśnie stworzyć. ### NextAuth.js Kiedy chcesz dodać system kont do swojej aplikacji Next.js, NextAuth.js to znakomite rozwiązanie. Pozwala ono wdrożyć złożone systemy bezpieczeństwa nie zmuszając Cię przy tym do pisania ich własnoręcznie. NextAuth.js zawiera rozległą listę providerów, które zapewnią Ci szybki sposób na dodanie OAutha. Paczka ta posiada również wiele adapterów dla baz danych i ORMów. ================================================ FILE: www/src/pages/pt/deployment/docker.md ================================================ --- title: Docker description: Deploy com Docker layout: ../../../layouts/docs.astro lang: pt --- Você pode colocar essa stack em contêineres e fazer deploy dela como um único contêiner usando o Docker ou como parte de um grupo de contêineres usando o docker-compose. Veja [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) para um repositório de exemplo baseado neste documento. ## Configuração do Projeto Docker Por favor, note que o Next.js requer um processo diferente para variáveis de ambiente em _build time_ (disponível no frontend, prefixado por `NEXT_PUBLIC`) e _runtime_, somente do lado do servidor. Nesta demonstração estamos usando duas variáveis. Preste atenção em suas posições no `Dockerfile`, argumentos de linha de comando e `docker-compose.yml`: - `DATABASE_URL` (usado pelo servidor) - `NEXT_PUBLIC_CLIENTVAR` (usado pelo cliente) ### 1. Configuração do Next Em seu arquivo [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), configure a opção `output` como `standalone` para [reduzir o tamanho de imagens](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Criar arquivo dockerignore
Clique aqui e inclua esse conteúdo no arquivo .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Criação do arquivo Dockerfile > Como não estamos puxando as variáveis de ambiente do servidor para nosso contêiner, a [validação do esquema do ambiente](/en/usage/env-variables) falhará. Para evitar isso, temos que adicionar a flag `SKIP_ENV_VALIDATION=1` ao comando de compilação para que os schemas env não sejam validados no momento da compilação.
Clique aqui e inclua esse conteúdos no .dockerignore:
```docker ##### DEPENDÊNCIAS FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Instalação do Prisma Client - remova se não estiver usando o Prisma COPY prisma ./ # Instalação de dependências com base no package manager padrão COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile não encontrado." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Observações_** > > - _A emulação de `--platform=linux/amd64` pode não ser necessária após a mudança para o Node 18._ > - _Consulte [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) para entender por que `libc6-compat` pode ser necessário._ > - _Usando imagens baseadas em Alpine 3.17 [pode causar problemas no Prisma](https://github.com/t3-oss/create-t3-app/issues/975). Realizando a configuração: `engineType = "binary"` corrige esse problema no Alpine 3.17, [mas apresenta um custo de performance adicional](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ > - _Next.js coleta [dados anônimos de telemetria sobre uso geral](https://nextjs.org/telemetry). Remova o primeiro comentário de `ENV NEXT_TELEMETRY_DISABLED 1` para desabilitar a telemetria durante o build. Remova o segundo comentário para desabilitar a telemetria durante o tempo de execução._
## Execute build e run da imagem localmente Execute `build` e `run` desta imagem localmente com os seguintes comandos: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Abra [localhost:3000](http://localhost:3000/) para ver sua aplicação rodando. ## Docker Compose Você também pode usar o Docker Compose para criar a imagem e executar o contêiner.
Siga as etapas 1 a 4 acima, clique aqui e inclua o conteúdo no arquivo docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Execute isso usando o comando `docker compose up`: ```bash docker compose up ``` Abra [localhost:3000](http://localhost:3000/) para ver sua aplicação rodando.
## Deploy na Railway Você pode usar [deploys automáticos de Dockerfile](https://docs.railway.app/deploy/dockerfiles) em uma PaaS como a [Railway](https://railway.app) para fazer deploy das suas aplicações. Se você tiver o [CLI da Railway instalado](https://docs.railway.app/develop/cli#install), poderá fazer deploy da sua aplicação com os seguintes comandos: ```bash railway login railway init railway link railway up railway open ``` Vá para "Variables" e inclua seu `DATABASE_URL`. Em seguida, vá para "Settings" e selecione "Generate Domain". Para ver um exemplo em execução no Railway, visite [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Recursos Úteis | Recurso | Link | | ----------------------------------------------- | -------------------------------------------------------------------- | | Referência do Dockerfile | https://docs.docker.com/engine/reference/builder/ | | Referência da versão 3 do arquivo Compose | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Referência da CLI do Docker | https://docs.docker.com/engine/reference/commandline/docker/ | | Referência da CLI do Docker Compose | https://docs.docker.com/compose/reference/ | | Deploy do Next.js com imagem do Docker | https://nextjs.org/docs/deployment#docker-image | | Next.js no Docker | https://benmarte.com/blog/nextjs-in-docker/ | | Exemplo de Next.js com Docker | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Criar imagem do Docker de um aplicativo Next.js | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/pt/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Deployment", layout: "docs", description: "Aprenda como fazer deploy para produção na sua aplicação T3.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/pt/deployment/netlify.mdx ================================================ --- title: Netlify description: Fazendo deploy para a Netlify layout: ../../../layouts/docs.astro lang: pt isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Netlify é um provedor de deploy alternativo semelhante à Vercel. Veja [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) para um exemplo de repositório baseado nesta documentação. ## Por que hospedar na Netlify A sabedoria popular diz que a Vercel possui um suporte melhor para Next.js já que foi ela que desenvolveu o Next.js. Eles possuem interesse em garantir que a plataforma esteja ajustada para um desempenho ideal e uma boa DX com Next.js. Isso é verdade para a maioria dos casos de uso, não fazendo sentido desviar-se do caminho padrão. Há também um sentimento comum de que diversos recursos do Next.js são suportados apenas na Vercel. Embora isso seja verdade para os novos recursos do Next.js que são testados e suportados na Vercel deste o seu lançamento, também é verdade que outros provedores como a Netlify buscam [implementar e liberar rapidamente versões de suporte](https://www.netlify.com/blog/deploy-nextjs-13/) para [recursos estáveis do Next.js](https://docs.netlify.com/integrations/frameworks/next-js/overview/) Existem diversos prós e contras dentre todos provedores de deploy, já que nenhum host pode consegue ter o melhor suporte para todos os casos de uso existente. Por exemplo, a Netlify criou o seu próprio [Next.js runtime customizado](https://github.com/netlify/next-runtime) para as Netlify Edge Functions (que rodam no Deno Deploy) e [mantém um único middleware para acessar e modificar respostas HTTP](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). Para verificar o status de recursos não estáveis do Next 13 veja [Using the Next 13 `app` directory on Netlify](https://github.com/netlify/next-runtime/discussions/1724). ## Configuração do Projeto Há diversas maneiras de configurar suas instruções de build, incluindo diretamente através da Netlify CLI ou pelo Netlify Dashboard. Embora não seja obrigatório, é recomendável criar e incluir um arquivo [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Isso garante que as versões fork ou clonadas do projeto sejam mais fáceis de implementar. ```toml [build] command = "next build" publish = ".next" ``` ## Usando o Netlify Dashboard 1. Dê o push de seu código para um repositório do GitHub e inscreva-se na [Netlify](https://app.netlify.com/signup). Depois de ter criado uma conta, clique em **Add new site** e então **Import an existing project**. ![Novo projeto na Netlify](/images/netlify-01-new-project.webp) 2. Conecte o seu provedor Git. ![Importar repositório](/images/netlify-02-connect-to-git-provider.webp) 3. Selecione o repositório de seu projeto. ![Selecione o repositório de seu projeto](/images/netlify-03-pick-a-repository-from-github.webp) 4. A Netlify detectará se você possui um arquivo `netlify.toml` e então vai automaticamente configurar seu comando de build e diretório de publicação. ![Configurações de build do Nextjs](/images/netlify-04-configure-build-settings.webp) 5. Clique em **Show advanced** e então **New variable** para adicionar suas variáveis de ambiente. ![Adicionar variáveis de ambiente](/images/netlify-05-env-vars.webp) 6. Clique em **Deploy site**, aguarde até o processo de build terminar, e então veja seu novo site. ## Usando a Netlify CLI Para fazer o deploy a partir da linha de comando, primeiramente você fazer o push de seu projeto para um repositório do GitHub e [instalar a Netlify CLI](https://docs.netlify.com/cli/get-started/). Você pode instalar a `netlify-cli` como uma dependência de seu projeto ou instalar globalmente em sua máquina usando o seguinte comando: ```bash npm i -g netlify-cli ``` Para testar o seu projeto localmente, rode o comando [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) e abra [`localhost:8888`](http://localhost:8888/) para ver sua aplicação Netlify rodando localmente: ```bash ntl dev ``` Rode o comando [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) para configurar seu projeto: ```bash ntl init ``` Importe as variáveis de ambiente de seu projeto que estão em seu arquivo `.env` com [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Faça o deploy de seu projeto com [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Você precisará passar a flag `--build` para rodar o comando de build antes do deploy e a flag `--prod` para fazer o deploy do seu site em sua URL principal: ```bash ntl deploy --prod --build ``` Para ver um exemplo rodando na Netlify, visite [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/pt/deployment/vercel.md ================================================ --- title: Vercel description: Fazendo deploy para a Vercel layout: ../../../layouts/docs.astro lang: pt --- Recomendamos fazer o deploy de sua aplicação na [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Isso facilita muito o deploy de sua aplicação Next.js. ## Configuração do projeto A Vercel provavelmente configurará seu comando de build e publicará o diretório automaticamente. No entanto, você também pode especificar essas informações junto com outras configurações criando um arquivo chamado [`vercel.json`](https://vercel.com/docs/project-configuration) e incluindo os seguintes comandos: ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Usando o painel de controle da Vercel 1. Depois de enviar seu código para um repositório GitHub, faça login na [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) com o GitHub e clique em **Add New Project**. ![Novo projeto no Vercel](/images/vercel-new-project.webp) 2. Importe o repositório GitHub com seu projeto. ![Importar repositório](/images/vercel-import-project.webp) 3. Adicione suas variáveis de ambiente. ![Adicionar variáveis de ambiente](/images/vercel-env-vars.webp) 4. Clique em **Deploy**. Agora, sempre que você enviar uma alteração ao seu repositório, a Vercel reimplementará automaticamente seu aplicativo! ## Usando a CLI da Vercel Para fazer deploy a partir da linha de comando, você deve primeiro [instalar a CLI da Vercel globalmente](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Execute o comando [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) para implantar seu projeto. ```bash vercel ``` Inclua `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` para variáveis de ambiente como a string de conexão do banco de dados. Use `--yes` se quiser pular as perguntas de implantação e dar a resposta padrão para cada uma. ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` Após o primeiro deploy, esse comando o fará em um branch de preview. Você precisará incluir `--prod` para enviar alterações diretamente para a produção. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/pt/faq.mdx ================================================ --- title: FAQ description: Perguntas frequentes sobre o Create T3 App layout: ../../layouts/docs.astro lang: pt isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Aqui estão algumas perguntas frequentes sobre o Create T3 App. ## O que vem a seguir? Como eu faço uma aplicação com isso? Nós tentamos manter esse projeto o mais simples o possível, assim você pode começar só com o básico que configuramos pra você, e adicionar mais tarde o que se tornar necessário. Caso você não esteja familiarizado com as diferentes tecnologias usadas neste projeto, por favor se dirija à documentação respectiva. Se você ainda estiver perdido, sinta-se convidade a entrar na nossa comunidade no [Discord](https://t3.gg/discord) e pedir ajuda. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## Como faço para manter minha aplicação atualizada? Create T3 App não é um framework, é uma ferramenta com o básico necessário para iniciar o desenvolvimento de uma aplicação. Isso significa que depois de inicializar uma aplicação, ele é seu. Não há nenhum comando de terminal ou algo do tipo para mantê-lo atualizado. Se desejas manter-se atualizado com qualquer atualização que façamos ao template, você pode [ativar as notificações de versões](https://docs.github.com/pt/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications) de nosso repositório. Sabendo de tudo isso, não é realmente necessário implementar toda e qualquer alteração de nosso template, em sua aplicação. ## Onde aprender sobre a stack? Por mais que os recursos listados abaixo são alguns dos melhores que existem para a T3 Stack, a comunidade (e o [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) recomendam que você comece apenas usando a stack e aprendendo enquanto desenvolve e constrói com ela. Se você está cogitando usar o Create T3 App, há altas chances que você já esteja usando algumas partes da stack. Então por que não se jogar de cabeça e aprender outras partes enquanto constrói algo? Agora, nós percebemos que essa parte não funciona para todos. Então, se você sente que testou as recomendações e ainda assim gostaria de alguns recursos extras, ou só não está confiante em fazer por conta própria e/ou se sente sobrecarregado com a stack, confira esses incríveis tutoriais (infelizmente, só em inglês até o momento) sobre o Create T3 App: ### Artigos - [Build a full stack app with Create T3 App](https://www.nexxel.dev/blog/ct3a-guestbook) - [A first look at Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Integrating Stripe into your T3 App](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Vídeos - [T3 Stack Tutorial - FROM 0 TO PROD FOR $0 (Next.js, tRPC, TypeScript, Tailwind, Prisma & More)](https://www.youtube.com/watch?v=YkOSUVzOAA4) **(recomendado)** - [Jack Herrington - Build a Note Taking app with the T3 Stack](https://www.youtube.com/watch?v=J1gzN1SAhyM) - [Build a Twitter Clone with the T3 Stack - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [An overview of the Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Por que tem arquivos `.js` no projeto? Conforme o axioma [Axioma T3 #3](/pt/introduction#segurança-de-tipo-não-é-opcional) nós consideramos _typesafety_ como um cidadão de primeira classe. Infelizmente, nem todos os frameworks e plugins suportam TypeScript, o que significa que alguns arquivos tiveram que ser arquivos `.js`. Nós buscamos enfatizar que esses arquivos são JavaScript por uma razão, explicitamente declarando cada tipo de arquivo (`cjs` ou `mjs`) dependendo do que é suportado pela biblioteca que o usa. Também, todos os arquivos `js` nesse projeto ainda assim são checados em relação à tipo, usando a opção checkJs no compilador (tsconfig). ## Estou tendo dificuldades em adicionar o i18n ao meu projeto. Existe alguma referência que eu possa usar? Nós decidimos não incluir o i18n por padrão no `create-t3-app` por conta de ser um tópico muito opinado e haverem muitas maneiras de implementá-lo. De qualquer forma, se você tiver dificuldades de implementá-lo e quiser ver um projeto de referência, nós temos um [repositório exemplo](https://github.com/juliusmarminge/t3-i18n) que mostra como você pode adicionar o i18n a um T3 app usando [next-i18next](https://github.com/i18next/next-i18next). ## Por que nós estamos usando `/pages` e não `/app` do Next.js 13? Conforme o [Axioma T3 #2](/pt/introduction#agir-com-responsabilidade), nós amamos _tecnologia de ponta_, porém valorizamos a estabilidade, nosso roteador inteiro é díficil de mover, [não é um bom lugar para utilizar _bleeding edges_](https://youtu.be/mnwUbtieOuI?t=1662). Enquanto `/app` é [instável e experimental](https://youtu.be/rnsC-12PVlM?t=818), não está pronto para produção; a API está em beta e é esperado que possua _breaking changes_. Para obter uma lista de recursos suportados, planejados e trabalhados no diretório `/app`, visite a [Documentação beta do Next.js](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/pt/folder-structure-pages.mdx ================================================ --- title: Estrutura de Pastas (Pages) description: Estrutura de pastas de um novo T3 App inicializado layout: ../../layouts/docs.astro lang: pt isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Por favor, selecione seus pacotes para ver a estrutura de pastas de um aplicativo recém-estruturado com essas seleções. Mais abaixo, você encontrará uma descrição de cada pacote.
### `prisma` A pasta `prisma` contém o arquivo `schema.prisma` que é usado para configurar a conexão com o banco de dados e o schema do banco de dados. Ela também é onde são salvos os arquivos de migração e/ou scripts de seed, se usados. Veja [Uso do Prisma](/en/usage/prisma) para mais informações.
### `public` A pasta `public` contém recursos estáticos que são servidos pelo servidor web. O `favicon.ico` é um exemplo de um recurso estático.
### `src/env` Usado para a validação de variáveis ambiente e definição de tipos - veja [Variáveis de Ambiente](usage/env-variables).
### `src/pages` A pasta `pages` contém todas as páginas da aplicação Next.js. O arquivo `index.tsx` na raiz da pasta `/pages` é a página inicial da aplicação. O arquivo `_app.tsx` é usado para envolver a aplicação com providers. Veja a [Documentação do Next.js](https://nextjs.org/docs/basic-features/pages) para mais informações.
#### `src/pages/api` A pasta `api` contém todas as rotas API da aplicação Next.js. O arquivo `examples.ts` contém um exemplo de rota que usa o recurso de [Rotas API do Next.js](https://nextjs.org/docs/api-routes/introduction) junto com o prisma. O arquivo `restricted.ts` contém um exemplo de rota que usa o recurso de [Rotas API do Next.js](https://nextjs.org/docs/api-routes/introduction) e é protegido pelo [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` O arquivo `[...nextauth].ts` é a rota de autenticação com slug do NextAuth.js. Ele é usado para lidar com as requisições de autenticação. Veja [Uso do NextAuth.js](usage/next-auth) para mais informações sobre o NextAuth.js, e a [Documentação de Rotas Dinâmicas do Next.js](https://nextjs.org/docs/routing/dynamic-routes) para informações sobre rotas com parâmetros/slugs.
#### `src/pages/api/trpc/[trpc].ts` O arquivo `[trpc].ts` é o ponto de entrada do tRPC. Ele é usado para lidar com as requisições do tRPC. Veja [Uso do tRPC](usage/trpc#-pagesapitrpctrpcts) para mais informações sobre esse arquivo, e [Documentação de Rotas Dinâmicas do Next.js](https://nextjs.org/docs/routing/dynamic-routes) para informações sobre rotas com parâmetros/slugs.
### `src/server` A pasta `server` é usada para de maneira clara separar código do servidor e código do cliente.
#### `src/server/auth.ts` Contém utilitários para autenticação, como recuperar a sessão do usuário no lado do servidor. Veja [uso do NextAuth.js](usage/next-auth#usage-with-trpc) para mais informações.
#### `src/server/db.ts` O arquivo db.ts é usado para instanciar o cliente Prisma no escopo global. Veja [uso do Prisma](usage/prisma#prisma-client) e [melhores práticas para usar o Prisma com o Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) para mais informações.
### `src/server/api` A pasta api contém o código do lado do servidor do tRPC.
#### `src/server/api/routers` A pasta routers contém todos as sub-rotas do tRPC.
#### `src/server/api/routers/example.ts` O arquivo `example.ts` é um exemplo de roteador tRPC que utiliza o utilitário `publicProcedure` para demonstrar como criar uma rota tRPC pública. Dependendo dos pacotes escolhidos, este roteador contém mais ou menos rotas para melhor demonstrar o uso de acordo com suas necessidades.
#### `src/server/trpc/trpc.ts` O arquivo `trpc.ts` é o arquivo de configuração principal para seu back-end tRPC. Aqui nós: 1. Definimos o contexto usado nas requests tRPC. Veja [uso do tRPC](usage/trpc#-serverapitrpcts) para mais informações. 2. Exportamos os auxiliares de procedures. Veja [uso do tRPC](usage/trpc#-serverapitrpcts) para mais informações.
#### `src/server/api/root.ts` O arquivo `root.ts` é usado para mesclar as rotas tRPC e exportá-las como um único roteador, bem como a definição de tipo da rota. Veja [uso do tRPC](usage/trpc#-serverapirootts) para mais informações.
### `src/styles` A pasta `styles` contém os estilos globais da aplicação.
### `src/types` A pasta `types` é usada para guardar tipos ou definições de tipos reutilizáveis.
#### `src/types/next-auth.d.ts` O arquivo `next-auth.d.ts` é usado para estender o tipo padrão de sessão do NextAuth para incluir o ID de usuário. Veja [Uso do NextAuth.js](usage/next-auth#inclusão-do-userid-na-sessão) para mais informações.
### `src/utils` A pasta `utils` é usada para guardar funções utilitárias comuns reutilizáveis.
#### `src/utils/api.ts` O arquivo `api.ts` é o ponto de entrada front-end para o tRPC. Veja [uso do tRPC](usage/trpc#-utilsapits) para mais informações.
### `.env` O arquivo `.env` é usado para armazenas as variáveis ambiente. Veja [Variáveis de Ambiente](usage/env-variables) para mais informações. Esse arquivo **não** deve ser commitado para o histórico do git.
### `.env.example` O arquivo `.env.example` mostra um exemplo de de variáveis ambiente baseado nas bibliotecas escolhidas. Esse arquivo deve ser commitado para o histórico do git.
### `.eslintrc.cjs` O arquivo `.eslintrc.cjs` é usado para configurar o ESLint. Veja [Documentação do ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) para mais informações.
### `next-env.d.ts` O arquivo `next-env.d.ts` garante que os tipos do Next.js sejam lidos pelo compilador do TypeScript. **Você não deve remover ou editar, uma vez que pode mudar a qualquer momento.** Veja [Documentação do Next.js ](https://nextjs.org/docs/basic-features/typescript#existing-projects) para mais informações.
### `next.config.mjs` O arquivo `next.config.mjs` é usado para configura o Next.js. Veja [Documentação do Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction) para mais informações. Nota: a extensão .mjs é usada para permitir importações de módulos ESM.
### `postcss.config.js` O arquivo `postcss.config.js` é usado para o uso do Tailwind PostCSS. Veja [Documentação do Tailwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss) para mais informações.
### `prettier.config.mjs` O arquivo `prettier.config.mjs` é usado para configura o Prettier e incluir o prettier-plugin-tailwindcss para a formatação das classes CSS. Veja o post no [Blog do Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) para mais informações.
### `tsconfig.json` O arquivo `tsconfig.json` é usado para configurar o TypeScript. Algumas configurações diferentes do padrão, como `strict mode`, foram habilitadas para garantir o melhor uso do TypeScript no create-t3-app e suas bibliotecas. Veja [Documentação do Typescript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) ou [Uso do TypeScript](usage/typescript) para mais informações.
================================================ FILE: www/src/pages/pt/installation.mdx ================================================ --- title: Instalação description: Instruções de Instalação do Create T3 App layout: ../../layouts/docs.astro lang: pt isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Para estruturar uma aplicação usando `create-t3-app`, rode qualquer um dos comandos seguintes e responda as perguntas: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` Após sua aplicação ter sido estruturada, verifique os [Primeiros Passos](/pt/usage/first-steps) para começar a usar sua nova aplicação. ## Uso avançado | Opção/Flag | Descrição | | ----------------- | --------------------------------------------------------------------------------- | | `[dir]` | Inclua um argumento de diretório com um nome para o projeto | | `--noGit` | Diga explicitamente à CLI para não inicializar um novo repositório git no projeto | | `-y`, `--default` | Ignore a CLI e inicialize um novo aplicativo t3 com todas as opções selecionadas | | `--noInstall` | Gera um projeto sem instalar as dependências | ## Uso experimental Para o nosso CI (Integração Contínua), nós temos algumas flags experimentais que te permitem inicializar qualquer aplicação sem responder nenhuma pergunta. Se esse caso de uso se aplicar a você, você pode usar essas flags. Note que essas flags são experimentais e podem mudar no futuro sem seguir nenhum versionamento. | Flag | Descrição | | ------------ | -------------------------------------- | | `--CI` | Informa a CLI que você está no modo CI | | `--trpc` | Inclui o tRPC no projeto | | `--prisma` | Inclui o Prisma no projeto | | `--nextAuth` | Inclui o NextAuth.js no projeto | | `--tailwind` | Inclui o Tailwind CSS no projeto | Se você não providenciar a flag `CI`, o resto dessas flags não possui efeito. Você não precisa explicitamente excluir os pacotes que não deseja. De qualquer forma, se você preferir ser explícito, você pode passar a flag `false`, ex: `--nextAuth false`. ### Exemplo O comando a seguir irá inicializar um T3 App com tRPC e Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/pt/introduction.md ================================================ --- title: Introdução description: Introdução à T3 Stack layout: ../../layouts/docs.astro lang: pt ---
## A T3 Stack A _"T3 Stack"_ é uma stack de desenvolvimento web feita por [Theo](https://twitter.com/t3dotgg) focada na simplicidade, modularidade e ser full-stack mantendo a segurança de tipos (typesafe). As peças principais são o [**Next.js**](https://nextjs.org/) e [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) é quase sempre incluso. Se você está fazendo algo que inclua back-end, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), e [**NextAuth.js**](https://next-auth.js.org/) são ótimas adições também. Talvez você tenha percebido que tem... várias peças. Mas isso é por design. Adicione, retire e troque as peças conforme necessário - essa stack é modular desde o núcleo :) ## Então... o que é o create-t3-app? Um modelo? Basicamente. O `create-t3-app` é uma CLI (Command Line Interface) construída pelos experientes desenvolvedores da T3 Stack para agilizar a configuração inicial de um aplicativo T3 modular. Isso significa que cada uma das peças é opcional, e o "modelo" é gerado baseado nas suas necessidades específicas. Depois de incontáveis projetos e muitos anos com essas tecnologias, nós tempos muitas opiniões e percepções. Nós fizemos o nosso melhor para introduzi-las nessa CLI. Isto **NÃO** é um modelo completo. Nós **esperamos** que você traga suas próprias bibliotecas que resolvam as necessidades da **SUA** aplicação. Enquanto nós não queremos prescrever soluções para problemas específicos como gerenciamento de estado e deploy, nós [possuímos algumas recomendações listadas aqui](/pt/other-recs). ## Axiomas T3 Sejamos francos - este é um _projeto opinado_. Nós compartilhamos um punhado de crenças fundamentais a respeito de desenvolvimento e as tratamos como base para nossas decisões. ### Resolver Problemas É fácil cair nessa armadilhas de "adicionar tudo" - e nós explicitamente não queremos fazer isso. Tudo adicionado ao `create-t3-app` deve resolver um problema específico que existe dentro das principais tecnologias inclusas. Isso significa que nós não iremos adicionar coisas como bibliotecas de gerenciamento de estado (`zustand`, `redux`) mas iremos adicionar coisas como NextAuth.js e integrar o Prisma e tRPC para você. ### Agir com Responsabilidade Nós amamos tecnologia de ponta. A quantidade de velocidade e honestamente, diversão que vem com essas coisas novas é realmente muito legal. Nós pensamos que é importante agir com responsabilidade, usando tecnologias mais arriscadas nas partes menos arriscadas. Isso significa que não iríamos ⛔️ apostar em uma nova tecnologia de banco de dados arriscada (SQL é bom!). Porém felizmente ✅ apostar no tRPC uma vez que são só funções triviais de se mover. ## Tipos seguros e bem definidos não são opcionais O objetivo declarado do `create-t3-app` é providenciar a maneira mais rápida de se começar uma aplicação web full-stack e **typesafe**. Nós levamos segurança de tipo muito a sério nessas partes, já que melhora nossa produtividade e nos ajuda a entregar menos bugs. Qualquer decisão que comprometa a natureza de segurança de tipo do `create-t3-app` é uma decisão que deve ser feita em um projeto diferente. ================================================ FILE: www/src/pages/pt/other-recs.md ================================================ --- title: Outras Recomendações description: Bibliotecas e Serviços que nós recomendamos para vários projetos. layout: ../../layouts/docs.astro lang: pt --- Nós reconhecemos que as bibliotecas inclusas no `create-t3-app` não resolvem todos os problemas. Enquanto nós te encorajamos a começar o seu projeto com as coisas que providenciamos, em algum momento você terá que adicionar novos pacotes. Apenas você pode saber o que o seu projeto precisa, mas aqui estão algumas coisas que nos encontramos recomendando com frequência. Estas são recomendações de contribuidores individuais do Create T3 App e não devem ser vistas como propriedades "oficiais" pela equipe Create T3 App ou T3-OSS. _**Faça sua própria pesquisa, especialmente antes de se comprometer com serviços pagos**_. ## Gerenciamento de Estado _**Nota do editor**_: Bibliotecas de gerenciamento de estado podem ser boas, mas normalmente não são necessárias. Os hooks do React Query + tRPC devem ser capazes de tomar conta do seu estado do lado do servidor. Para o lado do cliente, comece com o `useState` do React, e procure por alguma dessas opções quando precisar de mais. ### Zustand **Para nunca mais usar Redux** O "moderno e simples Redux", que você não sabia que precisava. Sempre podemos confiar em [Poimandres](https://github.com/pmndrs). Você pode construir tudo, desde aplicativos de videochamada até jogos e servidores com essa pequena biblioteca. - [Página inicial do Zustand](https://zustand-demo.pmnd.rs/) - [GitHub do Zustand](https://github.com/pmndrs/zustand) ### Jotai **Para nunca mais usar Context** Para uma abordagem mais atômica, o Jotai é difícil de ser batido. Também da [Poimandres](https://github.com/pmndrs), Jotai te permite definir singletons que parecem um useState global. Uma boa opção para manipulações de estado que não necessariamente precisam de uma máquina de estado. - [Página inicial do Jotai](https://jotai.org/) - [GitHub do Jotai](https://github.com/pmndrs/jotai) ## Biblioteca de Componentes A maior parte das aplicações necessitam de vários componentes - botões de toggle, menus dropdown, modais, e assim por diante. Essas bibliotecas providenciam components bons, acessíveis e que você pode customizar como achar melhor. ### Bibliotecas de Componentes não estilizados Também conhecidas como bibliotecas headless, elas providenciam componentes não estilizados, acessíveis e bons que você pode customizar como achar melhor. Aqui estão algumas recomendações - [Radix UI](https://www.radix-ui.com/) oferece um conjunto poderoso de primitivos convenientes e acessíveis que você pode estilizar com CSS puro ou Tailwind CSS. - [Headless UI](https://headlessui.com/) feito pela equipe do Tailwind CSS também fornece componentes acessíveis e sem estilo que se integram perfeitamente ao Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) fornece primitivos de UI acessíveis para seu design system. Seu componente Date Picker é animal. ### Bibliotecas de Componentes estilizados **Para quando você quer apenas que seu aplicativo seja OK** Às vezes, você está construindo um projeto em que deseja apenas que a interface do usuário pareça decente fora da caixa. Para painéis de administração e outros projetos semelhantes, qualquer uma dessas bibliotecas de componentes fará o trabalho. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **Para criar bibliotecas de UI** Crie declarativamente uma biblioteca de UI com diferentes variantes de cor, tamanho etc. Quando seu projeto atinge uma escala em que você deseja um conjunto padronizado de componentes de interface do usuário com várias variantes usando Tailwind CSS, o CVA é uma ótima ferramenta. - [GitHub do Class Variance Authority](https://github.com/joe-bell/cva) ## Animações Para quando você precisar de animações em seu aplicativo, aqui estão nossas recomendações. ### AutoAnimate **Para animações com uma única linha de código** A maioria das bibliotecas de animação tenta satisfazer todos os casos de uso possíveis e, como resultado, torna-se desajeitada. O AutoAnimate é uma ferramenta de configuração zero que oferece uma melhoria significativa no UX sem nenhum esforço adicional do desenvolvedor. - [Página inicial do AutoAnimate](https://auto-animate.formkit.com/) - [GitHub do AutoAnimate](https://github.com/formkit/auto-animate) - [Snippet de um componente com AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **Para animações complexas com código declarativo** O Framer Motion fornece uma sintaxe simples e declarativa e permite que você escreva menos código para criar tudo, desde animações complexas até gestos. - [Página inicial do Framer Motion](https://framer.com/motion) - [Documentation do Framer Motion](https://www.framer.com/docs/) ## Deploy, Infraestrutura, Bancos de Dados e CI ### Vercel **Para hospedar a sua aplicação** A Vercel tomou conta das implantações da web e transformou-as em uma integração do GitHub definida uma vez e esquecida. Nós escalamos para centenas de milhares de usuários sem problemas. Com tecnologia AWS, apenas uma interface muito melhor :) - [Página inicial da Vercel](https://vercel.com/) - [Guia de Deploy Create T3 App na Vercel](/pt/deployment/vercel) ### PlanetScale **Para bancos de dados sem preocupações** PlanetScale é a melhor "plataforma de banco de dados serverless" que nós já usamos até então. Insana escalabilidade, boa experiência de desenvolvimento, e preço incomparável. Se você está usando SQL (e esperançosamente Prisma), isso é difícil de bater. - [Página inicial do PlanetScale](https://planetscale.com/) ### Railway **Para hospedar sua infra** É a "Heroku moderna". A maneira mais fácil de colocar um servidor real em funcionamento. Se Vercel e PlanetScale não são suficientes, Railway provavelmente é. Aponte para um repositório do GitHub e pronto. - [Página inicial da Railway](https://railway.app/) ### Upstash **Para Redis serverless** Nós amamos Prisma e PlanetScale, mas alguns projetos precisam de uma solução mais performática. O Upstash permite que você obtenha o desempenho de memória do Redis em seu projeto sem servidor, sem precisar gerenciar a infraestrutura e escalar você mesmo. - [Página inicial do Upstash](https://upstash.com/) ### Pusher **Para WebSockets serverless** Se WebSockets for o foco principal do seu projeto, considere um back-end mais tradicional, como [Fastify](https://www.fastify.io/) (que [também funciona com tRPC!](https://trpc.io/docs/v10/fastify)). Mas para adicionar rapidamente WebSockets a um aplicativo T3, o Pusher é uma excelente escolha. - [Página inicial do Pusher](https://pusher.com/) ### Soketi O Soketi é uma alternativa auto-hospedável, simples e rápida ao Pusher. É totalmente compatível com o Pusher SDK, que você pode usar para se conectar ao servidor. Soketi serverless também está em beta. - [Página inicial do Soketi](https://soketi.app) - [GitHub do Soketi](https://github.com/soketi/soketi) ## Analytics Os dados do usuário são muito valiosos quando você está criando um aplicativo. Aqui estão alguns provedores de análise que recomendamos. ### Plausible Precisa de análise? Plausível é uma das maneiras mais rápidas de obtê-los. Super mínimo. Ele ainda tem um [plugin simples para Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Página inicial do Plausible](https://plausible.io/) ### Umami Umami é uma alternativa de código aberto, auto-hospedável, simples, rápida e focada em privacidade para o Google Analytics. Você pode implantá-lo facilmente em Vercel, Railway, etc. com PlanetScale como seu banco de dados. - [Página inicial do Umami](https://umami.is/) - [GitHub do Umami](https://github.com/umami-software/umami) ## Outros ### Next Bundle Analyzer Às vezes, pode ser difícil determinar o que será incluído na saída de compilação do seu aplicativo. O Next Bundle Analyzer é uma maneira fácil de visualizar e analisar os pacotes JavaScript gerados. - [@next/bundle-analyzer no npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/pt/t3-collection.mdx ================================================ --- title: Coleção T3 description: Projetos bacanas open source e empresas usando a T3 stack layout: ../../layouts/docs.astro lang: pt isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import Callout from "../../components/docs/callout.tsx"; Fez um projeto usando a T3 stack e deseja comparilhar? Adicione ele na lista! ## Aplicações Open Source desenvolvidas usando a T3 Stack ## Empresas usando a T3 Stack Nós adoraríamos saber de empresas usando a T3 Stack para seus apps. A sua empresa está usando a T3 Stacks e gostaria de compartilhar? Adicione à lista! Tem um projeto legal usando a T3 stack? Faça uma [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) e adicione-o aqui! ================================================ FILE: www/src/pages/pt/usage/env-variables.mdx ================================================ --- title: Variáveis de Ambiente description: Iniciando com Create T3 App layout: ../../../layouts/docs.astro lang: pt isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; O Create T3 App usa sua mais nova biblioteca [@t3-oss/env-nextjs](https://env.t3.gg) e [zod](https://zod.dev) para validar variáveis de ambiente durante o tempo de execução (runtime) _e_ buildtime fornecendo uma lógica simples no arquivo `src/env.js`: ## env.js _Muito longo, não li; Se você quiser adicionar uma nova variável de ambiente, você deve adicioná-la ao seu `.env` ao definir no validador em `src/env.js`._ Este arquivo é dividido em duas partes - o esquema e a desestruturação do objeto, bem como a lógica de validação. A lógica de validação não deve ser tocada. ```ts:env.js import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, }, }); ``` T3 Env traz um novo conceito através do `createEnv`, o qual é responsável por criar o esquema e irá incluir a lógica de validação para variáveis de ambiente no cliente e servidor. Para mais informações sobre como o `createEnv` funciona internamente, confira a documentação de [T3 Env](https://env.t3.gg/docs/introduction) ## Usando Variáveis de Ambiente Quando você quiser usar suas variáveis de ambiente, você pode importá-las de `env.js` e usá-las como faria normalmente. Se você importar isso no cliente e tentar acessar uma variável de ambiente do lado do servidor, receberá um erro em tempo de execução (runtime). ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` é totalmente tipado e fornece preenchimento automático const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ Isso lançará um erro em tempo de execução const dbUrl = env.DATABASE_URL; // ✅ Isso está correto const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example Como o arquivo `.env` padrão não está comprometido com o controle de versão, também incluímos um arquivo `.env.example`, no qual você pode, opcionalmente, manter uma cópia de seu arquivo `.env` com quaisquer segredos removidos. Isso não é necessário, mas recomendamos manter o exemplo atualizado para tornar mais fácil para os contribuidores começarem a usar seu ambiente. Alguns frameworks e outras ferramentas, como o Next.js, sugerem que você armazene variáveis em um arquivo `.env.local` e faça commit de arquivos `.env` em seu projeto. Isso não é recomendado, pois poderia facilitar o acidente de incluir variáveis ambiente secretas em seu histórico do git. Em vez disso, recomendamos que você armazene essas variáveis no arquivo `.env`, mantenha o arquivo `.env` em seu `.gitignore` e faça commit somente de arquivos `.env.example` em seu projeto. ## Adicionando Variáveis Ambiente Para garantir que sua compilação nunca seja concluída sem as variáveis de ambiente de que o projeto precisa, você precisará adicionar novas variáveis de ambiente em **dois** locais: 📄 `.env`: Insira sua variável de ambiente como faria normalmente em um arquivo `.env`, ou seja, `CHAVE=VALOR`. Além disso, certifique-se de desestruturar elas na opção `runtimeEnv`, por exemplo, `CHAVE: process.env.CHAVE`. 📄 `env.js`: Adicione a lógica de validação apropriada para as variáveis de ambiente definindo um esquema Zod, por exemplo `NOME_DA_VARIAVEL: z.string()` Opcionalmente, você também pode manter `.env.example` atualizado: 📄 `.env.example`: Insira sua variável de ambiente, mas certifique-se de não incluir o valor se for secreto, ou seja, `CHAVE=VALOR` ou `CHAVE=` ### Exemplo _Quero adicionar meu token da API do Twitter como uma variável de ambiente do lado do servidor_ 1. Adicione a variável de ambiente a `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Adicione a variável de ambiente a `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { TWITTER_API_TOKEN: z.string(), }, // ... }); ``` 3. opcional: adicione a variável de ambiente a `.env.example`, mas não inclua o token ``` export const env = createEnv({ // ... runtimeEnv: { TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }, }); ``` ================================================ FILE: www/src/pages/pt/usage/first-steps.md ================================================ --- title: Primeiros passos description: Começando com seu novo T3 App layout: ../../../layouts/docs.astro lang: pt --- Você acabou de criar um novo aplicativo T3 e está pronto para começar. Aqui está o mínimo para que seu aplicativo funcione. ## Banco de dados Se o seu aplicativo incluir o Prisma, certifique-se de executar `npx prisma db push` no diretório raiz do seu aplicativo. Este comando sincronizará o schema do Prisma com seu banco de dados e gerará as tipagens do TypeScript para o Prisma Client com base em seu schema. Observe que você precisa reiniciar o servidor TypeScript depois de fazer isso para que ele possa detectar os novos tipos gerados. ## Autenticação Se seu aplicativo incluir NextAuth.js, vamos começar com o `DiscordProvider`. Este é um dos provedores mais simples que o NextAuth.js oferece, mas ainda requer um pouco de configuração inicial de sua parte. Claro, se você preferir usar um provedor de autenticação diferente, também pode usar um dos [muitos provedores](https://next-auth.js.org/providers/) que o NextAuth.js oferece. 1. Você precisará de uma conta no Discord, então crie uma se ainda não tiver. 2. Navegue até https://discord.com/developers/applications e clique em "Novo aplicativo" no canto superior direito. Dê um nome ao seu aplicativo e concorde com os Termos de Serviço. 3. Depois de criar seu aplicativo, navegue até "Configurações → OAuth2 → Geral". 4. Copie o "ID do cliente" e adicione-o ao seu `.env` como `AUTH_DISCORD_ID`. 5. Clique em "Redefinir Segredo", copie o novo segredo e adicione-o ao seu `.env` como `AUTH_DISCORD_SECRET`. 6. Clique em "Adicionar redirecionamento" e digite `http://localhost:3000/api/auth/callback/discord`. - Para implantação de produção, siga as etapas anteriores para criar outro aplicativo Discord, mas desta vez substitua `http://localhost:3000` pela URL na qual você está implantando. 7. Salve as alterações. Agora você deve conseguir fazer login. ## Próximos passos - Se sua aplicação incluir tRPC, confira `src/pages/index.tsx` e `src/server/trpc/router/post.ts` para ver como funcionam as consultas tRPC. - Dê uma olhada na documentação `create-t3-app`, bem como nos documentos dos pacotes que sua aplicação inclui. - Junte-se ao nosso [Discord](https://t3.gg/discord) e dê-nos uma estrela no [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/pt/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Uso", layout: "docs", description: "Aprenda a usar as diferentes tecnologias da T3 Stack.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/pt/usage/next-auth.md ================================================ --- title: NextAuth.js description: Uso do NextAuth.js layout: ../../../layouts/docs.astro lang: pt --- Quando você deseja um sistema de autenticação em sua aplicação Next.js, o NextAuth.js é uma excelente solução para trazer a complexidade da segurança sem o incômodo de ter que construí-lo sozinho. Ele vem com uma extensa lista de provedores para adicionar rapidamente a autenticação OAuth e fornece adaptadores para muitos bancos de dados e ORMs. ## Context Provider No ponto de entrada do seu aplicativo, você verá que ele está envolvida pelo [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx ``` Este provedor de contexto permite que seu aplicativo acesse os dados da sessão de qualquer lugar em seu aplicativo, sem ter que passá-los como props: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Lida com o estado não autenticado, por exemplo renderizar um componente SignIn return ; } return

Bem-vindo {session.user.name}!

; }; ``` ## Recuperando a sessão do lado do servidor. Às vezes, você poderá querer solicitar a sessão no servidor. Para fazer isso, faça uma requisição usando o utilitário `getServerAuthSession` que `create-t3-app` fornece e passe-a para o cliente usando `getServerSideProps`: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // NOTA: `session` não terá um estado de carregamento, pois já foi pré-carregado no servidor ... } ``` ## Inclusão do `user.id` na Sessão `create-t3-app` está configurado para utilizar o [retorno de chamada de sessão (sesion callback)](https://next-auth.js.org/configuration/callbacks#session-callback) na configuração NextAuth.js para incluir o ID do usuário dentro do objeto `session`. ```ts:pages/api/auth/[...nextauth].ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Isso é acoplado a um arquivo de declaração de tipo para garantir que o `user.id` tenha seu tipo quando acessado no objeto `session`. Leia mais sobre [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) nos documentos de NextAuth.js. ```ts:types/next-auth.d.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` O mesmo padrão pode ser usado para adicionar quaisquer outros dados ao objeto `session`, como um campo `role`, mas **não deve ser mal utilizado para armazenar dados confidenciais** no cliente. ## Uso com tRPC Ao usar NextAuth.js com tRPC, você pode criar procedimentos protegidos e reutilizáveis usando [middleware](https://trpc.io/docs/v10/middlewares). Isso permite criar procedimentos que só podem ser acessados por usuários autenticados. `create-t3-app` configura tudo isso para você, permitindo que você acesse facilmente o objeto de sessão dentro de procedimentos autenticados. Isso é feito em um processo de duas etapas: 1. Pegar a sessão dos headers da request usando a função [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). A vantagem de usar `getServerSession` em vez do `getSession` regular é que é uma função que é executada somente do lado do servidor e não faz chamadas de busca desnecessárias. O `create-t3-app` cria uma função auxiliar que abstrai essa API peculiar. ```ts:server/common/get-server-auth-session.ts export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return await getServerSession(ctx.req, ctx.res, nextAuthOptions); }; ``` Usando esta função auxiliar, podemos pegar a sessão e passá-la para o contexto tRPC: ```ts:server/trpc/context.ts import { getServerAuthSession } from "../common/get-server-auth-session"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Criar um middleware tRPC que verifique se o usuário está autenticado. Em seguida, usamos o middleware em um `protectedProcedure`. Qualquer chamador para esses procedimentos deve ser autenticado, caso contrário, será lançado um erro que pode ser tratado adequadamente pelo cliente. ```ts:server/trpc/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // inferer `session` som ikke-nullbar session: { ...ctx.session, user: ctx.session.user }, }, }); })); ``` O objeto de sessão é uma representação leve e mínima do usuário e contém apenas alguns campos. Ao usar `protectedProcedures`, você tem acesso ao id do usuário que pode ser usado para buscar mais dados do banco de dados. ```ts:server/trpc/router/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Uso com Prisma Fazer o NextAuth.js funcionar com o Prisma requer muita [configuração inicial](https://authjs.dev/reference/adapter/prisma/). O `create-t3-app` lida com tudo isso para você e, se você selecionar Prisma e NextAuth.js, obterá um sistema de autenticação totalmente funcional com todos os modelos necessários pré-configurados. Enviamos seu aplicativo montado com um provedor do Discord OAuth pré-configurado, que escolhemos porque é um dos mais fáceis de começar - basta fornecer seus tokens no `.env` e pronto. No entanto, você pode adicionar facilmente mais provedores seguindo a [documentação do NextAuth.js](https://next-auth.js.org/providers/). Observe que certos provedores exigem que campos extras sejam adicionados a determinados modelos. Recomendamos que você leia a documentação do provedor que deseja usar para certificar-se de que possui todos os campos obrigatórios. ### Adicionando novos campos aos seus modelos Ao adicionar novos campos a qualquer um dos modelos `User`, `Account`, `Session` ou `VerificationToken` (provavelmente você só precisaria modificar o modelo `User`), você precisa ter em mente que o [Adaptador Prisma](https://next-auth.js.org/adapters/prisma) cria campos automaticamente nesses modelos quando novos usuários se inscrevem e fazem login. Portanto, ao adicionar novos campos a esses modelos, você deve fornecer padrão valores para eles, pois o adaptador não está ciente desses campos. Se, por exemplo, você quiser adicionar um `role` (cargo) ao modelo `User`, você precisará fornecer um valor padrão para o campo `role`. Isso é feito adicionando um valor `@default` ao campo `role` no modelo `User`: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Uso com o middleware Next.js Uso de NextAuth.js com middleware Next.js [requer o uso da estratégia de sessão JWT](https://next-auth.js.org/configuration/nextjs#caveats) para autenticação. Isso ocorre porque o middleware só consegue acessar o cookie da sessão se for um JWT. Por padrão, `create-t3-app` é configurado para usar a estratégia de banco de dados **padrão**, em combinação com o Prisma como o adaptador de banco de dados. ## Configurando o DiscordProvider padrão 1. Vá para [a seção Aplicativos no Portal do desenvolvedor do Discord](https://discord.com/developers/applications) e clique em "Novo aplicativo" 2. No menu de configurações, vá para "OAuth2 => Geral" - Copie o Client ID e cole-o em `AUTH_DISCORD_ID` em `.env`. - Em Client Secret, clique em "Reset Secret" e copie essa string para `AUTH_DISCORD_SECRET` em `.env`. Tenha cuidado, pois você não poderá ver esse segredo novamente e redefini-lo fará com que o existente expire. - Clique em "Add Redirect" e cole em `/api/auth/callback/discord` (exemplo para desenvolvimento local: http://localhost:3000/api/auth/callback/discord) - Salve suas alterações - É possível, mas não recomendado, usar o mesmo aplicativo Discord tanto para desenvolvimento quanto para produção. Você também pode considerar [mockar o Provider](https://github.com/trpc/trpc/blob/next/examples/next-prisma-starter-websockets/src/pages/api/auth/%5B...nextauth%5D.ts) durante o desenvolvimento. ## Recursos Úteis | Recurso | Link | | -------------------------------- | --------------------------------------- | | Documentação do NextAuth.js | https://next-auth.js.org/ | | GitHub do NextAuth.js | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - com NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/pt/usage/next-js.md ================================================ --- title: Next.js description: Uso do Next.js layout: ../../../layouts/docs.astro lang: pt --- Next.js é um framework back-end para suas aplicações React.
Veja a [Fala do Theo na Next.js Conf](https://www.youtube.com/watch?v=W4UhNo3HAMw) para entender melhor o que é Next.js e como ele funciona.

## Por que devo usá-lo? Nós amamos React. Ele tornou o desenvolvimento de UI acessível de maneiras que nunca imaginamos antes. Também pode levar os desenvolvedores a alguns caminhos difíceis. O Next.js oferece uma abordagem altamente otimizada e levemente opinada para a criação de aplicações usando o React. Do roteamento às definições de API e à renderização de imagens, confiamos no Next.js para conduzir os desenvolvedores a boas decisões. Juntando o Next.js com a [Vercel](https://vercel.com/) torna o desenvolvimento e o deploy de aplicações web mais fáceis do que nunca. Sua interface gratuita extremamente generosa e super intuitiva fornece uma solução de apontar e clicar para implantar seu site (Nós ❤️ a Vercel) ## Get Static/Server Props Um recurso chave do Next.js são seus recursos de busca de dados. É altamente recomendável ler a [documentação oficial](https://nextjs.org/docs/basic-features/data-fetching) para entender como usar cada método e como eles diferem. `getServerSideProps` geralmente é desencorajado, a menos que haja uma boa razão para isso, devido ao fato de que é uma chamada de bloqueio e deixará seu site lento. [Regeneração estática incremental (ISR)](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) é uma ótima alternativa para `getServerSideProps` quando os dados são dinâmicos e podem ser buscados de forma incremental. ## Recursos Úteis | Recurso | Link | | ---------------------------------- | ---------------------------------- | | Documetação do Next.js | https://nextjs.org/docs | | GitHub do Next.js | https://github.com/vercel/next.js | | Blog do Next.js | https://nextjs.org/blog | | Discord do Next.js | https://nextjs.org/discord | | Twitter do Next.js | https://twitter.com/nextjs | | Canal do Youtube da Vercel/Next.js | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/pt/usage/prisma.md ================================================ --- title: Prisma description: Uso do Prisma layout: ../../../layouts/docs.astro lang: pt --- Prisma é um ORM para TypeScript, que permite que você defina seu schema e modelos de banco de dados em um arquivo `schema.prisma` e, em seguida, gere um cliente type-safe que pode ser usado para interagir com seu banco de dados a partir de seu back-end. ## Prisma Client Localizado em `/server/db/client.ts`, o Prisma Client é instanciado como uma variável global (conforme recomendado como [melhor prática](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) pela equipe da Prisma) e exportado para ser usado em suas rotas de API. Incluímos o Prisma Client em [Context](/en/usage/trpc#-serverapitrpcts) por padrão e recomendamos usá-lo em vez de importá-lo separadamente em cada arquivo. ## Schema Você encontrará o arquivo de esquema do Prisma em `/prisma/schema.prisma`. Este arquivo é onde você define seu esquema e modelos de banco de dados e é usado ao gerar o Prisma Client. ### Com NextAuth.js Quando você seleciona NextAuth.js em combinação com Prisma, o arquivo de esquema é gerado e configurado para você com os valores recomendados para os modelos `User`, `Session`, `Account` e `VerificationToken`, de acordo com os modelos na [documentação do NextAuth.js](https://next-auth.js.org/adapters/prisma). ## Banco de dados padrão O banco de dados padrão é um banco de dados SQLite, que é ótimo para desenvolvimento e criação rápida de uma prova de conceito, mas não é recomendado para produção. Você pode alterar o banco de dados a ser usado alterando o `provider` no bloco `datasource` para `postgresql` ou `mysql` e, em seguida, atualizando a string de conexão nas variáveis de ambiente para apontar para seu banco de dados. ## Propagar seu banco de dados [Propagar seu banco de dados](https://www.prisma.io/docs/guides/database/seed-database) é uma ótima maneira de preencher rapidamente seu banco de dados com dados de teste para ajudá-lo a começar. Para configurar a propagação, você precisará criar um arquivo `seed.ts` no diretório `/prisma` e, em seguida, adicionar um script `seed` ao seu arquivo `package.json`. Você também precisará de algum compilador de TypeScript que possa executar o script de seed. Recomendamos [tsx](https://github.com/esbuild-kit/tsx), que é um compilador TypeScript de alto desempenho que usa esbuild e não requer nenhuma configuração ESM, mas `ts-node` ou outros compiladores funcionam também. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db/client"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Em seguida, basta executar `pnpm db-seed` (ou `npm`/`yarn`) para propagar seu banco de dados. ## Recursos Úteis | Recurso | Link | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Documentação do Prisma | https://www.prisma.io/docs/ | | GitHub do Prisma | https://github.com/prisma/prisma | | Adaptador de Prisma para NextAuth.js | https://next-auth.js.org/adapters/prisma | | Guia de Conexão com PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/pt/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Uso do Tailwind CSS layout: ../../../layouts/docs.astro lang: pt --- ## O que é Tailwind CSS? Tailwind CSS é um pequeno framework CSS focando em [classes utilitárias](https://tailwindcss.com/docs/utility-first) para criar designs personalizados, sem a troca de contexto que o CSS normal exige. É puramente uma estrutura CSS e não fornece nenhum componente ou lógica pré-criada, e fornece [um conjunto muito diferente de benefícios](https://www.youtube.com/watch?v=CQuTF-bkOgc) em comparação com um biblioteca de componentes como Material UI. Isso torna o CSS incrivelmente fácil e rápido de escrever, conforme mostrado no exemplo a seguir: CSS Antigo: 1. Escreva CSS, geralmente em um arquivo separado ```css .minha-classe { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Importe o CSS no seu componente ```jsx import "./minha-classe.css"; ``` 3. Adicione a classe no seu HTML ```html
...
``` Equivalente com Tailwind: 1. Basta escrever classes em seu HTML ```html
...
``` Quando usado em conjunto com Componentes React, é extremamente poderoso para construir UIs rapidamente. O Tailwind CSS possui um belo design system integrado, que vem pronto para uso com uma paleta de cores cuidadosamente escolhida, padrões de dimensionamento para estilos como largura/altura e preenchimento/margem para um design uniforme, bem como breakpoints para criar layouts responsivos. Este design system pode ser personalizado e estendido para criar a caixa de ferramentas exata de estilos que seu projeto precisa.
Tru Narla mais conhecido como [mewtru](https://twitter.com/trunarla) deu uma palestra incrível sobre [construindo um design system usando Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Uso Certifique-se de ter plug-ins de editor para Tailwind instalados para melhorar sua experiência de escrita Tailwind. ### Extensões e Plugins - [Extensão para o VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [Integration com JetBrains](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Formatação As classes Tailwind CSS podem facilmente ficar um pouco confusas, então um formatador para as classes é obrigatório. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) classifica as classes na [ordem recomendada](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) para que as classes correspondam ao pacote css gerado. Ao selecionar o Tailwind na CLI, instalaremos e configuraremos isso para você. ### Aplicação de classe condicional Adicionar classes condicionalmente usando ternários pode ficar muito confuso e difícil de ler. Esses pacotes ajudam a organizar suas classes ao usar alguma lógica condicional. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Recursos Úteis | Recurso | Link | | ----------------------------- | -------------------------------------------------------- | | Documentação Tailwind | https://tailwindcss.com/docs/editor-setup/ | | Tailwind Cheat Sheet | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Comunidade do Tailwind | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Servidor Discord do Tailwind | https://tailwindcss.com/discord/ | | Canal do Youtube TailwindLabs | https://www.youtube.com/tailwindlabs/ | | Playground do Tailwind | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/pt/usage/trpc.md ================================================ --- title: tRPC description: Uso do tRPC layout: ../../../layouts/docs.astro lang: pt --- O tRPC nos permite escrever APIs seguras de ponta a ponta sem nenhuma geração de código ou sobrecarga de tempo de execução. Ele usa a grande inferência do TypeScript para inferir as definições de tipo do seu roteador de API e permite que você chame seus procedimentos de API de seu front-end com total segurança de tipo e preenchimento automático. Ao usar tRPC, seu front-end e back-end parecem mais próximos do que nunca, permitindo uma excelente experiência de desenvolvedor.

Criei o tRPC para permitir que as pessoas se movam mais rapidamente, removendo a necessidade de uma camada de API tradicional, enquanto ainda tenho a confiança de que nossos aplicativos não serão interrompidos à medida que iteramos rapidamente.

Avatar of @alexdotjs
Alex - criador do tRPC @alexdotjs
## Como eu uso o tRPC?
o contribuidor do tRPC [trashh_dev](https://twitter.com/trashh_dev) fez [uma fala esplêndida na Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) sobre o tRPC. É altamente recomendável que você assista, caso ainda não o tenha feito. Com tRPC, você escreve funções TypeScript em seu back-end e, em seguida, as chama de seu front-end. Um procedimento tRPC simples poderia ser assim: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Este é um procedimento tRPC (equivalente a um manipulador de rota em um back-end tradicional) que primeiro valida a entrada usando Zod (que é a mesma biblioteca de validação que usamos para [variáveis de ambiente](./env-variables)) - neste caso , é garantir que a entrada seja uma string. Se a entrada não for uma string, ela enviará um erro informativo. Após a entrada, encadeamos uma função de resolução que pode ser uma [consulta](https://trpc.io/docs/v10/react-queries), [mutação](https://trpc.io/docs/v10/react-mutations) ou uma [assinatura](https://trpc.io/docs/v10/subscriptions). Em nosso exemplo, o resolvedor chama nosso banco de dados usando nosso cliente [prisma](./prisma) e retorna o usuário cujo `id` corresponde ao que passamos. Você define seus procedimentos em `routers` que representam uma coleção de procedimentos relacionados com um namespace compartilhado. Você pode ter um roteador para `users`, um para `posts` e outro para `messages`. Esses roteadores podem ser mesclados em um único `appRouter` centralizado: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Observe que precisamos apenas exportar as definições de tipo do nosso roteador, o que significa que nunca importaremos nenhum código de servidor em nosso cliente. Agora vamos chamar o procedimento em nosso frontend. tRPC fornece um wrapper para o `@tanstack/react-query` que permite que você utilize todo o poder dos hooks que eles fornecem, mas com o benefício adicional de ter suas chamadas de API digitadas e inferidas. Podemos chamar nossos procedimentos de nosso front-end assim: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Você notará imediatamente como o preenchimento automático e a segurança de tipo são bons. Assim que você escrever `api.`, seus roteadores aparecerão no preenchimento automático, e quando você selecionar um roteador, seus procedimentos também aparecerão. Você também receberá um erro de TypeScript se sua entrada não corresponder ao validador definido no back-end. ## Inferindo erros Por padrão, `create-t3-app` configura um [formatador de erros](https://trpc.io/docs/error-formatting) que permite que você infira os erros do Zod se você receber erros de validação no back-end. Exemplo de uso: ```tsx function MyComponent() { const { mutate, error } = api.post.create.useMutation(); return ( { e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ title: formData.get('title') }); }}> {error?.data?.zodError?.fieldErrors.title && ( {/** `mutate` retornado com um erro no `title` */} {error.data.zodError.fieldErrors.title} )} ... ); } ``` ## Arquivos O tRPC requer bastante do template que o `create-t3-app` configura para você. Vamos ver os arquivos que são gerados: ### 📄 `pages/api/trpc/[trpc].ts` Este é o ponto de entrada para sua API que expõe o roteador tRPC. Normalmente, você não mexerá muito nesse arquivo, mas se precisar, por exemplo, habilitar o middleware CORS ou similar, é útil saber que o `createNextApiHandler` exportado é um [handler da API do Next.js](https://nextjs.org/docs/api-routes/introduction) que recebe uma [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) e [response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Isso significa que você pode agrupar o `createNextApiHandler` em qualquer middleware que desejar. Veja abaixo um [trecho de exemplo](#ativando-o-cors) da adição de CORS. ### 📄 `server/api/trpc.ts` Este arquivo é dividido em duas partes, criação de contexto e inicialização do tRPC: 1. Definimos o contexto que é passado para seus procedimentos tRPC. O contexto são dados aos quais todos os seus procedimentos tRPC terão acesso, e é um ótimo lugar para colocar coisas como conexões com banco de dados, informações de autenticação, etc. Em create-t3-app, usamos duas funções, para habilitar o uso de um subconjunto do contexto quando não temos acesso ao objeto de solicitação. - `createInnerTRPCContext`: É aqui que você define o contexto que não depende da solicitação, por exemplo sua conexão com o banco de dados. Você pode usar esta função para [teste de integração](#exemplo-de-teste-de-integração) ou [ssg-helpers](https://trpc.io/docs/v10/ssg-helpers) onde você não tem um objeto de solicitação/request. - `createTRPCContext`: É aqui que você define o contexto que depende da solicitação, por exemplo a sessão do usuário. Você solicita a sessão usando o objeto `opts.req` e, em seguida, passa a sessão para a função `createContextInner` para criar o contexto final. 2. Inicializamos o tRPC e definimos [procedures](https://trpc.io/docs/v10/procedures) e [middlewares](https://trpc.io/docs/v10/middlewares) reutilizáveis. Por convenção, você não deve exportar o objeto inteiro t, mas sim criar procedures e middlewares reutilizáveis e exportá-los. Você perceberá que usamos 'superjson' como [transformador de dados](https://trpc.io/docs/v10/data-transformers). Isso faz com que seus tipos de dados sejam preservados quando eles chegam ao cliente, então, por exemplo, se você enviar um objeto `Date`, o cliente retornará um `Date` e não uma string, que é o caso para a maioria das APIs. ### 📄 `server/api/routers/*.ts` Aqui é onde você define as rotas e procedimentos da sua API. Por convenção, você cria [rotas separados](https://trpc.io/docs/v10/router) para procedimentos relacionados. ### 📄 `server/api/root.ts` Aqui [mesclamos](https://trpc.io/docs/v10/merging-routers) todos as sub-rotas definidas em `routers/**` em um único roteador de aplicativo. ### 📄 `utils/api.ts` Este é o ponto de entrada do front-end para tRPC. É aqui que você importará a **definição de tipo** do roteador e criará seu cliente tRPC junto com os hooks do react-query. Como habilitamos `superjson` como nosso transformador de dados no back-end, precisamos habilitá-lo também no front-end. Isso ocorre porque os dados serializados do back-end são "desserializados" no front-end. Você definirá seus [links tRPC](https://trpc.io/docs/v10/links) aqui, que determinarão o fluxo de solicitação do cliente para o servidor. Usamos o "padrão" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink) que permite [solicitar lotes](https://cloud.google.com/compute/docs/api/how-tos/batch), bem como um [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink) que gera logs de solicitação úteis durante o desenvolvimento. Por fim, exportamos um [tipo auxiliar](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type) que você pode usar para inferir seus tipos no frontend.
Contribuidor do Create T3 App [Christopher Ehrlich](https://twitter.com/ccccjjjjeeee) fez [um vídeo sobre fluxos de dados em tRPC](https://www.youtube.com/watch?v=x4mu-jOiA0Q). Este vídeo é recomendado se você já usou o tRPC, mas ainda se sente um pouco incerto sobre como ele funciona. ## Como faço para chamar minha API externamente? Com APIs regulares, você pode chamar seus endpoints usando qualquer cliente HTTP, como `curl`, `Postman`, `fetch`, `Insomnia` ou diretamente do seu navegador. Com tRPC, é um pouco diferente. Se você deseja chamar seus procedimentos sem o cliente tRPC, há duas maneiras recomendadas de fazer isso: ### Expor um único procedimento externamente Se você deseja expor um único procedimento externamente, está procurando por [chamadas do lado do servidor](https://trpc.io/docs/v10/server-side-calls). Isso permitiria que você criasse um terminal de API Next.js normal, mas reutilizasse a parte do resolvedor de seu procedimento tRPC. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Expondo cada procedimento como um endpoint REST Se você deseja expor todos os procedimentos externamente, verifique o plug-in criado pela comunidade [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Ao fornecer alguns metadados extras para seus procedimentos, você pode gerar uma API REST compatível com OpenAPI a partir de seu roteador tRPC. ### São apenas Requests HTTP O tRPC se comunica por meio de HTTP, portanto, também é possível chamar seus procedimentos tRPC usando solicitações HTTP "regulares". No entanto, a sintaxe pode ser complicada devido ao [protocolo RPC](https://trpc.io/docs/v10/rpc) que o tRPC usa. Se você estiver curioso, pode verificar como são as solicitações e respostas tRPC na guia de rede do seu navegador, mas sugerimos fazer isso apenas como um exercício educacional e aderir a uma das soluções descritas acima. ## Comparação com um endpoint da API Next.js Vamos comparar um endpoint da API Next.js com um procedimento tRPC. Digamos que queremos buscar um objeto de usuário de nosso banco de dados e retorná-lo ao frontend. Poderíamos escrever uma rota de API Next.js como esta: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Compare isso com o exemplo tRPC acima e você verá algumas das vantagens do tRPC: - Em vez de especificar um URL para cada rota, o que pode ser irritante de depurar se você mover algo, todo o seu roteador é um objeto com preenchimento automático. - Você não precisa validar qual método HTTP foi usado. - Você não precisa validar se a consulta ou o corpo da solicitação contém os dados corretos no procedimento, pois o Zod cuida disso. - Em vez de criar uma resposta, você pode lançar erros e retornar um valor ou objeto como faria em qualquer outra função TypeScript. - Chamar o procedimento no frontend fornece preenchimento automático e segurança de tipo. ## Snippets úteis Aqui estão alguns snippets que podem ser úteis. ### Ativando o CORS Se você precisar consumir sua API de um domínio diferente, por exemplo, em um monorepo que inclua um aplicativo React Native, talvez seja necessário habilitar o CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Ativar CORS await cors(req, res); // Criar e chamar o handler do tRPC return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### Atualizações otimistas As atualizações otimistas ocorrem quando atualizamos a interface do usuário antes que a chamada da API seja concluída. Isso dá ao usuário uma experiência melhor porque ele não precisa esperar que a chamada da API termine antes que a interface do usuário reflita o resultado de sua ação. No entanto, aplicativos que valorizam muito a exatidão dos dados devem evitar atualizações otimistas, pois não são uma representação "verdadeira" do estado de back-end. Você pode ler mais na [documentação do React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Cancele as requisições de saída (para que não substituam nossa atualização otimista) await utils.post.list.cancel(); // Obtenha os dados do queryCache const prevData = utils.post.list.getData(); // Atualizar os dados de forma otimista com nosso novo post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Retornar os dados anteriores para que possamos reverter se algo der errado return { prevData }; }, onError(err, newPost, ctx) { // Se a mutation falhar, usar o valor de contexto de onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sincronizar com o servidor assim que a mutação for estabelecida utils.post.list.invalidate(); }, }); }; ``` ### Exemplo de teste de integração Aqui está um exemplo de teste de integração que usa [Vitest](https://vitest.dev) para verificar se seu roteador tRPC está funcionando conforme o esperado, se o analisador de entrada infere o tipo correto e se os dados retornados correspondem à saída esperada. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` Se seu procedimento estiver protegido, você pode passar um objeto `session` mockado quando criar o contexto: ```ts test("protected example router", async () => { const ctx = await createInnerTRPCContext({ session: { user: { id: "123", name: "John Doe" }, expires: "1", }, }); const caller = appRouter.createCaller(ctx); // ... }); ``` ## Recursos Úteis | Recurso | Link | | --------------------------- | ------------------------------------------------------- | | Documentação do tRPC | https://www.trpc.io | | Muitos exemplos de tRPC | https://github.com/trpc/trpc/tree/next/examples | | Documentação do React Query | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/pt/usage/typescript.md ================================================ --- title: TypeScript description: Uso do TypeScript layout: ../../../layouts/docs.astro lang: pt ---

"Construa redes de segurança, não grades de proteção" (traduzido)

Avatar of @t3dotgg
Theo - Criador da T3 Stack @t3dotgg
Seja você um desenvolvedor novo ou experiente, achamos que o TypeScript é obrigatório. Pode parecer intimidador no começo, mas, assim como muitas ferramentas, é algo que muitos nunca voltam atrás depois de começar a usá-lo. Ele fornece feedback instantâneo enquanto você escreve seu código definindo os tipos de dados esperados e fornece autocomplete em seu editor de código ou grita com você com linhas onduladas vermelhas se você estiver tentando acessar uma propriedade que não existe ou tentando passe um valor do tipo errado, que de outra forma você teria que depurar mais adiante. É, talvez, a ferramenta que mais produtividade oferece aos desenvolvedores; fornecer documentação do código que você está escrevendo ou consumindo diretamente em seu editor e ter feedback instantâneo quando você inevitavelmente cometer erros é absolutamente inestimável. ## Inferência de Tipos Embora muitos novos desenvolvedores TypeScript se preocupem em _escrever_ TypeScript, muitos de seus benefícios na verdade não exigem que você altere seu código, em particular a inferência. Inferência significa que se algo for digitado, esse tipo o seguirá ao longo do fluxo do aplicativo sem precisar ser declarado novamente em outros lugares. Isso significa que, por exemplo, uma vez que você tenha definido os tipos de argumentos que uma função usa, o restante da função geralmente será typesafe sem exigir nenhum código adicional específico do TypeScript. Os desenvolvedores de bibliotecas trabalham muito para manter os tipos de suas bibliotecas, o que significa que nós, como desenvolvedores de aplicativos, podemos nos beneficiar da inferência e da documentação integrada em seu editor de código fornecida por esses tipos.
Confira o vídeo de Theo sobre como [você pode estar usando o TypeScript errado](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Usos poderosos da inferência de tipos ### Zod O [Zod](https://github.com/colinhacks/zod) é uma biblioteca de validação de esquema construída sobre TypeScript. Escreva um esquema que represente uma fonte única de verdade para seus dados, e o Zod garantirá que seus dados sejam válidos em todo o aplicativo, mesmo nos limites da rede e APIs externas. ### Tanstack Query O [Tanstack Query](https://tanstack.com/query/v4/) fornece consultas (queries) e mutações (mutations) declarativas, sempre atualizadas e autogerenciadas que melhoram diretamente as experiências do desenvolvedor e do usuário. ## Recursos Úteis | Recurso | Link | | ---------------------------------------------------------- | ----------------------------------------------------------------- | | Manual do TypeScript | https://www.typescriptlang.org/docs/handbook/ | | Tutorial de TypeScript para iniciantes | https://github.com/total-typescript/beginners-typescript-tutorial | | Desafios de Tipos | https://github.com/type-challenges/type-challenges | | Rodney Mullen do canal do YouTube TypeScript (Matt Pocock) | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/pt/why.md ================================================ --- title: Por que CT3A? description: Porque você deve escolher o Create T3 App para o seu próximo projeto layout: ../../layouts/docs.astro lang: pt --- Nós começamos o create-t3-app porque o [Theo](https://twitter.com/t3dotgg) se recusou a criar um template com suas tecnologias favoritas. Inspirado pelo create-next-app, [a CLI do Astro](https://astro.build) e um amor geral pela segurança de tipos, o time do create-t3-app trabalhou duro para construir o melhor ponto de início possível para novos projetos com a T3 Stack. Se você está interessado em usar o Next.js de uma maneira typesafe, este é o lugar para começar. Se você está curioso sobre alguma das escolhas de tecnologias que fizemos, siga com a leitura :) ## Por que TypeScript? Javascript é difícil. Pra quê mais regras? Nos firmemente acreditamos que a experiência que o Typescript proporciona irá te ajudar a ser um desenvolvedor melhor. Ele proporciona feedback instantâneo conforme você escreve seu código definindo os tipos esperados de data, e ou te fornece um autocomplete muito útil no seu editor ou grita por meio de linhas onduladas vermelhas se você está tentando acessar uma propriedade que não existe ou tentando passar um valor do tipo errado, o qual por outro lado você deveria debugar mais adiante. Se você é novo no desenvolvimento web ou um profissional experiente, o "rigor" do TypeScript vai te providenciar uma experiência menos frustrante, e mais consistente que o Javascript padrão. Segurança de tipo te faz mais rápido. Se você ainda não está convencido, você [deve estar usando o TypeScript errado...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## Por que Next.js? Nós amamos React. Ele tornou o desenvolvimento de interfaces mais acessível de maneira que nunca imaginávamos antes. Ele também pode levar os desenvolvedores para caminhos difíceis. o Next.js oferece uma abordagem levemente opinada e altamente otimizada de criar aplicações usando React. Desde roteamento até definições de API até renderizações de imagens, nós confiamos que o Next.js levará os desenvolvedores tomarem boas decisões. ## Por que tRPC/Prisma/Tailwind/etc? Enquanto nós acreditamos em manter as coisas o mais simples o possível, nós frequentemente encontramos essas tecnologias sendo usadas em qualquer "projetinho" que criamos. O `create-t3-app` faz um bom serviço deixando você adotar apenas as peças que precisa. ### tRPC O tRPC cumpre a promessa do GraphQL de desenvolvimento de cliente contínuo em um servidor typesafe sem todo o boilerplate. É um abuso inteligente do TypeScript que fornece uma experiência de desenvolvimento incrível. ### Prisma Prisma é para o SQL o que o Typescript é para o JS. Ele criou uma experiência de desenvolvimento que nunca existiu antes. Ao gerar tipos a partir de um esquema definido pelo usuário compatível com [vários bancos de dados](https://www.prisma.io/docs/concepts/database-connectors), o Prisma garante segurança de tipo de ponta-a-ponta do seu banco de dados até o seu aplicativo. O Prisma oferece todo um [conjunto de ferramentas](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows) fazendo interações diárias com o seu banco de dados mais fácil. Notavelmente, o Prisma Client é responsável por fazer queryes e tornar SQL tão fácil que você mal vai notar que está usando, e o Prisma Studio é uma GUI (Interface Gráfica do Usuário) conveniente para seu banco de dados que te deixa ler e manipular seus dados rapidamente sem ter que escrever código. ### Tailwind CSS Tailwind é como o "CSS no modo zen". Ao fornecer blocos de construção na forma de boas cores padrão, espaçamento e outros elementos primitivos, o Tailwind facilita a criação de um aplicativo de boa aparência. E, ao contrário das bibliotecas de componentes, isso não o impede quando você deseja levar seu aplicativo para o próximo nível e criar algo bonito e único. Além disso, com sua abordagem inline, o Tailwind incentiva você a estilizar sem se preocupar em nomear classes, organizar arquivos ou qualquer outro problema não diretamente relacionado ao problema que você está tentando resolver. ### NextAuth.js Quando você deseja um sistema de autenticação em seu aplicativo NextJS, o NextAuth.js é uma excelente solução para trazer a complexidade da segurança sem o incômodo de ter que construí-lo sozinho. Ele vem com uma extensa lista de provedores para adicionar rapidamente a autenticação OAuth e fornece adaptadores para muitos bancos de dados e ORMs. ================================================ FILE: www/src/pages/ru/deployment/docker.md ================================================ --- title: Docker description: Развертывание в Docker layout: ../../../layouts/docs.astro lang: ru --- Вы можете контейнеризировать этот стек и развернуть его как один контейнер с помощью Docker или как часть группы контейнеров с помощью docker-compose. Смотрите [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) для примера репозитория на основе этой документации. ## Конфигурация проекта Docker Пожалуйста обратите внимание, что Next.js нуждается в разных процессах для сборки (доступны во фронтенде, с префиксом `NEXT_PUBLIC`) и переменных окружения, доступных только на сервере. В этом примере мы используем две переменные, обратите внимание на их позиции в `Dockerfile`, аргументы командной строки и `docker-compose.yml`: - `DATABASE_URL` (используется сервером) - `NEXT_PUBLIC_CLIENTVAR` (используется клиентом) ### 1. Конфигурация Next В вашем [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), добавьте конфигурацию `output` со значением `standalone` для [уменьшения размера образа с помощью автоматического использования трассировок вывода](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Создайте dockerignore file
Нажмите здесь и вставьте содержимое в .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Создайте Dockerfile > Из-за того, что мы не извлекаем переменные окружения сервера в наш контейнер, [проверка схемы окружения](/ru/usage/env-variables) не пройдет. Чтобы этого избежать, мы должны добавить флаг `SKIP_ENV_VALIDATION=1` к команде сборки, чтобы схемы окружения не проверялись во время сборки.
Нажмите здесь и вставьте содержимое в Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Install Prisma Client - remove if not using Prisma COPY prisma ./ # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Заметки_** > > - _Эмуляция `--platform=linux/amd64` может не быть необходимой после перехода на Node 18._ > - _Посмотрите [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) чтобы понять, почему `libc6-compat` может быть необходим._ > - _Использование образов, основанных на Alpine 3.17 [может привести к проблемам с Prisma](https://github.com/t3-oss/create-t3-app/issues/975). Установка `engineType = "binary"` решает проблему с Alpine 3.17, [но имеет связанные с этим затраты производительности](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ > - _Next.js собирает [анонимные данные о телеметрии общего использования](https://nextjs.org/telemetry). Раскомментируйте первый экземпляр `ENV NEXT_TELEMETRY_DISABLED 1`, чтобы отключить телеметрию во время сборки. Раскомментируйте второй экземпляр, чтобы отключить телеметрию во время выполнения._
## Соберите и запустите образ локально Соберите и запустите этот образ локально с помощью следующих команд: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Откройте [localhost:3000](http://localhost:3000/) чтобы увидеть запущенное приложение. ## Docker Compose Вы также можете использовать Docker Compose для сборки образа и запуска контейнера.
Проследуйте шагам 1-4 выше, нажмите здесь и добавьте содержимое в docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Запустите с помощью команды `docker compose up`: ```bash docker compose up ``` Откройте [localhost:3000](http://localhost:3000/) чтобы увидеть запущенное приложение.
## Развертывание на Railway Вы можете использовать такой PaaS как автоматическое [развертывание Dockerfile](https://docs.railway.app/deploy/dockerfiles) от [Railway's](https://railway.app) для развертывания вашего приложения. Если у вас [установлен Railway CLI](https://docs.railway.app/develop/cli#install), вы можете развернуть свое приложение с помощью следующих команд: ```bash railway login railway init railway link railway up railway open ``` Перейдите к "Variables" и включите ваш `DATABASE_URL`. Затем перейдите к "Settings" и выберите "Generate Domain." Чтобы увидеть работающий пример на Railway, перейдите к [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Полезные ресурсы | Ресурс | Ссылка | | ----------------------------------------- | -------------------------------------------------------------------- | | Пример для Dockerfile | https://docs.docker.com/engine/reference/builder/ | | Пример для файла Compose 3 версии | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Пример Docker CLI | https://docs.docker.com/engine/reference/commandline/docker/ | | Пример Docker Compose CLI | https://docs.docker.com/compose/reference/ | | Развертывание Next.js с Docker Image | https://nextjs.org/docs/deployment#docker-image | | Next.js в Docker'е | https://benmarte.com/blog/nextjs-in-docker/ | | Пример Next.js с Docker | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Создание Docker образа Next.js приложения | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/ru/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Развертывание", layout: "docs", description: "Научитесь развертывать ваше приложение T3 в продакшен.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/ru/deployment/netlify.md ================================================ --- title: Netlify description: Развертывание в Netlify layout: ../../../layouts/docs.astro --- Netlify это альтернативый провайдер развертывания, похожий на Vercel. Вот [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) пример репозитория на основе этой документации. ## Зачем развертывать на Netlify Принято считать, что Vercel имеет лучшую поддержку Next.js потому что Vercel разрабатывает Next.js. Они заинтересованы в том, чтобы платформа была настроена для оптимальной производительности и DX с Next.js. В большинстве случаев это будет правдой и в отклонении от стандартного пути не будет смысла. Также существует общее мнение о том, что многие функции Next.js поддерживаются только на Vercel. Хотя это правда, что новые функции Next.js будут тестироваться и поддерживаться на Vercel в момент выпуска по умолчанию, также следует учитывать, что другие провайдеры, такие как Netlify, [быстро реализуют и выпускают поддержку](https://www.netlify.com/blog/deploy-nextjs-13/) для [стабильных функций Next.js](https://docs.netlify.com/integrations/frameworks/next-js/overview/). У всех провайдеров развертывания есть преимущества и недостатки, поскольку ни один хост не может иметь лучшую поддержку для всех случаев использования. Например, Netlify разработал свой собственный [пользовательский Next.js runtime](https://github.com/netlify/next-runtime) для Netlify Edge Functions (которые работают на Deno Deploy) и [поддерживают уникальные промежуточные программы для доступа и изменения HTTP-ответов](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). > _Обратите внимание: Для того, чтобы отслеживать статус нестабильных функций Next 13, см. [Использование каталога `app` Next 13 на Netlify](https://github.com/netlify/next-runtime/discussions/1724)._ ## Конфигурация проекта Существует несколько способов настройки инструкций сборки, включая прямое использование Netlify CLI или Netlify Dashboard. Хотя это не обязательно, рекомендуется создать и включить файл [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Это гарантирует, что форкнутые и клонированные версии проекта будут легче повторно развернуть. ```toml [build] command = "next build" publish = ".next" ``` ## Использование Netlify Dashboard 1. Запушьте свой код в репозиторий GitHub и зарегистрируйтесь на [Netlify](https://app.netlify.com/signup). После того, как вы создали аккаунт, нажмите **Add new site** и затем **Import an existing project**. ![Новый проект в Netlify](/images/netlify-01-new-project.webp) 2. Подключите свой Git-провайдер. ![Импортируйте репозиторий](/images/netlify-02-connect-to-git-provider.webp) 3. Выберите репозиторий вашего проекта. ![Выберите репозиторий вашего проекта](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify обнаружит, если у вас есть файл `netlify.toml` и автоматически настроит команду сборки и каталог публикации. ![Настройки сборки Nextjs](/images/netlify-04-configure-build-settings.webp) 5. Нажмите **Show advanced** и затем **New variable**, чтобы добавить свои переменные среды. ![Добавьте переменные среды](/images/netlify-05-env-vars.webp) 6. Нажмите **Deploy site**, подождите, пока сборка завершится, и просмотрите свой новый сайт. ## Использование Netlify CLI Для того, чтобы развернуть проект из командной строки, вы должны сначала запушить свой проект в репозиторий GitHub и [установить Netlify CLI](https://docs.netlify.com/cli/get-started/). Вы можете установить `netlify-cli` как зависимость проекта или установить его глобально на вашем компьютере с помощью следующей команды: ```bash npm i -g netlify-cli ``` Для того, чтобы протестировать свой проект локально, запустите команду [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) и откройте [`localhost:8888`](http://localhost:8888/) для просмотра вашего локально запущенного приложения Netlify: ```bash ntl dev ``` Запустите команду [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment), чтобы настроить ваш проект: ```bash ntl init ``` Импортируйте переменные среды вашего проекта из вашего файла `.env` с помощью [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Разверните ваш проект с помошью [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Вам нужно будет передать флаг `--build`, чтобы запустить команду сборки перед развертыванием, и флаг `--prod`, чтобы развернуть на основном URL вашего сайта: ```bash ntl deploy --prod --build ``` Для просмотра примера на Netlify, перейдите на [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/ru/deployment/vercel.md ================================================ --- title: Vercel description: Развертывание на Vercel layout: ../../../layouts/docs.astro lang: ru --- Мы рекомендуем развертывать ваше приложение на [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Он позволяет очень легко развертывать Next.js приложения. ## Конфигурация проекта Скорее всего Vercel настроит вашу команду сборки и каталог публикации автоматически. Однако вы также можете уточнить эту информацию вместе с другими конфигурациями, создав файл [`vercel.json`](https://vercel.com/docs/project-configuration) и включив в него следующие команды. **В большинстве проектов это делать необязательно.** ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Использование Vercel Dashboard 1. После отправки вашего кода в репозиторий GitHub зарегистрируйтесь на [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) с GitHub и нажмите **Add New Project**. ![Новый проект на Vercel](/images/vercel-new-project.webp) 2. Импортируйте репозиторий GitHub с вашим проектом. ![Импортируйте репозитоpий](/images/vercel-import-project.webp) 3. Добавьте ваши переменные среды. ![Добавьте ваши переменные среды](/images/vercel-env-vars.webp) 4. Нажмите **Deploy**. Теперь каждый раз, когда вы отправляете изменение в ваш репозиторий, Vercel автоматически переразвернет ваше приложение! ## Использование Vercel CLI Для того, чтобы развернуть приложение из командной строки, вам сначала нужно [установить Vercel CLI глобально](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Запустите команду [`vercel`](https://vercel.com/docs/cli/deploying-from-cli), чтобы развернуть ваш проект. ```bash vercel ``` Добавьте `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` для переменных среды, таких как строка подключения к базе данных. Используйте `--yes`, если хотите пропустить вопросы развертывания и дать ответ по умолчанию для каждого. ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` После первого развертывания эта команда развернет изменения в ветку предварительного просмотра. Вам нужно будет включить `--prod`, чтобы отправить изменения напрямую на сайт в продакшене для будущих развертываний. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/ru/faq.md ================================================ --- title: FAQ description: Часто задаваемые вопросы про Create T3 App layout: ../../layouts/docs.astro lang: ru --- Это некоторые часто задаваемые вопросы о `create-t3-app`. ## Что дальше? Как я могу создать приложение? Мы стараемся сделать этот проект как можно проще, поэтому вы можете начать с того, что мы настроили для вас, а затем добавлять дополнительные вещи позже, когда они станут необходимыми. Если вы не знакомы с различными технологиями, используемыми в этом проекте, обратитесь к соответствующей документации. Если что то все еще не понятно, присоединяйтесь к нашему [Discord](https://t3.gg/discord) и попросите помощи. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## Какие учебные ресурсы сейчас доступны? Не смотря на то, что перечисленные ниже ресурсы являются одиними из лучших для T3 Stack, сообщество (и [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) рекомендует просто начать использовать стек и учиться в процессе разработки с его помощью. Если вы рассматриваете `create-t3-app`, скорее всего, вы уже использовали некоторые части стека. Так почему бы не погрузиться в проект с головой и узнать остальные технологии в процессе разработки? Мы понимаем, что этот путь не подходит для каждого. Поэтому, если вы чувствуете, что рекомендации не достаточно, и вам все еще нужны ресурсы, или вы просто не уверены в своих силах и/или чувствуете себя перегруженным этим стеком, ознакомьтесь с этими потрясающими учебными материалами по `create-t3-app`: ### Статьи - [Build a full stack app with Create T3 App](https://www.nexxel.dev/blog/ct3a-guestbook) - [A first look at Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Integrating Stripe into your T3 App](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Видео - [Build a Twitter Clone with the T3 Stack - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [An overview of the Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Почему в проекте есть файлы `.js`? Согласно [T3-Аксиоме #3](/ru/introduction#typesafety-isnt-optional), мы считаем типобезопасность объектом первого класса. К сожалению, не все фреймворки и плагины поддерживают TypeScript, поэтому некоторые файлы конфигурации должны быть файлами `.js`. Мы пытаемся подчеркнуть, что эти файлы являются файлами JavaScript не без причины, явно объявляя тип каждого файла (`cjs` или `mjs`), в зависимости от того, что поддерживается библиотекой, которой он используется. Кроме того, все файлы `js` в этом проекте все еще проверяются на типы с помощью комментария `@ts-check` вверху. ## У меня не получается добавить i18n в мое приложение. Есть ли какие-либо рекомендации? Мы решили не включать i18n по умолчанию в `create-t3-app`, потому что это очень субъективная тема, и есть много способов ее реализации. В тоже время, если вы столкнулись с проблемами при реализации и хотите увидеть проект-образец, у нас есть [ссылка на репозиторий](https://github.com/juliusmarminge/t3-i18n) который показывает как вы можете добавить i18n в T3 App с помощью [next-i18next](https://github.com/i18next/next-i18next). ## Почему мы используем `/pages` а не `/app` из Next.js 13? Согласно [T3-Аксиоме #2](/ru/introduction#bleed-responsibly), мы любим новинки, но ценим стабильность, вам будет сложно перенести весь маршрутизатор, [не лучшеее место для экспериментов](https://youtu.be/mnwUbtieOuI?t=1662). Хотя `/app` [представляет собой взгляд в будущее](https://youtu.be/rnsC-12PVlM?t=818), он еще не готов к использованию в продакшене; API находится в бета-версии и ожидается, что он будет иметь обратно несовместимые изменения. Список поддерживаемых, запланированных и находящихся в разработке функций в каталоге `/app` можно найти в [бета-документации Next.js](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/ru/folder-structure-pages.mdx ================================================ --- title: Файловая структура (Pages) description: Файловая структура нового T3 приложения layout: ../../layouts/docs.astro lang: ru isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Пожалуйста выберите пакеты, чтобы увидеть файловую структуру нового приложения с выбранными пакетами. Ниже вы найдете описание каждого элемента.
### `prisma` Папка `prisma` содержит файл `schema.prisma`, который используется для настройки соединения с базой данных и схемы базы данных. Также это место для хранения файлов миграции и/или сценариев заполнения, если они используются. Смотрите [Использование Prisma](/ru/usage/prisma) для получения дополнительной информации.
### `public` Папка `public` содержит статические файлы, которые обслуживаются веб-сервером. Файл `favicon.ico` - это пример статического файла.
### `src/env` Используется для проверки переменных окружения и определения типов - смотрите [Переменные окружения](/ru/usage/env-variables).
### `src/pages` Папка `pages` содержит все страницы приложения Next.js. Файл `index.tsx` в корневой папке `/pages` является домашней страницей приложения. Файл `_app.tsx` используется для оборачивания приложения провайдерами. Смотрите [документацию Next.js](https://nextjs.org/docs/basic-features/pages) для получения дополнительной информации.
#### `src/pages/api` Папка `api` содержит все маршруты API приложения Next.js. Файл `examples.ts` (с Prisma) содержит пример маршрута, который использует функцию [Next.js API route](https://nextjs.org/docs/api-routes/introduction) вместе с Prisma. Файл `restricted.ts` (с Next-Auth) содержит пример маршрута, который использует функцию [Next.js API route](https://nextjs.org/docs/api-routes/introduction) и защищен с помошью [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` Файл `[...nextauth].ts` - это слаг-маршрут NextAuth.js для аутентификации. Он используется для обработки запросов аутентификации. Смотрите [Использование NextAuth.js](/ru/usage/next-auth) для получения дополнительной информации о NextAuth.js и [документацию Next.js о динамических маршрутах](https://nextjs.org/docs/routing/dynamic-routes) для получения информации о маршрутах catch-all/slug.
#### `src/pages/api/trpc/[trpc].ts` Файл `[trpc].ts` - это точка входа tRPC API. Он используется для обработки запросов tRPC. Смотрите [Использование tRPC](/ru/usage/trpc#-pagesapitrpctrpcts) для получения дополнительной информации об этом файле и [документацию Next.js о динамических маршрутах](https://nextjs.org/docs/routing/dynamic-routes) для получения информации о маршрутах catch-all/slug.
### `src/server` Папка `server` используется для четкого разделения серверного кода от клиентского кода.
#### `src/server/auth.ts` Содержит утилиты для аутентификации, такие как получение сеанса пользователя на стороне сервера. Смотрите [Использование NextAuth.js](/ru/usage/next-auth#usage-with-trpc) для получения дополнительной информации.
#### `src/server/db.ts` Файл `db.ts` используется для создания клиента Prisma на глобальном уровне. Смотрите [Использование Prisma](/ru/usage/prisma#prisma-client) и [лучшие практики по использованию Prisma с Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) для получения дополнительной информации.
### `src/server/api` Папка `api` содержит серверный код tRPC.
#### `src/server/api/routers` Папка `routers` содержит все ваши под-маршрутизаторы tRPC.
#### `src/server/api/routers/example.ts` Файл `example.ts` - это пример маршрутизатора tRPC, использующий вспомогательную функцию `publicProcedure` для демонстрации того, как создать публичный маршрут tRPC. Основываясь на выбранных вами пакетах, этот маршрутизатор содержит больше или меньше маршрутов для лучшего демонстрирования использования ваших потребностей.
#### `src/server/api/trpc.ts` Файл `trpc.ts` - это основной файл конфигурации для вашего tRPC-бэкэнда. В нем мы: 1. Определяем контекст используемый в запросах tRPC. Смотрите [Использование tRPC](/ru/usage/trpc#-serverapitrpcts) для получения дополнительной информации. 2. Экспортируем вспомогательные функции процедур. Смотрите [Использование tRPC](/ru/usage/trpc#-serverapitrpcts) для получения дополнительной информации.
#### `src/server/api/root.ts` Файл `root.ts` используется для слияния маршрутизаторов tRPC и экспорта их как единого маршрутизатора, а также определения типа маршрутизатора. Смотрите [Использование tRPC](/ru/usage/trpc#-serverapirootts) для получения дополнительной информации.
### `src/styles` Папка `styles` содержит глобальные стили приложения.
### `src/types` Папка `types` используется для хранения повторно используемых типов или объявлений типов.
#### `src/types/next-auth.d.ts` Файл `next-auth.d.ts` используется для расширения типа сеанса по умолчанию NextAuth на включение идентификатора пользователя. Смотрите [Использование NextAuth.js](/ru/usage/next-auth#включение-userid-в-сеанс) для получения дополнительной информации.
### `src/utils` Папка `utils` используется для хранения повторно используемых функций утилит.
#### `src/utils/api.ts` Файл `api.ts` является точкой входа для tRPC на стороне клиента. Смотрите [Использование tRPC](/ru/usage/trpc#-utilsapits) для получения дополнительной информации.
### `.env` Файл `.env` используется для хранения переменных среды. Смотрите [Переменные среды](/ru/usage/env-variables) для получения дополнительной информации. Этот файл **не** должен быть добавлен в историю git.
### `.env.example` Файл `.env.example` показывает пример переменных среды на основе выбранных библиотек. Этот файл должен быть добавлен в историю git.
### `.eslintrc.cjs` Файл `.eslintrc.cjs` используется для настройки ESLint. Смотрите [документацию ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) для получения дополнительной информации.
### `next-env.d.ts` Файл `next-env.d.ts` гарантирует, что типы Next.js будут обнаружены компилятором TypeScript. **Вы не должны удалять его или редактировать, поскольку он может измениться в любое время.** Смотрите [документацию Next.js](https://nextjs.org/docs/basic-features/typescript#existing-projects) для получения дополнительной информации.
### `next.config.mjs` Файл `next.config.mjs` используется для настройки Next.js. Смотрите [Документацию Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction) для получения дополнительной информации. Примечание: Расширение .mjs используется для разрешения импортов ESM.
### `postcss.config.js` Файл `postcss.config.js` используется для использования Tailwind PostCSS. Смотрите [документацию Taiwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss) для получения дополнительной информации.
### `prettier.config.mjs` Файл `prettier.config.mjs` используется для настройки Prettier для включения prettier-plugin-tailwindcss для форматирования классов Tailwind CSS. Смотрите [пост блога Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) для получения дополнительной информации.
### `tsconfig.json` Файл `tsconfig.json` используется для настройки TypeScript. Некоторые значения по умолчанию, такие как `strict mode`, были включены для обеспечения лучшего использования TypeScript для create-t3-app и его библиотек. Смотрите [документацию TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) или [Использование TypeScript](usage/typescript) для получения дополнительной информации.
================================================ FILE: www/src/pages/ru/installation.mdx ================================================ --- title: Установка description: Инструкции по установке Create T3 App layout: ../../layouts/docs.astro lang: ru isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Для создания приложения с помощью `create-t3-app`, запустите одну из следующих трёх команд и ответьте на вопросы командной строки: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` После того, как приложение будет создано, ознакомьтесь с [первыми шагами](/ru/usage/first-steps), чтобы начать работу над вашим новым приложением. ## Дополнительные параметры | Опции/Флаги | Описание | | ----------------- | ---------------------------------------------------------------------------- | | `[dir]` | Добавьте аргумент каталога с именем проекта | | `--noGit` | Явно сообщите CLI не инициализировать новый git репозиторий в проекте | | `-y`, `--default` | Обойти CLI и создать новое t3-приложение с помощью всех выбранных параметров | | `--noInstall` | Сгенерировать проект без установки зависимостей | ## Экспериментальное использование Для нашего CI мы имеем некоторые экспериментальные флаги, которые позволяют создавать любое приложение без каких-либо запросов. Если это применимо к вашему случаю, вы можете использовать эти флаги. Обратите внимание, что эти флаги являются экспериментальными и могут измениться в будущем без следования версированию semver. | Опции/Флаги | Описание | | ------------ | ------------------------------------------- | | `--CI` | Сообщите CLI, что вы находитесь в режиме CI | | `--trpc` | Добавить tRPC в проект | | `--prisma` | Добавить Prisma в проект | | `--nextAuth` | Добавить NextAuth в проект | | `--tailwind` | Добавить Tailwind CSS в проект | если вы не указываете флаг `CI`, то остальные флаги не имеют эффекта. Вам не нужно явно отказываться от пакетов, которые вам не нужны. Однако, если вы предпочитаете быть явным, вы можете передать `false`, например `--nextAuth false`. ### Например Следующий пример создаст приложение T3 с tRPC и Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/ru/introduction.md ================================================ --- title: Введение description: Введение в T3 стек layout: ../../layouts/docs.astro lang: ru ---
## T3 Stack _«T3 Stack»_ - это стек веб-разработки, созданный [Theo](https://twitter.com/t3dotgg), с упором на простоту, модульность и full-stack типизацию. Основные части - [**Next.js**](https://nextjs.org/) и [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) почти всегда включен. Если вы делаете что-то напоминающее бэкенд, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), и [**NextAuth.js**](https://next-auth.js.org/) - отличные дополнения. Вы могли заметить, что существует… много частей. Это сделано намеренно. Меняйте части в зависимости от ваших потребностей - этот стек является модульным по своей сути :) ## И... что такое create-t3-app? Шаблон? Возможно? `create-t3-app` это CLI, созданный опытными разработчиками T3 стека, чтобы упростить настройку модульного приложения T3 стека. Это означает, что каждая часть является необязательной, и «шаблон» генерируется на основе ваших конкретных потребностей. После множества проектов и многих лет с этой технологией у нас есть множество мнений и знаний. Мы постарались зафиксировать их в этом CLI. Это **НЕ** всеобъемлющий шаблон. Мы **ожидаем**, что вы будете использовать свои собственные библиотеки, которые решают потребности **ВАШЕГО** приложения. Хотя мы не хотим назначать решения для более конкретных проблем, таких как управление состоянием и развертывание, мы [имеем некоторые рекомендации, перечисленные здесь](/ru/other-recs). ## Аксиомы T3 Будем честными - это _субъективный проект_. Мы поделились несколькими основными убеждениями вокруг создания и рассматриваем их как основу для наших решений. ### Решайте проблемы Легко попасть в ловушку "добавления всего" - мы явно не хотим этого делать. Все, что добавляется в `create-t3-app`, должно решать конкретную проблему, которая существует в основных технологиях, включенных в него. Это означает, что мы не добавим библиотеки менеджмента состояния (`zustand`, `redux`), но мы добавим NextAuth.js и интегрируем Prisma и tRPC для вас. ### Ответственность к новинкам Мы любим технологические новинки. Получаемые скорость и, честно говоря, веселье от новых фиговин, действительно круто. Мы считаем важным использовать их ответственно, используя более рискованные технологии в менее рискованных частях. Это означает, что мы не будем ⛔️ делать ставку на рискованную новую технологию базы данных (SQL - это здорово!). Но мы с удовольствием ✅ сделаем ставку на tRPC, поскольку это просто функции, которые легко перенести. ### Типобезопасность не является опциональной Указаная цель `create-t3-app` - это предоставить самый быстрый способ начать новое полнофункциональное, **типобезопасное** веб-приложение. Мы серьезно относимся к типобезопасности в этих частях, поскольку она повышает нашу продуктивность и помогает нам отправлять меньше ошибок. Любое решение, которое снижает типобезопасность `create-t3-app`, должно быть принято в другом проекте. ================================================ FILE: www/src/pages/ru/other-recs.md ================================================ --- title: Дополнительные рекомендации description: Библиотеки и сервисы, которые мы рекомендуем для многих проектов layout: ../../layouts/docs.astro lang: ru --- Мы понимаем, что библиотеки, включенные в `create-t3-app`, не решают каждую проблему. Хотя мы рекомендуем начать свой проект с того, что мы предоставляем, придет время, когда вам нужно будет установить другие пакеты. Только вы можете знать, в чем нуждается ваш проект, но вот некоторые решения, которые мы часто рекомендуем. Это рекомендации отдельных участников create-t3-app и не должны рассматриваться как "официальные" одобрения команды create-t3-app или T3-OSS. _**Пожалуйста, проведите собственное исследование, особенно перед тем, как привязываться к платным услугам**_. ## Управление состоянием _**Примечание редактора**_: Библиотеки управления состоянием могут быть отличными, но часто не требуются. Хуки React Query tRPC должны справиться с вашим серверным состоянием. Для клиентского состояния начните с `useState` React, и обратитесь к одному из этих вариантов, когда вам нужно больше. ### Zustand **Для того, чтобы больше никогда не писать Redux** "Современный простой Redux", который вы не знали, что вам нужен. [Poimandres](https://github.com/pmndrs) всегда можно доверять. Вы можете создавать все, от видеозвонков до игр и серверов с этой маленькой библиотекой. - [Zustand Homepage](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **Для того, чтобы больше никогда не писать Context** В атомарном подходе Jotai трудно превзойти. Также от [Poimandres](https://github.com/pmndrs), Jotai позволяет определять синглтоны, которые кажутся глобальными useState. Отличный вариант для состояний, которые не требуют машины состояний прямо сейчас. - [Jotai Homepage](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## Библиотеки компонентов Большинство приложений нуждаются в некотором наборе компонентов: переключатели, выпадающие меню, модальные окна и т.д. Эти библиотеки предоставляют отличные, доступные компоненты, которые вы можете использовать и настраивать по своему усмотрению. ### Библиотеки компонентов без стилей Также известны как headless библиотеки, они предоставляют отличные, нестилизованные и доступные компоненты, которые вы можете настроить по своему усмотрению. Вот несколько рекомендаций. - [Radix UI](https://www.radix-ui.com/) предоставляет вам мощный набор удобных и доступных примитивов, которые вы можете стилизовать с помощью обычного CSS или Tailwind CSS. - [Headless UI](https://headlessui.com/) от команды Tailwind CSS также предоставляет нестилизованные, доступные компоненты, которые интегрируются безупречно с Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) предоставляет доступные примитивы UI для вашей системы дизайна. Их компонент выбора даты - это высший уровень. ### Библиотеки компонентов со стилями **Когда вам просто нужно, чтобы ваше приложение выглядело неплохо** Иногда вы создаете проект, где вам просто нужно, чтобы пользовательский интерфейс выглядел нормально из коробки. Для административных панелей управления и других подобных проектов любая из этих библиотек компонентов справится с этой задачей. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **Для создания библиотек пользовательского интерфейса** Декларативно создавайте библиотеку пользовательского интерфейса с различными цветовыми, размерными и т.д. вариантами. Когда ваш проект достигает масштаба, где вам нужен стандартизированный набор компонентов пользовательского интерфейса с несколькими вариантами с использованием Tailwind CSS, CVA - отличный инструмент. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Анимации Наши рекомендации для создания анимаций в вашем приложении. ### AutoAnimate **Для анимаций с одной строкой кода** Большенство библиотек анимации пытаются удовлетворить каждый возможный случай использования и в результате становятся громоздкими. AutoAnimate - это инструмент без конфигурации, который даст вам значительное улучшение UX без дополнительных усилий разработчика. - [Главная страница AutoAnimate](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [Компонент для AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **Для сложных анимаций с декларативным кодом** Framer Motion предоставляет простой, декларативный синтаксис и позволяет вам писать меньше кода для создания всего: от сложных анимаций и даже до жестов. - [Framer Motion Homepage](https://framer.com/motion) - [Framer Motion Documentation](https://www.framer.com/docs/) ## Развертывание, инфраструктура, базы данных и CI ### Vercel **Для хостинга (размещения) вашего приложения** Vercel убрал мучения из процесса развертывания веб-приложений и сделал его простой "установил-и-забыл" интеграцией GitHub. Мы масштабировались до сотен тысяч пользователей без проблем. Работает на базе AWS, но имеет простой и удобный интерфейс :) - [Главная страница Vercel](https://vercel.com/) - [Гайд по развертыванию T3 приложения на Vercel](/ru/deployment/vercel) ### PlanetScale **Для баз данных без беспокойства** PlanetScale - лучшая serverless платформа для баз данных, которую мы использовали до сих пор. Невероятная масштабируемость, отличный DX и фантастическая цена. Если вы используете SQL (и, надеюсь, Prisma), это трудно превзойти. - [Главная страница PlanetScale](https://planetscale.com/) ### Railway **Для размещения вашей инфраструктуры** "Современный Heroku". Самый простой способ запустить реальный сервер. Если вам не хватает функционала Vercel и PlanetScale, то Railway, вероятно, вам подойдет. Укажите его на репозиторий GitHub и готово. - [Главная страница Railway](https://railway.app/) ### Upstash **Для безсерверного Redis** Мы любим Prisma и PlanetScale, но некоторые проекты требуют более производительного решения. Upstash позволяет вам получить in-memory производительность Redis в вашем безсерверном проекте, не заботясь о самостоятельном управлении инфраструктурой и масштабированием. - [Главная страница Upstash](https://upstash.com/) ### Pusher **Для безсерверных WebSocket** Если WebSocket является основным фокусом вашего проекта, вы можете рассмотреть более традиционный бэкенд, такой как [Fastify](https://www.fastify.io/) (который [также работает с tRPC!](https://trpc.io/docs/v10/fastify)). Но для быстрого добавления WebSocket в T3 App, Pusher - отличный выбор. - [Главная страница Pusher](https://pusher.com/) ### Soketi Soketi - это само-развертываемая, простая и быстрая альтернатива Pusher. Он полностью совместим с Pusher SDK, который вы можете использовать для подключения к серверу. Soketi serverless также находится в бета-версии. - [Главная страница Soketi](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Аналитика Пользовательские данные очень ценны, когда вы создаете приложение. Вот несколько рекомендуемых провайдеров аналитики. ### Plausible Нужна аналитика? Plausible - один из самых быстрых способов ее получить. Супер минималистичный. У него даже есть [простой плагин для Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Главная страница Plausible](https://plausible.io/) ### Umami Umami - это само-развертываемая, простая, быстрая и конфиденциальная альтернатива Google Analytics. Вы можете легко развернуть его на Vercel, Railway и т.д. с PlanetScale в качестве базы данных. - [Главная страница Umami](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) ## Другое ### Next Bundle Analyzer Бывает сложно определить, что будет включено в сборку выходных данных для вашего приложения. Next Bundle Analyzer - это простой способ визуализировать и анализировать JavaScript-бандлы, которые генерируются. - [@next/bundle-analyzer на npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/ru/t3-collection.mdx ================================================ --- title: T3 коллекция description: Крутые open source проекты и компании использующие T3 стек layout: ../../layouts/docs.astro lang: ru isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import Callout from "../../components/docs/callout.tsx"; Сделали проект использующий T3 стек и хотите поделиться им? Добавьте его в список! ## Open Source приложения сделанные с использованием T3 стека ## Компании использующие T3 стек Нам бы очень хотелось узнать о компаниях использующих T3 стек для своих приложений. Ваша компания использует T3 стек и вы хотите поделиться этим? Добавьте её в список! Крутой проект использующий T3 стек? Сделайте [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) и добавьте его в список! ================================================ FILE: www/src/pages/ru/usage/env-variables.mdx ================================================ --- title: Переменные среды description: Начало работы с Create T3 App layout: ../../../layouts/docs.astro lang: ru isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Create T3 App использует собственный пакет [@t3-oss/env-nextjs](https://env.t3.gg) вместе с [Zod](https://github.com/colinhacks/zod) для валидации переменных среды во время выполнения _и_ во время сборки, предоставляя простую логику в файле `src/env.js`: ## env.js _TLDR; Если вы хотите добавить новую переменную среды, вам следует добавить валидатор в `src/env.js`, а затем пару ключ-значение в `.env`._ ```ts:env.js import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, }, }); ``` T3 Env использует `createEnv`, который отвечает за создание схемы и будет включать главную логику валидации для клиентских и серверных переменных среды. Для получения дополнительной информации о том, как `createEnv` работает изнутри, посмотрите документацию [T3 Env](https://env.t3.gg/docs/introduction) (EN) ## Использование переменных среды Когда вы хотите использовать переменные среды, вы можете импортировать их из `env.js` и использовать их как вы обычно использовали бы. Если вы импортируете этот файл на стороне клиента и попробуете получить серверную переменную, вы получите ошибку выполнения: ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` полностью типобезопасно и предоставляет автокомплит const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ Это даст ошибку const dbUrl = env.DATABASE_URL; // ✅ Тут все ОК const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example Из-за того, что файл `.env` по умолчанию не добавляется в систему контроля версий, мы также добавили файл `.env.example`, в котором вы можете по желанию сохранить копию вашего файла `.env` с удаленными секретами. Это необязательно, но мы рекомендуем держать пример в актуальном состоянии, чтобы сделать процесс настройки среды для новых участников проекта как можно проще. Некоторые фреймворки и инструменты сборки, такие как Next.js, предлагают хранить секреты в файле `.env.local` и коммитить файлы `.env` в ваш проект. Это не рекомендуется, поскольку это может облегчить случайный коммит секретов в ваш проект. Вместо этого мы рекомендуем хранить секреты в `.env`, держать ваш файл `.env` в `.gitignore` и коммитить только файлы `.env.example` в ваш проект. ## Добавление переменных среды Для того, чтобы убедиться, что ваша сборка никогда не завершится без переменных среды, которые проект требует, вам нужно добавить новые переменные среды в **двух** местах: 📄 `.env`: Введите переменную среды, как обычно делаете в файле `.env`, т.е. `KEY=VALUE` 📄 `env.js`: Добавьте соответствующую логику валидации для переменных среды, определив для каждой из них Zod схему внутри `createEnv`, например `KEY: z.string()`. Кроме этого, убедитесь в том, что вы деструкткрировали их в опции `runtimeEnv`, например `KEY: process.env.KEY`. Зачем нужно деструктурировать переменные среды внутри `runtimeEnv`? Это связано с тем, как Next.js собирает переменные среды в некоторых рантаймах. Деструктурируя их вручную, мы гарантируем, что эти переменные не будут убраны из финальной сборки. Опционально, вы также можете обновлять файл `.env.example`: 📄 `.env.example`: Введите вашу переменную среды, но убедитесь, что не включаете значение, если оно является секретным, т.е. `KEY=VALUE` или `KEY=` ### Пример _Я хочу добавить мой Twitter API токен в качестве переменной среды на стороне сервера_ 1. Добавьте переменную среды в `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Добавьте переменную среды в `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { TWITTER_API_TOKEN: z.string(), }, // ... runtimeEnv: { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }, }); ``` 3. _Опционально:_ Добавьте переменную среды в `.env.example`, но не включайте токен в `runtimeEnv` ``` TWITTER_API_TOKEN= ``` ================================================ FILE: www/src/pages/ru/usage/first-steps.md ================================================ --- title: Первые шаги description: Начало работы с вашим новым T3 App layout: ../../../layouts/docs.astro lang: ru --- Вы только что создали новое приложение T3 и готовы к работе. Вот минимальный набор для запуска вашего приложения. ## База данных Если ваше приложение включает Prisma, убедитесь, что вы запустили `npx prisma db push` из корневого каталога вашего приложения. Эта команда синхронизирует схему Prisma с вашей базой данных и генерирует типы TypeScript для Prisma Client на основе вашей схемы. Обратите внимание, что вам нужно перезапустить сервер TypeScript после этого, чтобы он мог обнаружить сгенерированные типы. ## Аутентификация Если ваше приложение включает NextAuth.js, мы начинаем с `DiscordProvider`. Это один из самых простых провайдеров, предлагаемый NextAuth.js, однако он все еще требует некоторой начальной настройки с вашей стороны. Конечно, если вы предпочитаете использовать другой провайдер аутентификации, вы также можете использовать один из [многих провайдеров](https://next-auth.js.org/providers/), которые предлагает NextAuth.js. 1. Вам нужен аккаунт Discord, поэтому зарегистрируйтесь, если еще не зарегистрировались. 2. Перейдите на https://discord.com/developers/applications и нажмите «New Application» в правом верхнем углу. Дайте вашему приложению имя и согласитесь с Условиями использования. 3. Когда вы создадите приложение, перейдите к «Settings → OAuth2 → General». 4. Скопируйте «Client ID» и добавьте его в ваш `.env` как `AUTH_DISCORD_ID`. 5. Нажмите «Reset Secret», скопируйте новый секрет и добавьте его в ваш `.env` как `AUTH_DISCORD_SECRET`. 6. Нажмите «Add Redirect» и введите `http://localhost:3000/api/auth/callback/discord`. - Для развертывания в продакшене следуйте предыдущим шагам для создания другого приложения Discord, но на этот раз замените `http://localhost:3000` на URL, на который вы развертываете. 7. Сохраните изменения. Теперь у вас должна быть возможность войти в систему. ## Следующие шаги - Если ваше приложение включает tRPC, ознакомьтесь с `src/pages/index.tsx` и `src/server/trpc/router/post.ts`, чтобы узнать, как работают запросы tRPC. - Посмотрите на документацию `create-t3-app`, а также на документацию пакетов, которые включает ваше приложение. - Присоединяйтесь к нашему [Discord](https://t3.gg/discord) и поставьте звезду на [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/ru/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Использование", layout: "docs", description: "Научитесь использовать различные технологии из T3 Stack.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/ru/usage/next-auth.md ================================================ --- title: NextAuth.js description: Использование NextAuth.js layout: ../../../layouts/docs.astro lang: ru --- Когда вы хотите иметь систему аутентификации в вашем приложении Next.js, NextAuth.js - отличное решение, чтобы не заморачиваться с реализацией сложной безопасности самостоятельно. Он имеет обширный список провайдеров для быстрого добавления аутентификации OAuth и предоставляет адаптеры для многих баз данных и ORM. ## Провайдер контекста В точке входа вашего приложения вы увидите, что ваше приложение обернуто в [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx ``` Этот провайдер контекста позволяет вашему приложению получить доступ к данным сессии из любого места вашего приложения, не передавая их как пропсы: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Handle unauthenticated state, e.g. render a SignIn component return ; } return

Welcome {session.user.name}!

; }; ``` ## Получение сессии на сервере Иногда вам может понадобиться запросить сессию на сервере. Чтобы сделать это, предварительно получите сессию с помощью функции-помощника `getServerAuthSession`, которую предоставляет `create-t3-app`, и передайте ее на клиент с помощью `getServerSideProps`: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // NOTE: `session` wont have a loading state since it's already prefetched on the server ... } ``` ## Включение `user.id` в сессию `create-t3-app` настроен для использования [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) в конфигурации NextAuth.js для включения ID пользователя в объект `session`. ```ts:pages/api/auth/[...nextauth].ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Это связано с файлом объявления типов, чтобы убедиться, что `user.id` типизирован при доступе к объекту `session`. Подробнее о [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) в документации NextAuth.js. ```ts:types/next-auth.d.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` Такой же шаблон может быть использован для добавления любых других данных в объект `session`, например, поля `role`, но **не следует злоупотреблять для хранения конфиденциальных данных** на клиенте. ## Использование с tRPC При использовании NextAuth.js с tRPC вы можете создавать повторно используемые, защищенные процедуры с помощью [middleware](https://trpc.io/docs/v10/middlewares). Это позволяет вам создавать процедуры, которые могут быть доступны только аутентифицированным пользователям. `create-t3-app` настраивает все это для вас, позволяя вам легко получать доступ к объекту сессии в аутентифицированных процедурах. Это делается в два шага: 1. Возьмите сессию из заголовков запроса с помощью функции [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). Преимущество использования `getServerSession` вместо обычного `getSession` заключается в том, что это server-side функция и она не вызывает ненужных вызовов fetch. `create-t3-app` создает вспомогательную функцию, которая абстрагирует этот особый API. ```ts:server/auth.ts export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return await getServerSession(ctx.req, ctx.res, authOptions); }; ``` Используя эту вспомогательную функцию, мы можем получить сессию и передать ее в контекст tRPC: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Создайте tRPC middleware, которое проверяет, аутентифицирован ли пользователь. Затем мы используем middleware в `protectedProcedure`. Любой вызывающий эти процедуры должен быть аутентифицирован, иначе будет сгенерирована ошибка, которую можно правильно обработать на стороне клиента. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }) ``` Обект сессии - это легкое, минимальное представление пользователя и содержит только несколько полей. При использовании `protectedProcedures` у вас есть доступ к идентификатору пользователя, который можно использовать для получения большего количества данных из базы данных. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Использование с Prisma Чтобы заставить NextAuth.js и Prisma работать вместе, необходимо большое количество [начальной настройки](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` выполняет все это для вас, и если вы выберете одновременно и Prisma, и NextAuth.js, вы получите полностью работающую систему аутентификации со всеми предварительно настроенными, необходимыми моделями. Мы предоставляем вашему сгенерированому приложению предварительно настроеный провайдер Discord OAuth, который мы выбрали потому, с ним легче всего начать - просто укажите свои токены в `.env` и вы готовы к работе. Однако вы можете легко добавить больше провайдеров, следуя [докуменетации NextAuth.js](https://next-auth.js.org/providers/). Обратите внимание, что некоторые провайдеры требуют дополнительных полей для добавления в определенные модели. Мы рекомендуем вам прочитать документацию для провайдера, который вы хотите использовать, чтобы убедиться, что у вас есть все необходимые поля. ### Добавление новых полей в ваши модели Когда вы добавляете новые поля в любую из моделей `User`, `Account`, `Session` или `VerificationToken` (в большинстве случаев вам потребуется только изменить модель `User`), вам нужно иметь в виду, что [адаптер Prisma](https://next-auth.js.org/adapters/prisma) автоматически создает поля в этих моделях при регистрации новых пользователей и входе в систему. Поэтому, добавляя новые поля в эти модели, вы должны предоставить значения по умолчанию для них, поскольку адаптер не знает о них. Если например, вы хотите добавить `role` в модель `User`, вам нужно будет предоставить значение по умолчанию для поля `role`. Это делается путем добавления значения `@default` к полю `role` в модели `User`: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Использование с Next.js middleware Использование NextAuth.js с Next.js middleware [требует использования стратегии сеанса JWT](https://next-auth.js.org/configuration/nextjs#caveats) для аутентификации. Это связано с тем, что middleware может получить доступ к сессионной cookie только в том случае, если это JWT. По умолчанию, `create-t3-app` настроен на использование **default** стратегии базы данных, в сочетании с Prisma в качестве адаптера базы данных. ## Настраиваем DiscordProvider по умолчанию 1. Перейдите в [раздел Applications в Discord Developer Portal](https://discord.com/developers/applications), и нажмите на "New Application" 2. В меню настроек перейдите к "OAuth2 => General" - Скопируйте Client ID и вставьте его в `AUTH_DISCORD_ID` в `.env`. - Возле Client Secret нажмите "Reset Secret" и скопируйте эту строку в `AUTH_DISCORD_SECRET` в `.env`. Будьте осторожны, поскольку вы больше не сможете увидеть этот секрет, и сброс его приведет к тому, что существующий истечет. - Нажмите "Add Redirect" и вставьте `/api/auth/callback/discord` (пример для локальной разработки: http://localhost:3000/api/auth/callback/discord) - Сохраните изменения - Возможно, но не рекомендуется, использовать одно и то же приложение Discord для разработки и продакшена. Вы также можете рассмотреть [Mocking the Provider](https://github.com/trpc/trpc/blob/next/examples/next-prisma-starter-websockets/src/pages/api/auth/%5B...nextauth%5D.ts) во время разработки. ## Полезные ресурсы | Ресурс | Ссылка | | --------------------------------- | --------------------------------------- | | Документация NextAuth.js | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/ru/usage/next-js.md ================================================ --- title: Next.js description: Использование Next.js layout: ../../../layouts/docs.astro lang: ru --- Next.js это бэкенд фреймворк для ваших React приложений.
Посмотрите [выступление Theo на Next.js Conf](https://www.youtube.com/watch?v=W4UhNo3HAMw) чтобы получить более подробное понимание того, что такое Next.js и как он работает. ## Почему я должен это использовать? Мы любим React. Он сделал разработку UI доступной в том смысле, в котором мы раньше этого не могли себе представить. Он также может провести разработчиков по некоторым тернистым тропам. Next.js предлагает слегка субъективный, но очень оптимизированный подход к созданию приложений с использованием React. От маршрутизации до определений API до рендеринга изображений, мы доверяем Next.js, чтобы вести разработчиков к правильным решениям. Совмещая Next.js с [Vercel](https://vercel.com/), вы можете разрабатывать и разворачивать веб-приложения легче, чем когда-либо. Их чрезвычайно щедрый бесплатный тариф и супер интуитивный интерфейс предоставляют решение в один клик для развертывания вашего сайта (Мы ❤️ Vercel) ## Get Static/Server Props Ключевые особенности Next.js - это возможности получения данных. Мы настоятельно рекомендуем прочитать [официальную документацию](https://nextjs.org/docs/basic-features/data-fetching), чтобы понять, как использовать каждый метод и чем они отличаются. `getServerSideProps` обычно не рекомендуется, если нет веской причины, из-за того, что это блокирующий вызов и он замедлит ваш сайт. [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) - это отличная альтернатива `getServerSideProps`, когда данные динамичны и могут быть получены постепенно. ## Полезные ресурсы | Ресурс | Ссылка | | ------------------------------- | ---------------------------------- | | Документация Next.js | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Блог Next.js | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Канал Vercel/Next.js на YouTube | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/ru/usage/prisma.md ================================================ --- title: Prisma description: Использование Prisma layout: ../../../layouts/docs.astro lang: ru --- Prisma это ORM для TypeScript, который позволяет определять схему и модели базы данных в файле `schema.prisma`, а затем генерировать клиент, который обеспечивает типобезопасность и может использоваться для взаимодействия с базой данных из вашего бэкенда. ## Prisma Client Расположенный в `/server/db/client.ts`, Prisma Client инициализируется как глобальная переменная (как рекомендовано [лучшими практиками](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) команды Prisma) и экспортируется для использования в ваших API маршрутах. Мы включаем Prisma Client в [Context](/ru/usage/trpc#-serverapitrpcts) по умолчанию и рекомендуем использовать его вместо отдельного импорта в каждом файле. ## Схема Вы найдете файл схемы Prisma в `/prisma/schema.prisma`. Этот файл используется для определения схемы и моделей базы данных, а также для генерации Prisma Client. ### С NextAuth.js Когда вы выбираете NextAuth.js в сочетании с Prisma, файл схемы генерируется и настраевается для вас с рекомендуемыми значениями для моделей `User`, `Session`, `Account` и `VerificationToken`, согласно [документации NextAuth.js](https://next-auth.js.org/adapters/prisma). ## База данных по умолчанию База данных по умолчанию - это база данных SQLite, которая отлично подходит для разработки и быстрого создания proof-of-concept, но не рекомендуется для использования в продакшене. Вы можете изменить базу данных, используя `provider` в блоке `datasource` на `postgresql` или `mysql`, а затем обновить строку подключения в переменных окружения, чтобы указать на вашу базу данных. ## Заполнение (seeding) базы данных [Заполнение (seeding) вашей базы данных](https://www.prisma.io/docs/guides/database/seed-database) - это отличный способ быстро заполнить вашу базу данных тестовыми данными, чтобы помочь вам начать. Чтобы настроить заполнение, вам нужно создать файл `seed.ts` в каталоге `/prisma` и затем добавить скрипт `seed` в файл `package.json`. Вам также понадобится некоторый TypeScript-раннер, который может выполнить скрипт заполнения. Мы рекомендуем [tsx](https://github.com/esbuild-kit/tsx), который является очень эффективным TypeScript-раннером, который использует esbuild и не требует какой-либо конфигурации ESM, но `ts-node` или другие раннеры также будут работать. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Затем, просто запустите `pnpm db-seed` (или `npm`/`yarn`), чтобы заполнить вашу базу данных. ## Полезные ресурсы | Ресурс | Ссылка | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Документация Prisma | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | NextAuth.JS Адаптер для Prisma | https://next-auth.js.org/adapters/prisma | | Гайд по подключению PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/ru/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Использование Tailwind CSS layout: ../../../layouts/docs.astro lang: ru --- ## Что такое Tailwind CSS? Tailwind CSS - это небольшой, [utility first](https://tailwindcss.com/docs/utility-first) CSS фреймворк для создания собственного дизайна, без переключения контекста, которое требуется для обычного CSS. Это чисто CSS фреймворк и не предоставляет никаких предварительно собранных компонентов или логики, и предоставляет [принципиально иной набор преимуществ](https://www.youtube.com/watch?v=CQuTF-bkOgc) по сравнению с библиотекой компонентов, такой как Material UI. Он делает CSS невероятно легким и быстрым для написания, как показано в следующем примере: Старый CSS: 1. Напишите CSS, часто в отдельном файле ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Импортируйте CSS в ваш компонент ```jsx import "./my-class.css"; ``` 3. Добавьте класс в ваш HTML ```html
...
``` Эквивалент в Tailwind: 1. Просто напишите классы в вашем HTML ```html
...
``` Используя вместе с React компонентами, он является чрезвычайно мощным для быстрого создания UI. Tailwind CSS содержит встроенную красивую систему дизайна, которая поставляется из коробки с тщательно выбранной цветовой палитрой, размерами для стилей, таких как ширина/высота и отступы для единого дизайна, а также точками прерывания для создания адаптивных макетов. Эта система дизайна может быть настроена и расширена для создания точного набора стилей, которые необходимы вашему проекту.
Tru Narla лучше известна как [mewtru](https://twitter.com/trunarla) дала потрясающую лекцию о [создании системы дизайна с использованием Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Использование Убедитесь, что у вас установлены плагины редактора для Tailwind, чтобы улучшить ваш опыт написания Tailwind. ### Расширения и плагины - [Расширение для VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Форматирование Классы Tailwind Css могут легко стать немного беспорядочными, поэтому форматтер для классов является необходимым. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) сортирует классы в [рекомендуемом порядке](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted), чтобы классы соответствовали собранному css-пакету. При выборе Tailwind в CLI мы установим и настроим это для вас. ### Добавление классов в зависимости от условий Добавление классов в зависимости от условий с использованием тернарных операторов может стать очень беспорядочным и трудночитаемым. Эти пакеты помогают организовать ваши классы при использовании некоторой логики с условиями. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Полезные ресурсы | Ресурс | Ссылка | | ----------------------------- | -------------------------------------------------------- | | Документация Tailwind | https://tailwindcss.com/docs/editor-setup/ | | Шпаргалка по Tailwind | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Сервер Tailwind в Discord | https://tailwindcss.com/discord/ | | Канал TailwindLabs на Youtube | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/ru/usage/trpc.md ================================================ --- title: tRPC description: Использование tRPC layout: ../../../layouts/docs.astro lang: ru --- tRPC позволяет нам писать типобезопасные API от конца до конца (end-to-end) без какой-либо генерации кода или накладных расходов на выполнение. Он использует отличную инференцию TypeScript для вывода типов маршрутизатора вашего API и позволяет вызывать процедуры API из вашего фронтенда с полной типобезопасностью и автодополнением. Используя tRPC, ваш фронтенд и бэкенд чувствуют себя ближе друг к другу, чем когда-либо, обеспечивая исключительный опыт разработчика.

I built tRPC to allow people to move faster by removing the need of a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.

Avatar of @alexdotjs
Alex - creator of tRPC @alexdotjs
## Файлы tRPC требует много шаблонного кода, который `create-t3-app` настраивает для вас. Давайте рассмотрим файлы, которые генерируются: ### 📄 `pages/api/trpc/[trpc].ts` Этот файл является точкой входа для вашего API и экспортирует ваш tRPC роутер. Обычно, вам не придется трогать этот файл, но если вам нужно, например, включить CORS middleware или что то подобное, полезно знать, что экспортируемый `createNextApiHandler` - это [Next.js API handler](https://nextjs.org/docs/api-routes/introduction) который принимает [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) и [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) объект. Это означает, что вы можете обернуть `createNextApiHandler` в любой middleware, который вам нужен. См. ниже для [примера](#enabling-cors) добавления CORS. ### 📄 `server/api/trpc.ts` Этот файл разделен на две части: создание контекста и инициализация tRPC. 1. Мы определяем контекст, который передается вашим tRPC процедурам. Контекст - это данные, к которым у вас есть доступ во всех ваших tRPC процедурах, и это отличное место, чтобы поместить вещи, такие как соединения с базой данных, информация об аутентификации и т.д. В create-t3-app мы используем две функции, чтобы включить использование подмножества контекста, когда у нас нет доступа к объекту запроса. - `createInnerTRPCContext`: Это то место, где вы определяете контекст, который не зависит от запроса, например, ваше соединение с базой данных. Вы можете использовать эту функцию для [интеграционного тестирования](#sample-integration-test) или [ssg-помощников](https://trpc.io/docs/v10/ssg-helpers), где у вас нет объекта запроса. - `createTRPCContext`: Здесь вы определяете контекст, который зависит от запроса: например, сессия пользователя. Вы запрашиваете сессию с помощью объекта `opts.req`, а затем передаете сессию в функцию `createInnerTRPCContext` для создания окончательного контекста. 2. Мы инициализируем tRPC и определяем повторно используемые [процедуры](https://trpc.io/docs/v10/procedures) и [посредники](https://trpc.io/docs/v10/middlewares). По соглашению, вы не должны экспортировать весь объект `t`, а вместо этого создавать повторно используемые процедуры и посредники и экспортировать их. Вы заметите что мы используем `superjson` как [преобразователь данных](https://trpc.io/docs/v10/data-transformers). Это позволяет сохранять типы данных, когда они достигают клиента, поэтому если вы, например, отправляете объект `Date`, клиент вернет `Date`, а не строку, что является случаем для большинства API. ### 📄 `server/api/routers/*.ts` Это то место, где вы определяете маршруты и процедуры вашего API. Вы [создаете отдельные маршрутизаторы](https://trpc.io/docs/v10/router) для связанных процедур. ### 📄 `server/api/root.ts` Здесь мы [объединяем](https://trpc.io/docs/v10/merging-routers) все под-маршрутизаторы, определенные в `routers/**` в единый маршрутизатор приложения. ### 📄 `utils/api.ts` Это точка входа для tRPC на фронтенде. Здесь вы импортируете **определение типов** маршрутизатора и создаете клиент tRPC вместе с хуками react-query. Поскольку мы включили `superjson` в качестве преобразователя данных на бэкенде, нам также нужно включить его на фронтенде. Это связано с тем, что сериализованные данные с бэкенда десериализуются на фронтенде. Здесь вы определите свои [ссылки](https://trpc.io/docs/v10/links) tRPC, которые определяют поток запросов от клиента к серверу. Мы используем "default" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink), который позволяет [группировать запросы](https://cloud.google.com/compute/docs/api/how-tos/batch), а также [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink), который выводит полезные журналы запросов во время разработки. В конце концов, мы экспортируем [вспомогательный тип](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type), который вы можете использовать для вывода типов на фронтенде. ## Как я могу использовать tRPC?
tRPC контрибьютор [trashh_dev](https://twitter.com/trashh_dev) [выступил на Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) с рассказом о tRPC. Мы настоятельно рекомендуем вам посмотреть его, если вы еще не сделали это. С tRPC вы пишете функции TypeScript на бэкенде, а затем вызываете их из фронтенда. Простая процедура tRPC может выглядеть так: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Это процедура tRPC (эквивалент обработчика маршрута в традиционном бэкенде), которая сначала проверяет входные данные с помощью Zod (который является той же библиотекой проверки, которую мы используем для [переменных окружения](./env-variables)) - в этом случае мы убеждаемся, что входные данные являются строкой. Если входные данные не являются строкой, он отправит информативную ошибку. После входных данных мы применяем resolver функцию, которая может быть либо [запросом](https://trpc.io/docs/v10/react-queries), [мутацией](https://trpc.io/docs/v10/react-mutations) или [подпиской](https://trpc.io/docs/v10/subscriptions). В нашем примере resolver вызывает нашу базу данных с помощью клиента [prisma](./prisma) и возвращает пользователя, у которого `id` совпадает с тем, который мы передали. Вы определяете свои процедуры в `роутерах` которые представляют собой коллекцию связанных процедур с общим пространством имен. У вас может быть один роутер для `пользователей`, один для `постов` и еще один для `сообщений`. Эти роутеры затем можно объединить в единый централизованный `appRouter`: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Обратите внимание на то, что нам нужно экспортировать только типы наших роутеров, что означает, что мы никогда не импортируем серверный код на клиенте. Теперь давайте вызовим процедуру на нашем фронтенде. tRPC предоставляет обертку для `@tanstack/react-query`, которая позволяет вам использовать все возможности хуков (hooks), которые он предоставляет, но с дополнительным преимуществом того, что ваши вызовы API типизированы и инферированы (inferred). Мы можем вызывать наши процедуры с нашего фронтенда следующим образом: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Вы сразу заметите, насколько хороши автодополнение и типобезопасность. Как только вы начнете писать `trpc.`, ваши роутеры появятся в автодополнении, и когда вы выберете роутер, его процедуры также появятся. Вы также получите ошибку TypeScript, если ваш ввод не соответствует валидатору, который вы определили на бэкенде. ## Как я могу вызывать свой API извне? В обычных API, вы можете вызвать ваши конечные точки (endpoints) используя любой HTTP клиент, как `curl`, `Postman`, `fetch` или прямо из вашего браузера. С tRPC, это немного иначе. Если вы хотите вызывать ваши процедуры без клиента tRPC, есть два рекомендуемых способа сделать это: ### Раскройте одну процедуру внешне Если вы хотите раскрыть вашу процедуру внешне, вы ищете [server side calls](https://trpc.io/docs/v10/server-side-calls). Это позволит вам создать обычную конечную точку (endpoint) API Next.js, но переиспользовать часть резолвера вашей tRPC процедуры. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Раскройте каждую процедуру внешне Если вы хотите раскрыть каждую процедуру внешне, ознакомьтесь с плагином сообщества [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Предоставляя дополнительные метаданные вашим процедурам, вы можете сгенерировать соответствующее REST API из вашего tRPC роутера. ### Это всего лишь HTTP запросы tRPC взаимодействует через HTTP, поэтому также возможно вызывать ваши процедуры tRPC используя "обычные" HTTP запросы. Однако синтаксис может быть неприятным из-за [RPC протокола](https://trpc.io/docs/v10/rpc), который использует tRPC. Если вам интересно, вы можете проверить, что tRPC запросы и ответы выглядят в вашей вкладке сети браузера, но мы рекомендуем делать это только в качестве образовательного упражнения и придерживаться одного из решений, описанных выше. ## Сравнение с конечной точкой (enpoint) Next.js API Давайте сравним конечную точку (endpoint) Next.js API с процедурой tRPC. Допустим, мы хотим получить объект пользователя из нашей базы данных и вернуть его на фронтенд. Мы могли бы написать конечную точку (endpoint) Next.js API следующим образом: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Сравните это с примером tRPC выше и вы увидите некоторые преймущества tRPC: - Вместо того, чтобы указывать URL для каждого маршрута, который может стать неудобным для отладки, если вы что-то переместите, ваш весь маршрутизатор - это объект с автозаполнением. - Вам не нужно проверять, какой HTTP метод был использован. - Вам не нужно проверять, что запрос или тело запроса содержат правильные данные в процедуре, потому что Zod позаботится об этом. - Вместо создания ответа, вы можете выбрасывать ошибки и возвращать значение или объект, как в любой другой функции TypeScript. - Вызывая процедуру на фронтенде, вы не получаете никакого автозаполнения или проверки типов. ## Полезные сниппеты Здесь приведены некоторые сниппеты, которые могут пригодиться. ### Включение CORS Если вам нужно использовать ваш API с другого домена, например в монорепозитории, который включает в себя приложение React Native, вам может потребоваться включить CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Enable cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### Оптимистические обновления Оптимистическое обновление - это когда мы обновляем пользовательский интерфейс до того, как API-запрос завершится. Это предоставляет пользователю лучший опыт, потому что он не должен ждать завершения API-запроса, прежде чем пользовательский интерфейс отобразит результат его действия. Однако приложения, которые ценят корректность данных, должны избегать оптимистические обновления, поскольку они не являются «истинным» представлением состояния бэкенда. Вы можете прочитать больше в [документации React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await utils.post.list.cancel(); // Get the data from the queryCache const prevData = utils.post.list.getData(); // Optimistically update the data with our new post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Return the previous data so we can revert if something goes wrong return { prevData }; }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sync with server once mutation has settled utils.post.list.invalidate(); }, }); }; ``` ### Простой Интеграционный Тест Здесь преведен простой интеграционный тест, который использует [Vitest](https://vitest.dev), чтобы проверить, что ваш маршрутизатор tRPC работает должным образом, парсер входных данных выводит правильный тип и возвращаемые данные соответствуют ожидаемому результату. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` ## Полезные ресурсы | Ресурс | Ссылка | | ------------------------------------- | ------------------------------------------------------- | | Документация по tRPC | https://www.trpc.io | | Несколько примеров использования tRPC | https://github.com/trpc/trpc/tree/next/examples | | Документация React Query | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/ru/usage/typescript.md ================================================ --- title: TypeScript description: Использование TypeScript layout: ../../../layouts/docs.astro lang: ru ---

Build safety nets, not guard rails

Avatar of @t3dotgg
Theo - creator of the T3 Stack @t3dotgg
Несмотря на то новичок вы или же опытный разработчик, мы считаем, что TypeScript - это маст-хэв. Поначалу он может выглядеть угнетающе, но, как и многие другие инструменты, многие разработчики не возвращаются назад после того как начали его использовать. Он предоставляет обратную связь в режиме реального времени при написании кода, определяя ожидаемые типы данных, и предоставляет полезные подсказки в редакторе кода либо кричит красными волнистыми линиями, если вы пытаетесь получить доступ к свойству, которого не существует, или пытаетесь передать значение неправильного типа, которое в противном случае пришлось бы отлаживать дальше по линии. Это инструмент, который, возможно, обеспечивает наибольшую производительность разработчикам; он предоставляет документацию для кода, который вы пишете или используете непосредственно в вашем редакторе и имеет мгновенную обратную связь, когда вы неизбежно делаете ошибки, что абсолютно бесценно. ## Выведение типов (Type Inference) Пока многие новые разработчики на TypeScript обеспокоены _написанием_ TypeScript, многие из его преимуществ на самом деле не требуют от вас изменения вашего кода вообще, в частности вывод типов. Вывод типов означает, что если что-то типизировано, этот тип будет следовать за ним в течение потока приложения без необходимости повторного объявления в других местах. Это означает, что, например, после того, как вы определили типы аргументов, которые принимает функция, остальная часть функции обычно будет безопасной в отношении типов без необходимости ввода какого-либо дополнительного кода, специфичного для TypeScript. Разработчики библиотек тратят огромное количество времени на поддержание типов для своих библиотек, что означает, что мы, как разработчики приложений, можем получить выгоду от вывода типов и встроенной документации в вашем редакторе кода, которые эти типы предоставляют.
Посмотрите видео Theo о том, что [вы, возможно, используете TypeScript неправильно](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Мощные применения вывода типов ### Zod [Zod](https://github.com/colinhacks/zod) - это библиотека проверки схем, построенная поверх TypeScript. Напишите схему, которая представляет собой единственный источник истины для ваших данных, и Zod гарантирует, что ваши данные будут действительными во всем приложении, даже вне границ сети и внешних API. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) предоставляет вам декларативные, всегда актуальные автоматически управляемые запросы и мутации, которые напрямую улучшают как Developer, так и User Experience. ## Полезные ресурсы | Ресурс | Ссылка | | --------------------------------------------------------------- | ----------------------------------------------------------------- | | Руководство по TypeScript | https://www.typescriptlang.org/docs/handbook/ | | Гайд по TypeScript для новичков | https://github.com/total-typescript/beginners-typescript-tutorial | | Type Challenges | https://github.com/type-challenges/type-challenges | | Канал Родни Маллена из мира TypeScript (Matt Pocock) на YouTube | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/ru/why.md ================================================ --- title: Почему CT3A? description: Почему вам следует выбрать Create T3 App для вашего следующего проекта layout: ../../layouts/docs.astro lang: ru --- Мы начали создавать create-t3-app, потому что [Theo](https://twitter.com/t3dotgg) отказался делать шаблон своих любимых технологий. Вдохновленный create-next-app, [CLI Astro](https://astro.build) и общей любовью к типобезопасности, команда create-t3-app трудилась, чтобы создать лучшую возможную отправную точку для новых проектов T3 Stack. Если вы заинтересованы в использовании Next.js в типобезопасном режиме, это то с чего нужно начать. Если вы интересуетесь любыми из конкретных технологических решений, которые мы приняли, читайте дальше :) ## Почему TypeScript? JavaScript сложен. Зачем добавлять еще больше правил? Мы всецело уверены, что опыт TypeScript позволяет вам быть лучшим разработчиком. Он предоставляет обратную связь во время написания кода, определяя ожидаемые типы данных, и либо предоставляет полезное автозаполнение в вашем редакторе, либо кричит вам красными волнистыми линиями, если вы пытаетесь получить доступ к свойству, которого не существует, или пытаетесь передать значение неправильного типа, что в противном случае пришлось бы отлаживать дальше по линии. Независимо от того, новичок вы в веб-разработке или опытный профессионал, «строгость» TypeScript обеспечивает менее раздражающий, более последовательный опыт, чем ванильный JS. Типобезопасность делает вас быстрее. Если вы еще не убеждены, вы [можете использовать TypeScript неправильно ...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## Почему Next.js? Мы любим React. Он сделал разработку UI доступной в том виде, в котором мы никогда не могли себе представить. Он также может привести разработчиков по некоторым тернистым путям. Next.js предлагает мение субъективный, но более оптимизированный подход к созданию приложений с использованием React. От маршрутизации до определения API до рендеринга изображений мы доверяем Next.js, чтобы вести разработчиков к правильным решениям. ## Почему tRPC/Prisma/Tailwind/и т.д.? Хотя мы верим, что все должно быть просто, мы находим, что эти части используются в каждом проекте, похожем на приложение. `create-t3-app` выполняет отличную работу, позволяя вам выбрать те части, которые вам нужны. ### tRPC tRPC предоставляет все преимущества GraphQL, такие как бесшовная разработка клиента против типобезопасного сервера без всех шаблонов. Это умное злоупотребление TypeScript, которое обеспечивает невероятный опыт разработки. ### Prisma Prisma для SQL то, что TypeScript для JS. Она создала опыт разработчика, которого раньше не было. Создавая типы из пользовательской схемы, совместимой с [несколькими базами данных](https://www.prisma.io/docs/concepts/database-connectors), Prisma гарантирует типобезопасность от начала до конца от вашей базы данных до вашего приложения. Prisma предоставляет целый [набор инструментов](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows), облегчающих ежедневное взаимодействие с вашей базой данных. В частности, Prisma Client отвечает за запросы и делает SQL настолько простым, что вы едва заметите, что его используете, а Prisma Studio - это удобный GUI для вашей базы данных, который позволяет быстро читать и манипулировать данными без необходимости писать код. ### Tailwind CSS Tailwind ощущается как "CSS в режиме дзен". Благодаря предоставлению строительных блоков в виде хороших цветов по умолчанию, отступов и других примитивов Tailwind позволяет легко создавать приложение с хорошим внешним видом. И в отличие от библиотек компонентов, он не ограничивает вас, когда вы хотите вывести свое приложение на следующий уровень и создать что-то красивое и уникальное. К тому же, благодаря инлайновому подходу, Tailwind побуждает вас стилизовать без беспокойства о названии классов, организации файлов или любой другой проблеме, не связаной с решением задачи. ### NextAuth.js Когда вы хотите добавить систему аутентификации в ваше приложение NextJS, NextAuth.js - отличное решение, чтобы не заморачиваться с реализацией сложной системы безопасности. Она имеет обширный список провайдеров для быстрого добавления аутентификации через OAuth и предоставляет адаптеры для многих баз данных и ORM. ================================================ FILE: www/src/pages/themeTest.astro ================================================ --- import HeadCommon from "../components/headCommon.astro"; import ThemeToggleButton from "../components/navigation/themeToggleButton.astro"; import "../styles/global.css"; import "../styles/accessibility.css"; ---

Colors

Neutral
Primary
Secondary
Accent
Warning
Error
Success
Info

Typography

Text 9xl

Text 8xl

Text 7xl

Text 6xl

Text 5xl

Text 4xl

Text 3xl

Text 2xl

Text xl

Text lg

Text base

Text sm

Text xs

bold

code

emphasized

underline

overline

line-through

no-underline

================================================ FILE: www/src/pages/uk/deployment/docker.md ================================================ --- title: Docker description: Деплоймент в Docker layout: ../../../layouts/docs.astro lang: uk --- Ви можете контейнеризувати цей стек і розгорнути його як один контейнер за допомогою Docker або як частину групи контейнерів за допомогою docker-compose. Дивіться [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker) для прикладу репозиторію на основі цієї документації. ## Конфігурація проекту Docker Будь ласка, зверніть увагу, що Next.js потребує різних процесів для білда (доступні у фронтенді, з префіксом `NEXT_PUBLIC`) та змінних середовища, доступних тільки на сервері. У цьому прикладі ми використовуємо дві змінні, зверніть увагу на їх позиції в `Dockerfile`, аргументи командного рядка та `docker-compose.yml`: - `DATABASE_URL` (використовується сервером) - `NEXT_PUBLIC_CLIENTVAR` (використовується клієнтом) ### 1. Конфігурація Next У вашому [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js), додайте конфігурацію ` output` зі значенням `standalone` для [зменшення розміру образу за допомогою автоматичного використання трасувань виводу](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. Створіть dockerignore file
Натисніть тут і вставте вміст у .dockerignore:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. Створіть Dockerfile > Через те, що ми не виймаємо змінні середовища сервера в наш контейнер, [перевірка схеми середовища](/uk/usage/env-variables) не пройде. Щоб цього уникнути, ми повинні додати прапор `SKIP_ENV_VALIDATION=1` до команди білда, щоб схеми оточення не перевірялися під час білда.
Натисніть тут і вставте вміст у Dockerfile:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl1.1-compat WORKDIR /app # Install Prisma Client - remove if not using Prisma COPY prisma ./ # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_Нотатки_** > > - _Емуляція `--platform=linux/amd64` може не бути необхідною після переходу на Node 18._ > - _Подивіться [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) щоб зрозуміти, чому `libc6-compat` може бути необхідним._ > - _Використання образів, заснованих на Alpine 3.17 [може призвести до проблем з Prisma](https://github.com/t3-oss/create-t3-app/issues/975). Встановлення `engineType = "binary"` вирішує проблему з Alpine 3.17, [але має пов'язані з цим витрати продуктивності](https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#the-query-engine-at-runtime)._ > - _Next.js збирає [анонімні дані про телеметрію загального використання](https://nextjs.org/telemetry). Розкоментуйте перший екземпляр `ENV NEXT_TELEMETRY_DISABLED 1`, щоб вимкнути телеметрію під час білда. Розкоментуйте другий екземпляр, щоб вимкнути телеметрію під час виконання._
## Зберіть та запустіть образ локально Зберіть і запустіть цей образ локально за допомогою наступних команд: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` Відкрийте [localhost:3000](http://localhost:3000/) щоб побачити запущений додаток. ## Docker Compose Ви також можете використовувати Docker Compose для збирання образу та запуску контейнера.
Пройдіть кроки 1-4 вище, натисніть тут і додайте вміст в docker-compose.yml:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` Запустіть за допомогою команди `docker compose up`: ```bash docker compose up ``` Відкрийте [localhost:3000](http://localhost:3000/) щоб побачити запущений додаток.
## Деплоймент на Railway Ви можете використовувати PaaS як автоматичний [деплоймент Dockerfile](https://docs.railway.app/deploy/dockerfiles) від [Railway's](https://railway.app) для деплою вашої програми. Якщо у вас [встановлений Railway CLI](https://docs.railway.app/develop/cli#install), ви можете задеплоїти свою програму за допомогою наступних команд: ```bash railway login railway init railway link railway up railway open ``` Перейдіть до "Variables" і увімкніть ваш `DATABASE_URL`. Потім перейдіть до "Settings" і виберіть "Generate Domain." Щоб побачити працюючий приклад на Railway, перейдіть до [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/). ## Корисні ресурси | Ресурс | Посилання | | ---------------------------------------- | -------------------------------------------------------------------- | | Приклад для Dockerfile | https://docs.docker.com/engine/reference/builder/ | | Приклад для файлу Compose 3 версії | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Приклад Docker CLI | https://docs.docker.com/engine/reference/commandline/docker/ | | Приклад Docker Compose CLI | https://docs.docker.com/compose/reference/ | | Розгортання Next.js із Docker Image | https://nextjs.org/docs/deployment#docker-image | | Next.js в Docker'е | https://benmarte.com/blog/nextjs-in-docker/ | | Приклад Next.js з Docker | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | Створення Docker образу Next.js програми | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/uk/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Деплоймент", layout: "docs", description: "Навчіться деплоїти вашу програму T3 в продакшен.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/uk/deployment/netlify.md ================================================ --- title: Netlify description: Деплоймент в Netlify layout: ../../../layouts/docs.astro --- Netlify - це альтернативний провайдер деплою, схожий на Vercel. Ось [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify) приклад репозиторію на основі цієї документації. ## Навіщо деплоїти на Netlify Вважають, що Vercel має кращу підтримку Next.js тому що Vercel розробляє Next.js. Вони зацікавлені в тому, щоб платформа була налаштована для оптимальної продуктивності та DX з Next.js. Найчастіше це так і у відхиленні від стандартного шляху не буде сенсу. Також існує спільна думка про те, що багато функцій Next.js підтримуються тільки на Vercel. Хоча це правда, що нові функції Next.js будуть тестуватись і підтримуватись на Vercel в момент випуску за замовчуванням, також слід враховувати, що інші провайдери, такі як Netlify, [швидко реалізують та випускають підтримку](https://www.netlify.com/blog/deploy-nextjs-13/) для [стабільних функцій Next.js](https://docs.netlify.com/integrations/frameworks/next-js/overview/). Всі провайдери деплойменту мають переваги і недоліки, оскільки жоден хост не може мати кращу підтримку для всіх випадків використання. Наприклад, Netlify розробив свій власний [користувальницький Next.js runtime](https://github.com/netlify/next-runtime) для Netlify Edge Functions (які працюють на Deno Deploy) та [підтримують унікальні проміжні програми для доступу та зміни HTTP-відповідей](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify). > _Зверніть увагу: Щоб відстежувати статус нестабільних функцій Next 13, див. [Використання каталогу `app` Next 13 на Netlify](https://github.com/netlify/next-runtime/discussions/1724)._ ## Конфігурація проекту Існує кілька способів налаштування інструкцій білда, включаючи пряме використання Netlify CLI або Netlify Dashboard. Хоча це не обов'язково, рекомендується створити та включити файл [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/). Це гарантує, що форкнуті та клоновані версії проекту будуть легше повторно задеплоєні. ```toml [build] command = "next build" publish = ".next" ``` ## Використання Netlify Dashboard 1. Запуште свій код до репозиторію GitHub і зареєструйтесь на [Netlify](https://app.netlify.com/signup). Після того, як ви створили обліковий запис, натисніть **Add new site** і потім **Import an existing project**. ![Новий проект у Netlify](/images/netlify-01-new-project.webp) 2. Підключіть свій провайдер Git. ![Імпортуйте репозиторій](/images/netlify-02-connect-to-git-provider.webp) 3. Виберіть репозиторій вашого проекту. ![Виберіть репозиторій вашого проекту](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify виявить, якщо у вас є файл `netlify.toml` і автоматично налаштує команду білда та директорію публікацій. ![Налаштування збірки Nextjs](/images/netlify-04-configure-build-settings.webp) 5. Натисніть **Show advanced**, а потім **New variable**, щоб додати свої змінні середовища. ![Додати змінні середовища](/images/netlify-05-env-vars.webp) 6. Натисніть **Deploy site**, зачекайте, поки білд завершиться, і перегляньте свій новий сайт. ## Використання Netlify CLI Для того, щоб задеплоїти проект із командного рядка, ви повинні спочатку запушити свій проект до репозиторію GitHub і [встановити Netlify CLI](https://docs.netlify.com/cli/get-started/). Ви можете встановити `netlify-cli` як залежність проекту або встановити його глобально на вашому комп'ютері за допомогою наступної команди: ```bash npm i -g netlify-cli ``` Для того, щоб протестувати свій проект локально, запустіть команду [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) та відкрийте [`localhost :8888`](http://localhost:8888/) для перегляду вашої локально запущеної програми Netlify: ```bash ntl dev ``` Запустіть команду [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment), щоб налаштувати ваш проект: ```bash ntl init ``` Імпортуйте змінні середовища вашого проекту з вашого файлу `.env` за допомогою [`ntl env:import`](https://cli.netlify.com/commands/env#envimport): ```bash ntl env:import .env ``` Задеплойте ваш проект за допомогою [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys). Вам потрібно буде передати прапор `--build`, щоб запустити команду білда перед деплойментом, та прапор `--prod`, щоб задеплоїти на основному URL вашого сайту: ```bash ntl deploy --prod --build ``` Щоб переглянути приклад на Netlify, перейдіть на [ct3a.netlify.app](https://ct3a.netlify.app/). ================================================ FILE: www/src/pages/uk/deployment/vercel.md ================================================ --- title: Vercel description: Деплоймент на Vercel layout: ../../../layouts/docs.astro lang: uk --- Ми рекомендуємо деплоїти вашу програму на [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss). Він дозволяє дуже легко деплоїти Next.js програми. ## Конфігурація проекту Швидше за все Vercel налаштує вашу команду збірки та каталог публікації автоматично. Однак ви також можете уточнити цю інформацію разом з іншими конфігураціями, створивши файл [vercel.json](https://vercel.com/docs/project-configuration) і включивши в нього наступні команди. **У більшості проектів це робити необов'язково.** ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## Використання Vercel Dashboard 1. Після відправки вашого коду до репозиторію GitHub зареєструйтесь на [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss) з GitHub і натисніть **Add New Project**. ![Новий проект на Vercel](/images/vercel-new-project.webp) 2. Імпортуйте репозиторій GitHub із вашим проектом. ![Імпортуйте репозиторій](/images/vercel-import-project.webp) 3. Додайте ваші змінні середовища. ![Додайте ваші змінні середовища](/images/vercel-env-vars.webp) 4. Натисніть **Deploy**. Тепер щоразу, коли ви відправляєте зміни до вашого репозиторію, Vercel автоматично передеплоїть вашу програму! ## Використання Vercel CLI Для того, щоб задеплоїти програму з командного рядка, вам спочатку потрібно [встановити Vercel CLI глобально](https://vercel.com/docs/cli#installing-vercel-cli). ```bash npm i -g vercel ``` Запустіть команду [`vercel`](https://vercel.com/docs/cli/deploying-from-cli), щоб задеплоїти ваш проект. ```bash vercel ``` Додайте `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` для змінних середовища, таких як рядок підключення до бази даних. Використовуйте `--yes`, якщо хочете пропустити питання деплою та дати відповідь за умовчанням для кожного. ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` Після першого деплою ця команда задеплоїть зміни у гілку попереднього перегляду. Вам потрібно буде включити `-prod`, щоб відправити зміни безпосередньо на сайт у продакшені для майбутніх деплоїв. ```bash vercel --prod ``` ================================================ FILE: www/src/pages/uk/examples.mdx ================================================ --- title: Приклади description: Приклади різних існуючих додатків layout: ../../layouts/docs.astro lang: uk isMdx: true --- import Callout from "../../components/docs/callout.tsx"; import Form from "../../components/docs/exampleOptionForm.astro"; Ви можете випробувати різні комбінації технологій, які пропонує create-t3-app. Ви не можете вибрати `prisma` та `drizzle` одночасно. Деякі функції можуть не працювати, якщо ви не створите файл env ================================================ FILE: www/src/pages/uk/faq.mdx ================================================ --- title: FAQ description: Найпоширеніші запитання про Create T3 App layout: ../../layouts/docs.astro lang: uk --- import Callout from "../../components/docs/callout.tsx"; Це деякі часті запитання про `create-t3-app`. ## Що далі? Як я можу створити додаток? Ми намагаємося зробити цей проект якомога простішим, тому ви можете почати з того, що ми налаштували для вас, а потім додавати додаткові вам речі, коли вони стануть вам необхідними. Якщо ви не знайомі з різними технологіями, що використовуються в цьому проєкті, зверніться до відповідної документації. Якщо щось усе ще не зрозуміло, приєднуйтесь до нашого [Discord](https://t3.gg/discord) і попросіть допомоги. - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) ## Як мені тримати свій додаток оновленим? Додаток Create T3 – це інструмент для будування, а не фреймворк. Це означає, що як тільки ви ініціалізуєте програму, вона стане вашою. Немає жодного інструменту CLI після інсталяції, який би допомагав вам залишатися в курсі подій. Якщо ви хочете відстежувати будь-які вдосконалення, які ми вносимо в шаблон, ви можете [увімкнути сповіщення про випуски](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository) у нашому репозиторії. Зважаючи це, насправді не обов'язково вносити кожну зміну, яку ми вносимо в шаблон у вашій програмі. ## Які навчальні ресурси зараз доступні? Не дивлячись на те, що перераховані нижче ресурси є одними з найкращих для T3 Stack, спільнота (і [Theo](https://youtu.be/rzwaaWH0ksk?t=1436)) рекомендує просто почати використовувати стек і вчитися в процесі розробки з його допомогою. Якщо ви розглядаєте Create T3 App, швидше за все, ви вже використовували деякі частини стека. То чому б не зануритися в проєкт з головою і не вивчити решту технологій у процесі розробки? Ми розуміємо, що цей шлях не підходить для кожного. Тому, якщо ви відчуваєте, що ви спробували рекомендований шлях і цього недостатньо, і вам все ще потрібні ресурси, або ви просто не впевнені у своїх силах та/або почуваєтесь перевантаженим цим стеком, ознайомтеся з цими приголомшливими навчальними матеріалами щодо Create T3 App: ### Статті Деякі з них можуть бути застарілими. - [A first look at Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [Migrating your T3 App into a Turborepo](https://www.jumr.dev/blog/t3-turbo) - [Integrating Stripe into your T3 App](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### Відео - [T3 Stack Tutorial - FROM 0 TO PROD FOR $0 (Next.js, tRPC, TypeScript, Tailwind, Prisma & More)](https://www.youtube.com/watch?v=YkOSUVzOAA4) **(рекомендовано)** - [Jack Herrington - Build a Note Taking app with the T3 Stack](https://www.youtube.com/watch?v=J1gzN1SAhyM) - [Build a Twitter Clone with the T3 Stack - tRPC, Next.js, Prisma, Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [Build a Blog With the T3 Stack - tRPC, TypeScript, Next.js, Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [Build a Live Chat Application with the T3 Stack - TypeScript, Tailwind, tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [The T3 Stack - How We Built It](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [An overview of the Create T3 App (Next, Typescript, Tailwind, tRPC, Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## Чому в проєкті є файли `.js`? Згідно з [T3-Аксіомою #3](/uk/introduction#%D1%82%D0%B8%D0%BF%D0%BE%D0%B1%D0%B5%D0%B7%D0%BF%D0%B5%D0%BA%D0%B0-%D0%BD%D0%B5-%D1%94-%D0%BE%D0%BF%D1%86%D1%96%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D1%8E), ми вважаємо типобезпеку об'єктом першого класу. На жаль, не всі фреймворки та плагіни підтримують TypeScript, тому деякі файли конфігурації мають бути файлами `.js`. Ми намагаємося підкреслити, що ці файли є файлами JavaScript не без причини, явно оголошуючи тип кожного файлу (`cjs` або `mjs`), залежно від того, що підтримується бібліотекою, якою він використовується. Крім того, всі файли `js` у цьому проєкті все ще перевіряються на типи за допомогою коментаря `@ts-check` вгорі. ## У мене не виходить додати i18n до мого додатка. Чи є якісь рекомендації? Ми вирішили не включати i18n за замовчуванням у `create-t3-app`, тому що це дуже суб'єктивна тема, і є багато способів її реалізації. У той же час, якщо ви зіткнулися з проблемами під час реалізації та хочете побачити проєкт-зразок, у нас є [посилання на репозиторій](https://github.com/juliusmarminge/t3-i18n), яке показує, як ви можете додати i18n в T3 App за допомогою [next-i18next](https://github.com/i18next/next-i18next). ## Чому ми використовуємо `/pages` а не `/app` з Next.js 13? Згідно з [T3-Аксіомою #2](/uk/introduction#%D0%BE%D0%BD%D0%BE%D0%B2%D0%BB%D1%8E%D0%B9%D1%82%D0%B5%D1%81%D1%8C-%D0%B7-%D0%B2%D1%96%D0%B4%D0%BF%D0%BE%D0%B2%D1%96%D0%B4%D0%B0%D0%BB%D1%8C%D0%BD%D1%96%D1%81%D1%82%D1%8E), ми любимо новинки, але цінуємо стабільність, вам буде складно перенести весь маршрутизатор, [не найкраще місце для експериментів](https://youtu.be/mnwUbtieOuI?t=1662). Хоча `/app` [являє собою погляд у майбутнє](https://youtu.be/rnsC-12PVlM?t=818), він ще не готовий до використання в продакшені; API перебуває в бета-версії, і очікується, що він матиме зворотно несумісні зміни. Список підтримуваних, запланованих і тих, що перебувають у розробці, функцій у каталозі `/app` можна знайти в [бета-документації Next.js](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/uk/folder-structure-pages.mdx ================================================ --- title: Файлова структура (Pages) description: Файлова структура нового T3 додатка layout: ../../layouts/docs.astro lang: uk isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; Будь ласка, виберіть пакети, щоб побачити файлову структуру нового додатка з обраними пакетами. Нижче ви знайдете опис кожного елемента.
### `prisma` Папка `prisma` містить файл `schema.prisma`, який використовується для налаштування з'єднання з базою даних і схеми бази даних. Також це місце для зберігання файлів міграції та/або сценаріїв заповнення, якщо вони використовуються. Дивіться [Використання Prisma](/uk/usage/prisma) для отримання додаткової інформації.
### `public` Папка `public` містить статичні файли, які обслуговуються веб-сервером. Файл `favicon.ico` - це приклад статичного файлу.
### `src/env` Використовується для перевірки змінних оточення і визначення типів - дивіться [Змінні оточення](/uk/usage/env-variables).
### `src/pages` Папка `pages` містить усі сторінки додатка Next.js. Файл `index.tsx` у кореневій папці `/pages` є домашньою сторінкою додатка. Файл `_app.tsx` використовується для обертання додатка провайдерами. Дивіться [документацію Next.js](https://nextjs.org/docs/basic-features/pages) для отримання додаткової інформації.
#### `src/pages/api` Папка `api` містить усі маршрути API додатка Next.js. Файл `examples.ts` (з Prisma) містить приклад маршруту, який використовує функцію [Next.js API route](https://nextjs.org/docs/api-routes/introduction) разом із Prisma. Файл `restricted.ts` (з Next-Auth) містить приклад маршруту, який використовує функцію [Next.js API route](https://nextjs.org/docs/api-routes/introduction) і захищений за допомогою [NextAuth.js](https://next-auth.js.org/).
#### `src/pages/api/auth/[...nextauth].ts` Файл `[...nextauth].ts` - це slug-маршрут NextAuth.js для аутентифікації. Він використовується для обробки запитів аутентифікації. Дивіться [Використання NextAuth.js](/uk/usage/next-auth) для отримання додаткової інформації про NextAuth.js і [документацію Next.js про динамічні маршрути](https://nextjs.org/docs/routing/dynamic-routes) для отримання інформації про маршрути catch-all/slug.
#### `src/pages/api/trpc/[trpc].ts` Файл `[trpc].ts` - це точка входу tRPC API. Він використовується для обробки запитів tRPC. Дивіться [Використання tRPC](/uk/usage/trpc#-pagesapitrpctrpcts) для отримання додаткової інформації про цей файл і [документацію Next.js про динамічні маршрути](https://nextjs.org/docs/routing/dynamic-routes) для отримання інформації про маршрути catch-all/slug.
### `src/server` Папка `server` використовується для чіткого поділу серверного коду від клієнтського коду.
#### `src/server/auth.ts` Містить утиліти для автентифікації, такі як отримання сесії користувача на стороні сервера. Дивіться [Використання NextAuth.js](/uk/usage/next-auth#usage-with-trpc) для отримання додаткової інформації.
#### `src/server/db.ts` Файл `db.ts` використовується для створення клієнта Prisma на глобальному рівні. Дивіться [Використання Prisma](/uk/usage/prisma#prisma-client) і [кращі практики по використанню Prisma з Next.js](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) для отримання додаткової інформації.
### `src/server/api` Папка `api` містить серверний код tRPC.
#### `src/server/api/routers` Папка `routers` містить усі ваші під-маршрутизатори tRPC.
#### `src/server/api/routers/example.ts` Файл `example.ts` - це приклад маршрутизатора tRPC, який використовує допоміжну функцію `publicProcedure` для демонстрації того, як створити публічний маршрут tRPC. Базуючись на обраних вами пакетах, цей маршрутизатор містить більше або менше маршрутів для кращого демонстрування використання ваших потреб.
#### `src/server/api/trpc.ts` Файл `trpc.ts` - це основний файл конфігурації для вашого tRPC-бекенду. У ньому ми: 1. Визначаємо контекст, який використовується в запитах tRPC. Дивіться [Використання tRPC](/uk/usage/trpc#-serverapitrpcts) для отримання додаткової інформації. 2. Експортуємо допоміжні функції процедур. Дивіться [Використання tRPC](/uk/usage/trpc#-serverapitrpcts) для отримання додаткової інформації.
#### `src/server/api/root.ts` Файл `root.ts` використовується для мерджу маршрутизаторів tRPC і експорту їх як єдиного маршрутизатора, а також визначення типу маршрутизатора. Дивіться [Використання tRPC](/uk/usage/trpc#-serverapirootts) для отримання додаткової інформації.
### `src/styles` Папка `styles` містить глобальні стилі додатка.
### `src/types` Папка `types` використовується для зберігання повторно використовуваних типів або оголошень типів.
#### `src/types/next-auth.d.ts` Файл `next-auth.d.ts` використовується для розширення типу сесії за замовчуванням NextAuth на включення ідентифікатора користувача. Дивіться [Використання NextAuth.js](/uk/usage/next-auth#включення-userid-в-сеанс) для отримання додаткової інформації.
### `src/utils` Папка `utils` використовується для зберігання повторно використовуваних функцій утиліт.
#### `src/utils/api.ts` Файл `api.ts` є точкою входу для tRPC на стороні клієнта. Дивіться [Використання tRPC](/uk/usage/trpc#-utilsapits) для отримання додаткової інформації.
### `.env` Файл `.env` використовується для зберігання змінних середовища. Дивіться [Змінні середовища](/uk/usage/env-variables) для отримання додаткової інформації. Цей файл **не** повинен бути доданий в історію git.
### `.env.example` Файл `.env.example` показує приклад змінних середовища на основі обраних бібліотек. Цей файл має бути доданий в історію git.
### `.eslintrc.cjs` Файл `.eslintrc.cjs` використовується для налаштування ESLint. Дивіться [документацію ESLint](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) для отримання додаткової інформації.
### `next-env.d.ts` Файл `next-env.d.ts` гарантує, що типи Next.js будуть виявлені компілятором TypeScript. **Ви не повинні видаляти його або редагувати, оскільки він може змінитися в будь-який час.** Дивіться [документацію Next.js](https://nextjs.org/docs/basic-features/typescript#existing-projects) для отримання додаткової інформації.
### `next.config.mjs` Файл `next.config.mjs` використовується для налаштування Next.js. Дивіться [Документацію Next.js](https://nextjs.org/docs/api-reference/next.config.js/introduction) для отримання додаткової інформації. Примітка: Розширення .mjs використовується для дозволу імпорту ESM.
### `postcss.config.js` Файл `postcss.config.js` використовується для використання Tailwind PostCSS. Дивіться [документацію Taiwind PostCSS](https://tailwindcss.com/docs/installation/using-postcss) для отримання додаткової інформації.
### `prettier.config.mjs` Файл `prettier.config.mjs` використовується для налаштування Prettier для ввімкнення prettier-plugin-tailwindcss для форматування класів Tailwind CSS. Дивіться [пост блогу Tailwind CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) для отримання додаткової інформації.
### `tsconfig.json` Файл `tsconfig.json` використовується для налаштування TypeScript. Деякі значення за замовчуванням, такі як `strict mode`, були увімкнені для забезпечення кращого використання TypeScript для create-t3-app та його бібліотек. Дивіться [документацію TypeScript](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) або [Використання TypeScript](usage/typescript) для отримання додаткової інформації.
### `drizzle.config.ts` Файл `drizzle.config.ts` використовується для налаштування drizzle kit. Дивіться [документацію](https://orm.drizzle.team/kit-docs/config-reference) для отримання додаткової інформації.
================================================ FILE: www/src/pages/uk/installation.mdx ================================================ --- title: Встановлення description: Інструкції зі встановлення Create T3 App layout: ../../layouts/docs.astro lang: uk isMdx: true --- import Callout from "../../components/docs/callout.tsx"; Для створення додатка за допомогою `create-t3-app`, запустіть одну з наступних трьох команд і дайте відповідь на запитання командного рядка: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` Після того, як додаток буде створено, ознайомтеся з [першими кроками](/uk/usage/first-steps), щоб почати роботу над вашим новим додатком. ## Додаткові параметри | Опції/Флаги | Опис | | ----------------- | ------------------------------------------------------------------ | | `[dir]` | Додання аргументу з директорії з ім'ям проекту | | `--noGit` | Повідомлення CLI не ініціалізувати новий git репозиторій у проекті | | `-y`, `--default` | Створення нового t3-додатку з дефолтними параметрами | | `--noInstall` | Генерація проекту без встановлення залежностей | ## Експериментальне використання Для нашого CI ми маємо деякі експериментальні прапори, які дають змогу створювати будь-який додаток без будь-яких запитів. Якщо це стосується вашого випадку, ви можете використовувати ці прапори. Зверніть увагу, що ці прапори є експериментальними і можуть змінитися в майбутньому без зміни версії. | Опції/Флаги | Опис | | ------------ | ----------------------------------------------- | | `--CI` | Повідомлення CLI, що ви перебуваєте в режимі CI | | `--trpc` | Додавання tRPC у проєкт | | `--prisma` | Додавання Prisma у проєкт | | `--nextAuth` | Додавання NextAuth у проєкт | | `--tailwind` | Додавання Tailwind CSS у проєкт | Якщо ви не вказуєте прапор `CI`, то інші прапори не мають ефекту. Вам не потрібно явно відмовлятися від пакетів, які вам не потрібні. Однак, якщо ви віддаєте перевагу бути точним, ви можете передати `false`, наприклад `--nextAuth false`. ### Приклад Наступний приклад створить додаток T3 з tRPC і Tailwind CSS. ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/uk/introduction.md ================================================ --- title: Вступ description: Вступ в T3 стек layout: ../../layouts/docs.astro lang: uk ---
## T3 Stack _"T3 Stack"_ - це стек веб-розробки, створений [Theo](https://twitter.com/t3dotgg), з упором на простоту, модульність і full-stack типобезпека. Основні частини - [**Next.js**](https://nextjs.org/) і [**TypeScript**](https://typescriptlang.org/). [**Tailwind CSS**](https://tailwindcss.com/) майже завжди включені. Якщо ви робите щось, що нагадує бекенд, [**tRPC**](https://trpc.io/), [**Prisma**](https://prisma.io/), і [**NextAuth.js**](https://next-auth.js.org/) - чудові доповнення. Ви могли помітити, що тут... багато частин. Це зроблено наміренно. Змінюйте частини залежно від ваших потреб - цей стек є модульним за своєю суттю :) ## І... що таке create-t3-app? Шаблон? Щось типу? `create-t3-app` - це CLI, створений досвідченими розробниками T3 стека, щоб спростити налаштування модульного додатка T3 стека. Це означає, що кожна частина є необов'язковою, і "шаблон" генерується на основі ваших конкретних потреб. Після безлічі проєктів і багатьох років із цією технологією у нас є безліч думок та інсайтів. Ми постаралися зафіксувати їх у цьому CLI. Це **НЕ** всеосяжний шаблон. Ми **очікуємо**, що ви будете використовувати свої власні бібліотеки, які вирішують потреби **ВАШОГО** додатка. Хоча ми не хочемо призначати рішення для більш конкретних проблем, таких як управління станом і деплоймент, ми [маємо деякі рекомендації, перераховані тут](/uk/other-recs). ## Аксіоми T3 Будемо чесними - це _суб'єктивний проєкт_. Ми поділилися кількома основними переконаннями навколо створення і розглядаємо їх як основу для наших рішень. ### Вирішуйте проблеми Легко потрапити в пастку "додавання всього" - ми явно не хочемо цього робити. Усе, що додається в `create-t3-app`, повинно вирішувати конкретну проблему, яка існує в основних технологіях, включених до нього. Це означає, що ми не додамо бібліотеки менеджменту стану (`zustand`, `redux`), але ми додамо NextAuth.js та інтегруємо Prisma і tRPC для вас. ### Оновлюйтесь з відповідальністю Ми любимо технологічні новинки. Швидкість і, будемо чесними, приколів від нових фіговін, дійсно круті. Ми вважаємо важливим використовувати їх відповідально, використовуючи більш ризиковані технології в менш ризикованих частинах. Це означає, що ми не будемо ⛔️ робити ставку на ризиковану нову технологію бази даних (SQL - це здорово!). Але ми із задоволенням ✅ зробимо ставку на tRPC, оскільки це просто функції, які легко замінити. ### Типобезпека не є опціональною Зазначена мета Create T3 App - це надати найшвидший спосіб розпочати новий повнофункціональний, **типобезпечний** веб-додаток. Ми серйозно ставимося до типобезпеки в цих частинах, оскільки вона підвищує нашу продуктивність і допомагає нам створювати менше багів. Будь-яке рішення, яке знижує типобезпеку Create T3 App, це рішення, яке повинно бути прийнято в іншому проекті. ================================================ FILE: www/src/pages/uk/other-recs.md ================================================ --- title: Додаткові рекомендації description: Бібліотеки та сервіси, які ми рекомендуємо для багатьох проектів layout: ../../layouts/docs.astro lang: uk --- Ми розуміємо, що бібліотеки, включені до `create-t3-app`, не вирішують кожну проблему. Хоча ми рекомендуємо розпочати свій проект з того, що ми надаємо, настане час, коли вам потрібно буде встановити інші пакети. Тільки ви можете знати, чого потребує ваш проект, але ось деякі рішення, які ми часто рекомендуємо. Це рекомендації окремих учасників Create T3 App і не повинні розглядатись як "офіційні" рекомендації команди Create T3 App або T3-OSS. _**Будь ласка, проведіть власне дослідження, особливо перед тим, як прив'язуватись до платних сервісів**_. ## Управління станом _**Примітка редактора**_: Бібліотеки керування станом можуть бути відмінними, але часто не потрібні. tRPC's хуки React Query повинні впоратися із вашим серверним станом. Для клієнтського стану почніть з `useState` React, і зверніться до одного з цих варіантів, коли вам буде потрібно більше. ### Zustand **Для того, щоб більше ніколи не використовувати Redux** "Сучасний простий Redux", який ви не знали, що вам потрібен. [Poimandres](https://github.com/pmndrs) завжди можна довіряти. Ви можете створювати все, від відеодзвінків до ігор та серверів із цією маленькою бібліотекою. - [Головна Zustand](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **Для того, щоб більше ніколи не використовувати Context** Jotai важко перевершити в підході Atomic. Також від [Poimandres](https://github.com/pmndrs), Jotai дозволяє визначати сінгелтони, які здаються глобальними useState. Відмінний варіант для станів, які не вимагають стейт машини прямо зараз. - [Головна Jotai](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## Бібліотеки компонентів Більшість додатків потребують однакову низку компонентів: перемикачі, меню, модальні вікна і т.д. Ці бібліотеки надають відмінні, доступні компоненти, які ви можете використовувати та налаштовувати на свій розсуд. ### Бібліотеки компонентів без стилів Також відомі як headless бібліотеки, вони надають відмінні, нестилізовані та доступні компоненти, які ви можете налаштувати на свій розсуд. Ось кілька рекомендацій. - [Radix UI](https://www.radix-ui.com/) надає потужний набір зручних і доступних примітивів, які ви можете стилізувати за допомогою звичайного CSS або Tailwind CSS. - [Headless UI](https://headlessui.com/) від команди Tailwind CSS також надає нестилізовані, доступні компоненти, які бездоганно інтегруються з Tailwind CSS. - [React Aria](https://react-spectrum.adobe.com/react-aria/) надає доступні примітивні налаштування UI для вашої системи дизайну. Їхній компонент вибору дати - це найвищий рівень. ### Бібліотеки компонентів зі стилями **Коли вам просто потрібно, щоб ваш додаток виглядав непогано** Іноді ви створюєте проект, де вам просто потрібно, щоб інтерфейс користувача виглядав від початку. Для адміністративних панелей управління та інших подібних проектів будь-яка з цих бібліотек компонентів упорається з цим завданням. - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **Для створення бібліотек інтерфейсу користувача** Декларативно створюйте бібліотеку інтерфейсу користувача з різними кольоровими, розмірними і т.д. варіантами. Коли ваш проект досягає масштабу, де вам потрібен стандартизований набір компонентів інтерфейсу користувача з кількома варіантами з використанням Tailwind CSS, CVA - відмінний інструмент. - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## Анімації Наші рекомендації для створення анімацій у вашому додатку. ### AutoAnimate **Для анімацій з одним рядком коду** Більшість бібліотек анімації намагаються задовольнити кожен можливий випадок використання і стають громіздкими. AutoAnimate – це інструмент без конфігурації, який дасть вам значне покращення UX без додаткових зусиль розробника. - [Головна AutoAnimate](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [Компонент для AutoAnimate](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **Для складних анімацій із декларативним кодом** Framer Motion надає простий, декларативний синтаксис і дозволяє писати менше коду для створення всього: від складних анімацій і навіть до жестів. - [Головна Framer Motion](https://framer.com/motion) - [Документація Framer Motion](https://www.framer.com/docs/) ## Деплоймент, інфраструктура, бази даних і CI ### Vercel **Для хостинга вашого додатка** Vercel позбавив нас болю при деплойменті веб-додатків, зробивши його простою інтеграцією з GitHub, яку можна встановити і забути. Ми без проблем масштабувалися до сотень тисяч користувачів. Він працює на AWS, але має набагато кращий інтерфейс :) - [Головна Vercel](https://vercel.com/) - [Гайд із деплою T3 додатка на Vercel](/uk/deployment/vercel) ### PlanetScale **Для баз даних без занепокоєння** PlanetScale - найкраща serverless платформа для баз даних, яку ми використовували. Неймовірна масштабованість, чудовий DX і фантастична ціна. Якщо ви використовуєте SQL (і, сподіваюся, Prisma), це важко перевершити. - [Головна PlanetScale](https://planetscale.com/) ### Railway **Для розміщення вашої інфраструктури** "Сучасний Heroku". Найпростіший спосіб запустити реальний сервер. Якщо вам не вистачає функціоналу Vercel і PlanetScale, то Railway, ймовірно, вам підійде. Вкажіть його на репозиторій GitHub і готово. - [Головна Railway](https://railway.app/) ### Upstash **Для serverless Redis** Ми любимо Prisma і PlanetScale, але деякі проєкти вимагають більш продуктивного рішення. Upstash дає вам змогу отримати in-memory продуктивність Redis у вашому безсерверному проєкті, не переймаючись самостійним управлінням інфраструктурою та масштабуванням. - [Головна Upstash](https://upstash.com/) ### Pusher **Для безсерверних WebSocket-ів** Якщо WebSocket є основним фокусом вашого проекту, ви можете розглянути більш традиційний бекенд, такий як [Fastify](https://www.fastify.io/) (який [також працює з tRPC!](https://trpc.io/docs/v10/fastify)). Але для швидкого додавання WebSocket у T3 App, Pusher - чудовий вибір. - [Головна Pusher](https://pusher.com/) ### Soketi Soketi - це самодостатня, проста і швидка альтернатива Pusher. Він повністю сумісний з Pusher SDK, який ви можете використовувати для підключення до сервера. Soketi serverless також перебуває в бета-версії. - [Головна Soketi](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## Аналітика Користувацькі дані дуже цінні, коли ви створюєте додаток. Ось кілька рекомендованих провайдерів аналітики. ### Plausible Потрібна аналітика? Plausible - один із найшвидших способів її отримати. Супер мінімалістичний. У нього навіть є [простий плагін для Next.js](https://plausible.io/docs/proxy/guides/nextjs). - [Головна Plausible](https://plausible.io/) ### Umami Umami - це самодостатня, проста, швидка і конфіденційна альтернатива Google Analytics. Ви можете легко задеплоїти його на Vercel, Railway і т.д. з PlanetScale як базою даних. - [Головна Umami](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) - [Umami Cloud](https://cloud.umami.is/) ## Інше ### Next Bundle Analyzer Буває складно визначити, що буде включено до збірки вихідних даних для вашого додатка. Next Bundle Analyzer - це простий спосіб візуалізувати й аналізувати JavaScript-бандли, які генеруються. - [@next/bundle-analyzer на npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/uk/t3-collection.mdx ================================================ --- title: T3 колекція description: Круті open source проекти та компанії, що використовують T3 стек layout: ../../layouts/docs.astro lang: uk isMdx: true --- import Callout from "../../components/docs/callout.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; Зробили проект, що використовує T3 стек і хочете поділитися ним? Додайте його до списку! ## Open Source програми зроблені з використанням T3 стека ## Компанії використовують T3 стек Нам би дуже хотілося дізнатися про компанії, які використовують T3 стек для своїх додатків. Ваша компанія використовує стек T3 і ви хочете поділитися цим? Додайте її до списку! Є крутий проект, що використовує T3 стек? Зробіть [pull request](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) та додайте його до списку! ================================================ FILE: www/src/pages/uk/usage/drizzle.mdx ================================================ --- title: Drizzle description: Використання Drizzle layout: ../../../layouts/docs.astro lang: uk isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Опція `drizzle` є новим додатком и жодні документи ще не були написані. Ми будемо раді вашим внескам! ================================================ FILE: www/src/pages/uk/usage/env-variables.mdx ================================================ --- title: Змінні середовища description: Початок роботи з Create T3 App layout: ../../../layouts/docs.astro lang: uk isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Create T3 App використовує власний пакет [@t3-oss/env-nextjs](https://env.t3.gg) разом із [Zod](https://github.com/colinhacks/zod) для валідації змінних середовища під час виконання _та_ під час збирання, надаючи просту логіку у файлі `src/env.js`: ## env.js _TLDR; Якщо ви хочете додати нову змінну середовища, вам слід додати валідатор до `src/env.js`, а потім пару ключ-значення в `.env`._ ```ts:env.js import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, }, }); ``` T3 Env використовує `createEnv`, який відповідає за створення схеми і буде включати головну логіку валідації для клієнтських і серверних змінних середовища. Для отримання додаткової інформації про те, як `createEnv` працює зсередини, подивіться документацію [T3 Env](https://env.t3.gg/docs/introduction) (EN) ## Використання змінних середовища Коли ви хочете використовувати змінні середовища, ви можете імпортувати їх із `env.js` і використовувати їх так, як ви зазвичай використовували б. Якщо ви імпортуєте цей файл на стороні клієнта і спробуєте отримати серверну змінну, ви отримаєте помилку виконання: ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` is fully typesafe and provides autocompletion const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ This will throw a runtime error const dbUrl = env.DATABASE_URL; // ✅ This is fine const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example Через те, що файл `.env` за замовчуванням не додається до системи контролю версій, ми також додали файл `.env.example`, в якому ви можете за бажанням зберегти копію вашого файлу `.env` з видаленими secrets. Це необов'язково, але ми рекомендуємо тримати приклад в актуальному стані, щоб зробити процес налаштування середовища для нових учасників проекту якомога простішим. Деякі фреймворки та інструменти збірки, такі як Next.js, пропонують зберігати secrets у файлі `.env.local` і коммітити файли `.env` у ваш проект. Це не рекомендується, оскільки це може полегшити випадковий комміт secrets у ваш проект. Натомість ми рекомендуємо зберігати secrets в `.env`, тримати ваш файл `.env` в `.gitignore` і коммітити лише файли `.env.example` у ваш проект. ## Додавання змінних середовища Для того, щоб переконатися, що ваша збірка ніколи не завершиться без змінних середовища, які проєкт вимагає, вам потрібно додати нові змінні середовища в **двох** місцях: 📄 `.env`: Введіть змінну середовища, як зазвичай робите у файлі `.env`, тобто `KEY=VALUE` 📄 `env.js`: Додайте відповідну логіку валідації для змінних середовища, визначивши для кожної з них Zod схему всередині `createEnv`, наприклад `KEY: z.string()`. Крім цього, переконайтеся в тому, що ви деструктурували їх в опції `runtimeEnv`, наприклад `KEY: process.env.KEY`. Навіщо потрібно деструктурувати змінні середовища всередині `runtimeEnv`? Це пов'язано з тим, як Next.js збирає змінні середовища в деяких рантаймах. Деструктуруючи їх вручну, ми гарантуємо, що ці змінні не будуть прибрані з фінальної збірки. Опціонально, ви також можете оновлювати файл `.env.example`: 📄 `.env.example`: Введіть вашу змінну середовища, але переконайтеся, що не включаєте значення, якщо воно є секретним, тобто `KEY=VALUE` або `KEY=`. ### Приклад _Я хочу додати мій Twitter API токен як змінну середовища на стороні сервера_ 1. Додайте змінну середовища в `.env`: ``` TWITTER_API_TOKEN=1234567890 ``` 2. Додайте змінну середовища в `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { TWITTER_API_TOKEN: z.string(), }, // ... runtimeEnv: { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }, }); ``` 3. _Опціонально:_ Додайте змінну середовища в `.env.example`, але не включайте токен в `runtimeEnv`. ``` TWITTER_API_TOKEN= ``` ## Type Coercion Усі змінні які ви додаєте до `.env` будуть импортовані як рядки, навіть якщо їх значення має представляти інший тип. Якщо ви хочете використовувати ваші змінні середовища як інший тип під час рантайму, ви можете використовувати `coerce` з Zod для конвертації рядків в тип, який ви хочете. Це викине помилку, якщо конвертація не вдасться. Додайте змінну до вашого `.env`: ``` SOME_NUMBER=123 SOME_BOOLEAN=true ``` Потім, провалідуйте їх в `env.js`: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { SOME_NUMBER: z.coerce.number(), SOME_BOOLEAN: z.coerce.boolean(), }, // ... runtimeEnv: { SOME_NUMBER: process.env.SOME_NUMBER, SOME_BOOLEAN: process.env.SOME_BOOLEAN, }, }); ``` ================================================ FILE: www/src/pages/uk/usage/first-steps.md ================================================ --- title: Перші кроки description: Початок роботи з вашим новим T3 App layout: ../../../layouts/docs.astro lang: uk --- Ви щойно створили новий додаток T3 і готові до роботи. Ось мінімум для запуску вашої програми. ## База даних ### MySQL, PostgreSQL Якщо ви обираєте MySQL або PostgreSQL як свою базу даних, ваше T3 додаток буде починатись з `start-database.sh` bash-скриптом, який може створити контейнер Docker з базою даних для локальної розробки. Якщо у вас вже є база даних, ви можете видалити цей файл і вказати свої облікові дані для бази даних в `.env`. На macOS ви також можете використовувати [DBngin](https://dbngin.com/), якщо ви не хочете використовувати Docker. ### Prisma Якщо ваш додаток включає Prisma, переконайтеся, що ви запустили `npx prisma db push` з кореневої директорії вашої програми. Ця команда синхронізує схему Prisma з вашою базою даних і генерує типи TypeScript для Prisma Client на основі вашої схеми. Зверніть увагу, що вам потрібно [перезапустити сервер TypeScript](https://tinytip.co/tips/vscode-restart-ts/) після цього, щоб він міг виявити згенеровані типи. ### Drizzle Якщо ваш додаток включає в себе Drizzle, перевірте `.env` файл для інструкцій щодо побудови вашої змінної середовища `DATABASE_URL`. Як тільки ваш файл середовища готовий, запустіть `pnpm db:push` (або еквівалент для інших менеджерів пакетів), щоб відправити вашу схему. ## Аутентифікація Якщо ваш додаток включає NextAuth.js, ми починаємо з `DiscordProvider`. Це один з найпростіших провайдерів, пропонований NextAuth.js, однак він все ще вимагає певного початкового налаштування з вашого боку. Звичайно, якщо ви віддаєте перевагу використанню іншого провайдера автентифікації, ви також можете використати один із [багатьох провайдерів](https://next-auth.js.org/providers/), які пропонує NextAuth.js. 1. Вам потрібен обліковий запис Discord, тому зареєструйтеся, якщо ще не зареєструвалися. 2. Перейдіть на https://discord.com/developers/applications і натисніть "New Application" у правому верхньому куті. Дайте вашому додатку ім'я та погодьтеся з Умовами використання. 3. Коли ви створите додаток, перейдіть до "Settings → OAuth2 → General". 4. Скопіюйте "Client ID" і додайте його у ваш `.env` як `AUTH_DISCORD_ID`. 5. Натисніть "Reset Secret", скопіюйте новий secret і додайте його у ваш `.env` як `AUTH_DISCORD_SECRET`. 6. Натисніть "Add Redirect" і введіть `http://localhost:3000/api/auth/callback/discord`. - Для деплойменту в продакшені дотримуйтесь попередніх кроків для створення іншого додатка Discord, але цього разу замініть `http://localhost:3000` на URL, на який ви деплоїте. 7. Збережіть зміни. Тепер у вас має бути можливість увійти в систему. ## Сетап едітора Наступні розширення рекомендуються для оптимального досвіду розробки. Нижче наведені посилання на підтримку плагінів для редактора. - [Prisma Extension](https://www.prisma.io/docs/guides/development-environment/editor-setup) - [Tailwind CSS IntelliSense Extension](https://tailwindcss.com/docs/editor-setup) - [Prettier Extension](https://prettier.io/docs/en/editors.html) ## Наступні кроки - Якщо ваш додаток включає tRPC, ознайомтеся з `src/pages/index.tsx` і `src/server/trpc/router/post.ts`, щоб дізнатися, як працюють запити tRPC. - Подивіться на документацію `create-t3-app`, а також на документацію пакетів, які включає ваш додаток. - Приєднуйтесь до нашого [Discord](https://t3.gg/discord) і поставте зірку на [GitHub](https://github.com/t3-oss/create-t3-app)! :) ================================================ FILE: www/src/pages/uk/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Використання", layout: "docs", description: "Навчіться використовувати різні технології з T3 Stack.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/uk/usage/next-auth.mdx ================================================ --- title: NextAuth.js description: Використання NextAuth.js layout: ../../../layouts/docs.astro lang: uk isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Коли ви хочете мати систему автентифікації у вашому додатку Next.js, NextAuth.js - чудове рішення, щоб не морочитися з реалізацією складної безпеки самостійно. Він має великий список провайдерів для швидкого додавання аутентифікації OAuth і надає адаптери для багатьох баз даних і ORM. ## Провайдер контексту У точці входу вашого додатка ви побачите, що ваш додаток загорнутий у [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider): ```tsx:pages/_app.tsx > ``` Цей провайдер контексту дає змогу вашому застосунку отримати доступ до даних сесії з будь-якого місця вашого застосунку, не передаючи їх як пропси: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // Handle unauthenticated state, e.g. render a SignIn component return ; } return

Welcome {session.user.name}!

; }; ``` ## Отримання сесії на сервері Іноді вам може знадобитися запросити сесію на сервері. Щоб зробити це, попередньо отримайте сесію за допомогою функції-помічника `getServerAuthSession`, яку надає `create-t3-app`, і передайте її на клієнт за допомогою `getServerSideProps`: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // NOTE: `session` wont have a loading state since it's already prefetched on the server ... } ``` ## Включення `user.id` у сесію Create T3 App налаштований для використання [session callback](https://next-auth.js.org/configuration/callbacks#session-callback) у конфігурації NextAuth.js для включення ID користувача в об'єкт`session`. ```ts:pages/api/auth/[...nextauth].ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` Це пов'язано з файлом оголошення типів, щоб переконатися, що `user.id` типізовано під час доступу до об'єкта `session`. Детальніше про [`Module Augmentation`](https://next-auth.js.org/getting-started/typescript#module-augmentation) у документації NextAuth.js. ```ts:types/next-auth.d.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { інтерфейс Session { user? { id: string; } & DefaultSession["user"]; } } ``` Такий самий шаблон може бути використаний для додавання будь-яких інших даних в об'єкт `session`, наприклад, поля `role`, але **ним не слід зловживати для зберігання конфіденційних даних** на клієнті. ## Використання з tRPC При використанні NextAuth.js з tRPC ви можете повторно створити використовувані, захищені процедури за допомогою [middleware](https://trpc.io/docs/v10/middlewares). Це дозволяє вам створювати процедури, які можуть бути доступні тільки автентифікованим користувачам. `create-t3-app` налаштовує все це для вас, дозволяючи вам легко отримувати доступ до сесії об’єкта в аутентифікованих процедурах. Це робиться в два кроки: 1. Викликайте сесію із заголовків запиту за допомогою функції [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession). Перевага використання `getServerSession` замість звичайного `getSession` полягає в тому, що це серверна функція і вона не викликає непотрібних викликів fetch. `create-t3-app` створює допоміжну функцію, яка абстрагує це конкретне API щоб вам не треба було імпортувати обидва ваших NextAuth.js варіанти так як і `getServerSession` функцію кожного разу коли вам треба отримати доступ до сесії. ```ts:server/auth.ts export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return await getServerSession(ctx.req, ctx.res, authOptions); }; ``` Використовуючи цю допоміжну функцію, ми можемо отримати сесію та передати її в контексті tRPC: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. Створіть tRPC middleware, яке перевіряє, аутентифіковано чи користувач. Потім ми використовуємо middleware в `protectedProcedure`. Будь-який виклик процедур повинен бути автентифікований, інакше буде сгенерована помилка, яку можна правильно опрацювати на стороні клієнта. ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // infers the `session` as non-nullable session: { ...ctx.session, user: ctx.session.user }, }, }); }) ``` Об'єкт сесії - це легке, мінімальне представлення користувача і містить лише кілька полів. При використанні `protectedProcedures` у вас є доступ до ідентифікатора користувача, який можна використовувати для отримання більшої кількості даних з бази даних. ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## Використання з Prisma Щоб змусити NextAuth.js і Prisma працювати разом, необхідна велика кількість [початкових налаштувань](https://authjs.dev/reference/adapter/prisma/). `create-t3-app` виконує все це для вас, і якщо ви виберете одночасно і Prisma, і NextAuth.js, ви отримаєте повністю працюючу систему аутентифікації з усіма попередньо вбудованими, необхідними моделями. Ми надаємо вашому сгенерованому додатку попередньо вбудований провайдер Discord OAuth, який ми вибрали тому, що з ним легше всього почати – просто введіть свої токени в `.env` і ви готові до роботи. Однак ви можете легко додати більше провайдерів, слідуючи [докуменації NextAuth.js](https://next-auth.js.org/providers/). Зверніть увагу, що деякі провайдери вимагають додаткових полів для додавання в певні моделі. Ми рекомендуємо вам прочитати документацію для провайдера, який ви хочете використовувати, щоб переконатися, що у вас є всі необхідні поля. ### Додавання нових полів у ваші моделі Коли ви додаєте нові поля до будь-якої з моделей `User`, `Account`, `Session` або `VerificationToken` (у більшості випадків вам потрібно лише змінити модель `User`), вам потрібно мати на увазі, що [адаптер Prisma](https://next-auth.js.org/adapters/prisma) автоматично створює поля в цих моделях при реєстрації нових користувачів та вході до системи. Тому, додаючи нові поля до цих моделей, ви повинні надати значення за замовчуванням для них, оскільки адаптер не знає про них. Якщо, наприклад, ви хочете додати `role` у модель `User`, вам потрібно буде надати значення за замовчуванням для поля `role`. Це робиться шляхом додавання значення `@default` до поля `role` у моделі `User`: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## Використання з Next.js middleware Використання NextAuth.js з Next.js middleware [вимагає використання стратегії сеансу JWT](https://next-auth.js.org/configuration/nextjs#caveats) для автентифікації. Це пов'язано з тим, що middleware може отримати доступ до сесійного cookie тільки в тому випадку, якщо це JWT. За замовчуванням, create-t3-app налаштований на використання **default** стратегії бази даних, у поєднанні з Prisma як адаптера бази даних. Використання сесій баз даних - рекомендований підхід, й вам варто почитати про JWT перед тим як переходити до стратегії JWT сесії, щоб уникнути будь-яких проблем з безпекою. Після переходу до стратегії JWT сесії, переконайтеся, що ви оновили `session` колбек в `src/server/auth.ts`. Об'єкт `user` буде `undefined`. Замість цього, витягніть ID користувача з об'єкту `token`. Наприклад: ```diff:server/auth.ts export const authOptions: NextAuthOptions = { + session: { + strategy: "jwt", + }, callbacks: { - session: ({ session, user }) => ({ + session: ({ session, token }) => ({ ...session, user: { ...session.user, - id: user.id, + id: token.sub, }, }), }, } ``` ## Налаштовуємо DiscordProvider за замовчуванням 1. Перейдіть до розділу Applications у [Discord Developer Portal](https://discord.com/developers/applications) і натисніть "New Application" 2. У меню налаштувань перейдіть до "OAuth2 => General" - Скопіюйте Client ID і вставте його в `AUTH_DISCORD_ID` у `.env`. - Біля Client Secret натисніть "Reset Secret" і скопіюйте цей рядок у `AUTH_DISCORD_SECRET` у `.env`. Будьте обережними, оскільки ви більше не зможете побачити цей secret, і скидання його призведе до того, що існуючий протермінується. - Натисніть "Add Redirect" і вставте `/api/auth/callback/discord` (приклад для локальної розробки: http://localhost:3000/api/auth/callback/discord) - Збережіть зміни - Можливо, але не рекомендується, використовувати один і той же додаток Discord для розробки та продакшену. Ви також можете розглянути [Mocking the Provider](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts) під час розробки. ## Корисні ресурси | Ресурс | Посилання | | --------------------------------- | --------------------------------------- | | Документація NextAuth.js | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/uk/usage/next-js.md ================================================ --- title: Next.js description: Використання Next.js layout: ../../../layouts/docs.astro lang: uk --- Next.js це бекенд фреймворк для ваших React додатків.
Подивіться [виступ Theo на Next.js Conf](https://www.youtube.com/watch?v=W4UhNo3HAMw) щоб отримати більш детальне розуміння того, що таке Next.js і як він працює. ## Чому я повинен це використовувати? Ми любимо React. Він зробив розробку UI доступною у тому вигляді, в якому ми ніколи не могли собі уявити. Він також може вести розробників тернистими шляхами. Next.js пропонує злегка суб'єктивний, більш оптимізований підхід до створення додатків з використанням React. Від маршрутизації до визначення API до візуалізації зображень ми довіряємо Next.js, щоб вести розробників до правильних рішень. Поєднуючи Next.js з [Vercel](https://vercel.com/), ви можете розробляти та деплоїти веб-програми легше, ніж будь-коли. Їх надзвичайно щедрий безкоштовний тариф та супер інтуїтивний інтерфейс надають рішення в один клік для деплойменту вашого сайту (Ми ❤️ Vercel) ## Get Static/Server Props Ключові особливості Next.js – це можливості отримання даних. Ми наполегливо рекомендуємо прочитати [офіційну документацію](https://nextjs.org/docs/basic-features/data-fetching), щоб зрозуміти, як використовувати кожен метод і чим вони відрізняються. `getServerSideProps` зазвичай не рекомендується, якщо немає вагомої причини, тому що це блокуючий виклик і він уповільнить ваш сайт. [Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) - це відмінна альтернатива `getServerSideProps`, коли дані динамічні та можуть бути отримані поступово. Якщо ви все одно хочете використовувати цю функцію, перегляньте ці посилання: [Advanced tRPC - Callers, functions, and gSSP](https://www.youtube.com/watch?v=G2ZzmgShHgQ) та [SSG-Helpers](https://trpc.io/docs/v9/ssg-helpers) ## Корисні ресурси | Ресурс | Посилання | | ------------------------- | ---------------------------------- | | Next.js Документація | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js Блог | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js Twitter | https://twitter.com/nextjs | | Vercel/Next.js на YouTube | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/uk/usage/prisma.md ================================================ --- title: Prisma description: Використання Prisma layout: ../../../layouts/docs.astro lang: uk --- Prisma це ORM для TypeScript, який дозволяє визначати схему та моделі бази даних у файлі `schema.prisma`, а потім генерувати клієнт, який забезпечує типобезпеку та може використовуватись для взаємодії з базою даних з вашого бекенда. ## Prisma Client Розташований в `/server/db/client.ts`, Prisma Client ініціалізується як глобальна змінна (як рекомендовано [кращими практиками](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem) команди Prisma) та експортується для використання у ваших API маршрутах. Ми включаємо Prisma Client у [Context](/uk/usage/trpc#-serverapitrpcts) за замовчуванням і рекомендуємо використовувати його замість окремого імпорту у кожному файлі. ## Схема Ви знайдете файл схеми Prisma в `/prisma/schema.prisma`. Цей файл використовується для визначення схеми та моделей баз даних, а також для генерації Prisma Client. ### З NextAuth.js Коли ви вибираєте NextAuth.js у поєднанні з Prisma, файл схеми генерується і налаштовується для вас з рекомендованими значеннями для моделей `User`, `Session`, `Account` та `VerificationToken`, згідно з [документацією NextAuth.js](https://next-auth.js.org/adapters/prisma). ## База даних за замовчуванням База даних за замовчуванням - це база даних SQLite, яка відмінно підходить для розробки та швидкого створення proof-of-concept, але не рекомендується для використання у продакшені. Ви можете змінити базу даних, використовуючи 'provider' в блоці 'datasource' на 'postgresql' або 'mysql', а потім оновити рядок підключення до змінних середовища, щоб вказати на вашу базу даних. ## Заповнення (seeding) бази даних [Заповнення (seeding) вашої бази даних](https://www.prisma.io/docs/guides/database/seed-database) - це чудовий спосіб швидко заповнити вашу базу даних тестовими даними, щоб допомогти вам розпочати. Щоб налаштувати заповнення, вам потрібно створити файл `seed.ts` у каталозі `/prisma` і потім додати скрипт `seed` у файл `package.json`. Вам також знадобиться певний TypeScript-раннер, який може виконати скрипт заповнення. Ми рекомендуємо [tsx](https://github.com/esbuild-kit/tsx), який є дуже ефективним TypeScript-раннером, який використовує esbuild і не вимагає будь-якої конфігурації ESM, але `ts-node` або інші раннери також працюватимуть. ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` Потім, просто запустіть `pnpm db-seed` (або `npm`/`yarn`), щоб заповнити вашу базу даних. ## Корисні ресурси | Ресурс | Посилання | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Документація Prisma | https://www.prisma.io/ | | Prisma GitHub | https://github.com/prisma/ | | Prisma Migrate Playground | https://playground.prisma.io/guides | | NextAuth.JS Адаптер для Prisma | https://next-auth.js.org/adapters/ | | Гайд з підключення PlanetScale | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/uk/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Використання Tailwind CSS layout: ../../../layouts/docs.astro lang: uk --- ## Що таке Tailwind CSS? Tailwind CSS - це невеликий [utility first](https://tailwindcss.com/docs/utility-first) CSS фреймворк для створення власного дизайну, без перемикання контексту, який потрібний для звичайного CSS. Це чисто CSS фреймворк і він не надає ніяких попередньо зібраних компонентів або логіки, і надає [принципово інший набір переваг](https://www.youtube.com/watch?v=CQuTF-bkOgc) у порівнянні з бібліотекою компонентів, такою як наприклад Material UI. Він робить CSS неймовірно легким і швидким для написання, як показано в наступному прикладі: Старий CSS: 1. Напишіть CSS, часто в окремому файлі ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. Імпортуйте CSS у ваш компонент ```jsx import "./my-class.css"; ``` 3. Додайте клас у ваш HTML ```html
...
``` Еквівалент у Tailwind: 1. Просто напишіть класи у вашому HTML ```html
...
``` Використовуючи разом із React компонентами, він є надзвичайно потужним для швидкого створення UI. Tailwind CSS містить вбудовану красиву систему дизайну, яка поставляється від початку з ретельно вибраною палітрою кольорів, розмірами для стилів, таких як ширина/висота і відступи для єдиного дизайну, а також точками переривання для створення адаптивних макетів. Ця система дизайну може бути налаштована та розширена для створення точного набору стилів, які потрібні вашому проекту.
Tru Narla краще відома як [mewtru](https://twitter.com/trunarla) дала приголомшливу лекцію про [створення системи дизайну з використанням Tailwind CSS](https://www.youtube.com/watch?v=T-Zv73yZ_QI). ## Використання Переконайтеся, що у вас є плагіни редактора для Tailwind, щоб поліпшити ваш досвід написання Tailwind. ### Розширення та плагіни - [Розширення для VSCode](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### Форматування Класи Tailwind CSS можуть легко стати трохи безладними, тому форматтер для класів є необхідним. [Tailwind CSS Prettier Plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) сортує класи у [рекомендованому порядку](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted), щоб класи відповідали зібраному css-пакету. При виборі Tailwind у CLI ми встановимо та налаштуємо це для вас. ### Додавання класів в залежності від умов Додавання класів в залежності від умов з використанням тернарних операторів може стати дуже безладним і важким для читання. Ці пакети допомагають організувати ваші класи, використовуючи деяку логіку з умовами. - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## Корисні ресурси | Ресурс | Посилання | | ----------------------------- | -------------------------------------------------------- | | Документація Tailwind | https://tailwindcss.com/docs/editor-setup/ | | Шпаргалка по Tailwind | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind Community | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Сервер Tailwind в Discord | https://tailwindcss.com/discord/ | | Канал TailwindLabs на Youtube | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/uk/usage/trpc.md ================================================ --- title: tRPC description: Використання tRPC layout: ../../../layouts/docs.astro lang: uk --- tRPC дозволяє нам писати типобезпечні API від кінця до кінця (end-to-end) без будь-якої генерації коду чи подовження рантайму. Він використовує чудовий висновок TypeScript для виведення визначень типів вашого API-маршрутизатора і дозволяє викликати ваші API-процедури з інтерфейсу з повною безпекою типів і автоматичним завершенням. Використовуючи tRPC, ваш фронтенд і бекенд почуваються ближче один до одного, ніж будь-коли, забезпечуючи винятковий досвід розробника.

I built tRPC to allow people to move faster by removing the need of a traditional API-layer, while still having confidence that our apps won't break as we rapidly iterate.

Avatar of @alexdotjs
Alex - creator of tRPC @alexdotjs
## Як я можу використовувати tRPC?
tRPC контриб'ютор [trashh_dev](https://twitter.com/trashh_dev) [виступив на Next.js Conf](https://www.youtube.com/watch?v=2LYM8gf184U) з розповіддю про tRPC. Ми рекомендуємо вам подивитися, якщо ви ще не зробили це. З tRPC ви пишете функції TypeScript на бекенді, а потім викликаєте їх із фронтенду. Проста процедура tRPC може виглядати так: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` Це процедура tRPC (еквівалент route handler-а у традиційному бекенді), яка спочатку перевіряє вхідні дані за допомогою Zod (який є тією самою бібліотекою перевірки, яку ми використовуємо для [змінних середовища](./env-variables)) – у цьому випадку ми переконуємось , що вхідні дані є рядком. Якщо вхідні дані не є рядком, він надішле інформативну помилку. Після вхідних даних ми застосовуємо функцію, яка може бути або [запитом](https://trpc.io/docs/v10/react-queries), [мутацією](https://trpc.io/docs/v10/react-mutations) або [підпискою](https://trpc.io/docs/v10/subscriptions). У нашому прикладі resolver викликає нашу базу даних за допомогою клієнта [prisma](./prisma) і повертає користувача, у якого `id` збігається з тим, що ми передали. Ви визначаєте свої процедури в `routers` які є колекцією пов'язаних процедур із спільним ім'ям. У вас може бути один роутер для користувачів, один для постів і ще один для повідомлень. Ці роутери потім можна об'єднати в єдиний централізований `appRouter`: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` Зверніть увагу, що нам потрібно експортувати тільки типи наших роутерів, що означає, що ми ніколи не імпортуємо серверний код на клієнта. Тепер давайте викличемо процедуру на нашому фронтенді. tRPC надає обгортку для `@tanstack/react-query`, яка дозволяє використовувати всі можливості хуків (hooks), які він надає, але з додатковою перевагою того, що ваші виклики API типізовані та інферовані (inferred). Ми можемо викликати наші процедури з фронтенду так: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` Ви відразу помітите, наскільки хороші автодоповнення та типобезпека. Як тільки ви почнете писати `api.`, ваші роутери з'являться в автодоповненні, і коли ви оберете роутер, його процедури також з'являться. Ви також отримаєте помилку TypeScript, якщо ваше введення не відповідає валідатору, який ви визначили на бекенді. ## Обробка помилок По дефолту, `create-t3-app` створює [error formatter](https://trpc.io/docs/error-formatting) який дозволяє вам виводити ваші Zod помилки якщо ви отримуєте помилки валідації на бекенді. Приклад використання: ```tsx function MyComponent() { const { mutate, error } = api.post.create.useMutation(); return ( { e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ title: formData.get('title') }); }}> {error?.data?.zodError?.fieldErrors.title && ( {/** `mutate` returned with an error on the `title` */} {error.data.zodError.fieldErrors.title} )} ... ); } ``` ## Файли tRPC вимагає багато шаблонного коду, який `create-t3-app` налаштовує для вас. Давайте розглянемо файли, що генеруються: ### 📄 `pages/api/trpc/[trpc].ts` Цей файл є точкою входу для вашого API та експортує ваш tRPC роутер. Зазвичай, вам не доведеться чіпати цей файл, але якщо вам потрібно, наприклад, включити CORS middleware або щось подібне, корисно знати, що експортований `createNextApiHandler` - це [Next.js API handler](https://nextjs.org/docs/api-routes/introduction) який приймає [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) та [response](https://developer.mozilla.org/en-US/docs/Web/API/Response) об'єкт. Це означає, що ви можете обернути `createNextApiHandler` у будь-який middleware, який вам потрібен. Дивись нижче для [прикладу](#enabling-cors) додавання CORS. ### 📄 `server/api/trpc.ts` Цей файл поділено на дві частини: створення контексту та ініціалізація tRPC. 1. Ми визначаємо контекст, який передається вашим tRPC процедурам. Контекст - це дані, до яких у вас є доступ у всіх ваших процедурах tRPC, і це відмінне місце, щоб помістити речі, такі як з'єднання з базою даних, інформація про аутентифікацію і т.д. У create-t3-app ми використовуємо дві функції, щоб увімкнути використання підмножини контексту, коли ми не маємо доступу до об'єкта запиту. - `createInnerTRPCContext`: Це те місце, де ви визначаєте контекст, який не залежить від запиту, наприклад, ваше з'єднання з базою даних. Ви можете використовувати цю функцію для [інтеграційного тестування](#sample-integration-test) або [ssg-помічників](https://trpc.io/docs/v10/ssg-helpers), де у вас немає об'єкта запиту. - `createTRPCContext`: Тут ви визначаєте контекст, який залежить від запиту: наприклад, сесія користувача. Ви запитуєте сесію за допомогою об'єкта `opts.req`, а потім передаєте сесію у функцію `createInnerTRPCContext` для створення остаточного контексту. 2. Ми ініціалізуємо tRPC і визначаємо повторно використовані [процедури](https://trpc.io/docs/v10/procedures) та [посередники](https://trpc.io/docs/v10/middlewares). За угодою, ви не повинні експортувати весь об'єкт `t`, а натомість створювати повторно використовувані процедури та посередники та експортувати їх. Ви помітите, що ми використовуємо `superjson` як [перетворювач даних](https://trpc.io/docs/v10/data-transformers). Це дозволяє зберігати типи даних, коли вони досягають клієнта, тому якщо ви, наприклад, відправляєте об'єкт `Date`, клієнт поверне `Date`, а не рядок, що є нагодою для більшості API. ### 📄 `server/api/routers/*.ts` Це те місце, де ви визначаєте маршрути та процедури вашого API. Ви [створюєте окремі маршрутизатори](https://trpc.io/docs/v10/router) для пов'язаних процедур. ### 📄 `server/api/root.ts` Тут ми [об'єднуємо](https://trpc.io/docs/v10/merging-routers) всі під-маршрутизатори, визначені в `routers/**` в єдиний додатковий маршрутизатор. ### 📄 `utils/api.ts` Це точка входу для tRPC на фронтенді. Тут ви імпортуєте **визначення типів** маршрутизатора та створюєте клієнт tRPC разом із хуками react-query. Оскільки ми включили `superjson` як перетворювач даних на бекенді, нам також потрібно включити його на фронтенді. Це тому що серіалізовані дані з бэкенда десеріалізуються на фронтенді. Тут ви визначите свої tRPC [посилання](https://trpc.io/docs/v10/links), які визначають потік запитів від клієнта до сервера. Ми використовуємо "default" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink), який дозволяє [групувати запити](https://cloud.google.com/compute/docs/api/how-tos/batch), а також [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink), що виводить корисні журнали запитів під час розробки. Зрештою, ми експортуємо [допоміжний тип](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type), який ви можете використовувати для виведення типів на фронтенді.
Create T3 App контриб’ютор [Christopher Ehrlich](https://twitter.com/ccccjjjjeeee) зробив [відео про потоки даних у tRPC](https://www.youtube.com/watch?v=x4mu-jOiA0Q). Це відео рекомендовано якщо ви використовували tRPC, але все ще відчуваєте, що трохи не розумієте як воно працює. ## Як я можу викликати свій API ззовні? У звичайних API, ви можете викликати ваші кінцеві точки (endpoints) використовуючи будь-який HTTP клієнт, як `curl`, `Postman`, `fetch` або прямо з вашого браузера. З tRPC, це працює трохи інакше. Якщо ви хочете викликати ваші процедури без клієнта tRPC, є два рекомендовані способи зробити це: ### Розкрийте одну процедуру зовні Якщо ви бажаєте розкрити вашу процедуру зовні, вам варто шукати [server side calls](https://trpc.io/docs/v10/server-side-calls). Це дозволить вам створити звичайну кінцеву точку (endpoint) API Next.js, але перевикористовувати частину резолвера вашої процедури tRPC. ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### Розкрийте кожну процедуру зовнішньо Якщо ви хочете розкрити будь-яку процедуру ззовні, познайомтеся з плагіном спільноти [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master). Надаючи додаткові метаданні вашим процедурам, ви можете створити відповідний REST API з вашого маршрутизатора tRPC. ### Це всього лише HTTP-запити tRPC взаємодіє через HTTP, тому також можна викликати ваші процедури tRPC за допомогою "звичайних" HTTP-запитів. Однак синтаксис може бути неприйнятним із-за [протоколу RPC](https://trpc.io/docs/v10/rpc), який використовує tRPC. Якщо вам цікаво, ви можете перевірити, як виглядають запити та відповіді tRPC у вашій network вкладці веб-браузера, але ми рекомендуємо робити це лише в якості навчальної вправи і притримуватися одного з рішень, описаних вище. ## Порівняння з кінцевою точкою (enpoint) Next.js API Давайте порівняємо кінцеву точку Next.js API з процедурою tRPC. Припустимо, ми хочемо отримати об'єкт користувача з нашої бази даних і повернути його на фронтенд. Ми могли б написати кінцеву точку Next.js API таким чином: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` Порівняйте це з прикладом tRPC вище і ви побачите деякі переваги tRPC: - Замість того, щоб вказувати URL для кожного маршруту, який може стати незручним для дебагінгу, якщо ви перемістите щось, ваш весь маршрутизатор - це об'єкт з автозаповненням. - Вам не потрібно перевіряти, який метод HTTP був використаний. - Вам не потрібно перевіряти, що запит або тіло запиту містять правильні дані у процедурі, тому що Zod подбає про це. - Замість створення відповіді, ви можете викидати помилки та повертати значення або об'єкт, як у будь-якій іншій функції TypeScript. - Викликаючи процедуру на фронтенді, ви отримуєте автозаповнення та перевірку типів. ## Корисні сніпети Тут наведені деякі сніпети, які можуть стати в нагоді. ### Включення CORS Якщо вам потрібно використовувати ваш API з іншого домену, наприклад в монорепозиторії, який включає додаток React Native, вам може знадобитися включити CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // Enable cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### Оптимістичні оновлення Оптимістичне оновлення - це коли ми оновлюємо інтерфейс користувача до того, як API-запит завершиться. Це надає користувачеві кращий досвід, тому що він не повинен чекати завершення API-запиту, перш ніж інтерфейс користувача відобразить результат його дії. Однак програми, які цінують коректність даних, повинні уникати оптимістичних оновлень, оскільки вони не є «вірним» уявленням стану бекенда. Ви можете прочитати більше у [документації React Query](https://tanstack.com/query/v4/docs/guides/optimistic-updates). ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // Cancel outgoing fetches (so they don't overwrite our optimistic update) await utils.post.list.cancel(); // Get the data from the queryCache const prevData = utils.post.list.getData(); // Optimistically update the data with our new post utils.post.list.setData(undefined, (old) => [...old, newPost]); // Return the previous data so we can revert if something goes wrong return { prevData }; }, onError(err, newPost, ctx) { // If the mutation fails, use the context-value from onMutate utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // Sync with server once mutation has settled utils.post.list.invalidate(); }, }); }; ``` ### Простий Інтеграційний Тест Тут приведено простий інтеграційний тест, який використовує [Vitest](https://vitest.dev), щоб перевірити, що ваш маршрутизатор tRPC працює належним чином, парсер вхідних даних виводить правильний тип і дані, що повертаються, відповідають очікуваному результату. ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` Якшо ваша процедура захищена, ви можете передати замоканий об'єкт `session` при створенні контексту: ```ts test("protected example router", async () => { const ctx = await createInnerTRPCContext({ session: { user: { id: "123", name: "John Doe" }, expires: "1", }, }); const caller = appRouter.createCaller(ctx); // ... }); ``` ## Корисні ресурси | Ресурс | Посилання | | ------------------------------------ | ------------------------------------------------------- | | Документація tRPC | https://www.trpc.io | | Декілька прикладів використання tRPC | https://github.com/trpc/trpc/tree/next/examples | | Документація React Query | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/uk/usage/typescript.md ================================================ --- title: TypeScript description: Використання TypeScript layout: ../../../layouts/docs.astro lang: uk ---

Build safety nets, not guard rails

Avatar of @t3dotgg
Theo - creator of the T3 Stack @t3dotgg
Незважаючи на те новачок ви або досвідчений розробник, ми вважаємо, що TypeScript - це маст-хев. Спочатку він може виглядати гнітюче, але, як і багато інших інструментів, багато розробників не повертаються назад після того, як почали його використовувати. Він надає зворотний зв'язок в режимі реального часу при написанні коду, визначаючи очікувані типи даних, і надає корисні підказки в редакторі коду або кричить червоними хвилястими лініями, якщо ви намагаєтеся отримати доступ до властивості, якої не існує, або намагаєтеся передати значення неправильного типу, яке в іншому випадку довелося б налагоджувати далі по лінії. Це інструмент, який, мабуть, забезпечує найбільшу продуктивність розробникам; він надає документацію для коду, який ви пишете або використовуєте безпосередньо у вашому редакторі та має миттєвий зворотний зв'язок, коли ви неминуче робите помилки, що абсолютно безцінно. ## Виведення типів (Type Inference) Поки багато нових розробників на TypeScript стурбовані написанням TypeScript, багато з його переваг насправді не вимагають від вас зміни вашого коду взагалі, зокрема виведення типів. Висновок типів означає, що якщо щось типізовано, цей тип слідуватиме за ним протягом потоку програми без необхідності повторного оголошення в інших місцях. Це означає, що, наприклад, після того, як ви визначили типи аргументів, які приймає функція, решта функції зазвичай буде безпечною щодо типів без необхідності введення будь-якого додаткового коду, специфічного для TypeScript. Розробники бібліотек витрачають величезну кількість часу на підтримку типів для своїх бібліотек, що означає, що ми, як розробники додатків, можемо отримати вигоду від виведення типів та вбудованої документації у вашому редакторі коду, який ці типи надають.
Перегляньте відео Theo про те, що [ви, можливо, використовуєте TypeScript неправильно](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## Потужні застосування виведення типів ### Zod [Zod](https://github.com/colinhacks/zod) - це бібліотека перевірки схем, побудована поверх TypeScript. Напишіть схему, яка є єдиним джерелом істини для ваших даних, і Zod гарантує, що ваші дані будуть дійсними у всьому додатку, навіть поза межами мережі та зовнішніх API. ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) надає вам декларативні, завжди актуальні автоматично керовані запити та мутації, які безпосередньо покращують як Developer, так і User Experience. ## Корисні ресурси | Ресурс | Посилання | | ---------------------------------------------------------------- | ----------------------------------------------------------------- | | Посібник з TypeScript | https://www.typescriptlang.org/docs/handbook/ | | Гайд по TypeScript для новачків | https://github.com/total-typescript/beginners-typescript-tutorial | | Type Challenges | https://github.com/type-challenges/type-challenges | | Канал Родні Маллена зі світу TypeScript (Matt Pocock) на YouTube | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/uk/why.md ================================================ --- title: Чому CT3A? description: Чому вам слід вибрати Create T3 App для наступного проекту? layout: ../../layouts/docs.astro lang: uk --- Ми почали Create T3 App, тому що [Theo](https://twitter.com/t3dotgg) відмовився робити шаблон своїх улюблених технологій. Натхненний create-next-app, [Astro's CLI](https://astro.build) та загальною любов'ю до типобезпеки, команда Create T3 App працювала, щоб створити кращу можливу відправну точку для нових проектів T3 Stack. Якщо ви зацікавлені у використанні Next.js у типобезпечному варіанті, це те, з чого потрібно почати. Якщо ви цікавитеся будь-якими з конкретних технологічних рішень, які ми зробили, читайте далі :) ## Чому TypeScript? JavaScript складний. Навіщо додавати ще більше правил? Ми цілком впевнені, що досвід TypeScript дозволяє вам бути кращим розробником. Він надає зворотний зв'язок під час написання коду, визначаючи очікувані типи даних, і, або допомгає корисним автозаповненням у вашому редакторі, або кричить на вас червоними хвилястими лініями, якщо ви намагаєтеся отримати доступ до властивості, якої не існує, або намагаєтеся передати значення неправильного типу, що в іншому випадку довелося б налагоджувати в подальшому. Незалежно від того, новачок ви у веб-розробці або досвідчений професіонал, "суворість" TypeScript забезпечує менш дратівливий, більш послідовний досвід, ніж ванільний JS. Типобезпека робить вас швидше. Якщо ви ще не переконані, ви [можете використовувати TypeScript неправильно...](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## Чому Next.js? Ми любимо React. Він зробив розробку UI доступною у тому вигляді, в якому ми ніколи не могли собі уявити. Він також може вести розробників тернистими шляхами. Next.js пропонує злегка суб'єктивний, більш оптимізований підхід до створення додатків з використанням React. Від маршрутизації до визначення API до візуалізації зображень ми довіряємо Next.js, щоб вести розробників до правильних рішень. ## Чому tRPC/Prisma/Tailwind/и т.д.? Хоча ми віримо, що все має бути якнайлегше, ми знаходимо, що ці частини використовуються в кожному проекті, складнішому ніж "лендінг" (напр. проєкти зі складнішими технологіями і логікою). `create-t3-app` виконує чудову роботу, дозволяючи вам вибрати ті частини, які вам потрібні. ### tRPC tRPC надає всі переваги GraphQL, такі як гладка та безперервна розробка клієнта проти типобезпечного сервера без будь-якого бойлерплейту. Це розумний аб'юз TypeScript, яке забезпечує неймовірний досвід розробки. ### Prisma Prisma для SQL це те й що TypeScript для JS. Вона створила досвід розробки, якого раніше не було. Створюючи типи з схеми користувача, сумісної з [кількома базами даних](https://www.prisma.io/docs/concepts/database-connectors), Prisma гарантує безпеку типів від початку до кінця від вашої бази даних до вашої програми. Prisma надає цілий [набір інструментів](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows), що полегшують щоденну взаємодію з вашою базою даних. Зокрема, Prisma Client відповідає за запити та робить SQL настільки простим, що ви ледве помітите, що його використовуєте, а Prisma Studio – це зручний GUI для вашої бази даних, який дозволяє швидко читати та маніпулювати даними без необхідності писати код. ### Tailwind CSS Tailwind відчувається як "CSS у режимі дзен". Завдяки наданню будівельних блоків у вигляді гарних кольорів за замовчуванням, відступів та інших примітивних налаштувань Tailwind дозволяє легко створювати додаток із гарним зовнішнім виглядом. І на відміну від бібліотек компонентів, він не обмежує вас, коли ви хочете вивести свою програму на наступний рівень і створити щось красиве та унікальне. До того ж, завдяки інлайновому підходу, Tailwind спонукає вас стилізувати без занепокоєння про назву класів, організацію файлів або будь-яку іншу проблему, не пов'язану з вирішенням завдання. ### NextAuth.js Коли ви хочете додати систему аутентифікації у вашу програму NextJS, NextAuth.js - відмінне рішення, щоб не морочитися з реалізацією складної системи безпеки. Вона має великий список провайдерів для швидкого додавання аутентифікації через OAuth і надає адаптери для багатьох баз даних та ORM. ================================================ FILE: www/src/pages/zh-hans/deployment/docker.md ================================================ --- title: Docker description: 使用 Docker 部署 layout: ../../../layouts/docs.astro lang: zh-hans --- 你可以将这个 stack 容器化,将它作为一个单独容器使用 Docker 来部署,或者作为一系列容器的部分,使用 docker-compose 来部署。参看基于本文档的示例仓库 [`ajcwebdev/ct3a-docker`](https://github.com/ajcwebdev/ct3a-docker)。 ## Docker 项目配置 请注意 Next.js 会针对编译时客户端的环境变量(在客户端,名称前缀含有 `NEXT_PUBLIC`) 和运行时环境的服务器端变量有不同的处理过程。在本次演示中我们将使用两个环境变量,请注意它们在 `Dockerfile` 文件中的位置、命令行参数以及文件 `docker-compose.yml`: - `DATABASE_URL` (被用于服务器端) - `NEXT_PUBLIC_CLIENTVAR` (被用于客户端) ### 1. Next 的配置 在你的 [`next.config.js`](https://github.com/t3-oss/create-t3-app/blob/main/cli/template/base/next.config.js) 文件里,添加构建输出选项 `standalone`,以[自动借助追踪输出文件来降低镜像文件的大小](https://nextjs.org/docs/advanced-features/output-file-tracing): ```diff export default defineNextConfig({ reactStrictMode: true, swcMinify: true, + output: "standalone", }); ``` ### 2. 创建 dockerignore 文件
点击这里并将下方内容添加到 .dockerignore 里:
``` .env Dockerfile .dockerignore node_modules npm-debug.log README.md .next .git ```
### 3. 创建 Dockerfile > 因为我们并没有将服务端环境变量导入到我们的容器里,因此 [环境变量的 schema 验证](/zh-hans/usage/env-variables) 肯定不会通过。为了防止这种情况发生,我们必须在构建命令中添加 `SKIP_ENV_VALIDATION=1` 标识,以便 env-schema 不会在编译时被验证。
点击这里并将下方内容添加到 Dockerfile 里:
```docker ##### DEPENDENCIES FROM --platform=linux/amd64 node:16-apline3.17 AS deps RUN apk add --no-cache libc6-compat openssl WORKDIR /app # 安装 Prisma 客户端 - 如果不需要 Prisma,移除下一行 COPY prisma ./ # 使用 PNPM 包管理工具安装依赖包 COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \ else echo "Lockfile not found." && exit 1; \ fi ##### BUILDER FROM --platform=linux/amd64 node:16-apline3.17 AS builder ARG DATABASE_URL ARG NEXT_PUBLIC_CLIENTVAR WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \ elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \ elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi ##### RUNNER FROM --platform=linux/amd64 node:16-apline3.17 AS runner WORKDIR /app ENV NODE_ENV production # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/next.config.js ./ COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 CMD ["node", "server.js"] ``` > **_注意_** > > - _在迁移到 Node 18 后,可能就没有必要再使用 `--platform=linux/amd64` 了。_ > - _参看 [`node:alpine`](https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine) 来了解为什么使用 `libc6-compat`_ > - _Next.js 会收集 [关于使用情况的匿名观测数据](https://nextjs.org/telemetry)。取消注释第一个 `ENV NEXT_TELEMETRY_DISABLED 1`,以在构建过程中关闭该观测功能。取消注释第二个 `ENV NEXT_TELEMETRY_DISABLED 1` 以关闭在运行时的观测功能。_
## 在本地构建和运行镜像 通过下方命令在本地构建和运行该镜像: ```bash docker build -t ct3a-docker --build-arg NEXT_PUBLIC_CLIENTVAR=clientvar . docker run -p 3000:3000 -e DATABASE_URL="database_url_goes_here" ct3a-docker ``` 在浏览器中访问 [localhost:3000](http://localhost:3000/) 来查看运行的应用。 ## Docker Compose 你也可以使用 Docker Compose 来创建镜像以运行容器。
遵循以上 1 - 4 的步骤,点击这里,将下方内容添加到 docker-compose.yml 里:
```yaml version: "3.9" services: app: platform: "linux/amd64" build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_CLIENTVAR: "clientvar" working_dir: /app ports: - "3000:3000" image: t3-app environment: - DATABASE_URL=database_url_goes_here ``` 命令行运行 `docker compose up`: ```bash docker compose up ``` 在浏览器中访问 [localhost:3000](http://localhost:3000/) 来查看运行的应用。
## 部署到 Railway 你可以使用 PaaS(平台即服务)工具,例如 [Railway's](https://railway.app),自动完成 [Dockerfile 部署](https://docs.railway.app/deploy/dockerfiles) 来部署你的应用。如果你已经 [安装了 Railway CLI 命令行工具](https://docs.railway.app/develop/cli#install),你可以使用下方命令来部署你的应用: ```bash railway login railway init railway link railway up railway open ``` 前往 "Variables",填入你的 `DATABASE_URL`。然后前往 "Settings" 里,选择 "Generate Domain"。访问 [ct3a-docker.up.railway.app](https://ct3a-docker.up.railway.app/) 来查看托管在 Railway 上的项目。 ## 有用的资源 | 资源 | 链接 | | ------------------------------------- | -------------------------------------------------------------------- | | Dockerfile 参考手册 | https://docs.docker.com/engine/reference/builder/ | | Compose file 第三版参考手册 | https://docs.docker.com/compose/compose-file/compose-file-v3/ | | Docker CLI 命令行工具参考手册 | https://docs.docker.com/engine/reference/commandline/docker/ | | Docker Compose CLI 命令行工具参考手册 | https://docs.docker.com/compose/reference/ | | 使用 Docker 镜像 部署 Next.js | https://nextjs.org/docs/deployment#docker-image | | Docker 中的 Next.js | https://benmarte.com/blog/nextjs-in-docker/ | | Next.js 搭配 Docker 示例 | https://github.com/vercel/next.js/tree/canary/examples/with-docker | | 创建一个 Next.js 应用的 Docker 镜像 | https://blog.tericcabrel.com/create-docker-image-nextjs-application/ | ================================================ FILE: www/src/pages/zh-hans/deployment/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Deployment", layout: "docs", description: "Learn how to deploy your T3 app to production.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Deployment"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/zh-hans/deployment/netlify.mdx ================================================ --- title: Netlify description: 部署到 Netlify layout: ../../../layouts/docs.astro lang: zh-hans isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Netlify 是与 Vercel 类似的托管平台服务商。参看基于本文档的示例仓库 [`ajcwebdev/ct3a-netlify`](https://github.com/ajcwebdev/ct3a-netlify)。 ## 为什么托管到 Netlify 传统观点认为 Vercel 有着对 Next.js 绝佳的支持,因为他们开发了 Next.js。他们通过对平台的调校,确保搭配 Next.js 的最佳性能和开发体验,从而从中营利。对于大多数使用场景,使用 Vercel 部署 Next.js 无可厚非,偏离标准选择也没有多大意义。 然而也有一种普遍的看法是,很多 Next.js 的功能特性都仅限于 Vercel。虽然说默认情况下,Next.js 的新特性在发布时确实都只会在 Vercel 平台上被测试,然后被其所支持,但对于 [稳定的 Next.js 特性](https://docs.netlify.com/integrations/frameworks/next-js/overview/),像 Netlify 这样的其他部署平台也会 [迅速为之提供支持](https://www.netlify.com/blog/deploy-nextjs-13/)。 所有的部署平台都有相对的优缺点,因为没有一家供应商能对所有的使用场景都提供最好的支持。举例来说,Netlify 为他们的边缘函数(它们运行在 Deno 环境)自行创建了一个 [定制的 Next.js 运行时](https://github.com/netlify/next-runtime),[使用了独有的中间件来访问和修改 HTTP 响应](https://github.com/netlify/next-runtime#nextjs-middleware-on-netlify)。 要追踪 Next 13 非稳定特性的最新状态,参看 [在 Netlify 上使用 Next.js 13 的 `app` 目录](https://github.com/netlify/next-runtime/discussions/1724)。 ## 项目配置 有很多方法来配置你应用的打包部署流程,包括直接通过 Netlify CLI 命令行工具或 Netlify 管理面板。虽然说并不强求,但我们还是建议创建一个 [`netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/) 文件,并将它放在你的项目中。这么做能使被 fork 和 clone 的项目版本都可以更便利地被重复部署。 ```toml [build] command = "next build" publish = ".next" ``` ## 使用 Netlify 管理面板 1. 将你的代码 push 到 GitHub 仓库,然后注册 [Netlify](https://app.netlify.com/signup)。当你创建完账号后,点击 **Add new site** 然后再点击 **Import an existing project**。 ![New project on Netlify](/images/netlify-01-new-project.webp) 2. 连接 Git 服务商。 ![Import repository](/images/netlify-02-connect-to-git-provider.webp) 3. 选择你项目的仓库地址。 ![Select your project's repository](/images/netlify-03-pick-a-repository-from-github.webp) 4. Netlify 将会检测你的项目是否包含文件 `netlify.toml`,然后会自动为你配置好构建命令和打包目录。 ![Nextjs build settings](/images/netlify-04-configure-build-settings.webp) 5. 点击 **Show advanced** 然后点击 **New variable** 来添加你的环境变量。 ![Add environment variables](/images/netlify-05-env-vars.webp) 6. 点击 **Deploy site**,等待部署完成,然后就可以访问你的新网站了。 ## 使用 Netlify CLI 命令行工具 要使用命令行工具来部署到 Netlify,你必须首先将你的项目部署到 GitHub,并 [安装 Netlify CLI 命令行工具](https://docs.netlify.com/cli/get-started/)。你既可以将 `netlify-cli` 作为项目依赖来安装,也可以通过下方命令全局安装: ```bash npm i -g netlify-cli ``` 为了在本地测试你的项目,运行命令 [`ntl dev`](https://docs.netlify.com/cli/get-started/#run-a-local-development-environment) 然后访问 [`localhost:8888`](http://localhost:8888/) 来查看本地运行的 Netlify 应用: ```bash ntl dev ``` 运行命令 [`ntl init`](https://docs.netlify.com/cli/get-started/#continuous-deployment) 来配置你的项目: ```bash ntl init ``` 使用命令 [`ntl env:import`](https://cli.netlify.com/commands/env#envimport) 从 `.env` 文件中导入环境变量: ```bash ntl env:import .env ``` 通过命令 [`ntl deploy`](https://docs.netlify.com/cli/get-started/#manual-deploys) 来部署你的项目。在部署之前你需要先传入 `--build` 标识来将你的项目打包,然后传入 `--prod` 标识来部署到生产环境: ```bash ntl deploy --prod --build ``` 访问 [ct3a.netlify.app](https://ct3a.netlify.app/) 来查看运行在 Netlify 上的示例项目。 ================================================ FILE: www/src/pages/zh-hans/deployment/vercel.md ================================================ --- title: Vercel description: 部署到 Vercel layout: ../../../layouts/docs.astro lang: zh-hans --- 我们推荐将你的应用部署到 [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss)。它大大简化了 Next.js 应用的部署流程。 ## 项目配置 Vercel 很大可能会自动帮你配置好部署命令,以及构建导出目录。然而,你也可以通过创建一个 [`vercel.json`](https://vercel.com/docs/project-configuration) 文件,并加入下列命令。**这对大部分项目来说不是必需的。** ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "devCommand": "npm run dev", "installCommand": "npm install" } ``` ## 使用 Vercel 管理面板 1. 当你将代码成功 push 到 GitHub 仓库后,使用 GitHub 注册 [Vercel](https://vercel.com/?utm_source=t3-oss&utm_campaign=oss),然后点击**添加新项目**。 ![New project on Vercel](/images/vercel-new-project.webp) 2. 从 GitHub 仓库导入你的项目。 ![Import repository](/images/vercel-import-project.webp) 3. 添加环境变量。 ![Add environment variables](/images/vercel-env-vars.webp) 4. 点击**部署**。现在只要当你将修改 push 到仓库后,Vercel 会自动为你重新部署应用。 ## 使用 Vercel CLI 命令行工具 为了使用命令行工具来部署,你必须先 [全局安装 Vercel 的命令行工具](https://vercel.com/docs/cli#installing-vercel-cli)。 ```bash npm i -g vercel ``` 运行 [`vercel`](https://vercel.com/docs/cli/deploying-from-cli) 命令来部署你的项目。 ```bash vercel ``` 加入参数 `--env DATABASE_URL=YOUR_DATABASE_URL_HERE` 来指定环境变量的值,例如数据库连接的地址。如果你想要跳过问答,使用默认的部署配置,加入标识 `--yes`。 ```bash vercel --env DATABASE_URL=YOUR_DATABASE_URL_HERE --yes ``` 在第一次部署之后,这个命令会将项目部署到一个预览分支。你在之后的部署中需要加入 `--prod` 标识来直接将修改 push 到生产环境中。 ```bash vercel --prod ``` ================================================ FILE: www/src/pages/zh-hans/faq.mdx ================================================ --- title: 常见疑问 description: 关于 Create T3 App 的常见疑问 layout: ../../layouts/docs.astro lang: zh-hans isMdx: true --- import Callout from "../../components/docs/callout.tsx"; 这里罗列了一些关于 Create T3 App 的常见疑问。 ## 下一步呢?我怎么通过它开发一个应用? 我们努力保持这个项目尽可能的简单,这样你就可以通过我们设定的脚手架来开始制作应用,而且可以在之后有需要的时候添入额外的东西。 如果你对项目中的不同技术不熟悉,请参看它们对应的文档。倘若看过文档后你依然有不明白的地方,请加入到我们的 [Discord](https://t3.gg/discord) 来寻求帮助。 - [Next.js](https://nextjs.org/) - [NextAuth.js](https://next-auth.js.org) - [Prisma](https://prisma.io) - [Tailwind CSS](https://tailwindcss.com) - [tRPC](https://trpc.io) - [Drizzle](https://orm.drizzle.team/docs/overview) ## 如何使我的应用保持更新? Create T3 App 是一个脚手架工具,而不是一个框架。这意味着一旦你初始化了一个应用程序,它就是你的了。没有类似于 postinstall CLI 的工具来帮助你保持最新状态。如果你想跟踪我们对模板所做的任何改进,你可以在我们的存储库上[启用发布通知](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)功能。 话虽如此,但不一定需要将我们对模板所做的每一个更改都应用到你的应用程序中。 ## 现在有哪些关于 T3 Stack 的学习资源可供参考? 尽管以下列出的资源已经是现有关于 T3 Stack 的最好教程了,但社区(和 [Theo](https://youtu.be/rzwaaWH0ksk?t=1436))依然推荐你直接开始使用它,与此同时你可以在通过它构建应用的过程中来学习。 如果你考虑使用 Create T3 App,很大可能是因为你已经用到了其包含的某些库。所以何不先直接用起来,然后在构建应用的过程中去学习其他陌生的部分? 好吧,现在我们意识到这种方法不一定适用于每个人。所以,倘若你已经尝试了上述方法,却依然想要一些教学资源,或者你对自己不太自信,抑或觉得这个脚手架包含太多东西,一时难以消化,那你可以尝试参考下方一些关于 Create T3 App 的非常棒的教程: ### 文章 其中的一些也许已经过时。 - [初探 Create T3 App](https://dev.to/ajcwebdev/a-first-look-at-create-t3-app-1i8f) - [将你的 T3 App 迁移到 Turborepo](https://www.jumr.dev/blog/t3-turbo) - [将 Stripe 集成到你的 T3 App 中](https://blog.nickramkissoon.com/posts/integrate-stripe-t3) ### 视频 - [T3 Stack 教程 - 从 0 到上线,0 元成本 (Next.js, tRPC, TypeScript, Tailwind, Prisma 等)](https://www.youtube.com/watch?v=YkOSUVzOAA4) **(推荐)** - [Jack Herrington - 使用 T3 Stack 创建一个笔记应用](https://www.youtube.com/watch?v=J1gzN1SAhyM) - [使用 T3 Stack 创建推特克隆应用 - tRPC、Next.js、Prisma、Tailwind & Zod](https://www.youtube.com/watch?v=nzJsYJPCc80) - [使用 T3 Stack 创建博客应用 - tRPC、TypeScript、Next.js、Prisma & Zod](https://www.youtube.com/watch?v=syEWlxVFUrY) - [使用 T3 Stack 来创建实时聊天应用 - TypeScript、Tailwind、tRPC](https://www.youtube.com/watch?v=dXRRY37MPuk) - [ T3 Stack - 我们是如何创建它的](https://www.youtube.com/watch?v=H-FXwnEjSsI) - [Create T3 App 的概览(Next、Typescript、Tailwind、tRPC、Next-Auth)](https://www.youtube.com/watch?v=VJH8dsPtbeU) ## 项目中为什么会有 `.js` 格式的文件? 正如在 [T3 - 原则第三条](/zh-hans/introduction#类型安全不是可选的) 里所讲到的,我们将类型安全放在首位。不幸的是,并非所有的框架和插件都支持 TypeScript,这也就意味着有一些配置文件必须是 `.js` 格式。 我们想要强调的是,这些文件是 JavaScript 是有原因的,我们通过根据它们所使用的库的支持程度来显式声明每个文件的类型(`cjs` 或 `mjs`)。而且,项目中所有的 `js` 文件也会使用编译器(tsconfig)中的 checkJs 选项进行类型检查。 ## 我正努力为我的应用添加多语言功能。有我能够借鉴的吗? 我们已经决定 `create-t3-app` 默认不支持多语言功能,因为这是一个非常 opinionated 的话题,有很多不同的方法来实现它。 然而,如果你觉得实现它很困难,想参考一些项目的话,这里有一个 [参考仓库](https://github.com/juliusmarminge/t3-i18n),展示了如何可以通过 [next-i18next](https://github.com/i18next/next-i18next) 为 T3 App 提供多语言支持。 ## 为什么我们还用 `/pages` 而不是 Next.js 13 新推出的 `/app`? 正如在 [T3-原则第二条](/zh-hans/introduction#负责任地尝鲜) 里提到的,我们热爱新技术,但是也看重稳定性,项目的整个路由不是很容易迁移,而你却在此使用风险较高的新技术,[这不是一个明智的选择](https://youtu.be/mnwUbtieOuI?t=1662)。`/app` 目前只是 [对未来特性的一瞥而已](https://youtu.be/rnsC-12PVlM?t=818),它还未做好准备被用于生产环境;这项 API 还处于 beta 阶段,可以预见接下来还会有破坏性的改变。 对于 `/app` 目录中支持的、计划的和正在开发的功能列表,请访问 [beta Next.js docs](https://beta.nextjs.org/docs/app-directory-roadmap#supported-and-planned-features). ================================================ FILE: www/src/pages/zh-hans/folder-structure-pages.mdx ================================================ --- title: 文件夹结构 (Pages) description: 新创建的 T3 App 的文件夹结构 layout: ../../layouts/docs.astro lang: zh-hans isMdx: true --- import Diagram from "../../components/docs/folderStructureDiagramPages.astro"; import Form from "../../components/docs/folderStructureForm.astro"; 请选择你要安装的依赖包来查看一个对应新建的 T3 App 的文件夹结构。继续往下阅读,你可以从每个文件夹的描述了解它们对应的用途。
### `prisma` 文件夹 `prisma` 包含了 `schema.prisma` 文件,它被用于配置数据库连接和定义数据结构。数据库的迁移记录文件以及初始化种子脚本(如果你需要的话)也会被放在这个文件夹里。 参看 [Prisma 的用法](/zh-hans/usage/prisma) 来了解更多。
### `public` 文件夹 `public` 包含了一些将被托管在服务器上的静态资源。例如文件 `favicon.ico`。
### `src/env` 用于环境变量的验证和类型定义 - 参看 [环境变量](/zh-hans/usage/env-variables)。
### `src/pages` 文件夹 `pages` 包含了 Next.js 应用的所有页面。根目录下的文件 `index.tsx` 则是应用程序的首页。文件 `_app.tsx` 被用于使用 provider 来包裹整个应用程序。参看 [Next.js 文档](https://nextjs.org/docs/basic-features/pages) 来了解更多。
#### `src/pages/api` 文件夹 `api` 包含了 Next.js 应用的所有 API 路由。参看 [Next.js API 路由](https://nextjs.org/docs/api-routes/introduction) 来获取有关 API 路由的信息。
#### `src/pages/api/auth/[...nextauth].ts` 文件 `[...nextauth].ts` 包含了 NextAuth.js 认证的动态 slug 路由。它被用于处理认证请求。参看 [NextAuth.js 用法](/zh-hans/usage/next-auth) 来了解更多关于 NextAuth.js 的用法,以及 [Next.js 动态路由文档](https://nextjs.org/docs/routing/dynamic-routes) 来了解更多关于 catch-all 和 slug 路由的信息。
#### `src/pages/api/trpc/[trpc].ts` 文件 `[trpc].ts` 是 tRPC API 的入口。它被用于处理 tRPC 请求。参看 [tRPC 用法](/zh-hans/usage/trpc#-pagesapitrpctrpcts) 来了解更多关于这个文件的信息,以及 [Next.js 动态路由文档](https://nextjs.org/docs/routing/dynamic-routes) 来了解更多关于 catch-all 和 slug 路由的信息。
### `src/server` 文件夹 `server` 被用于清晰划分服务端和客户端代码。
#### `src/server/auth.ts` 服务器端认证逻辑的主入口。在这里,我们设置 NextAuth.js 的 [配置选项](/zh-hans/usage/next-auth),执行 [模块扩展](/zh-hans/usage/next-auth#inclusion-of-userid-on-the-session),并提供一些认证的 DX 工具,例如在服务器端检索用户的会话。参看 [NextAuth.js 用法](/zh-hans/usage/next-auth#搭配-trpc-的用法) 来了解更多。
#### `src/server/db.ts` 文件 `db.ts` 被用于初始化一个全局的 Prisma 客户端。 参看 [Prisma 用法](/zh-hans/usage/prisma#prisma-客户端) 和 [使用 Prisma 与 Next.js 的最佳实践](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices) 来了解更多。
### `src/server/api` 文件夹 `api` 包含了 tRPC 服务端的代码。
#### `src/server/api/routers` 文件夹 `routers` 包含了你的所有 tRPC 子路由。
#### `src/server/api/routers/example.ts` 文件 `example.ts` 包含了一段 tRPC 路由的示例代码,它利用 `publicProcedure` 来展示如何创建一个公开不受保护的 tRPC 路由。 根据你选择的包,这个路由包含更多或更少的路由,以最好地展示符合你需求的用法。
#### `src/server/api/trpc.ts` 文件 `trpc.ts` 是 tRPC 后端的主要配置文件。我们在这里进行以下操作: 1. 定义在 tRPC 请求中使用的上下文。 参看 [tRPC 用法](usage/trpc#-serverapitrpcts) 来了解更多。 2. 导出 procedure。 参看 [tRPC 用法](usage/trpc#-serverapitrpcts) 来了解更多。
#### `src/server/api/root.ts` root.ts 文件用于合并 tRPC 子路由并将它们导出为一个单一的路由,同时也导出了路由的类型定义。详见 tRPC 用法。 文件 `root.ts` 被用于合并 tRPC 子路由并将它们导出为一个单一的路由,同时也导出了路由的类型定义。参看 [tRPC 用法](usage/trpc#-serverapirootts) 来了解更多。
### `src/styles` 文件夹 `styles` 包含了应用的全局样式。
### `src/utils` 文件夹 `utils` 被用于存储经常复用的工具函数。
#### `src/utils/api.ts` 文件 `trpc.ts` 是 tRPC 的前端入口。参看 [tRPC 用法](/zh-hans/usage/trpc#-utilsapits) 来了解更多。
### `.env` 文件 `.env` 被用于保存环境变量。参看 [环境变量](/zh-hans/usage/env-variables) 来了解更多。该文件**不**应该被提交到 git 历史记录里。
### `.env.example` 文件 `.env.example` 根据所选库包展示了一个环境变量示例配置。这个文件需要被提交到 git 历史记录里。
### `.eslintrc.cjs` 文件 `.eslintrc.cjs` 被用于配置 ESLint。参看 [ESLint 文档](https://eslint.org/docs/latest/user-guide/configuring/configuration-files) 来了解更多。
### `next-env.d.ts` 文件 `next-env.d.ts` 确保 TypeScript 编译器能够采用 Next.js 的类型。**你不应该移除或修改该文件,因为它可以随时发生变化。** 参看 [Next.js 文档](https://nextjs.org/docs/basic-features/typescript#existing-projects) 来了解更多。
### `next.config.mjs` 文件 `next.config.mjs` 被用于配置 Next.js。参看 [Next.js 文档](https://nextjs.org/docs/api-reference/next.config.js/introduction) 来了解更多。注意:.mjs 格式的文件被用于允许以 ESM 方式导入。
### `postcss.config.js` 文件 `postcss.config.js` 被用于配置 Tailwind PostCSS 的用法。参看 [Tailwind PostCSS 文档](https://tailwindcss.com/docs/installation/using-postcss) 来了解更多。
### `prettier.config.mjs` 文件 `prettier.config.mjs` 被用于配置 Prettier,以使用插件 prettier-plugin-tailwindcss 来格式化 Tailwind CSS 的类名。参看 [Tailwind CSS 博客](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier) 来了解更多。
### `tsconfig.json` 文件 `tsconfig.json` 被用于配置 TypeScript。一些非默认项,例如 `strict mode` 已经被启用来保证使用 Create T3 App 和它所含库时能获得最佳的 TypeScript 开发体验。参看 [TypeScript 文档](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) 或 [TypeScript 用法](/zh-hans/usage/typescript) 来了解更多。
### `drizzle.config.ts` 文件 `drizzle.config.ts` 被文件用于配置 drizzle kit。更多信息请参看 [文档](https://orm.drizzle.team/kit-docs/config-reference)。
================================================ FILE: www/src/pages/zh-hans/installation.mdx ================================================ --- title: 安装 description: 创建 T3 应用程序的安装说明 layout: ../../layouts/docs.astro lang: zh-hans isMdx: true --- import Callout from "../../components/docs/callout.tsx"; 要使用 `create-t3-app` 创建一个应用程序,请运行以下任意一个命令,并回答命令提示符的问题: ### npm ```bash npm create t3-app@latest ``` ### yarn ```bash yarn create t3-app ``` ### pnpm ```bash pnpm create t3-app@latest ``` ### bun ```bash bun create t3-app@latest ``` 在你的应用程序被创建后,请查看 [第一步](/zh-hans/usage/first-steps) 以开始你的新应用。 ## 高级用法 | 选项 / 标识 | 说明 | | ----------------- | ------------------------------------------ | | `[dir]` | 添加一个带有项目名称的目录参数 | | `--noGit` | 显式告诉 CLI 不在项目中初始化新的 git 仓库 | | `-y`, `--default` | 跳过 CLI 并选中所有选项来初始化新的 t3-app | | `--noInstall` | 创建项目,但不自动安装依赖包 | ## 实验性用法 我们的 CI 有一些实验性的标识,允许你可以无需问答式地直接创建应用程序。如果这种用例适用于你,你可以使用这些标识。请注意,这些标识是实验性的,将来可能会在不遵循 semver 版本的情况下改变。 | 标识 | 描述 | | ------------ | ------------------------- | | `--CI` | 让 CLI 知道你在 CI 模式中 | | `--trpc` | 添加 tRPC 到项目 | | `--prisma` | 添加 Prisma 到项目 | | `--nextAuth` | 添加 NextAuth.js 到项目 | | `--tailwind` | 添加 Tailwind CSS 到项目 | 如果你没有提供 `CI` 标识,那么其余的这些标识将无法生效。 你不需要明确地选择不要的依赖包。然而如果你想这样做,你可以在包名后加个 `false`,例如 `--nextAuth false`。 ### 示例 下面的命令将使用 tRPC 和 Tailwind CSS 创建一个 T3 应用程序。 ```bash pnpm dlx create-t3-app@latest --CI --trpc --tailwind ``` ================================================ FILE: www/src/pages/zh-hans/introduction.md ================================================ --- title: 简介 description: T3 Stack 简介 layout: ../../layouts/docs.astro lang: zh-hans ---
## T3 Stack _"T3 Stack"_ 是由 [Theo](https://twitter.com/t3dotgg) 创建的一个 web 开发技术栈,专注于简单性、模块化和全栈类型安全。 核心部分是 [**Next.js**](https://nextjs.org/) 和 [**TypeScript**](https://typescriptlang.org/)。[**Tailwind CSS**](https://tailwindcss.com/) 几乎总是被包含在内。如果你正在做任何类似后端的事情,[**tRPC**](https://trpc.io/)、[**Prisma**](https://prisma.io/) 和 [**NextAuth.js**](https://next-auth.js.org/) 都是很好的补充。 你可能已经注意到它有很多组成部分,这是设计如此。按需调换——本技术栈的核心是模块化的 :) ## 所以 ... create-t3-app 是什么?一个模板吗? 算是吧?`create-t3-app` 是由经验丰富的 T3 Stack 开发者构建的 CLI 命令行工具,用于简化一个模块化的 T3 Stack 应用程序的配置过程。这意味着每个部分都是可选的,而“模板”则是根据你的具体需求生成的。 经过无数项目和多年的技术积累,我们沉淀了很多的观点和见解。我们尽力将它们融入到了这个 CLI 中。 这**不是**一个包罗万象的模板。我们**期望**你能带来自己的库,以解决**你自己的**应用程序需求。虽然我们不想为更具体的问题(如状态管理和项目部署)指定解决方案,但我们 [在这里列出了一些推荐选项](/zh-hans/other-recs)。 ## T3 原则 坦白地说——这是一个非常 _opinionated 的项目_。我们在构建方面有不少共同的核心信念,并把它们作为我们做出决策的基础。 ### 解决问题 很容易陷入“添加一切”的陷阱 —— 我们明确不想这样做。被添入到 `create-t3-app` 中的每个部分都应该解决存在于核心技术中的特定问题。这意味着我们不会添加诸如状态库(`zustand`、`redux`)之类的东西,但我们会添加像 NextAuth.js 之类的库,并为你集成 Prisma 和 tRPC。 ### 负责任地“尝鲜” 我们热爱前沿技术。诚然,新技术带来的速度和乐趣真的很酷。我们认为,负责任地尝试新技术很重要,可以在风险较小的部分使用风险较大的技术。这意味着我们不会 ⛔️ 把赌注押在有风险的新数据库技术上(SQL 很棒!)。但我们很乐意 ✅ 押注于 tRPC,因为它只是一些很容易被移除的函数而已。 ### 类型安全不是可选的 Create T3 App 宣称的目标是提供最快捷的方式来创建一个新的全栈**类型安全**的 web 应用。我们在这方面十分认真对待类型安全,因为它确实提高了我们的生产力,并帮助我们减少了错误的产生。倘若你想在类型安全上做出妥协,你应该另外创建一个项目,而不是使用 Create T3 App。 ================================================ FILE: www/src/pages/zh-hans/other-recs.md ================================================ --- title: 其他推荐 description: 我们推荐的一些库和服务 layout: ../../layouts/docs.astro lang: zh-hans --- 我们意识到 `create-t3-app` 所包含的库或工具并不能解决每一个问题。虽然我们鼓励你用我们提供的这些工具来创建项目,但有的时候你还是会需要用到其他库。只有你自己才能了解你的项目真正需要什么,不过这里有一份我们自己经常推荐的库或工具的清单,你大可参考一下。 这些推荐来自于 Create T3 App 的个人贡献者们,不应该被视为 Create T3 App 团队或这个开源软件的“官方”认可。_**请自行搜索,尤其是在使用付费服务之前**_。 ## 状态管理 _**编者注词**_: 状态管理库本身很棒,但经常没有必要使用。tRPC 集成的 React Query hooks 应该能够胜任处理服务端的状态。对于客户端的状态,先用 React 自带的 `useState`,倘若还有需求再从这些库里挑一个使用。 ### Zustand **没有必要再使用 Redux 了** 你不知道你是否需要这个现代版的精简 Redux。但 [Poimandres](https://github.com/pmndrs) 这个团队永远值得你的信任。无论你想构建什么,从视频通话应用到游戏,乃至服务器端,你都可以通过这个轻量库来处理状态。 - [Zustand 官网](https://zustand-demo.pmnd.rs/) - [Zustand GitHub](https://github.com/pmndrs/zustand) ### Jotai **没有必要再使用 Context 了** 没有比 Jotai 更能以原子化的方式处理状态的了。同样由 [Poimandres](https://github.com/pmndrs) 出品,Jotai 可以让你定义一些状态原子,它们像是全局的 useState。对于一些状态,用状态机来解决似乎“大材小用”,这时候用 Jotai 来处理是非常不错的选择。 - [Jotai 官网](https://jotai.org/) - [Jotai GitHub](https://github.com/pmndrs/jotai) ## 组件库 大部分应用都需要不少组件 —— 开关按钮、下拉菜单、对话框等等。以下这些库都提供了非常不错的支持无障碍访问的组件,并且能够让你根据喜好自定义修改。 ### 无样式的组件库 也常以无头组件库为人所知,它们提供了无样式、支持无障碍访问的组件,支持根据个人喜好自定义样式。这里罗列了一些推荐。 - [Radix UI](https://www.radix-ui.com/) 给你提供了一系列方便的无障碍底层组件,能够让你搭配原生 CSS 或 Tailwind CSS 使用。 - [Headless UI](https://headlessui.com/) 由 Tailwind CSS 官方团队出品,同样也提供了无样式、支持无障碍访问的组件,你可以无缝将它们与 Tailwind CSS 搭配使用。 - [React Aria](https://react-spectrum.adobe.com/react-aria/) 来自于 Adobe,同样也提供了支持无障碍访问的底层组件,你可以拿来搭建设计系统。他们的日期选择组件绝对是数一数二的。 ### 包含样式的组件库 **适用于当你想要你的应用看起来还不错时** 有时候在创建应用时,你只想要你的应用 UI 一开始就看起来还不错,不想自行逐一设计每个组件。那对于管理面板或其他类似的项目,以下任何一个组件库都能帮你达到目标。 - [Chakra UI](https://chakra-ui.com) - [Mantine](https://mantine.dev) - [@shadcn/ui](https://ui.shadcn.com/) ### Class Variance Authority **适用于拿来构建 UI 库** CVA 能让你声明式地通过不同的颜色、尺寸等组成不同变体,从而构建一个 UI 库。当你的项目达到一定规模后,你可能想要通过使用 Tailwind CSS 来实现多个组件变体,进而来实现 UI 组件标准化,这时候就是 CVA 派上用场的时候了。 - [Class Variance Authority GitHub](https://github.com/joe-bell/cva) ## 动画 当你需要在应用中使用动画,这里是我们的推荐。 ### AutoAnimate **适用于用一行代码完成动画** 大部分动画库都试着满足每种使用场景,这会导致它们变得很笨重。AutoAnimate 是一款零配置的工具,它能够显著地为你带来 UX 的提升,而不需要额外的代码。 - [AutoAnimate 官网](https://auto-animate.formkit.com/) - [AutoAnimate GitHub](https://github.com/formkit/auto-animate) - [AutoAnimate 组件代码片段](https://gist.github.com/hwkr/3fdea5d7f609b98c162e5325637cf3cb) ### Framer Motion **适用于用声明式代码来完成复杂动画** Framer Motion 提供了一种简单、声明式的语法,从复杂的动画,甚至到手势操作,它都能让你可以通过少量代码来实现。 - [Framer Motion 官网](https://framer.com/motion) - [Framer Motion 文档](https://www.framer.com/docs/) ## 部署、基础设施、数据库和持续集成 ### Vercel **适用于部署你的 App** Vercel 帮你解决了 web 应用部署的痛点,可以让你轻松通过 GitHub 仓库部署你的应用。我们部署在 Vercel 的服务已经扩展达到数十万用户,目前还没有任何问题。Vercel 基于 AWS,不过有更好的交互界面 :) - [Vercel 官网](https://vercel.com/) - [Create T3 App Vercel 部署指引](/zh-hans/deployment/vercel) ### PlanetScale **适用于免操心地使用数据库** PlanetScale 是我们目前用过的最好的“无服务数据库平台”。它有着令人惊叹的规模、出色的开发者体验,以及超值的价格。如果你正在使用 SQL(希望是 Prisma),PlanetScale 是你的不二之选。 - [PlanetScale 官网](https://planetscale.com/) ### Railway **适用于托管你的基建** “现代版的 Heroku”。最快速简单的方式来启用一个服务器。如果 Vercel 和 PlanetScale 都无法满足你的需求,Railway 很可能是唯一的选择了。将它对接到 GitHub 仓库就行了。 - [Railway 官网](https://railway.app/) ### Upstash **适用于需要无服务的 Redis** 我们很喜欢 Prisma 和 PlanetScale,但是有一些项目要求更高的性能。Upstash 允许你的应用能获得 Redis 提供的内存高性能,而无需自己去管理和扩容服务器。 - [Upstash 官网](https://upstash.com/) ### Pusher **适用于需要无服务的 WebSockets** 如果 WebSockets 是你项目的主要聚焦点,你可能需要考虑使用一个更传统的后端服务,例如 [Fastify](https://www.fastify.io/) (它同样也可以 [搭配 tRPC 使用!](https://trpc.io/docs/v10/fastify))。但是要快速将 Webstockets 添加到 T3 项目中, Pusher 是一个很好的选择。 - [Pusher 官网](https://pusher.com/) ### Soketi Soketi 是一个支持自行部署的、简单快速的 Pusher 替代方案。它完全与 Pusher SDK 兼容。无服务的 Soketi 也正处于 beta 阶段。 - [Soketi 官网](https://soketi.app) - [Soketi GitHub](https://github.com/soketi/soketi) ## 数据统计分析 当你创建一个应用的时候,用户统计数据是十分宝贵的。这里罗列了一些我们推荐的数据统计分析工具。 ### Plausible 需要数据统计和分析?使用 Plausible 是最快的方法之一。它超级轻量,甚至还有一个针对 [Next.js 的简单插件](https://plausible.io/docs/proxy/guides/nextjs)。 - [Plausible 官网](https://plausible.io/) ### Umami Umami 是一个开源的、支持自行部署、简单快速、注重隐私的 Google Analytics 备选方案。你可以使用 PlanetScale 作为数据库,轻松地将其部署到 Vercel、Railway 等平台上,也可以使用其云版本。 - [Umami 官网](https://umami.is/) - [Umami GitHub](https://github.com/umami-software/umami) - [Umami Cloud](https://cloud.umami.is/) ## 其他 ### Next Bundle Analyzer 有时候想要确定 App 中哪部分被纳入打包文件中是很困难的。Next Bundle Analyzer 这个工具可以为你可视化并分析自动生成的 JavaScript 打包文件。 - [@next/bundle-analyzer on npm](https://www.npmjs.com/package/@next/bundle-analyzer) ================================================ FILE: www/src/pages/zh-hans/t3-collection.mdx ================================================ --- title: T3 合集 description: 那些使用 T3 stack 的有趣开源项目和公司 layout: ../../layouts/docs.astro lang: zh-hans isMdx: true --- import OpenSourceAppList from "../../components/docs/openSourceAppList.tsx"; import CompanyList from "../../components/docs/companyList.tsx"; import Callout from "../../components/docs/callout.tsx"; 使用 T3 stack 创建了一个项目并想要分享?将它添加到这个列表里吧。 ## 使用 T3 Stack 的开源项目 ## 使用 T3 Stack 的公司 我们很乐意知道哪些公司使用了 T3 stack 来构建他们的应用。你所在的公司正在使用 T3 stack 并且想要分享它?快加入到下方的列表中吧! 用 T3 stack 构建了很酷的项目?开一个 [PR](https://github.com/t3-oss/create-t3-app/tree/next/www/src/components/docs/openSourceAppList.tsx) 将它加进来吧! ================================================ FILE: www/src/pages/zh-hans/usage/drizzle.mdx ================================================ --- title: Drizzle description: Drizzle 的用法 layout: ../../../layouts/docs.astro lang: zh-hans isMdx: true --- Drizzle 是一个无头的 TypeScript ORM,提供 [关系型](https://orm.drizzle.team/docs/rqb) 和 [类 SQL](https://orm.drizzle.team/docs/select) 的查询 API。它可以处理数据库迁移和模式,并提供类型安全的数据库客户端。同时,它还附带 [Drizzle-Kit](https://orm.drizzle.team/drizzle-studio/overview),一组用于帮助查询数据库的工具。 ## Drizzle 客户端 Drizzle 客户端位于 `src/server/db/index.ts` 文件中。在此文件中,您可以定义数据库连接 URL,并将数据库模式连接到数据库对象。 ```ts:src/server/db/index.ts import { env } from "~/env"; import * as schema from "./schema"; import postgres from "postgres"; const conn = postgres(env.DATABASE_URL); export const db = drizzle(conn, { schema }); ``` 我们建议将数据库客户端包含在 tRPC 的 Context 中: ```ts:src/server/api/trpc.ts import { db } from "~/server/db"; export const createTRPCContext = async (opts: { headers: Headers }) => { const session = await auth(); return { db, session, ...opts, }; }; ``` ## 模式(Schema) Drizzle 的模式文件位于 `src/server/db/schema.ts`。此文件是您定义数据库模式和模型的地方,并与 Drizzle 客户端连接。 当您选择使用 NextAuth.js 配合 Prisma 时,模式文件会按照 [Auth.js 文档](https://authjs.dev/getting-started/adapters/drizzle) 中的推荐值,为 `User`、`Session`、`Account` 和 `VerificationToken` 模型自动生成并设置。 ## Drizzle Kit Drizzle Kit 是一组命令行工具,旨在帮助您管理数据库。当您选择 Drizzle 作为 ORM 时,T3 Stack 会自动包含 Drizzle Kit。 ```json:package.json "scripts": { ... "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", ... }, ``` ### 脚本说明 `db:generate` 从数据库模式生成 TypeScript 类型和模型,确保类型安全并轻松与 Drizzle ORM 集成。 `db:migrate` 将待处理的迁移应用到数据库中,保持模式与项目中的更改和更新同步。 `db:push` 将本地模式更改直接推送到数据库,而无需显式的迁移文件。这在开发中快速同步时非常有用。 `db:studio` 打开一个可视化界面,用于管理和检查数据库表、数据和关系。 ## 实用资源 | 资源 | 链接 | | ---------------------- | --------------------------------------------------- | | Drizzle 文档 | https://orm.drizzle.team/docs/overview | | Drizzle GitHub | https://github.com/drizzle-team/drizzle-orm | | Auth.JS Drizzle 适配器 | https://authjs.dev/getting-started/adapters/drizzle | | Drizzle Kit 迁移指南 | https://orm.drizzle.team/docs/kit-overview | ================================================ FILE: www/src/pages/zh-hans/usage/env-variables.mdx ================================================ --- title: 环境变量 description: 开始使用 create-t3-app layout: ../../../layouts/docs.astro lang: zh-hans isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; Create T3 App 使用自己的包 [@t3-oss/env-nextjs](https://env.t3.gg) 和 [zod](https://zod.dev) ,通过在 `src/env.js` 中提供一个简单的逻辑,在运行时和构建时验证环境变量。 ## env.js _简而言之,如果你想要添加一个新的环境变量,你需要在 src/env.js 中添加一个验证器,然后在 .env 文件中添加键值对。_ ```ts:env.js import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { NODE_ENV: z.enum(["development", "test", "production"]), }, client: { // NEXT_PUBLIC_CLIENTVAR: z.string(), }, runtimeEnv: { NODE_ENV: process.env.NODE_ENV, }, }); ``` T3 Env 使用 `createEnv` 函数来创建 schema,以验证客户端和服务端的环境变量。 如果你想了解更多 `createEnv` 函数的内部工作原理,可以参看 [T3 Env](https://env.t3.gg/docs/introduction) 的文档。 ## 使用环境变量 当你想使用你的环境变量时,你可以从创建的 `env.js` 中导入,并像平常一样使用它们。如果你在客户端导入并尝试访问服务端环境变量,你将得到一个运行时错误。 ```ts:pages/api/hello.ts import { env } from "../../env.js"; // `env` 是完全类型安全的,并提供自动完成功能 const dbUrl = env.DATABASE_URL; ``` ```ts:pages/index.tsx import { env } from "../env.js"; // ❌ 这将抛出一个运行时错误 const dbUrl = env.DATABASE_URL; // ✅ 这是正确的 const wsKey = env.NEXT_PUBLIC_WS_KEY; ``` ## .env.example 由于默认的 `.env` 文件不会被提交到版本控制中,因此我们还提供了一个 `.env.example` 文件,你可以在其中选择性地保留你的 `.env` 文件的副本,并删除其中的任何机密信息。这不是必需的,但我们建议保持示例文件的更新,以便让贡献者更轻松地开始配置他们的环境。 有些框架和构建工具(例如 Next.js)建议你将机密信息存储在 `.env.local` 文件中,并将 `.env` 文件提交到你的项目中。我们不建议这样做,因为这样很容易意外地将机密信息提交到你的项目中。相反,我们建议你将机密信息存储在 `.env` 文件中,将 `.env` 文件添加到 `.gitignore` 中,只将 `.env.example` 文件提交到你的项目中。 ## 添加环境变量 为了确保项目所需的环境变量不会丢失,您需要在两个位置添加新的环境变量。 📄 `.env`:像在 `.env` 文件中一样输入您的环境变量,例如:`KEY=VALUE` 📄 `env.js`:为每个环境变量添加适当的验证逻辑,在 `createEnv` 中定义一个Zod模式,例如:`KEY: z.string()`。此外,确保在 `runtimeEnv` 选项中对它们进行解构,例如:KEY: `process.env.KEY` 为什么需要在 `runtimeEnv` 中对环境变量进行解构? 这是由于 Next.js 在某些运行时中绑定环境变量的方式所致。 通过手动解构,可以确保变量永远不会从 bundle 中剔除。 你也可以选择更新 `.env.example` 📄 `.env.example`:输入你的环境变量,但确保不要在 `runtimeEnv` 选项中包含机密信息, 例如: `KEY=VALUE` 或 `KEY=` ### 示例 1. 在 `.env` 中添加环境变量: ``` TWITTER_API_TOKEN=1234567890 ``` 2. 在 `env.js` 中添加环境变量: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { TWITTER_API_TOKEN: z.string(), }, // ... runtimeEnv: { // ... TWITTER_API_TOKEN: process.env.TWITTER_API_TOKEN, }, }); ``` 3. _可选:_ 将环境变量添加到 `.env.example`,并确保不在 `runtimeEnv` 选项中包含机密信息。 ```bash TWITTER_API_TOKEN= ``` ## 类型强制 所有添加到 `.env` 中的变量都将以字符串的形式导入,即使它们的值表示不同的类型。如果你想在运行时使用不同类型的环境变量,可以使用 Zod 的 `coerce` 将字符串转换为你想要的类型。如果转换失败,它将抛出错误。 将变量添加到你的 `.env`: ``` SOME_NUMBER=123 SOME_BOOLEAN=true ``` 然后,在 `env.js` 中验证它们: ```ts import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; export const env = createEnv({ server: { SOME_NUMBER: z.coerce.number(), SOME_BOOLEAN: z.coerce.boolean(), }, // ... runtimeEnv: { SOME_NUMBER: process.env.SOME_NUMBER, SOME_BOOLEAN: process.env.SOME_BOOLEAN, }, }); ``` ================================================ FILE: www/src/pages/zh-hans/usage/first-steps.md ================================================ --- title: 第一步 description: 从你的全新 T3 App 开始吧 layout: ../../../layouts/docs.astro lang: zh-hans --- 你刚刚创建了一个全新的 T3 App,并准备好继续。下面是一些最简化的必要准备,来让你的 App 运行起来。 ## 数据库 如果你的 App 包括了 Prisma,务必记得从项目根目录运行命令 `npx prisma db push`。 这行命令将同步 Prisma schema 和你的数据库,并且会根据 schema 为 Prisma 客户端生成对应的类型声明。注意你在完成这一步之后,需要 [重启 TypeScript 服务器](https://tinytip.co/tips/vscode-restart-ts/),以便它能够检测到那些生成的类型。 ### Drizzle 如果你的 App 包含了 Drizzle,请查看 `.env` 文件,了解如何构建你的 `DATABASE_URL` 环境变量。当 env 文件准备就绪后,运行 `pnpm db:push`(或其他软件包管理器的类似方法)来推送你的 schema。 ## 认证 如果你的 App 包含了 NextAuth.js,那我们可以先从 `DiscordProvider` 开始。这是 NextAuth.js 提供的最简单的第三方服务之一,但是它仍然需要你做一些初始化的操作。 当然,如果你倾向于使用另一个第三方认证服务,你可以在 NextAuth.js 支持的 [服务列表](https://next-auth.js.org/providers/) 中找到你想要的服务。 1. 你将需要一个 Discord 账号,所以如果你没有,请先注册一个。 2. 前往 然后在右上角点击 "New Application"。给你的应用创建一个名称,并同意相关的服务条款。 3. 当你的应用被创建后,前往 "Settings → OAuth2 → General"。 4. 复制 "Client ID" 然后作为 `AUTH_DISCORD_ID` 添加到 `.env`。 5. 点击 "Reset Secret",复制新的密钥,然后作为 `AUTH_DISCORD_SECRET` 添加到 `.env`。 6. 点击 "Add Redirect",然后输入 `http://localhost:3000/api/auth/callback/discord`。 - 对于生产环境的部署,按照之前的步骤来创建另一个 Discord 应用,但是这一次将链接 `http://localhost:3000` 替换为实际生产环境的链接。 7. 保存你的更改。 你现在应该可以登入到你的应用中了。 ## 编辑器设置 为了获得最佳的开发者体验,建议安装以下扩展。下面的链接提供了针对特定编辑器的插件支持。 - [Prisma 扩展](https://www.prisma.io/docs/guides/development-environment/editor-setup) - [Tailwind CSS IntelliSense 扩展](https://tailwindcss.com/docs/editor-setup) - [Prettier 扩展](https://prettier.io/docs/en/editors.html) ## 下一步 - 如果你的应用包含了 tRPC,参看 `src/pages/index.tsx` 和 `src/server/api/routers/post.ts` 来了解 tRPC 查询是如何工作的。 - 浏览 Create T3 App 的文档,以及你应用中所用到包的文档。 - 加入到我们的 [Discord](https://t3.gg/discord) 中,并记得在 [GitHub](https://github.com/t3-oss/create-t3-app) 上给我们的项目点颗星! :) ================================================ FILE: www/src/pages/zh-hans/usage/index.astro ================================================ --- import IndexPage from "../../../components/docs/indexPage.astro"; import { SIDEBAR, type Frontmatter } from "../../../config"; import { getLanguageFromURL } from "../../../languages"; import Layout from "../../../layouts/docs.astro"; const frontmatter: Frontmatter = { title: "Usage", layout: "docs", description: "Learn how to use the different technology from the T3 Stack.", }; const lang = getLanguageFromURL(Astro.url.pathname); const sidebarEntries = SIDEBAR[lang]["Usage"]!; const files = await Astro.glob("./*.{md,mdx,astro}"); --- ================================================ FILE: www/src/pages/zh-hans/usage/next-auth.mdx ================================================ --- title: NextAuth.js description: NextAuth.js 的用法 layout: ../../../layouts/docs.astro lang: zh-hans isMdx: true --- import Callout from "../../../components/docs/callout.tsx"; 当你想在你的 Next.js 应用中集成认证系统时,NextAuth.js 是一个很好的解决方案,它为我们引入了足够复杂的安全措施,而无需我们自己去构建。它广泛支持不同的服务,能够快速将 OAuth 认证集成进来,并为许多数据库和 ORM 提供了适配器。 ## Context Provider 在你应用的入口处,你会看到整个应用被 [SessionProvider](https://next-auth.js.org/getting-started/client#sessionprovider) 包裹: ```tsx:pages/_app.tsx ``` 这个上下文 provider 能使你在应用的任何地方访问到 session 数据,而不需要一层一层地将它作为参数传递: ```tsx:pages/users/[id].tsx import { useSession } from "next-auth/react"; const User = () => { const { data: session } = useSession(); if (!session) { // 处理未认证的情况,例如渲染一个登入组件 return ; } return

Welcome {session.user.name}!

; }; ``` ## 在服务端检索 session 有时你可能想在服务端请求 session。 为此, 可以使用 `create-t3-app` 提供的`getServerAuthSession` 辅助函数来预获取 session,然后使用 `getServerSideProps` 将其传递给客户端: ```tsx:pages/users/[id].tsx import { getServerAuthSession } from "../server/auth"; import { type GetServerSideProps } from "next"; export const getServerSideProps: GetServerSideProps = async (ctx) => { const session = await getServerAuthSession(ctx); return { props: { session }, }; }; const User = () => { const { data: session } = useSession(); // 注意: `session` 不会有加载状态,因为它已经在服务器上预获取了。 ... } ``` ## 在 session 中添加 `user.id` 默认设置下, Create T3 App 利用了 NextAuth.js 配置里的 [session 回调函数](https://next-auth.js.org/configuration/callbacks#session-callback) 来帮你将用户的 ID 添加到 `session` 对象里。 ```ts:server/auth.ts callbacks: { session({ session, user }) { if (session.user) { session.user.id = user.id; } return session; }, }, ``` 这耦合了类型声明文件,来保证从 `session` 对象里获取 `user.id` 时,它是类型安全的。 参看 NextAuth.js 的文档,阅读更多关于 [`模块扩充`](https://next-auth.js.org/getting-started/typescript#module-augmentation)。 ```ts:server/auth.ts import { DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user?: { id: string; } & DefaultSession["user"]; } } ``` 你也可以用同样的方法将其他数据,例如 `role` 字段加入到 `session` 对象里,**但不应该被滥用于在客户端存储敏感数据**。 ## 搭配 tRPC 的用法 当你搭配 NextAuth.js 和 tRPC 一同使用时,你可以通过 [中间件](https://trpc.io/docs/v10/middlewares) 来创建可复用的、受保护的 procedure 路由函数。只有登入后的用户才能访问这些受保护的路由。`create-t3-app` 已经为你铺好了路,使你能够在认证路由里轻松访问到 session 里的数据。 这个过程可以被分为两步完成: 1. 先通过函数 [`getServerSession`](https://next-auth.js.org/configuration/nextjs#getServerSession) 从请求头里获得 session。使用 `getServerSession` 而不是普通的 `getSession` 函数的优势是,它是一个服务端的函数,不会触发无必要的数据请求调用。 `create-t3-app` 创建了一个帮助函数,将这个特殊的 API 抽象了出来,这样你就不需要每次访问会话时都导入你的 NextAuth.js 选项以及 `getServerSession` 函数。 ```ts:server/auth.ts export const getServerAuthSession = (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); }; ``` 通过使用这个帮助函数,我们可以获取 session,并将它传给 tRPC 的上下文: ```ts:server/api/trpc.ts import { getServerAuthSession } from "../auth"; export const createContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; const session = await getServerAuthSession({ req, res }); return await createContextInner({ session, }); }; ``` 2. 创建一个 tRPC 中间件,来检测用户是否已通过认证。然后我们可以在一个 `protectedProcedure` 里调用该中间件。任何对该路由的调用都会被要求认证,否则会抛出一个错误,由客户端妥善处理。 ```ts:server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { // inferer `session` som ikke-nullbar session: { ...ctx.session, user: ctx.session.user }, }, }); })); ``` 这个 session 对象是对用户数据的一个轻量、最小化表示,仅包含了少量字段。当使用 `protectedProcedures` 时,你可以借助访问用户 ID 来从数据库里读取该用户的更多数据。 ```ts:server/api/routers/user.ts const userRouter = router({ me: protectedProcedure.query(async ({ ctx }) => { const user = await prisma.user.findUnique({ where: { id: ctx.session.user.id, }, }); return user; }), }); ``` ## 搭配 Prisma 使用 要将 NextAuth.js 搭配 Prisma 一起使用需要非常多的 [初始化步骤](https://authjs.dev/reference/adapter/prisma/)。`create-t3-app` 可以帮你完成这些工作,如果你在用 CLI 创建应用时同时选择了 Prisma 和 NextAuth.js,那你的应用已经集成了一个完全正常运行的认证系统,所有的数据库模型都已经预先配置好了。我们为你的应用预先配置了 Discord OAuth 认证服务,因为它是最方便实现的服务之一 —— 只需要在 `.env` 文件里提供令牌即可。然而,你也可以根据 [NextAuth.js 文档](https://next-auth.js.org/providers/) 来轻松添加更多其他第三方认证服务。要注意的是有部分特定的第三方服务需要你在数据模型上添加额外的字段。我们推荐你去阅读你想要添加的第三方验证服务的文档,以便确保你已经提供所有要求的字段了。 ### 给你的模型添加新的字段 当你给 `User`、`Account`、`Session` 或 `VerificationToken` 中任何一个模型添加字段时(大部分情况你只需要修改 `User` 模型),你要注意的是 [Prisma 适配器](https://next-auth.js.org/adapters/prisma) 会在用户注册和登入时自动给这些模型添加额外的字段,因此你必须给这些字段提供初始值,因为这个适配器并不能知道这些字段是否已经存在。 如果你想要,例如给 `User` 模型添加一个 `role` 字段,你需要为它提供一个默认初始值。你可以通过给 `User` 模型的 `role` 字段添加一个 `@default` 属性来完成: ```diff:prisma/schema.prisma + enum Role { + USER + ADMIN + } model User { ... + role Role @default(USER) } ``` ## 搭配 Next.js 中间件使用 将 NextAuth.js 搭配 Next.js 中间件一同使用[需要采用 JWT session 策略](https://next-auth.js.org/configuration/nextjs#caveats) 来进行认证。这是因为只有当会话 cookie 为 JWT 时,中间件才能够获取到它。默认情况下,Create T3 App 会用 Prisma 作为数据库适配器,并采取**默认**的数据库策略。 使用数据库会话是推荐的方法,如果要切换到 JWT 会话策略,请先了解 JWT,以避免出现任何安全问题。 在切换到 JWT 会话策略后,请确保更新 `src/server/auth.ts` 中的 `session` 回调函数。 `user` 对象将为 `undefined`。相反,应从 `token` 对象中检索用户的 ID。 例如: ```diff:server/auth.ts export const authOptions: NextAuthOptions = { + session: { + strategy: "jwt", + }, callbacks: { - session({ session, user }) { + session({ session, token }) { - if (session.user) { + if (session.user && token.sub) { - session.user.id = user.id; + session.user.id = token.sub; } return session; } }, }; ``` ## 配置默认的 DiscordProvider 1. 前往 [Discord 开发者页面的应用部分](https://discord.com/developers/applications),然后点击 "New Application" 2. 在设置菜单中,依次前往 "OAuth2 => General" - 复制 Client ID,然后粘贴到 `.env` 文件中的 `AUTH_DISCORD_ID`。 - 在 Client Secret 下方,点击 "Reset Secret",然后复制该字符串到 `env` 中的 `AUTH_DISCORD_SECRET`。务必要细心,因为你无法再次查看该密钥了,而将它重置会让现存的密钥失效。 - 点击 "Add Redirect",然后将你应用的网址替换 `/api/auth/callback/discord` 里的 ``(例如,一个开发阶段的完整链接像这样:http://localhost:3000/api/auth/callback/discord) - 保存你的更改 - 在开发和生产环境使用同一个 Discord 应用是可行的,但不鼓励这么做。你应该也考虑在开发阶段 [模拟认证服务](https://github.com/trpc/trpc/blob/main/examples/next-prisma-websockets-starter/src/pages/api/auth/%5B...nextauth%5D.ts)。 ## 有用的资源 | 资源 | 链接 | | --------------------------------- | --------------------------------------- | | NextAuth.js 文档 | https://next-auth.js.org/ | | NextAuth.js GitHub | https://github.com/nextauthjs/next-auth | | tRPC Kitchen Sink - with NextAuth | https://kitchen-sink.trpc.io/next-auth | ================================================ FILE: www/src/pages/zh-hans/usage/next-js.md ================================================ --- title: Next.js description: Next.js 的用法 layout: ../../../layouts/docs.astro lang: zh-hans --- Next.js 是你 React 应用的后端框架。
不妨观看 [Theo 在 Next.js Conf 上的演讲](https://www.youtube.com/watch?v=W4UhNo3HAMw) 来更好地理解 Next.js 究竟是什么以及它是如何运作的。

## 为什么我应该使用它? 我们热爱 React。它以一种我们从未想过的方式将 UI 开发变得如此简单。但是它同样也给开发者们带来了一些困难。而 Next.js 就为我们提供了一种轻量 opinionated,却高度优化的方式来使用 React 创建应用。从路由到 API 定义,再到图片渲染,我们相信 Next.js 会帮助开发者做出正确的决定。 而将 Next.js 搭配 [Vercel](https://vercel.com/) 一同使用使得 web 应用的开发体验前所未有的棒。他们慷慨的免费套餐以及非常直观的用户界面让我们可以通过点击几下的方式就可以部署网站(我们 ❤️ Vercel)。 ## Get Static / Server Props Next.js 的一项关键特性就是它支持的几种获取数据的方法。我们强烈推荐先通读 [官方文档](https://nextjs.org/docs/basic-features/data-fetching) 来理解如何使用每一种方法,以及它们之间的区别。一般情况下不推荐使用 `getServerSideProps`,除非有好的理由,因为它是一个阻塞 UI 的方法,会减慢你的网站运行速度。当数据是动态且需要被增量获取时,[Incremental Static Regeneration](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration) 便是 `getServerSideProps` 的一个非常好的备选方案。 如果你仍需要使用这个特性,可以查看这些链接:[Advanced tRPC - Callers, functions, and gSSP](https://www.youtube.com/watch?v=G2ZzmgShHgQ) 和 [SSG-Helpers](https://trpc.io/docs/v9/ssg-helpers)。 ## 有用的资源 | 资源 | 链接 | | --------------------------- | ---------------------------------- | | Next.js 文档 | https://nextjs.org/docs | | Next.js GitHub | https://github.com/vercel/next.js | | Next.js 博客 | https://nextjs.org/blog | | Next.js Discord | https://nextjs.org/discord | | Next.js 推特 | https://twitter.com/nextjs | | Vercel/Next.js YouTube 频道 | https://www.youtube.com/c/VercelHQ | ================================================ FILE: www/src/pages/zh-hans/usage/prisma.md ================================================ --- title: Prisma description: Prisma 的用法 layout: ../../../layouts/docs.astro lang: zh-hans --- Prisma 是一个 TypeScript 的 ORM (Object-relational mapping) 工具,它允许你在文件 `schema.prisma` 中为你的数据库定义数据结构和模型,然后借此生成一个类型安全的客户端,从而在后端和你的数据库进行交互。 ## Prisma 客户端 Prisma 客户端位于文件 `/server/db.ts`, 以全局变量被初始化(这被推荐为 [最佳实践](https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices#problem)),然后被导出用于你的各个 API 路由函数中。我们默认将 Prisma 客户端放置在 [上下文](/zh-hans/usage/trpc#-serverapitrpcts),鼓励通过访问上下文来使用它,这样就无需在每个文件里导入了。 ## Schema 你可以在 `/prisma/schema.prisma` 找到 Prisma 的 schema。在这个文件中你可以定义数据库的数据结构和模型,同时它也会被用于生成 Prisma 客户端。 ### 搭配 NextAuth.js 当你选择 NextAuth.js 搭配 Prisma 使用时,根据 [NextAuth.js 文档](https://next-auth.js.org/adapters/prisma),schema 文件会被自动生成,其中默认包含了 `User`、`Session`、`Account` 和 `VerificationToken` 模型,并已经自动添加了推荐的字段。 ## 默认数据库 默认数据库是一个 SQLite 数据库,这对开发阶段来说很棒,因为它能够快速启动概念验证,但不推荐用于生产环境。你可以将 `datasource` 中的 `provider` 修改为 `postgresql` 或 `mysql`,然后在环境变量里修改一下对应数据库的连接地址,来更换数据库。 ## 给数据库填充初始数据 [给数据库填充初始数据](https://www.prisma.io/docs/guides/database/seed-database) 是一种很好的方法,它能快速给你的数据库填充测试数据以帮助你开始。为了设置填充,你将需要在目录 `/prisma` 下创建一个 `seed.ts` 脚本文件,然后在 `package.json` 文件中添加 `seed` 命令。你也会需要一个 TypeScript 执行环境来运行该数据填充脚本。我们推荐 [tsx](https://github.com/esbuild-kit/tsx),它采用 esbuild,是一个性能很好的 TypeScript 执行环境,无需额外的 ESM 配置,不过也可以使用 `ts-node` 或其他执行环境。 ```jsonc:package.json { "scripts": { "db-seed": "NODE_ENV=development prisma db seed" }, "prisma": { "seed": "tsx prisma/seed.ts" } } ``` ```ts:prisma/seed.ts import { db } from "../src/server/db"; async function main() { const id = "cl9ebqhxk00003b600tymydho"; await db.example.upsert({ where: { id, }, create: { id, }, update: {}, }); } main() .then(async () => { await db.$disconnect(); }) .catch(async (e) => { console.error(e); await db.$disconnect(); process.exit(1); }); ``` 然后,运行命令 `pnpm db-seed`(或 `npm` / `yarn`)来给数据库填充初始数据。 ## 有用的资源 | 资源 | 链接 | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Prisma 文档 | https://www.prisma.io/docs/ | | Prisma GitHub | https://github.com/prisma/prisma | | Prisma Migrate 演练场 | https://playground.prisma.io/guides | | NextAuth.JS Prisma 适配器 | https://next-auth.js.org/adapters/prisma | | PlanetScale 连接指引 | https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/connect-your-database-typescript-planetscale | ================================================ FILE: www/src/pages/zh-hans/usage/tailwind.md ================================================ --- title: Tailwind CSS description: Tailwind CSS 的用法 layout: ../../../layouts/docs.astro lang: zh-hans --- ## 什么是 Tailwind CSS? Tailwind CSS 是一个[功能优先](https://tailwindcss.com/docs/utility-first) 的微型 CSS 框架,它可被用于创建自定义设计,而无需常规 CSS 要求的上下文切换。Tailwind CSS 纯粹是一个 CSS 框架,并不提供预设的组件或逻辑,相比如 Material UI 的组件库,它带来了非常 [不同的好处](https://www.youtube.com/watch?v=CQuTF-bkOgc)。 它使得写 CSS 变得格外简单快速,如下面的例子所示: 以往的 CSS: 1. 通常在单独的文件里写 CSS ```css .my-class { display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; border: 1px solid #e2e8f0; border-radius: 0.25rem; padding: 1rem; } ``` 2. 在组件里导入 CSS 文件 ```jsx import "./my-class.css"; ``` 3. 将类名添加到 HTML 元素 ```html
...
``` 在 Tailwind CSS 中,这等同于: 1. 直接在 HTML 元素里写类名 ```html
...
``` 当搭配 React 组件一同使用时,它强大的功能能够帮助我们快速搭建 UI。 Tailwind CSS 有一套内置的精美设计系统,它包含了精心挑选的色彩方案、为统一设计的尺寸模式(例如宽度 / 高度和填充 / 边距),以及为创建响应式布局所需的断点功能,这一切都开箱即用。这套设计系统可以完全按照你项目的需求来进行自定义配置和扩展。
Tru Narla 也即 [mewtru](https://twitter.com/trunarla) 之前发表过关于 [使用 Tailwind CSS 构建设计系统](https://www.youtube.com/watch?v=T-Zv73yZ_QI) 的精彩演讲。 ## 用法 务必为你的代码编辑器添加对应的 Tailwind 插件,以提升开发体验。 ### 扩展与插件 - [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - [JetBrains Integration](https://www.jetbrains.com/help/webstorm/tailwind-css.html#ws_css_tailwind_install) - [Neovim LSP](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#tailwindcss) ### 格式化 Tailwind CSS 的类名很容易变得混乱,所以一个类名格式化工具非常有必要。[Tailwind CSS Prettier 插件](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) 会按照 [推荐的顺序](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted) 给不同的类排序,这样代码里的类名顺序就和最终打包文件中的类名顺序保持一致。当你用命令行工具创建 T3 应用时选择了 Tailwind,我们会默认帮你安装并配置好该插件。 ### 条件性地添加类 使用三元操作符来条件性地添加类会变得混乱,且难以阅读。以下两个包可以在需要条件性逻辑时帮你组织好类名。 - [clsx](https://github.com/lukeed/clsx) - [classnames](https://github.com/JedWatson/classnames) ## 有用的资源 | 资源 | 链接 | | ------------------------- | -------------------------------------------------------- | | Tailwind 文档 | https://tailwindcss.com/docs/editor-setup/ | | Tailwind 备忘录 | https://nerdcave.com/tailwind-cheat-sheet/ | | awesome-tailwindcss | https://github.com/aniftyco/awesome-tailwindcss/ | | Tailwind 社区 | https://github.com/tailwindlabs/tailwindcss/discussions/ | | Tailwind Discord 服务器 | https://tailwindcss.com/discord/ | | TailwindLabs Youtube 频道 | https://www.youtube.com/tailwindlabs/ | | Tailwind Playground | https://play.tailwindcss.com/ | ================================================ FILE: www/src/pages/zh-hans/usage/trpc.md ================================================ --- title: tRPC description: tRPC 的用法 layout: ../../../layouts/docs.astro lang: zh-hans --- tRPC 能够让我们在无需代码自动生成器的帮助或在运行时额外检测的情况下,写出端对端类型安全的 API。它利用了 TypeScript 的强大推断功能来推断出你 API 路由的类型定义,并让你在前端能够调用 API 时充分享受完全类型安全和全自动补全的特性。当使用 tRPC 时,你应用的前端和后端部分会感觉比以前更加紧密,这带来了非常棒的开发体验。

我创建 tRPC 的目的是为了通过移除传统的 API 层来帮助人们更快地构建应用,但与此同时依然保证 App 在快速迭代的构建过程中不会出现大问题。

Avatar of @alexdotjs
Alex - tRPC 的创建者 @alexdotjs
## 我怎么使用 tRPC?
tRPC 贡献者 [trashh_dev](https://twitter.com/trashh_dev) 在 [Next.js Conf 上关于 tRPC 做了一个非常棒的演讲](https://www.youtube.com/watch?v=2LYM8gf184U)。若还没有看过的话,我们强烈建议你观看一下。 通过 tRPC,你可以在后端编写 TypeScript 函数,然后从你的前端直接调用。一个简单的 tRPC 路由函数 procedure 可能长这样: ```ts:server/api/routers/user.ts const userRouter = createTRPCRouter({ getById: publicProcedure.input(z.string()).query(({ ctx, input }) => { return ctx.prisma.user.findFirst({ where: { id: input, }, }); }), }); ``` 这是一个 tRPC procedure(等同于传统后端中的路由函数),它将先通过 Zod(我们将同样用它来验证 [环境变量](/zh-hans/usage/env-variables))来验证输入 —— 在上述的例子里,它将确保输入是合法的字符串。如果输入不是字符串,它会返回一个直观的错误信息。 在输入之后,我们链式地添加了一个 resolver 函数,它可以被用于 [查询](https://trpc.io/docs/v10/react-queries)、[修改](https://trpc.io/docs/v10/react-mutations) 或 [订阅](https://trpc.io/docs/v10/subscriptions)。在我们的例子中,这个 resolver 函数通过 [Prisma](/zh-hans/usage/prisma) 客户端读取了数据库,然后返回一条 `id` 匹配传入参数的用户数据。 你在 `routers` 中定义许多 procedure 路由函数,它表示这些相关路由函数的公共命名空间。你可以有不同的路由,例如 `users`、`posts` 以及 `messages`。然后将这些路由统一集中合并到 `appRouter` 里: ```ts:server/api/root.ts const appRouter = createTRPCRouter({ users: userRouter, posts: postRouter, messages: messageRouter, }); export type AppRouter = typeof appRouter; ``` 需要注意的是我们只需要将合并后的路由的类型定义导出,这意味着我们将不会在客户端导入任何服务端的代码。 现在让我们在前端调用路由函数。tRPC 为 `@tanstack/react-query` 做了一层封装,这既可以让你充分利用它所提供的各种 hooks 功能,又能在调用 API 时享受类型安全和类型推断带来的好处。我们可以这样调用后端的路由函数: ```tsx:pages/users/[id].tsx import { useRouter } from "next/router"; import { api } from "../../utils/api"; const UserPage = () => { const { query } = useRouter(); const userQuery = api.users.getById.useQuery(query.id); return (

{userQuery.data?.name}

); }; ``` 你会立即感受到类型安全和自动补全带来的好处。只要当你输入 `api.` 时,你所定义的路由都会显示在自动补全的菜单里,然后当你选择了一个路由,它所包含的路由函数也会显示出来。如果你的输入不符合你在后端定义的验证器的要求,TypeScript 也会将错误显示出来。 ## 推断错误 默认情况下,`create-t3-app` 设置了一个 [error formatter](https://trpc.io/docs/error-formatting),让你在后端出现验证错误时可以推断出你的 Zod 错误。 使用示例: ```tsx function MyComponent() { const { mutate, error } = api.post.create.useMutation(); return ( { e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ title: formData.get('title') }); }}> {error?.data?.zodError?.fieldErrors.title && ( {/** `mutate` returned with an error on the `title` */} {error.data.zodError.fieldErrors.title} )} ... ); } ``` ## 文件 tRPC 需要不少样板代码,不过 `create-t3-app` 已经帮你完成。让我们看一下这些被自动创建的文件: ### 📄 `pages/api/trpc/[trpc].ts` 这里是你项目 API 的入口,它暴露了 tRPC 的路由。正常情况下,你不会去修改这个文件,但如果需要的话,例如开启 CORS 中间件或其他类似的情况,知道下面的信息是很有用的:被导出的 `createNextApiHandler` 是一个 [Next.js API 的 handler](https://nextjs.org/docs/api-routes/introduction),它分别接受一个 [request](https://developer.mozilla.org/en-US/docs/Web/API/Request) 和一个 [response](https://developer.mozilla.org/en-US/docs/Web/API/Response?retiredLocale=sv-SE) 对象作为参数。这意味着 `createNextApiHandler` 可以被任何你想要的中间件包裹。查看下方添加 CORS 的 [示例代码](#enabling-cors)。 ### 📄 `server/api/trpc.ts` 这个文件分为两部分,上下文创建和 tRPC 初始化。 1. 我们定义传递给你的 tRPC 路由函数的上下文。上下文就是一个数据对象,你定义的所有 tRPC 路由函数都会访问它来获取数据,它被用来存放了一些例如数据库的连接、认证信息等数据。在 create-t3-app 里,当我们不需要获取整个请求对象时,我们分别使用两个函数来取得上下文的部分数据。 - `createInnerTRPCContext`: 这里你可以定义不依赖请求的上下文,例如数据库的连接。你可以使用这个函数来做 [集成测试](#sample-integration-test) 或 [ssg-helpers](https://trpc.io/docs/v10/ssg-helpers),这些场景下你都没有一个请求对象。 - `createTRPCContext`: 你可以在这里定义依赖于请求的上下文,例如用户的 session。你通过使用 `opts.req` 来获取 session,然后将它传给 `createInnerTRPCContext` 函数来创建最后完整的上下文。 2. 我们初始化 tRPC,并定义可复用的 [procedure](https://trpc.io/docs/v10/procedures) 路由函数和 [中间件](https://trpc.io/docs/v10/middlewares)。按照惯例,你不应该将整个 `t` 对象导出,而是通过转换创建复用的路由和中间件,并导出它们。 你会注意到我们使用了 `superjson` 作为 [数据解析工具](https://trpc.io/docs/v10/data-transformers)。在数据被发送到客户端时,它会帮你保留数据类型。例如你发送了一个 `Date` 类型的对象,客户端会返回一个相同类型的 `Date`,而不是像其他大多数 API 一样返回一个字符串。 ### 📄 `server/api/routers/*.ts` 你可以在这里定义 API 的路由及其函数。按照惯例,你在这里为相关的路由函数 procedure [创建分离的路由](https://trpc.io/docs/v10/router) ### `server/api/root.ts` 在这里我们把所有在 `routers/**` 中定义的子路由 [合并](https://trpc.io/docs/v10/merging-routers) 到一个单一的应用路由里。 ### 📄 `utils/api.ts` 这里是 tRPC 的前端入口。你可以在这里导入路由的**类型定义**,创建你的 tRPC 客户端以及 react-query hooks。因为我们已经在后端将 `superjson` 设置为数据序列化工具,我们同样需要在前端开启它。这是因为从后端传入的序列化在前端还未被反序列化。 你将在这里定义 tRPC [links](https://trpc.io/docs/v10/links),它们被用于决定从客户端到服务器的请求流。我们采用 "default" [`httpBatchLink`](https://trpc.io/docs/v10/links/httpBatchLink),它会帮我们开启 [批量请求](https://cloud.google.com/compute/docs/api/how-tos/batch) 的功能,以及采用 [`loggerLink`](https://trpc.io/docs/v10/links/loggerLink),它则帮我们在开发阶段打印出有用的请求日志信息。 最后,我们导出一个 [helper 类型](https://trpc.io/docs/v10/infer-types#additional-dx-helper-type),你可以通过它在前端来推断类型。
Create T3 App 的贡献者 [Christopher Ehrlich](https://twitter.com/ccccjjjjeeee) 制作了 [一个关于 tRPC 数据流的视频](https://www.youtube.com/watch?v=x4mu-jOiA0Q)。如果你已经使用过 tRPC 但仍感到有些不清楚它的工作原理,我们建议你观看这个视频。 ## 如何从外部调用我的 API? 对于常规的 API 来说,你可以使用诸如 `curl`、`Postman`、`fetch` 这样的 HTTP 客户端或直接在浏览器里访问你的 API 端点。但对于 tRPC,调用方法有点不同。如果你不想使用 tRPC 客户端来访问路由函数,这边有两种推荐方式来实现: ### 向外暴露单个路由函数 如果你想向外暴露单个路由函数,你可以查阅 [服务端调用](https://trpc.io/docs/v10/server-side-calls)。这允许你使用常规的 Next.js API 端点,但同时让你可以复用 tRPC 路由函数的 resolver 部分。 ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { appRouter, createCaller } from "../../../server/api/root"; import { createTRPCContext } from "../../../server/api/trpc"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { // Create context and caller const ctx = await createTRPCContext({ req, res }); const caller = createCaller(ctx); try { const { id } = req.query; const user = await caller.user.getById(id); res.status(200).json(user); } catch (cause) { if (cause instanceof TRPCError) { // An error from tRPC occurred const httpCode = getHTTPStatusCodeFromError(cause); return res.status(httpCode).json(cause); } // Another error occurred console.error(cause); res.status(500).json({ message: "Internal server error" }); } }; export default userByIdHandler; ``` ### 将每个路由函数作为 REST API 端点暴露出来 如果你想要将每一个 procedure 对外暴露出来,不妨使用这个社区维护的插件 [trpc-openapi](https://github.com/jlalmes/trpc-openapi/tree/master)。通过给你的路由函数提供一些额外的元数据,你可以用 tRPC 路由来生成兼容 OpenAPI 规范的 REST API。 ### 它只是 HTTP 请求 tRPC 通过 HTTP 协议来传输数据,因此使用“常规”的 HTTP 请求来调用你的 tRPC 路由是没有问题的。然而,由于 tRPC 使用的 [RPC 通讯协议](https://trpc.io/docs/v10/rpc) 不同,写起来的语法会十分繁琐。如果你好奇的话,不妨在你的浏览器开发工具下的网络标签里查看一下 tRPC 的请求和响应长什么样,但是我们建议使用常规 HTTP 请求只是出于教学意义,你还是应该坚持使用上述提到的方式之一来调用 tRPC。 ## 与 Next.js API 对比 让我们将 Next.js API 和 tRPC 路由做个对比吧。假设我们想要从数据库获取用户的数据,然后在前端显示出来。我们可能会写一段如下方所示的 Next.js API 代码: ```ts:pages/api/users/[id].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { prisma } from "../../../server/db"; const userByIdHandler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method !== "GET") { return res.status(405).end(); } const { id } = req.query; if (!id || typeof id !== "string") { return res.status(400).json({ error: "Invalid id" }); } const examples = await prisma.example.findFirst({ where: { id, }, }); res.status(200).json(examples); }; export default userByIdHandler; ``` ```ts:pages/users/[id].tsx import { useState, useEffect } from "react"; import { useRouter } from "next/router"; const UserPage = () => { const router = useRouter(); const { id } = router.query; const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/user/${id}`) .then((res) => res.json()) .then((data) => setUser(data)); }, [id]); }; ``` 将这段代码和上面的 tRPC 示例做比较,你可以清楚地看到 tRPC 的优势所在: - 你无需为每一个路由定义一个 URL,倘若这样做,只要你修改了一些代码就会让调试变得很困难,而现在你的整个路由就只是一个支持自动补全的对象而已。 - 你无需验证使用了哪个 HTTP 方法(比如 POST 还是 GET)。 - 你无需验证请求查询参数或请求体,因为 Zod 已经替你完成了。 - 你无需创建一个响应,而是可以像在任何 TypeScript 函数中一样,直接抛出错误、返回值或对象即可。 - 在前端直接调用 procedure 函数为你带来了自动补全和类型安全的好处。 ## 有用的代码片段 这里有一些可能比较有用的代码片段。 ### 开启 CORS(跨源资源共享) 如果你需要从不同的域来访问你的 API,例如在一个包含 React Native 应用的 monorepo 的项目里,你可能需要开启 CORS: ```ts:pages/api/trpc/[trpc].ts import { type NextApiRequest, type NextApiResponse } from "next"; import { createNextApiHandler } from "@trpc/server/adapters/next"; import { appRouter } from "~/server/api/root"; import { createTRPCContext } from "~/server/api/trpc"; import cors from "nextjs-cors"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { // 开启 cors await cors(req, res); // Create and call the tRPC handler return createNextApiHandler({ router: appRouter, createContext: createTRPCContext, })(req, res); }; export default handler; ``` ### 乐观更新 乐观更新是指在 API 调用完成之前更新 UI,达到前端界面快速响应用户交互的效果。这给用户带来了更好的体验,因为他们不需要等待 API 调用完成,然后 UI 来反映操作结果。然而,那些特别着重数据准确性的应用应该避免使用乐观更新,因为它们并不是后端数据状态的真实表达。你可以在 [React Query 文档](https://tanstack.com/query/v4/docs/guides/optimistic-updates) 阅读更多相关内容。 ```tsx const MyComponent = () => { const listPostQuery = api.post.list.useQuery(); const utils = api.useContext(); const postCreate = api.post.create.useMutation({ async onMutate(newPost) { // 取消发送中的 fetch 请求(所以它们不会覆盖掉我们的乐观更新) await utils.post.list.cancel(); // 从 queryCache 中获取数据 const prevData = utils.post.list.getData(); // 用我们的新文章来做乐观更新 utils.post.list.setData(undefined, (old) => [...old, newPost]); // 返回之前的数据,这样做可以让我们在错误发生时回滚 return { prevData }; }, onError(err, newPost, ctx) { // 当修改失败后,使用来自 onMutate 中的值 utils.post.list.setData(undefined, ctx.prevData); }, onSettled() { // 当修改完成后,与服务端同步 utils.post.list.invalidate(); }, }); }; ``` ### 集成测试示例 这个集成测试示例使用了 [Vitest](https://vitest.dev) 来检测 tRPC 路由如预期正常工作,输入解析函数会自行推断出正确的类型,然后返回的数据与预期的结果相匹配。 ```ts import { type inferProcedureInput } from "@trpc/server"; import { expect, test } from "vitest"; import { appRouter, type AppRouter } from "~/server/api/root"; import { createInnerTRPCContext } from "~/server/api/trpc"; test("example router", async () => { const ctx = await createInnerTRPCContext({ session: null }); const caller = appRouter.createCaller(ctx); type Input = inferProcedureInput; const input: Input = { text: "test", }; const example = await caller.example.hello(input); expect(example).toMatchObject({ greeting: "Hello test" }); }); ``` 如果你的 procedure 是受保护的,你可以传入一个模拟的 `session` 对象来创建上下文: ```ts test("protected example router", async () => { const ctx = await createInnerTRPCContext({ session: { user: { id: "123", name: "John Doe" }, expires: "1", }, }); const caller = appRouter.createCaller(ctx); // ... }); ``` ## 有用的资源 | 资源 | 链接 | | ---------------- | ------------------------------------------------------- | | tRPC 文档 | https://www.trpc.io | | 大量的 tRPC 示例 | https://github.com/trpc/trpc/tree/next/examples | | React Query 文档 | https://tanstack.com/query/v4/docs/adapters/react-query | ================================================ FILE: www/src/pages/zh-hans/usage/typescript.md ================================================ --- title: TypeScript description: TypeScript 的用法 layout: ../../../layouts/docs.astro lang: zh-hans ---

搭建安全的防护网,而不是护栏。

Avatar of @t3dotgg
Theo - T3 Stack 的创建者 @t3dotgg
无论你是菜鸟还是老手,我们认为 TypeScript 是一个必选项。初看起来这像是恐吓,但就像很多其他工具一样,你用过 TypeScript 之后就很难再回头了。 在你写代码时,它通过预设的数据类型来提供实时反馈;而如果你在代码中尝试访问不存在的属性或尝试传递错误类型的值时,它要么会在编辑器中提供有用的自动补全功能,要么会用红色的波浪线来提醒你错误代码,让你不得不进行进一步调试。 它很可能是一款给程序员带来最高生产力的工具;给你写的或引用的代码添加文档说明,并在你不可避免犯错的情况下提供即时反馈,这绝对是无价的。 ## 类型推断 当许多 TypeScript 开发者担心需要额外 _编写_ TypeScript 代码,但实际上你不需要修改任何代码就获得了很多它带来的好处,特别是类型推断。类型推断意味着,如果某个数据被加了类型,无论你在应用的任何地方使用这个数据,都能得到它所携带的类型,而无需额外在使用它的地方重新声明它的类型。这也就意味着,比方说一旦你定义了一个函数的参数类型,函数的剩余部分一般会被保证类型安全,无需再写更多 TypeScript 的代码。开源库项目的开发者们投入了大量工作来为他们的库维护类型定义,这意味着我们作为应用程序的开发者在编写代码过程中,借助编辑器可以从这些库的类型推断和内置文档中获益。
点击观看 Theo 的视频,了解为什么说 [你可能用错了 TypeScript](https://www.youtube.com/watch?v=RmGHnYUqQ4k). ## 类型推断的强大用途 ### Zod [Zod](https://github.com/colinhacks/zod) 是一个基于 TypeScript 的 schema 验证库。写一个 schema,它代表了你项目中数据的唯一真实来源,而 Zod 则会保证应用中所有的数据都是有效合法的,甚至是来自跨网络和外部 API 的数据。 ### Tanstack Query [Tanstack Query](https://tanstack.com/query/v4/) 提供了声明式的、永远最新的、自动管理的数据查询和修改方法,这直接改进了开发体验和用户体验。 ## 有用的资源 | 资源 | 链接 | | ------------------------------------------------------ | ----------------------------------------------------------------- | | TypeScript 手册 | https://www.typescriptlang.org/docs/handbook/ | | TypeScript 入门教程 | https://github.com/total-typescript/beginners-typescript-tutorial | | 类型挑战 | https://github.com/type-challenges/type-challenges | | Rodney Mullen of TypeScript (Matt Pocock) Youtube 频道 | https://www.youtube.com/c/MattPocockUk/videos | ================================================ FILE: www/src/pages/zh-hans/why.md ================================================ --- title: 为什么选择 CT3A? description: 为什么你应该选择 Create T3 App 来创建你的下一个项目 layout: ../../layouts/docs.astro lang: zh-hans --- 我们创建 Create T3 App 的初衷,是因为 [Theo](https://twitter.com/t3dotgg) 拒绝将他最喜欢的几种技术做成模板。受到 create-next-app、[Astro 的 CLI 命令行工具](https://astro.build) 和对类型安全普遍热爱的启发,Create T3 App 团队努力地为创建新的 T3 Stack 项目建立了最好的起点。 如果你对以类型安全的方式使用 Next.js 感兴趣,这就是开始的地方。如果你对我们的具体技术选择感到好奇,请继续阅读 :) ## 为什么使用 TypeScript? JavaScript 已经很难了。为什么还要增加更多规则? 我们坚信 TypeScript 提供的经验会帮助你成为更好的开发者。在你写代码时,它通过预设的数据类型来提供实时反馈;而如果你在代码中尝试访问不存在的属性或尝试传递错误类型的值时,它要么会在编辑器中提供有用的自动补全功能,要么会用红色的波浪线来提醒你错误代码,让你不得不进行进一步调试。无论你是 web 开发的新手还是老手,TypeScript 的“严格性”可以让它比原生 JavaScript 为你带来更少的挫败感,却带来更一致的开发体验。 类型安全可以让你更快地写代码。如果你不承认这一点,你可能 [用错了 TypeScript。。。](https://www.youtube.com/watch?v=RmGHnYUqQ4k) ## 为什么使用 Next.js? 我们热爱 React。它使 UI 开发以我们从未想象过的方式进行。但它也会将开发者引向一些艰难的道路。 Next.js 为使用 React 创建应用程序提供了一种轻度 opinionated、高度优化的方法。从路由到 API 定义,再到图片渲染,我们都相信 Next.js 能引导开发者做出正确的决定。 ## 为什么使用 tRPC / Prisma / Tailwind 等? 虽然我们相信要尽可能保持简单,但我们发现我们创建的每一个类似“应用程序”的项目中都会用到这些技术。`create-t3-app` 很好地让你能够采用你需要的部分。 ### tRPC tRPC 实现了 GraphQL 的承诺,即针对类型安全服务端的无缝客户端开发,且不需要任何样板代码。这是对 TypeScript 的巧妙滥用,它提供了令人难以置信的开发体验。 ### Prisma Prisma 之于 SQL 就像 TypeScript 之于 JS。它创造了前所未有的开发者体验。通过从与 [几种数据库](https://www.prisma.io/docs/concepts/database-connectors) 兼容的自定义 schema 中生成类型,Prisma 保证了从数据库到应用程序的端到端类型安全。 Prisma 提供了 [一整套工具](https://www.prisma.io/docs/concepts/overview/should-you-use-prisma#-you-want-a-tool-that-holistically-covers-your-database-workflows),使与数据库的日常交互更加容易。值得注意的是,用 Prisma 客户端来负责数据库查询,使得 SQL 变得十分简单,以至于你几乎不会注意到你在使用它,而 Prisma Studio 是一个方便的数据库 GUI,可以让你快速读取和操作数据,而无需编写代码。 ### Tailwind CSS Tailwind 给人的感觉就像“禅式的 CSS”。 Tailwind 通过良好的默认颜色、间距和其他原语的形式为我们提供了构建块,使得创建漂亮美观的应用程序变得十分容易。而当你想将应用程序提升到一个新的水平并创造美丽而独特的 UI 时,不同于组件库,它并不会在这方面阻碍你。 此外,由于其类似于内联的方法,Tailwind 鼓励你进行样式设计,而不必担心命名 class、组织文件或任何其他与你试图解决的问题没有直接联系的问题。 ### NextAuth.js 当你想在 NextJS 应用程序中添加身份验证系统时,NextAuth.js 是一个很好的解决方案,可以引入足够复杂的安全措施,而无需自己构建它。它广泛支持不同的第三方服务,可以快速添加 OAuth 认证,并为许多数据库和 ORM 提供适配器。 ================================================ FILE: www/src/styles/accessibility.css ================================================ @media prefers-reduced-motion { * { animation: none !important; transition: none !important; scroll-behavior: auto !important; } } *:not(.focus-none) { @apply outline-2 outline-offset-2 !outline-t3-purple-400 transition-[outline-offset] focus-visible:outline; } *:not(:active):focus-visible { @apply outline-offset-4; } a:has(> img) { @apply inline-block; } ================================================ FILE: www/src/styles/algolia/button.css ================================================ /*! @docsearch/css Button 3.2.1 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */ .DocSearch-Button { align-items: center; background: var(--docsearch-searchbox-background); border: 0; border-radius: 40px; color: var(--docsearch-muted-color); cursor: pointer; display: flex; font-weight: 500; height: 36px; justify-content: space-between; margin: 0 0 0 16px; padding: 0 8px; user-select: none; } .DocSearch-Button:active, .DocSearch-Button:focus, .DocSearch-Button:hover { background: var(--docsearch-searchbox-focus-background); box-shadow: var(--docsearch-searchbox-shadow); color: var(--docsearch-text-color); outline: none; } .DocSearch-Button-Container { align-items: center; display: flex; } .DocSearch-Search-Icon { stroke-width: 1.6; } .DocSearch-Button .DocSearch-Search-Icon { color: var(--docsearch-text-color); } .DocSearch-Button-Placeholder { font-size: 1rem; padding: 0 12px 0 6px; } .DocSearch-Button-Keys { display: flex; min-width: calc(40px + 0.8em); } .DocSearch-Button-Key { align-items: center; background: var(--docsearch-key-gradient); border-radius: 3px; box-shadow: var(--docsearch-key-shadow); color: var(--docsearch-muted-color); display: flex; height: 18px; justify-content: center; margin-right: 0.4em; position: relative; padding: 0 0 2px; border: 0; top: -1px; width: 20px; } @media (max-width: 768px) { .DocSearch-Button-Keys, .DocSearch-Button-Placeholder { display: none; } } ================================================ FILE: www/src/styles/algolia/modal.css ================================================ /*! @docsearch/css Modal 3.2.1 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */ .DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:focus{outline:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;color:var(--docsearch-muted-color);border:0;width:20px}@media (max-width:768px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}} ================================================ FILE: www/src/styles/algolia/style.css ================================================ /*! @docsearch/css 3.2.1 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */ html { --docsearch-primary-color: rgb(var(--color-primary)); --docsearch-text-color: rgb(var(--color-foreground)); --docsearch-spacing: 12px; --docsearch-icon-stroke-width: 1.4; --docsearch-highlight-color: var(--docsearch-primary-color); --docsearch-muted-color: #969faf; --docsearch-container-background: rgba(22, 24, 30, 0.1); --docsearch-logo-color: #e8ddff; --docsearch-modal-border: rgb(63, 65, 95, 0.25); --docsearch-modal-width: 560px; --docsearch-modal-height: 600px; --docsearch-modal-background: rgb(var(--color-background)); --docsearch-modal-shadow: 0px 4px 15px 6px rgba(0, 0, 0, 0.1); --docsearch-searchbox-height: 45px; --docsearch-searchbox-background: rgba(0, 0, 0, 0); --docsearch-searchbox-focus-background: rgba(0, 0, 0, 0); --docsearch-searchbox-shadow: none; --docsearch-hit-height: 56px; --docsearch-hit-color: rgb(var(--color-foreground)); --docsearch-hit-active-color: rgb(232 221 255); --docsearch-hit-background: rgba(0, 0, 0, 0); --docsearch-hit-shadow: none; --docsearch-key-background: rgb(220, 216, 248); --docsearch-key-color: rgb(152, 145, 187); --docsearch-key-border: rgb(152, 145, 187); --docsearch-footer-height: 44px; --docsearch-footer-background: rgba(0, 0, 0, 0); --docsearch-footer-shadow: none, 0 -3px 6px 0 rgba(69, 98, 155, 0.12); } html.dark { --docsearch-key-background: rgb(41, 29, 100); --docsearch-key-border: rgb(14, 6, 50); --docsearch-modal-border: rgba(95, 98, 146, 0.25); } .DocSearch-Button { align-items: center; background: var(--docsearch-searchbox-background); border: 0; border-radius: 40px; color: var(--docsearch-muted-color); cursor: pointer; display: flex; font-weight: 500; height: 36px; justify-content: space-between; margin: 0 0 0 16px; padding: 0 8px; user-select: none; } .DocSearch-Button:active, .DocSearch-Button:focus, .DocSearch-Button:hover { background: var(--docsearch-searchbox-focus-background); box-shadow: var(--docsearch-searchbox-shadow); color: var(--docsearch-text-color); outline: none; } .DocSearch-Button-Container { align-items: center; display: flex; } .DocSearch-Search-Icon { stroke-width: 1.6; margin-inline-end: 15px; } .DocSearch-Button .DocSearch-Search-Icon { color: var(--docsearch-text-color); } .DocSearch-Button-Placeholder { font-size: 1rem; padding: 0 12px 0 6px; } .DocSearch-Button-Keys { display: flex; min-width: calc(40px + 0.8em); } @media (max-width: 768px) { .DocSearch-Button-Keys, .DocSearch-Button-Placeholder { display: none; } } .DocSearch-Container, .DocSearch-Container * { box-sizing: border-box; } .DocSearch-Container *:hover { text-decoration: none !important; } .DocSearch-Container { background-color: var(--docsearch-container-background); backdrop-filter: blur(5px); height: 100vh; position: fixed; left: 0; top: 0; width: 100vw; z-index: 200; display: flex; justify-content: center; } .DocSearch-Container a { text-decoration: none; } .DocSearch-Link { appearance: none; background: none; border: 0; color: var(--docsearch-highlight-color); cursor: pointer; font: inherit; margin: 0; padding: 0; } .DocSearch-Modal { background: var(--docsearch-modal-background); border-radius: 6px; box-shadow: var(--docsearch-modal-shadow); flex-direction: column; margin-top: 60px; width: var(--docsearch-modal-width); height: fit-content; position: relative; border: 1px solid var(--docsearch-modal-border); cursor: default; } .DocSearch-SearchBar { display: flex; padding: var(--docsearch-spacing) var(--docsearch-spacing) 0; } .DocSearch-Form { align-items: center; background: var(--docsearch-searchbox-focus-background); border-radius: 4px; box-shadow: var(--docsearch-searchbox-shadow); display: flex; height: var(--docsearch-searchbox-height); margin: 0; padding: 0 var(--docsearch-spacing); position: relative; width: 100%; } .DocSearch-Input { appearance: none; background: transparent; border: 0; color: var(--docsearch-text-color); flex: 1; font: inherit; font-size: 1.2em; height: 100%; outline: none; padding-inline-start: 8px; width: 80%; } .DocSearch-Input::placeholder { color: var(--docsearch-muted-color); opacity: 1; } .DocSearch-Input::-webkit-search-cancel-button, .DocSearch-Input::-webkit-search-decoration, .DocSearch-Input::-webkit-search-results-button, .DocSearch-Input::-webkit-search-results-decoration { display: none; } .DocSearch-LoadingIndicator, .DocSearch-MagnifierLabel, .DocSearch-Reset { margin: 0; padding: 0; } .DocSearch-Reset { margin-inline-start: 12px; } .DocSearch-MagnifierLabel, .DocSearch-Reset { align-items: center; color: var(--docsearch-highlight-color); display: flex; justify-content: center; } .DocSearch-Container--Stalled .DocSearch-MagnifierLabel, .DocSearch-LoadingIndicator { display: none; } .DocSearch-Container--Stalled .DocSearch-LoadingIndicator { align-items: center; color: var(--docsearch-highlight-color); display: flex; justify-content: center; } @media screen and (prefers-reduced-motion: reduce) { .DocSearch-Reset { animation: none; appearance: none; background: none; border: 0; border-radius: 50%; color: var(--docsearch-icon-color); cursor: pointer; right: 0; stroke-width: var(--docsearch-icon-stroke-width); } } .DocSearch-Reset { animation: fade-in 0.1s ease-in forwards; appearance: none; background: none; border: 0; border-radius: 50%; color: var(--docsearch-icon-color); cursor: pointer; padding: 2px; right: 0; stroke-width: var(--docsearch-icon-stroke-width); } .DocSearch-Reset[hidden] { display: none; } .DocSearch-Reset:focus { outline: none; } .DocSearch-Reset:hover { color: var(--docsearch-highlight-color); } .DocSearch-LoadingIndicator svg, .DocSearch-MagnifierLabel svg { height: 24px; width: 24px; } .DocSearch-Cancel { display: none; } .DocSearch-Dropdown { margin-top: 10px; max-height: calc( var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height) ); min-height: var(--docsearch-spacing); overflow-y: auto; overflow-y: overlay; padding: 0 var(--docsearch-spacing); scrollbar-color: var(--docsearch-muted-color) var(--docsearch-modal-background); scrollbar-width: thin; } .DocSearch-Dropdown::-webkit-scrollbar { width: 12px; } .DocSearch-Dropdown::-webkit-scrollbar-track { background: transparent; } .DocSearch-Dropdown::-webkit-scrollbar-thumb { background-color: var(--docsearch-muted-color); border: 3px solid var(--docsearch-modal-background); border-radius: 20px; } .DocSearch-Dropdown ul { list-style: none; margin: 0; padding: 0; } .DocSearch-Label { font-size: 0.75em; line-height: 1.6em; } .DocSearch-Help, .DocSearch-Label { color: var(--docsearch-muted-color); } .DocSearch-Help { font-size: 0.9em; margin: 0; user-select: none; } .DocSearch-Title { font-size: 1.2em; } .DocSearch-Logo a { display: flex; } [dir='rtl'] .DocSearch-Logo a { flex-direction: row-reverse; } .DocSearch-Logo svg { color: var(--docsearch-logo-color); margin-inline-start: 8px; } [dir='rtl'] .DocSearch-Logo svg { margin-inline-end: 8px; } .DocSearch-Hits:last-of-type { margin-bottom: 24px; } .DocSearch-Hits mark { background: none; color: var(--docsearch-highlight-color); } .DocSearch-HitsFooter { color: var(--docsearch-muted-color); display: flex; font-size: 0.85em; justify-content: center; margin-bottom: var(--docsearch-spacing); padding: var(--docsearch-spacing); } .DocSearch-HitsFooter a { border-bottom: 1px solid; color: inherit; } .DocSearch-Hit { border-radius: 4px; display: flex; padding-bottom: 4px; position: relative; } @media screen and (prefers-reduced-motion: reduce) { .DocSearch-Hit--deleting { transition: none; } } .DocSearch-Hit--deleting { opacity: 0; transition: all 0.25s linear; } @media screen and (prefers-reduced-motion: reduce) { .DocSearch-Hit--favoriting { transition: none; } } .DocSearch-Hit--favoriting { transform: scale(0); transform-origin: top center; transition: all 0.25s linear; transition-delay: 0.25s; } .DocSearch-Hit a { background: var(--docsearch-hit-background); border-radius: 4px; box-shadow: var(--docsearch-hit-shadow); display: block; padding-left: var(--docsearch-spacing); width: 100%; } .DocSearch-Hit-source { background: var(--docsearch-modal-background); color: rgb(var(--color-foreground)); font-size: 1em; font-weight: 500; line-height: 32px; margin: 0 -4px; padding: 8px 4px 8px; position: sticky; top: 0; z-index: 10; } .DocSearch-Hit-Tree { color: var(--docsearch-muted-color); height: var(--docsearch-hit-height); opacity: 0.5; stroke-width: var(--docsearch-icon-stroke-width); width: 24px; } .DocSearch-Hit[aria-selected="true"] a { background-color: var(--docsearch-highlight-color); } .DocSearch-Hit[aria-selected="true"] mark { text-decoration: underline; } .DocSearch-Hit-Container { align-items: center; color: var(--docsearch-hit-color); display: flex; flex-direction: row; height: var(--docsearch-hit-height); padding: 0 var(--docsearch-spacing) 0 0; } .DocSearch-Hit-icon { height: 20px; width: 20px; } .DocSearch-Hit-action, .DocSearch-Hit-icon { color: var(--docsearch-muted-color); stroke-width: var(--docsearch-icon-stroke-width); } .DocSearch-Hit-action { align-items: center; display: flex; height: 22px; width: 22px; } .DocSearch-Hit-action svg { display: block; height: 18px; width: 18px; } .DocSearch-Hit-action + .DocSearch-Hit-action { margin-left: 6px; } .DocSearch-Hit-action-button { appearance: none; background: none; border: 0; border-radius: 50%; color: inherit; cursor: pointer; padding: 2px; } svg.DocSearch-Hit-Select-Icon { display: none; } .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-Select-Icon { display: block; } .DocSearch-Hit-action-button:focus, .DocSearch-Hit-action-button:hover { background: rgba(0, 0, 0, 0.2); transition: background-color 0.1s ease-in; } @media screen and (prefers-reduced-motion: reduce) { .DocSearch-Hit-action-button:focus, .DocSearch-Hit-action-button:hover { transition: none; } } .DocSearch-Hit-action-button:focus path, .DocSearch-Hit-action-button:hover path { fill: rgb(var(--color-foreground)); } .DocSearch-Hit-content-wrapper { display: flex; flex: 1 1 auto; flex-direction: column; font-weight: 500; justify-content: center; line-height: 1.2em; margin: 0 8px; overflow-x: hidden; position: relative; text-overflow: ellipsis; white-space: nowrap; width: 80%; } .DocSearch-Hit-title { font-size: 0.9em; } .DocSearch-Hit-path { color: var(--docsearch-muted-color); font-size: 0.75em; } .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-action, .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-icon, .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-path, .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-text, .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-title, .DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-Tree, .DocSearch-Hit[aria-selected="true"] mark { color: var(--docsearch-hit-active-color) !important; } @media screen and (prefers-reduced-motion: reduce) { .DocSearch-Hit-action-button:focus, .DocSearch-Hit-action-button:hover { background: rgba(0, 0, 0, 0.2); transition: none; } } .DocSearch-ErrorScreen, .DocSearch-NoResults, .DocSearch-StartScreen { font-size: 0.9em; margin: 0 auto; padding: 36px 0; text-align: center; width: 80%; } .DocSearch-Screen-Icon { color: var(--docsearch-muted-color); padding-bottom: 12px; } .DocSearch-NoResults-Prefill-List { display: inline-block; padding-bottom: 24px; text-align: left; } .DocSearch-NoResults-Prefill-List ul { display: inline-block; padding: 8px 0 0; } .DocSearch-NoResults-Prefill-List li { list-style-position: inside; list-style-type: "» "; } .DocSearch-Prefill { appearance: none; background: none; border: 0; border-radius: 1em; color: var(--docsearch-highlight-color); cursor: pointer; display: inline-block; font-size: 1em; font-weight: 700; padding: 0; } .DocSearch-Prefill:focus, .DocSearch-Prefill:hover { outline: none; text-decoration: underline; } .DocSearch-Footer { align-items: center; background: var(--docsearch-footer-background); border-radius: 0 0 8px 8px; box-shadow: var(--docsearch-footer-shadow); display: flex; flex-direction: row-reverse; flex-shrink: 0; height: var(--docsearch-footer-height); justify-content: space-between; padding: 0 var(--docsearch-spacing); position: relative; user-select: none; width: 100%; z-index: 300; } .DocSearch-Commands { color: var(--docsearch-muted-color); display: flex; list-style: none; margin: 0; padding: 0; } .DocSearch-Commands li { align-items: center; display: flex; } .DocSearch-Commands li:not(:last-of-type) { margin-inline-end: 0.8em; } .DocSearch-Commands-Key, .DocSearch-Button-Key { align-items: center; background-color: var(--docsearch-key-background); border-radius: 2px; display: flex; height: 18px; justify-content: center; margin-inline-end: 0.4em; padding: 0 0 1px; color: var(--docsearch-key-color); border: 1px solid var(--docsearch-key-border); width: 20px; } @media (max-width: 768px) { :root { --docsearch-spacing: 10px; --docsearch-footer-height: 40px; } .DocSearch-Dropdown { height: 100%; } .DocSearch-Container { height: 100vh; height: -webkit-fill-available; height: calc(var(--docsearch-vh, 1vh) * 100); } .DocSearch-Footer { border-radius: 0; bottom: 0; position: absolute; } .DocSearch-Hit-content-wrapper { display: flex; position: relative; width: 80%; } .DocSearch-Modal { border-radius: 0; box-shadow: none; height: 100vh; height: -webkit-fill-available; height: calc(var(--docsearch-vh, 1vh) * 100); margin: 0; max-width: 100%; width: 100%; } .DocSearch-Dropdown { max-height: calc( var(--docsearch-vh, 1vh) * 100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height) ); } .DocSearch-Cancel { appearance: none; background: none; border: 0; color: var(--docsearch-highlight-color); cursor: pointer; display: inline-block; flex: none; font: inherit; font-size: 1em; font-weight: 500; margin-left: var(--docsearch-spacing); outline: none; overflow: hidden; padding: 0; user-select: none; white-space: nowrap; } .DocSearch-Commands, .DocSearch-Hit-Tree { display: none; } } @keyframes fade-in { 0% { opacity: 0; } to { opacity: 1; } } ================================================ FILE: www/src/styles/global.css ================================================ @tailwind base; @tailwind components; @tailwind utilities; @layer base { /* Design System Theming */ html { --color-default: 236 228 236; --color-neutral: 29 40 58; --color-primary: 112 64 255; --color-secondary: 58 191 248; --color-accent: 244 113 181; --color-warning: 244 193 82; --color-error: 251 111 132; --color-success: 43 212 189; --color-info: 12 166 233; --color-foreground: 15 23 42; --color-background: 236 228 236; @apply bg-default text-slate-900; } html.dark { --color-default: 15 23 41; --color-neutral: 29 40 58; --color-primary: 112 64 255; --color-secondary: 58 191 248; --color-accent: 244 113 181; --color-warning: 244 193 82; --color-error: 251 111 132; --color-success: 43 212 189; --color-info: 12 166 233; --color-foreground: 232 221 255; --color-background: 15 23 41; @apply bg-default text-t3-purple-100; } html { @apply scroll-smooth accent-primary scrollbar-thin scrollbar-track-transparent scrollbar-thumb-t3-purple-300 dark:scrollbar-track-transparent dark:scrollbar-thumb-t3-purple-300; } /* Typewriter */ .cursor::after { display: block; content: ""; position: absolute; width: 4px; height: 100%; background-color: #fff; animation: cursor 0.6s linear infinite alternate; will-change: opacity; } @keyframes cursor { 0%, 40% { opacity: 1; } 60%, 100% { opacity: 0; } } /* Base stylings */ *:focus:not(:focus-visible) { outline: none; } a:hover, a:focus { text-decoration: underline; } a:focus { outline: 2px solid currentColor; outline-offset: 0.25em; } button:focus { outline: 2px solid currentColor; outline-offset: 0.25em; } article > section :is(ul, ol) > * + * { margin-top: 0.75rem; } article > section li > :is(p, pre, blockquote):not(:first-child) { margin-top: 1rem; } article > section :is(ul, ol) { padding-inline-start: 1em; } article > section nav :is(ul, ol) { padding-inline-start: inherit; } td, th { padding: 6px; text-align: inline-start; } .t3-scrollbar { @apply scrollbar-thin scrollbar-track-slate-300 scrollbar-thumb-t3-purple-300 dark:scrollbar-track-t3-purple-200/10 dark:scrollbar-thumb-t3-purple-300; } .t3-scrollbar::-webkit-scrollbar-thumb { @apply rounded-full; } .line-clamp-1 { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; } h1[id], h2[id], h3[id], h4[id], h5[id], #content { @apply scroll-mt-36 lg:scroll-mt-24; } /* only at less than md */ @media (max-width: 767.5px) { .mobile-sidebar-toggle { overflow: hidden; height: 100vh; } .mobile-sidebar-toggle #grid-left { z-index: 20; display: flex; width: 100vw; height: 100vh; } } /* Markdown Styling */ .markdown { @apply flex w-full flex-col items-start justify-start px-4 leading-relaxed text-slate-900 selection:bg-t3-purple-200 selection:text-t3-purple-500 dark:text-t3-purple-100 dark:selection:bg-t3-purple-100 dark:selection:text-slate-900; } .markdown h1 { @apply mt-8 mb-4 text-3xl font-extrabold; } .markdown h2 { @apply mt-8 mb-4 text-2xl font-semibold; } .markdown h3 { @apply mt-4 mb-2 text-xl font-semibold; } .markdown h4 { @apply mt-2 mb-1 text-base font-medium; } .markdown li { @apply list-disc text-slate-900 dark:text-white; } .markdown ol > li { @apply mb-2 list-decimal; } .markdown p { @apply mb-4 dark:text-white; } .markdown a > p { @apply mb-0 text-sm; } .markdown a:not(.heading-link--hidden---effects) { @apply text-t3-purple-500 underline decoration-slate-500 decoration-dotted underline-offset-2 hover:text-t3-purple-700 dark:text-t3-purple-200 dark:decoration-t3-purple-200 dark:hover:text-t3-purple-300; } :is([dir="rtl"]) .markdown a[rel~="noreferrer"] > span { @apply inline-flex -scale-x-100; } .markdown code { @apply break-words [direction:ltr] [unicode-bidi:embed] lg:break-normal; } .markdown .code-wrapper { @apply inline rounded border border-t3-purple-200 bg-t3-purple-200/40 px-0.5 py-[0.5px] dark:border-t3-purple-400/30 dark:bg-t3-purple-400/20; } .markdown .remark-code-title { @apply z-10 -mb-1 mt-3 w-full rounded-t-md border border-t3-purple-300/80 bg-[#191724] px-2 py-1 font-mono text-t3-purple-200; } .markdown pre { @apply mx-auto mb-3 rounded border border-t3-purple-300/80 p-2 pl-3 font-mono shadow-xl sm:pt-2; } .markdown ol { @apply w-full; } .markdown details { @apply mt-2 w-full rounded-md border border-t3-purple-300/80 bg-[#191724] px-3 transition-all duration-300 ease-in-out; } .markdown details[open] { @apply bg-t3-purple-200/10; } .markdown details pre { @apply border-0 shadow-none; } .markdown details > summary { @apply cursor-pointer text-[#A6ACCD]; } .markdown summary { @apply py-3; } .markdown p > img { @apply mt-8 text-sm; } .markdown table > tbody > tr > td > a { overflow-wrap: anywhere; } .markdown table { @apply max-w-full table-fixed; } .markdown table > tbody > tr > td { @apply align-top; } .markdown blockquote a { @apply text-slate-900 dark:text-t3-purple-50; } .DocSearch-SearchBar { @apply my-3 rounded-md bg-purple-400 p-2 text-sm; } #DocSearch-SearchBar { @apply my-3 rounded-md bg-purple-400 p-2 text-sm; } .embed { @apply relative mb-2 h-0 w-full pb-[56.25%]; } .embed iframe { @apply absolute top-0 left-0 h-full w-full; } } ================================================ FILE: www/src/styles/swiper.css ================================================ :root { --swiper-theme-color: rgb(var(--color-primary)) !important; --swiper-pagination-bullet-inactive-color: #fff; --swiper-pagination-bullet-inactive-opacity: 20%; } ================================================ FILE: www/src/utils/fetchGithub.ts ================================================ import { z } from "zod"; interface Options { throwIfNotOk?: boolean; throwIfNoAuth?: boolean; fetchType: T; } export const repoSchema = z.object({ stargazers_count: z.number(), }); export const commitsSchema = z.array( z.object({ commit: z.object({ author: z.object({ date: z.string(), }), }), author: z.object({ login: z.string(), id: z.number(), }), }), ); export type Commit = z.infer[number]; /** Helper function to fetch the GitHub API with an auth token to avoid rate limiting. */ export const fetchGithub = async ( url: string, opts: Options, ): Promise | null> => { const { throwIfNotOk = true, throwIfNoAuth = true, fetchType } = opts; const schema = fetchType === "commits" ? commitsSchema : repoSchema; const token = import.meta.env.PUBLIC_GITHUB_TOKEN as string | undefined; if (!token) { const msg = "No Github token found. Please set PUBLIC_GITHUB_TOKEN in .env to avoid rate limiting."; if (throwIfNoAuth) { throw new Error(msg); } console.warn(msg); const response = await fetch(url); const data = (await response.json()) as Promise; const parsed = schema.safeParse(data); if (!parsed.success) { console.warn( "Could not parse GitHub API response. This could be caused by rate limiting.", ); return null; } return parsed.data; } const auth = `Basic ${Buffer.from(token, "binary").toString("base64")}`; const res = await fetch(url, { headers: { Authorization: auth, "User-Agent": "@ct3a-www/1.0", }, }); const data = await res.json(); if (!res.ok) { const msg = `Request to fetch ${url} failed. Reason: ${res.statusText} Message: ${ data && typeof data === "object" && "message" in data ? data.message : "unknown" }`; if (throwIfNotOk) { throw new Error(msg); } console.warn(msg); } const parsed = schema.safeParse(data); if (!parsed.success) { const msg = "Could not parse GitHub API response."; if (throwIfNotOk) { throw new Error(msg); } console.warn(msg); return null; } return parsed.data; }; ================================================ FILE: www/src/utils/ogFont.ts ================================================ /** https://github.com/juliusmarminge/jumr.dev/blob/main/app/og-image/get-fonts.ts */ export async function getFont({ family, weights, text, }: { family: string; weights: TWeights; text?: string; }): Promise> { const API = `https://fonts.googleapis.com/css2?family=${family}:wght@${weights.join( ";", )}${text ? `&text=${encodeURIComponent(text)}` : ""}`; const css = await ( await fetch(API, { headers: { // Make sure it returns TTF. "User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1", }, }) ).text(); const fonts = css .split("@font-face {") .splice(1) .map((font) => { const u = /src: url\((.+)\) format\('(opentype|truetype)'\)/.exec(font); const w = /font-weight: (\d+)/.exec(font); return u?.[1] && w?.[1] ? { url: u[1], weight: parseInt(w[1]) } : null; }) .filter( (font): font is { url: string; weight: TWeights[number] } => !!font, ); const promises = fonts.map(async (font) => { const res = await fetch(font.url); return [font.weight, await res.arrayBuffer()]; }); // Object.fromEntries is typed as returning any *sigh* // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Object.fromEntries(await Promise.all(promises)); } ================================================ FILE: www/src/utils/pagination.ts ================================================ import { SIDEBAR, type Sidebar, type SidebarItem, type SidebarItemLink, } from "../config"; export function paginate(lang: keyof Sidebar, path: SidebarItemLink) { const routes = Object.values(SIDEBAR[lang]).flat() as SidebarItem[]; const index = routes.map((item) => item.link).indexOf(path); if (index === -1) return { prev: undefined, next: undefined }; const prev = index > 0 ? routes[index - 1] : undefined; const next = index < routes.length - 1 ? routes[index + 1] : undefined; return { prev, next }; } ================================================ FILE: www/src/utils/siteUrl.ts ================================================ export const SITE_URL = import.meta.env.VERCEL_URL ? `https://${import.meta.env.VERCEL_URL}` : "http://localhost:3000"; ================================================ FILE: www/tailwind.config.ts ================================================ import scrollbar from "tailwind-scrollbar"; import { type Config } from "tailwindcss"; export default { content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], darkMode: "selector", theme: { extend: { colors: { default: "rgb(var(--color-default) / )", neutral: "rgb(var(--color-neutral) / )", primary: "rgb(var(--color-primary) / )", secondary: "rgb(var(--color-secondary) / )", accent: "rgb(var(--color-accent) / )", warning: "rgb(var(--color-warning) / )", error: "rgb(var(--color-error) / )", success: "rgb(var(--color-success) / )", info: "rgb(var(--color-info) / )", "t3-purple-50": "#F3EEFF", "t3-purple-100": "#e8ddff", "t3-purple-200": "#c3b4fc", "t3-purple-300": "#9e8bf9", "t3-purple-400": "#7a62f6", "t3-purple-500": "#5749f3", "t3-purple-600": "#4a3eeb", "t3-purple-700": "#3d34e3", "t3-purple-800": "#3029db", "t3-purple-900": "#231ed3", "t3-purple-1000": "#1613cb", }, animation: { draw: "draw 2s linear normal", }, }, keyframes: { draw: { to: { strokeDashoffset: "200" }, }, }, fontFamily: { sans: [ '"InterVariable"', "ui-sans-serif", "system-ui", "-apple-system", "BlinkMacSystemFont", '"Segoe UI"', "Roboto", '"Helvetica Neue"', "Arial", '"Noto Sans"', "sans-serif", '"Apple Color Emoji"', '"Segoe UI Emoji"', '"Segoe UI Symbol"', '"Noto Color Emoji"', ], serif: ["ui-serif", "Georgia"], mono: [ '"JetBrains Mono"', "monospace", "ui-monospace", "Menlo", "Monaco", '"Cascadia Mono"', '"Segoe UI Mono"', '"Roboto Mono"', '"Oxygen Mono"', '"Ubuntu Monospace"', '"Source Code Pro"', '"Fira Mono"', '"Droid Sans Mono"', '"Courier New"', ], }, }, plugins: [scrollbar({ nocompatible: true })], } satisfies Config; ================================================ FILE: www/tsconfig.json ================================================ { "extends": "astro/tsconfigs/strictest", "compilerOptions": { "jsx": "preserve", // Skip type checking of external libraries. "skipLibCheck": true }, "include": [ "src", "astro.config.mjs", ".eslintrc.cjs", "**/*.js", "**/*.cjs", "**/*.mjs", "../reset.d.ts", "./tailwind.config.ts" ] } ================================================ FILE: www/vercel.json ================================================ { "headers": [ { "source": "/:all*(ttf|otf|woff|woff2)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ], "cleanUrls": true, "redirects": [ { "source": "/en", "destination": "/" }, { "source": "/docs", "destination": "/en/introduction" }, { "source": "/discord", "destination": "https://discord.gg/xHdCpcPHRE" }, { "source": "/github", "destination": "https://github.com/t3-oss/create-t3-app" }, { "source": "/showcase", "destination": "/en/t3-collection" }, { "source": "/collection", "destination": "/en/t3-collection" }, { "source": "/awesome", "destination": "/en/t3-collection" } ] }