Разработка сайтов в Новогродовке, ДНР. Создайте клон Twitter, используя TypeScript, Prisma и Next.js

 
 

Лучший способ изучить такой инструмент, как React, — создать что-то с его помощью. Next.js — это мощный фреймворк, который поможет вам создавать продукты для производства. В этом руководстве мы узнаем, как создать клон Twitter с помощью Next.js и Prisma.

Наше приложение будет иметь следующие функции:

аутентификация с использованием NextAuth и Twitter OAuth

возможность добавить новый твит

возможность просмотра списка твитов

возможность просмотра профиля пользователя только с его твитами

Код приложения, которое мы будем создавать, доступен на GitHub. Мы будем использовать TypeScript для создания нашего приложения.

Предварительные

Next.js — один из самых популярных фреймворков React.js. Он имеет множество функций, таких как рендеринг на стороне сервера, поддержка TypeScript, оптимизация изображений, поддержка I18n, маршрутизация файловой системы и многое другое.

Prisma — это ORM для Node.js и TypeScript. Он также предоставляет множество функций, таких как необработанный доступ к базе данных, бесшовный API отношений, собственные типы баз данных и так далее.

Требуется программное обеспечение

Для запуска нашего приложения нам потребуется следующее:

Докер

над уровнем моря

пряжа

идти

В приложении будут использоваться следующие технологии:

Next.js: для создания нашего приложения

Prisma: для извлечения и сохранения данных в базу данных.

Chakra UI: для добавления стилей в наше приложение

NextAuth: для обработки аутентификации

React Query: для получения и обновления данных в нашем приложении.

Создание нового приложения Next.js

Теперь давайте начнем! Сначала мы создадим новое приложение Next.js, выполнив следующую команду из нашего терминала:

yarn create next-app

Нам нужно будет ввести имя приложения, когда команда запросит его. Мы можем назвать это как угодно. Однако в данном случае я назову его twitter-clone. Мы должны увидеть аналогичный вывод на нашем терминале:

$ yarn create next-app

yarn create v1.22.5

[¼] 🔍 Resolving packages...

[½] 🚚 Fetching packages...

[¾] 🔗 Linking dependencies...

[4/4] 🔨 Building fresh packages...

success Installed «create-next-app@10.0.4» with binaries:

— create-next-app

✔ What is your project named? twitter-clone

Creating a new Next.js app in /twitter-clone.

...

Initialized a git repository.

Success! Created twitter-clone at /twitter-clone

Inside that directory, you can run several commands:

yarn dev

Starts the development server.

yarn build

Builds the app for production.

yarn start

Runs the built app in production mode.

We suggest that you begin by typing:

cd twitter-clone

yarn dev

Теперь мы можем зайти в каталог twitter-clone и запустить наше приложение, выполнив следующую команду:

cd twitter-clone && yarn dev

Наше приложение Next.js должно быть запущено на http: //localhost:3000. Мы должны увидеть следующий экран:

Приложение Next.js, работающее на локальном хосте: 3000

Добавление базы данных Dockerized PostgreSQL

Затем давайте добавим базу данных Dockerized PostgreSQL, чтобы мы могли сохранять в ней пользователей и твиты. Мы можем создать новый docker-compose.ymlфайл в корне нашего приложения со следующим содержимым:

version: «3»

services:

db:

container_name: db

image: postgres:11.3-alpine

ports:

— «5432:5432»

volumes:

— db_data: /var/lib/postgresql/data

restart: unless-stopped

volumes:

db_data:

Если Docker запущен на нашем компьютере, мы можем выполнить следующую команду из корня нашего приложения, чтобы запустить наш контейнер PostgreSQL:

docker-compose up

Приведенная выше команда запустит контейнер PostgreSQL, и к нему можно будет получить доступ на postgresql: //postgres: @localhost:5432/postgres. Обратите внимание, что вы также можете использовать локальную установку Postgres вместо Dockerized.

Добавление пользовательского интерфейса чакры

Chakra UI — это очень простая библиотека компонентов React.js. Он очень популярен и имеет такие функции, как доступность, поддержка как светлого, так и темного режима и многое другое. Мы будем использовать Chakra UI для стилизации нашего пользовательского интерфейса. Мы можем установить этот пакет, выполнив следующую команду из корня нашего приложения:

yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion

Давайте переименуем наш _app.jsфайл _app.tsxв pagesкаталог и заменим его содержимое следующим:

// pages/_app.tsx

import { ChakraProvider } from «@chakra-ui/react»;

import { AppProps } from «next/app»;

import Head from «next/head»;

import React from «react»;

const App = ({ Component, pageProps }: AppProps) => {

return (

<>

 

 

 

 

</>

) ;

};

export default App;

Поскольку мы добавили новый файл TypeScript, нам нужно перезапустить наш сервер Next.js. Как только мы перезапустим наш сервер, мы получим следующую ошибку:

$ yarn dev

yarn run v1.22.5

$ next dev

ready — started server on http: //localhost:3000

It looks like you’re trying to use TypeScript but do not have the required package (s) installed.

Please install typescript, @types/react, and @types/node by running:

yarn add —dev typescript @types/react @types/node

If you are not trying to use TypeScript, please remove the tsconfig.json file from your package root (and any TypeScript files in your pages directory).

Это связано с тем, что мы добавили новый файл TypeScript, но не добавили необходимые зависимости, необходимые для их запуска. Мы можем исправить это, установив недостающие зависимости. Из корня нашего приложения мы можем выполнить следующую команду, чтобы установить недостающие зависимости:

yarn add —dev typescript @types/react @types/node

Теперь, если мы запустим наш сервер Next.js, наше приложение должно скомпилироваться:

$ yarn dev

yarn run v1.22.5

$ next dev

ready — started server on http: //localhost:3000

We detected TypeScript in your project and created a tsconfig.json file for you.

event — compiled successfully

Добавление NextAuth

NextAuth — это библиотека аутентификации для Next.js. Он простой и понятный, гибкий и безопасный по умолчанию. Чтобы настроить NextAuth в нашем приложении, нам нужно установить его, выполнив следующую команду из корня нашего приложения:

yarn add next-auth

Далее нам нужно будет обновить наш pages/_app.tsxфайл со следующим содержимым:

// pages/_app.tsx

import { ChakraProvider } from «@chakra-ui/react»;

import { Provider as NextAuthProvider } from «next-auth/client»;

import { AppProps } from «next/app»;

import Head from «next/head»;

import React from «react»;

const App = ({ Component, pageProps }: AppProps) => {

return (

<>

 

 

 

 

 

</>

) ;

};

export default App;

Здесь мы оборачиваем наше приложение файлом NextAuthProvider. Далее нам нужно создать новый файл с именем [...nextauth].tsвнутри pages/api/authкаталога со следующим содержимым:

// pages/api/auth/[...nextauth].ts

import { NextApiRequest, NextApiResponse } from «next»;

import NextAuth from «next-auth»;

import Providers from «next-auth/providers»;

const options = {

providers: [

Providers.Twitter ({

clientId: process.env.TWITTER_KEY,

clientSecret: process.env.TWITTER_SECRET,

}),

],

};

export default NextAuth (options) ;

Приведенный выше файл будет отвечать за обработку нашей аутентификации с использованием маршрутов API Next.js. Затем мы создадим новый файл с именем.envв корне нашего приложения для хранения всех наших переменных среды со следующим содержимым:

DATABASE_URL="postgresql://postgres:@localhost:5432/postgres?synchronize=true"

NEXTAUTH_URL=http: //localhost:3000

NEXT_PUBLIC_API_URL=http: //localhost:3000

TWITTER_KEY=""

TWITTER_SECRET=""

Переменные среды Twitter будут генерироваться из Twitter API. Мы будем делать это дальше. Мы можем создать новое приложение Twitter из панели инструментов Twitter Developer.

Создайте новое приложение Twitter, введя его имя и нажав кнопку «Завершить «.

Создайте новое приложение Twitter

Скопируйте ключ API, секретный ключ API и токен носителя на следующем экране.

Учетные данные нашего приложения Twitter

Измените разрешения приложения с «Только чтение» на «Чтение и запись «на следующем экране.

Разрешения приложения Твиттера

Нажмите кнопку «Изменить «рядом с настройками аутентификации, чтобы включить 3-сторонний OAuth.

Настройки аутентификации для нашего приложения Twitter

Включите трехсторонний протокол OAuth и запросите адрес электронной почты у пользователей, а также добавьте http: //localhost:3000/api/auth/callback/twitter в качестве URL-адреса обратного вызова.

Измените настройки аутентификации в нашем приложении Twitter.

URL-адрес веб — сайта, файлы условий обслуживания и политики конфиденциальности могут быть любыми (например https: //yourwebsite.com, https://yourwebsite.com/termsи https://yourwebsite.com/privacyсоответственно).

Теперь наш трехсторонний OAuth должен быть включен.

Включите трехэтапный протокол OAuth для нашего приложения Twitter.

Вставьте значение ключа API из шага 2 в переменную среды TWITTER_KEY, а значение секретного ключа API — в переменную среды TWITTER_SECRET.

Теперь наш.envфайл должен выглядеть так:

DATABASE_URL="postgresql://postgres:@localhost:5432/postgres"

NEXTAUTH_URL=http: //localhost:3000

NEXT_PUBLIC_API_URL=http: //localhost:3000

TWITTER_KEY="1234" // Replace this with your own API key

TWITTER_SECRET="secret" // Replaces this with your own API secret key

Теперь, если мы перезапустим наш сервер Next.js и посетим http: //localhost:3000/api/auth/signin, мы сможем увидеть кнопку «Войти через Twitter «:

Войти с помощью кнопки Twitter

Если мы нажмем на эту кнопку, мы сможем авторизовать наше приложение Twitter, но не сможем войти в наше приложение. Наш терминал покажет следующую ошибку:

[next-auth][warn][jwt_auto_generated_signing_key]

https://next-auth.js.org/warnings#jwt_auto_generated_signing_key

Мы исправим эту проблему позже, когда будем добавлять и настраивать Prisma.

Добавление и настройка Prisma

Во-первых, нам нужно установить все необходимые зависимости. Мы можем сделать это, выполнив следующую команду из корня нашего приложения:

yarn add prisma @prisma/client

Затем давайте создадим новый файл с именем prisma.tsвнутри lib/clientsкаталога со следующим содержимым:

// lib/clients/prisma.ts

import { PrismaClient } from «@prisma/client»;

const prisma = new PrismaClient () ;

export default prisma;

Это PrismaClientбудет повторно использоваться в нескольких файлах. Далее нам нужно будет обновить наш pages/api/auth/[...nextauth].tsфайл со следующим содержимым:

...

import prisma from «.../... /... /lib/clients/prisma»;

import Adapters from «next-auth/adapters»;

...

const options = {

providers: [

...

],

adapter: Adapters.Prisma.Adapter ({ prisma }),

};

...

Теперь, если мы посетим http: //localhost:3000/api/auth/signin, мы получим следующую ошибку на нашем терминале:

Error: @prisma/client did not initialize yet. Please run «prisma generate» and try to import it again.

Чтобы решить эту проблему, нам нужно сделать следующее:

Запускаем npx prisma initиз корня нашего приложения:

$ npx prisma init

Environment variables loaded from.env

✔ Your Prisma schema was created at prisma/schema.prisma.

You can now open it in your favorite editor.

warn Prisma would have added DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" but it already exists in.env

Next steps:

1. Set the DATABASE_URL in the.env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started.

2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql or sqlite.

3. Run prisma introspect to turn your database schema into a Prisma data model.

4. Run prisma generate to install Prisma Client. You can then start querying your database.

More information in our documentation:

https://pris.ly/d/getting-started

Запускаем npx prisma generateиз корня нашего приложения:

$ npx prisma generate

4s

Environment variables loaded from.env

Prisma schema loaded from prisma/schema.prisma

Error:

You don’t have any models defined in your schema.prisma, so nothing will be generated.

You can define a model like this:

model User {

id Int @id @default (autoincrement ())

email String @unique

name String?

}

More information in our documentation:

https://pris.ly/d/prisma-schema

Обновите prisma/schema.prismaфайл схемой, которую ожидает NextAuth:

// prisma/schema.prisma

generator client {

provider = «prisma-client-js»

}

datasource db {

provider = «postgresql»

url = env («DATABASE_URL»)

}

model Account {

id Int @id @default (autoincrement ())

compoundId String @unique @map («compound_id»)

userId Int @map («user_id»)

providerType String @map («provider_type»)

providerId String @map («provider_id»)

providerAccountId String @map («provider_account_id»)

refreshToken String? @map («refresh_token»)

accessToken String? @map («access_token»)

accessTokenExpires DateTime? @map («access_token_expires»)

createdAt DateTime @default (now ()) @map («created_at»)

updatedAt DateTime @default (now ()) @map («updated_at»)

@@index ([providerAccountId], name: «providerAccountId»)

@@index ([providerId], name: «providerId»)

@@index ([userId], name: «userId»)

@@map («accounts»)

}

model Session {

id Int @id @default (autoincrement ())

userId Int @map («user_id»)

expires DateTime

sessionToken String @unique @map («session_token»)

accessToken String @unique @map («access_token»)

createdAt DateTime @default (now ()) @map («created_at»)

updatedAt DateTime @default (now ()) @map («updated_at»)

@@map («sessions»)

}

model User {

id Int @id @default (autoincrement ())

name String?

email String? @unique

emailVerified DateTime? @map («email_verified»)

image String?

createdAt DateTime @default (now ()) @map («created_at»)

updatedAt DateTime @default (now ()) @map («updated_at»)

tweets Tweet[]

@@map («users»)

}

model VerificationRequest {

id Int @id @default (autoincrement ())

identifier String

token String @unique

expires DateTime

createdAt DateTime @default (now ()) @map («created_at»)

updatedAt DateTime @default (now ()) @map («updated_at»)

@@map («verification_requests»)

}

Добавьте схему для Tweet в prisma/schema.prismaфайл:

// prisma/schema.prisma

...

model Tweet {

id Int @id @default (autoincrement ())

body String

userId Int

createdAt DateTime @default (now ()) @map («created_at»)

updatedAt DateTime @default (now ()) @map («updated_at»)

author User @relation (fields: [userId], references: [id])

@@map («tweets»)

}

Запустите npx prisma migrate dev —preview-featureиз корня нашего приложения, чтобы создать новую миграцию. Введите имя миграции (например, init-database) при появлении запроса.

Теперь, если мы посетим http: //localhost:3000/api/auth/signin и нажмем кнопку Sign in with Twitter, мы войдем в наше приложение с помощью Twitter.

Добавление некоторых исходных данных

Чтобы пользовательский интерфейс не был полностью голым, пока мы работаем над приложением, давайте добавим некоторые начальные данные.

Начнем с установки пары зависимостей:

yarn add -D faker ts-node

Это задействует faker.js, который поможет нам генерировать поддельные данные, а также его зависимость от ts-node.

Затем создайте новый seed.tsфайл в prismaпапке и добавьте следующее содержимое:

import faker from «faker»;

import prisma from «.../lib/clients/prisma»;

async function main () {

const listOfNewUsers = [...new Array (5) ].map (() => {

return {

email: faker.internet.email (),

name: faker.name.findName (),

image: faker.image.image (),

tweets: {

create: {

body: faker.lorem.sentence (),

},

},

};

}) ;

for (let data of listOfNewUsers) {

const user = await prisma.user.create ({

data,

}) ;

console.log (user) ;

}

}

main ()

.catch ((e) => {

console.error (e) ;

process.exit (1) ;

})

.finally (async () => {

await prisma. $disconnect () ;

}) ;

Нам также потребуется обновить наш tsconfig.jsonфайл, как показано ниже:

{

«compilerOptions»: {

«target»: «es5»,

«lib»: [

«dom»,

«dom.iterable»,

«esnext»

],

«allowJs»: true,

«skipLibCheck»: true,

«strict»: false,

«forceConsistentCasingInFileNames»: true,

«noEmit»: true,

«esModuleInterop»: true,

«module»: «commonjs»,

«moduleResolution»: «node»,

«resolveJsonModule»: true,

«isolatedModules»: true,

«jsx»: «preserve»,

«baseUrl»: «.»,

«paths»: {

«*»: [

«/*»

],

«components/*»: [

«components/*»

],

«pages/*»: [

«pages/*»

],

«types/*»: [

«types/*»

],

«lib/*»: [

«lib/*»

],

},

},

«include»: [

«next-env.d.ts»,

«**/*.ts»,

«**/*.tsx»

],

«exclude»: [

«node_modules»

]

}

Наконец, мы можем запустить npx prisma db seed —preview-feature, чтобы заполнить нашу базу данных некоторыми тестовыми данными.

Добавление реагирующего запроса

React Query — очень популярный и эффективный способ получения данных в приложениях React.js. Давайте добавим React Query в наше приложение. Мы можем установить React Query, выполнив следующую команду из корня нашего приложения:

yarn add react-query

Затем давайте создадим новый файл с именем react-query.tsвнутри lib/clientsкаталога со следующим содержимым:

// lib/clients/react-query.ts

import { QueryClient } from «react-query»;

const queryClient = new QueryClient () ;

export default queryClient;

Нам также нужно обновить наш pages/_app.tsxфайл со следующим содержимым:

// pages/_app.tsx

...

import { QueryClientProvider } from «react-query»;

import { Hydrate } from «react-query/hydration»;

import queryClient from «.../lib/clients/react-query»;

const App = ({ Component, pageProps }: AppProps) => {

return (

 

 

 

 

 

 

 

) ;

};

export default App;

Здесь мы оборачиваем наше приложение с помощью QueryClientProvider, который предоставит QueryClientнашему приложению a.

Возможность просмотра списка твитов

Давайте создадим новый файл fetch-tweets.tsвнутри lib/queriesкаталога со следующим содержимым:

// lib/queries/fetch-tweets.ts

const fetchTweets = async () => {

const res = await fetch (`${process.env.NEXT_PUBLIC_API_URL}/api/tweets`) ;

const data = await res.json () ;

return data;

};

export default fetchTweets;

Эта функция будет отвечать за получение всех твитов в нашем приложении. Затем создайте новый файл tweets.tsxвнутри pagesкаталога со следующим содержимым:

// pages/tweets.tsx

import fetchTweets from «.../lib/queries/fetch-tweets»;

import queryClient from «.../lib/clients/react-query»;

import { GetServerSideProps, InferGetServerSidePropsType } from «next»;

import { useSession } from «next-auth/client»;

import Head from «next/head»;

import React from «react»;

import { useQuery } from «react-query»;

import { dehydrate } from «react-query/hydration»;

const TweetsPage: InferGetServerSidePropsType<

typeof getServerSideProps

> = ({}) => {

const { data } = useQuery («tweets», fetchTweets) ;

const [session] = useSession () ;

if (! session) {

return

Not authenticated.

;

 

}

return (

<>

 

 

 

{console.log (JSON.stringify (data, null, 2))}

</>

) ;

};

export const getServerSideProps: GetServerSideProps = async ({ req }) => {

await queryClient.prefetchQuery («tweets», fetchTweets) ;

return {

props: {

dehydratedState: dehydrate (queryClient),

},

};

};

export default TweetsPage;

getServerSideProps — это функция Next.js, которая помогает получать данные с сервера. Давайте также создадим новый файл с именем index.tsвнутри pages/api/tweetsкаталога со следующим содержимым:

// pages/api/tweets/index.ts

import prisma from «.../... /... /lib/clients/prisma»;

import type { NextApiRequest, NextApiResponse } from «next»;

export default async (req: NextApiRequest, res: NextApiResponse) => {

if (req.method === «POST») {

try {

const { body } = req;

const tweet = await prisma.tweet.create ({ data: JSON.parse (body) }) ;

return res.status (200).json (tweet) ;

} catch (error) {

return res.status (422).json (error) ;

}

} else if (req.method === «GET») {

try {

const tweets = await prisma.tweet.findMany ({

include: {

author: true,

},

orderBy: [

{

createdAt: «desc»,

},

],

}) ;

return res.status (200).json (tweets) ;

} catch (error) {

return res.status (422).json (error) ;

}

}

res.end () ;

};

Здесь мы проверяем запрос. Если это POSTзапрос, мы создаем новый твит. Если это GETзапрос, мы отправляем все твиты с данными автора. Теперь, если мы посетим http: //localhost:3000/tweets, мы увидим все твиты в консоли нашего браузера.

Список твитов из конечной точки API

Обратите внимание, что, поскольку faker.js генерирует случайные данные, то, что вы видите в консоли браузера, будет отличаться от скриншота. Мы добавим возможность добавить твит позже.

Далее давайте создадим пользовательский интерфейс для отображения списка твитов. Мы можем создать новый файл с именем index.tsxвнутри components/pages/tweetsкаталога со следующим содержимым:

// components/pages/tweets/index.tsx

import { Box, Grid, Stack } from «@chakra-ui/react»;

import Tweet from «. /tweet»;

import React from «react»;

import ITweet from «types/tweet»;

const TweetsPageComponent = ({ tweets }) => {

return (

{tweets?.map ((tweet: ITweet) => {

return (

={tweet}>

 

) ;

}) }

 

 

) ;

};

export default TweetsPageComponent;

Давайте также создадим новый файл с именем tweet.tsxв том же каталоге (components/pages/tweets) со следующим содержимым:

// components/pages/tweets/tweet.tsx

import { Avatar, Box, Stack, Text } from «@chakra-ui/react»;

import React, { FC } from «react»;

const Tweet: FC = ({ tweet }) => {

const authorNode = () => {

return (

<Stack

spacing={4}

isInline

alignItems="center"

p={4}

borderBottomWidth={1}

>

{tweet.author.name}

 

 

) ;

};

const bodyNode = () => {

return (

{tweet.body}

 

) ;

};

return (

{authorNode () }

{bodyNode () }

 

 

) ;

};

export default Tweet;

Далее давайте обновим наш pages/tweets.tsxфайл следующим содержимым:

// pages/tweets.tsx

...

import Page from «.../components/pages/tweets»;

...

const TweetsPage: InferGetServerSidePropsType<

typeof getServerSideProps

> = ({}) => {

...

return (

<>

 

 

 

</>

) ;

...

}

...

Здесь мы изменили интерфейс нашего приложения. Теперь, если мы посетим http: //localhost:3000/tweets, мы сможем увидеть следующее:

Список твитов

Возможность добавить новый твит

Давайте добавим текстовую область, через которую мы можем добавить новый твит. Для этого давайте создадим новый файл с именем add-new-tweet-form.tsxвнутри components/pages/tweetsкаталога со следующим содержимым:

// components/pages/tweets/add-new-tweet-form.tsx

import {

Box,

Button,

FormControl,

FormLabel,

Stack,

Textarea,

} from «@chakra-ui/react»;

import saveTweet from «.../... /... /lib/mutations/save-tweet»;

import fetchTweets from «.../... /... /lib/queries/fetch-tweets»;

import queryClient from «.../... /... /lib/clients/react-query»;

import { useSession } from «next-auth/client»;

import React, { ChangeEvent, useState } from «react»;

import { useMutation, useQuery } from «react-query»;

const AddNewTweetForm = () => {

const [body, setBody] = useState («») ;

const [session] = useSession () ;

const { refetch } = useQuery («tweets», fetchTweets) ;

const mutation = useMutation (saveTweet, {

onSuccess: async () => {

await queryClient.invalidateQueries («tweets») ;

refetch () ;

},

}) ;

if (! session) {

return

Not authenticated.

;

 

}

const handleSubmit = () => {

const data = {

body,

author: {

connect: { email: session.user.email },

},

};

mutation.mutate (data) ;

if (! mutation.error) {

setBody («») ;

}

};

return (

What’s on your mind?

<Textarea

id="body"

value={body}

onChange={ (e: ChangeEvent) =>

setBody (e.currentTarget.value)

}

/>

 

<Button

loadingText="Posting..."

onClick={handleSubmit}

isDisabled={! body.trim () }

>

Post

 

 

 

 

 

) ;

};

export default AddNewTweetForm;

Функция мутации отвечает за выполнение POSTзапроса к серверу. Он также повторно извлекает данные после успешного выполнения запроса. Кроме того, давайте создадим новый файл с именем save-tweet.tsвнутри lib/mutationsкаталога со следующим содержимым:

// lib/mutations/save-tweet.ts

const saveTweet = async (body: any) => {

const res = await fetch (`${process.env.NEXT_PUBLIC_API_URL}/api/tweets`, {

method: «POST»,

body: JSON.stringify (body),

}) ;

const data = await res.json () ;

return data;

};

export default saveTweet;

Нам также нужно изменить наш components/pages/tweets/index.tsxфайл со следующим содержимым:

// components/pages/tweets/index.tsx

...

import AddNewTweetForm from «. /add-new-tweet-form»;

...

const TweetsPageComponent = ({ tweets }) => {

return (

 

...

 

) ;

};

export default TweetsPageComponent;

Теперь мы должны иметь возможность просматривать текстовое поле, если мы посетим http: //localhost:3000/tweets:

Textarea для добавления новых твитов

Мы также должны иметь возможность добавить новый твит, используя текстовое поле (это не будет твитом в вашей реальной учетной записи!):

Добавить новый твит

Далее мы добавим возможность просмотра профиля пользователя, который показывает только твиты, опубликованные этим пользователем.

Возможность просмотра профиля пользователя только с его твитами

Сначала мы создадим страницу, на которой будет отображаться список всех пользователей. Для этого нам нужно создать новый файл с именем index.tsxвнутри pages/usersкаталога со следующим содержимым:

// pages/users/index.tsx

import { GetServerSideProps, InferGetServerSidePropsType } from «next»;

import { useSession } from «next-auth/client»;

import Head from «next/head»;

import React from «react»;

import { useQuery } from «react-query»;

import { dehydrate } from «react-query/hydration»;

import Page from «.../... /components/pages/users»;

import queryClient from «.../... /lib/clients/react-query»;

import fetchUsers from «.../... /lib/queries/fetch-users»;

const MyAccountPage: InferGetServerSidePropsType<

typeof getServerSideProps

> = ({}) => {

const { data } = useQuery («users», fetchUsers) ;

const [session] = useSession () ;

if (! session) {

return

Not authenticated.

;

 

}

return (

<>

 

 

 

</>

) ;

};

export const getServerSideProps: GetServerSideProps = async ({ req }) => {

await queryClient.prefetchQuery («users», fetchUsers) ;

return {

props: {

dehydratedState: dehydrate (queryClient),

},

};

};

export default MyAccountPage;

Нам также нужно создать новый файл с именем fetch-users.tsвнутри lib/queriesкаталога со следующим содержимым:

// lib/queries/fetch-users.ts

const fetchUsers = async () => {

const res = await fetch (`${process.env.NEXT_PUBLIC_API_URL}/api/users`) ;

const data = await res.json () ;

return data;

};

export default fetchUsers;

Эта функция будет отвечать за получение всех пользователей из конечной точки API. Нам также нужно создать новый файл с именем index.tsxвнутри components/pages/usersкаталога со следующим содержимым:

// components/pages/users/index.tsx

import { Box, Grid, Stack } from «@chakra-ui/react»;

import React from «react»;

import User from «. /user»;

const UsersPageComponent = ({ users }) => {

return (

{users?.map ((user) => {

return (

={user}>

 

) ;

}) }

 

 

) ;

};

export default UsersPageComponent;

Далее давайте создадим файл с именем user.tsxвнутри того же каталога (components/pages/users) со следующим содержимым:

// components/pages/users/user.tsx

import { Avatar, Box, Stack, Text, Button } from «@chakra-ui/react»;

import Link from «next/link»;

import React, { FC } from «react»;

const User: FC = ({ user }) => {

const authorNode = () => {

return (

<Stack

spacing={4}

isInline

alignItems="center"

p={4}

borderBottomWidth={1}

>

{user.name}

 

 

) ;

};

const bodyNode = () => {

return (

{user.email}

 

) ;

};

const buttonNode = () => {

return (

<Link href={`/users/${user.id}`}>

 

 

) ;

};

return (

{authorNode () }

{bodyNode () }

{buttonNode () }

 

 

) ;

};

export default User;

И еще один файл с именем index.tsвнутри pages/api/usersкаталога со следующим содержимым:

// pages/api/users/index.ts

import prisma from «.../... /... /lib/clients/prisma»;

import type { NextApiRequest, NextApiResponse } from «next»;

export default async (req: NextApiRequest, res: NextApiResponse) => {

if (req.method === «GET») {

try {

const users = await prisma.user.findMany ({

orderBy: [

{

createdAt: «desc»,

},

],

}) ;

return res.status (200).json (users) ;

} catch (error) {

return res.status (422).json (error) ;

}

}

res.end () ;

};

Вышеупомянутая функция отвечает за отправку сведений обо всех пользователях. Теперь, если мы посетим http: //localhost:3000/users, мы сможем увидеть список пользователей:

Список пользователей

Теперь давайте создадим страницу для отображения сведений об одном пользователе. Для этого нам нужно создать новый файл с именем [id].tsxвнутри pages/usersкаталога со следующим содержимым:

// pages/users/[id].tsx

import Page from «.../... /components/pages/users/[id]»;

import queryClient from «.../... /lib/clients/react-query»;

import fetchUser from «.../... /lib/queries/fetch-user»;

import { GetServerSideProps, InferGetServerSidePropsType } from «next»;

import { getSession, useSession } from «next-auth/client»;

import Head from «next/head»;

import React from «react»;

import { useQuery } from «react-query»;

import { dehydrate } from «react-query/hydration»;

const MyAccountPage: InferGetServerSidePropsType = ({

id,

}) => {

const { data } = useQuery («user», () => fetchUser (parseInt (id as string)));

const [session] = useSession () ;

if (! session) {

return

Not authenticated.

;

 

}

return (

<>

 

 

 

</>

) ;

};

export const getServerSideProps: GetServerSideProps = async ({ query }) => {

await queryClient.prefetchQuery («user», () =>

fetchUser (parseInt (query.id as string))

) ;

return {

props: {

dehydratedState: dehydrate (queryClient),

id: query.id,

},

};

};

export default MyAccountPage;

Значение query.idопределяет idтекущий пользователь. Нам также нужно создать новый файл с именем fetch-user.tsвнутри lib/queriesкаталога со следующим содержимым:

// lib/queries/fetch-user.ts

const fetchUser = async (userId: number) => {

const res = await fetch (

`${process.env.NEXT_PUBLIC_API_URL}/api/users/${userId}`

) ;

const data = await res.json () ;

return data;

};

export default fetchUser;

Вышеупомянутая функция будет отвечать за выполнение GETзапроса к конечной точке API. Далее нам нужно создать новый файл с именем index.tsxвнутри components/pages/users/[id]каталога со следующим содержимым:

// components/pages/users/[id]/index.tsx

import { Avatar, Box, Grid, Stack, Text } from «@chakra-ui/react»;

import Tweet from «. /tweet»;

import React, { FC } from «react»;

const UsersPageComponent: FC = ({ user }) => {

const authorNode = () => {

return (

{user?.name}

 

 

 

) ;

};

return (

{authorNode () }

{user?.tweets.map ((tweet) => {

return (

={tweet}>

 

) ;

}) }

 

 

) ;

};

export default UsersPageComponent;

Далее нам нужно создать еще один файл с именем tweet.tsxв том же каталоге (components/pages/users/[id]) со следующим содержимым:

// components/pages/users/[id]/tweet.tsx

import { Box, Stack, Text } from «@chakra-ui/react»;

import React, { FC } from «react»;

const Tweet: FC = ({ tweet }) => {

const bodyNode = () => {

return (

{tweet.body}

 

) ;

};

return (

{bodyNode () }

 

) ;

};

export default Tweet;

Наконец, нам нужно создать еще один файл с именем [id].tsвнутри pages/api/usersкаталога со следующим содержимым:

// pages/api/users/[id].ts

import prisma from «.../... /... /lib/clients/prisma»;

import type { NextApiRequest, NextApiResponse } from «next»;

export default async (req: NextApiRequest, res: NextApiResponse) => {

if (req.method === «GET») {

const userId = parseInt (req.query.id as string) ;

try {

const tweets = await prisma.user.findUnique ({

include: {

tweets: true,

},

where: {

id: userId,

},

}) ;

return res.status (200).json (tweets) ;

} catch (error) {

console.log (error) ;

return res.status (422).json (error) ;

}

}

res.end () ;

};

Вышеупомянутая функция будет отвечать за отправку сведений о пользователе, idкоторый совпадает с req.query.id. Мы конвертируем его в число, так как Prisma требует, чтобы оно было числовым. Теперь, если мы посетим http: //localhost:3000/users и нажмем кнопку View profile для пользователя, мы сможем увидеть список твитов, опубликованных этим пользователем.

Профиль пользователя со всеми твитами, опубликованными этим пользователем

Вывод

В этом руководстве мы узнали, как мы можем использовать Next.js и Prisma вместе для создания клона Twitter. Очевидно, что Twitter состоит из множества других функций, таких как ретвиты, комментарии и функции обмена для каждого твита. Тем не менее, это руководство должно предоставить основу для создания таких функций.

3D-печать5GABC-анализAndroidAppleAppStoreAsusCall-центрChatGPTCRMDellDNSDrupalExcelFacebookFMCGGoogleHuaweiInstagramiPhoneLinkedInLinuxMagentoMicrosoftNvidiaOpenCartPlayStationPOS материалPPC-специалистRuTubeSamsungSEO-услугиSMMSnapchatSonyStarlinkTikTokTwitterUbuntuUp-saleViasatVPNWhatsAppWindowsWordPressXiaomiYouTubeZoomАвдеевкаАктивные продажиАкцияАлександровск ЛНРАлмазнаяАлчевскАмвросиевкаАнализ конкурентовАнализ продажАнтимерчандайзингАнтрацитАртемовскАртемовск ЛНРАссортиментная политикаБелгородБелицкоеБелозерскоеБердянскБизнес-идеи (стартапы)БрендБрянкаБукингВахрушевоВендорВидеоВикипедияВирусная рекламаВирусный маркетингВладивостокВнутренние продажиВнутренний маркетингВолгоградВолновахаВоронежГорловкаГорнякГорскоеДебальцевоДебиторкаДебиторская задолженностьДезинтермедитацияДзержинскДивизионная система управленияДизайнДимитровДирект-маркетингДисконтДистрибьюторДистрибьюцияДобропольеДокучаевскДоменДружковкаЕкатеринбургЕнакиевоЖдановкаЗапорожьеЗимогорьеЗолотоеЗоринскЗугрэсИжевскИловайскИрминоКазаньКалининградКировскКировскоеКомсомольскоеКонстантиновкаКонтент-маркетингКонтент-планКопирайтингКраматорскКрасноармейскКрасногоровкаКраснодарКраснодонКраснопартизанскКрасный ЛиманКрасный ЛучКременнаяКураховоКурскЛисичанскЛуганскЛутугиноМакеевкаМариупольМаркетингМаркетинговая информацияМаркетинговые исследованияМаркетинговый каналМаркетинг услугМаркетологМарьинкаМедиаМелекиноМелитопольМенеджментМерчандайзерМерчандайзингМиусинскМолодогвардейскМоскваМоспиноНижний НовгородНиколаевНиколаевкаНишевой маркетингНовоазовскНовогродовкаНоводружескНовосибирскНумерическая дистрибьюцияОдессаОмскОтдел маркетингаПартизанский маркетингПервомайскПеревальскПетровскоеПлата за кликПоисковая оптимизацияПопаснаяПравило ПаретоПривольеПрогнозирование продажПродвижение сайтов в ДонецкеПроизводство видеоПромоПромоушнПрямой маркетингРабота для маркетологаРабота для студентаРазработка приложенийРаспродажаРегиональные продажиРекламаРеклама на асфальтеРемаркетингРетро-бонусРибейтРитейлРовенькиРодинскоеРостов-на-ДонуРубежноеСамараСанкт-ПетербургСаратовСватовоСвердловскСветлодарскСвятогорскСевастопольСеверодонецкСеверскСедовоСейлз промоушнСелидовоСимферопольСинергияСколковоСлавянскСнежноеСоздание сайтов в ДонецкеСоледарСоциальные сетиСочиСтаробельскСтаробешевоСтахановСтимулирование сбытаСуходольскСчастьеТелемаркетингТельмановоТираспольТорговый представительТорезТрейд маркетингТрейд промоушнТюменьУглегорскУгледарУкраинскХабаровскХарцызскХерсонХостингЦелевая аудиторияЦифровой маркетингЧасов ЯрЧелябинскШахтерскЮжно-СахалинскЮнокоммунаровскЯндексЯсиноватая