Créer Une Application CRUD Avec Next.js Et FaunaDB

 Lors de la programmation, les développeurs rencontrent des problèmes nécessitant la réutilisation du code, ce qui entraîne une programmation répétitive qui peut faire perdre du temps et réduire la productivité. Cela donne lieu au besoin d'un code source réutilisable appelé "extraits de code". Ces extraits empêchent le code répétitif pendant la programmation, peuvent être enregistrés pour une utilisation future et sont partageables.

Dans ce didacticiel, nous allons créer un site Web pour aider les utilisateurs à enregistrer des extraits de code quotidiens à l'aide du cadre de développement Web Next.js et alimenté par la base de données Fauna pour gérer le stockage, la manipulation et l'affichage des extraits de code. En travaillant sur ce projet amusant, nous apprendrons également à créer une application CRUD de base avec Next.js et FaunaDB qui pourrait également être utilisée pour créer d'autres projets similaires.

Une version de travail de ce projet est disponible sur GitHub . Pour suivre, vous aurez besoin de Node installé sur votre machine , ainsi que d'un compte FaunaDB et d'un compte Google (pour l'authentification).

Installation des dépendances

Dans cette section, nous verrons comment installer Next.js à l'aide de la npx create-next-appcommande. Cela initialisera la Next CLI et créera une nouvelle application Next.js.

Nous installerons également les dépendances que nous utiliserons pour le back-end - FaunaDB et SWR - via la ligne de commande. SWR (state-while-revalidate) est un crochet Next.js pour récupérer des données. Nous aborderons cela en profondeur plus tard dans ce tutoriel.

Installation de Next.js

Pour installer Next.js, saisissez la commande suivante dans la CLI :

npx create-next-app snippetapp

La commande ci-dessus crée un répertoire de projet appelé snippetappavec le modèle de démarrage Next.js, qui contient les fichiers requis pour le codage avec Next. Une fois l'installation de Next terminée, accédez au répertoire nouvellement créé :

cd snippetapp

Installation de FaunaDB et SWR

Pour installer Fauna, nous utiliserons la commande suivante dans la CLI :

npm install --save faunadb

Ensuite pour installer SWR :

npm install swr@0.3.8

Avec cela, nous avons installé toutes les dépendances que nous utiliserons pour construire notre application et pouvons maintenant procéder à la configuration de notre base de données sur Fauna.

Configuration de FaunaDB

FaunaDB est une base de données en temps réel sans serveur. Il transforme une base de données traditionnelle en une API de données flexible qui conserve les capacités d'une base de données et ses performances tout en offrant un accès sécurisé et évolutif aux données des applications.

Ici, nous allons créer un compte utilisateur et configurer la base de données pour stocker les données que nous utiliserons dans notre application d'extrait de code.

Création d'un compte utilisateur

Pour créer un compte utilisateur, accédez à la page d'inscription Fauna et créez un compte.

Créer un compte FaunaDB

Après avoir créé un compte utilisateur, vous serez redirigé vers le tableau de bord.

Le tableau de bord FaunaDB

Créer la base de données et la collection d'extraits

Ici, nous allons créer une base de données avec les collections nécessaires pour gérer les extraits de code de notre application. Cliquez sur CRÉER UNE BASE DE DONNÉES . Nous allons créer une base de données appelée snippets.

Création d'une base de données d'extraits FaunaDB

Sur la nouvelle page qui s'ouvre, cliquez sur NOUVELLE COLLECTION et créez une collection appelée codesnippet.

page de collecte

Après avoir créé une collection, nous obtenons une page où nous pouvons créer un document.

Créer un document

Ici, vous cliquerez sur NOUVEAU DOCUMENT . Un document JSON s'ouvrira, dans lequel vous pourrez entrer les détails, comme illustré ci-dessous.

création de documents

{
  name: "Prompt User",
  description: "prompts the user",
  language: "javascript",
  code: "prompt('would you like to continue')"
}

Ici, nous définissons un extrait avec les attributs : name, descriptionet language. codeCliquez sur ENREGISTRER pour enregistrer la nouvelle collection. Nous avons ajouté avec succès un extrait à notre base de données. Nous pouvons maintenant procéder à l'obtention de nos identifiants d'accès à utiliser sur notre application.

Création de clé secrète

Sur le tableau de bord, cliquez sur Sécurité . Cela ouvre une nouvelle page pour créer notre clé de sécurité.

Créer une clé de sécurité FaunaDB

Ici, nous allons définir le rôle sur "serveur" au lieu de "admin", et vous pouvez donner un nom à la clé. Cliquez sur le bouton SAVE pour générer votre clé.

Création d'un .envfichier

Nous allons maintenant créer un .envfichier dans le répertoire de notre projet. Ce fichier stockera notre clé secrète générée. Dans le .envfichier nous avons ceci :

FAUNA_SECRET = paste your key here

Création d'une page d'extrait de code

Dans cette section, nous allons créer la page d'affichage et de téléchargement des extraits de code, et y ajouter des fonctionnalités.

Ouvrez le répertoire du projet dans votre éditeur de code et accédez au index.jsfichier dans votre dossier de pages. Ici, nous allons effacer le code et commencer à créer notre application :

import Head from "next/head"
import Image from "next/image"
import styles from "../styles/Home.module.css"

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>View Snippet</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>Re-usuable Code Snippets</h1>
        <p className={styles.info}>Add your code snippets here...</p>
        <button>Create new snippet</button>
      </main>
    </div>
  )
}

Création de nos composants

Nous allons maintenant créer un fichier de composant qui affichera nos extraits. Créez un dossier nommé componentdans votre répertoire de travail et créez un fichier nommé Snippets.jsà l'intérieur avec le code suivant :

import React from "react"
import styles from "../styles/Home.module.css"

function Snippets() {
  return (
    <div className={styles.cont}>
      <p className={styles.lang}>language</p>
      <h3 className={styles.name}>name of snippet</h3>
      <p className={styles.descp}>description of snippet</p>
      {/* Code will be displayed here*/}
      <div className={styles.links}>
        <a>Edit</a>
        <a>Delete</a>
      </div>
    </div>
  )
}

export default Snippets

Importation de nos composants dans l'application

Nous allons maintenant ajouter des importations pour ce fichier dans notreindex.js :

import Snippets from "../components/Snippets"

Et utilisez-le dans notre application :

<button>Create new snippet</button>
<Snippets/>

Styliser notre application

Nous pouvons maintenant styliser notre page. Accédez au Home.module.cssfichier dans le stylesdossier et remplacez les styles par ce qui suit :

.container{
  display: flex;
  height: 100%;
  min-height: 100vh;
  background: rgb(48, 48, 255);
  flex-direction: column;
  align-items: center;
  color: #fff;
  font-family: Montserrat;
}
.cont{
  color: #333;
  margin-top: 5px;
  background: rgb(214, 214, 214);
  border-radius: 15px;
  padding: 10px 15px;
}
.main button{
  width: fit-content;
  flex-grow: unset;
  display: inline-block;
  padding: 5px 10px;
  outline: none;
  border: none;
  border-radius: 5%;
  font-weight: bold;
  color: rgb(48, 48, 255);
}
.main button:hover{
  cursor: pointer;
}
.links{
  margin-top: 10px;
}
.links a{
  margin-left: 5px;
}
.links a:hover{
  cursor: pointer;
}

Affichage de notre application

À ce stade, vous devriez pouvoir démarrer le serveur de développement avec npm run dev, visiter http://localhost:3000 et voir le squelette de notre application.

Configuration de la zone d'affichage des extraits

Ensuite, nous allons créer la section d'affichage pour l'extrait de code. Créez un nouveau fichier appelé Code.jsdans le dossier des composants et importez-le dans Snippets.js:

import React from 'react'
import styles from '../styles/Home.module.css'
import Code from "./Code";

function Snippets() {
  return (
    <div className={styles.cont}>
      <p className={styles.lang}>language</p>
      <h3 className={styles.name}>name of snippet</h3>
      <p className={styles.descp}>description of snippet</p>

      {/* Code will be displayed here*/}
      <Code />

      <div className={styles.links}>
        <a>Edit</a>
        <a>Delete</a>
      </div>
    </div>
  )
}

export default Snippets

Pour la coloration syntaxique du code, nous utiliserons deux packages, à savoir react-syntax-highlighter et react-copy-to-clipboard . Nous pouvons le télécharger via la CLI :

npm install react-syntax-highlighter react-copy-to-clipboard --save

Puis dans Code.js:

import React from "react"
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"
import {atomDark} from "react-syntax-highlighter/dist/cjs/styles/prism"
import { CopyToClipboard } from "react-copy-to-clipboard"
import styles from "../styles/Home.module.css"
function Code() {
  const codeString = "npm install import react from 'react'"
  const [show, setshow] = React.useState(false)
  return (
    <div>
      <button onClick={() => setshow(!show)}>Show Code</button>
      {show ? (
        <div>
          <CopyToClipboard text={codeString}>
            <button className={styles.btn}>Copy</button>
          </CopyToClipboard>
          <SyntaxHighlighter language="javascript" style={atomDark}>
            {codeString}
          </SyntaxHighlighter>
        </div>
      ) : null}
    </div>
  )
}
export default Code

Ici, nous avons créé un composant pour afficher le code avec la coloration syntaxique. Nous avons également ajouté des fonctionnalités de copie et de basculement d'affichage. Maintenant dans le stylesfichier :

.btn{
  left: 80%;
  position: relative;
}

Test des blocs de code

Pour afficher cette modification, vous pouvez exécuter npm run devla ligne de commande et l'afficher dans votre navigateur. Nous avons la chaîne "npm install import react from 'react'" affichée avec la coloration syntaxique en tant que bloc de code. Il y a aussi un bouton pour masquer et afficher l'extrait de code, et un bouton qui nous permet de copier le code du bloc de code.

Initialisation de FaunaDB

Dans cette section, nous allons récupérer les données de notre base de données FaunaDB vers notre application. Créez un fichier appelé Fauna.jsdans votre répertoire de projet :

const faunadb = require("faunadb")
const faunaClient = new faunadb.Client({
  secret: process.env.FAUNA_SECRET
})
const q = faunadb.query
const getResponse = async () => {
  const { data } = await faunaClient.query(
    q.Map(
      q.Paginate(q.Documents(q.Collection("codesnippet"))),
      q.Lambda("doc", q.Get(q.Var("doc")))
    )
  )
  const snippets = data.map((snippet) => {
    snippet.id = snippet.ref.id
    delete snippet.ref
    return snippet
  })
  return snippets
}

module.exports = {
  getResponse,
}

Ici, nous avons initialisé FaunaDB avec notre clé secrète. Nous avons également mis en place une asyncrequête pour interroger notre collection et renvoyer les données. Nous avons stocké les données renvoyées dans une variable nommée snippetset supprimé la référence pour mieux structurer les données. D'autres fonctionnalités de création, de mise à jour et de suppression de snippets seront ajoutées ultérieurement dans ce tutoriel.

Notez que si vous obtenez une erreur non autorisée dans la console, vous devrez peut-être spécifier le nom de domaine du point de terminaison cible. La valeur par défaut est db.fauna.com, mais depuis l'introduction des groupes de régions , trois domaines cloud sont disponibles. Utilisez le domaine correct pour le groupe de régions de votre base de données :

  • Classique (États-Unis et UE) :db.fauna.com
  • États-Unis (États-Unis) :db.us.fauna.com
  • Europe (UE):db.eu.fauna.com

Exemple de code :

const faunaClient = new faunadb.Client({
  secret: process.env.FAUNA_SECRET,
  domain: "db.eu.fauna.com"
})

Traitement de nos requêtes API

Nous allons également créer un fichier pour gérer notre requête API pour notre base de données. Dans le apidossier de pages, créez un fichier appelé snippets.jsavec le code suivant :

import { getResponse } from "../../Fauna.js"
export default async function handler(req, res) {
  console.log(req)
  if (req.method !== "GET") {
    return res.status(405)
  }
  try {
    const snippets = await getResponse()
    return res.status(200).json(snippets)
  } catch (err) {
    console.log(err)
      res.status(500).json({ msg: "Something went wrong." })
  }
}

Ci-dessus, nous avons simplement mis en place une fonction pour gérer les requêtes de notre base de données. Les extraits sont renvoyés au fur et à mesure Jsonet consigneront les erreurs le cas échéant. Dans Next.js, tout fichier stocké dans le apidossier est traité comme des points de terminaison d'API plutôt que comme une page et est rendu côté serveur.

Qu'est-ce que SWR ?

Comme indiqué précédemment, SWR (state-while-revalidate) est un crochet Next.js pour récupérer des données. C'est une solution parfaite pour récupérer des données fréquemment mises à jour et convient parfaitement à notre application.

Configuration de SWR

Nous l'utiliserons pour récupérer les données de FaunaDB. Pour l'utiliser, nous devons l'importer dans index.js:

import useSWR from "swr"

export default function Home() {
  const { data:snippets, mutate }=useSWR("api/snippets")
  ...
})

Ici, nous avons importé SWR et l'avons utilisé pour récupérer les données telles que configurées dans snippets.js. Nous avons ensuite stocké ces extraits dans la snippetsvariable et les afficherons à partir de là. Nous allons maintenant passer le snippetsà notre Snippetscomposant pour afficher :

- <Snippets />

+ {snippets &&
+   snippets.map((snippet) => (
+     <Snippets
+       key={snippet.id}
+       snippet={snippet}
+       snippetDeleted={mutate}
+     />
+   ))
+ }

Ci-dessus, nous avons transmis la clé et l'extrait à Snippets. Nous avons également configuré une mutatepropriété pour mettre à jour (récupérer à nouveau) les extraits lorsqu'un extrait est supprimé. Pour utiliser les données transmises, nous modifions le Snippetscomposant avec ce qui suit :

function Snippets({snippet}) {
  return (
    <div className={styles.cont}>
      <p className={styles.lang}>{snippet.data.language}</p>
      <h3 className={styles.name}>{snippet.data.name}</h3>
      <p className={styles.descp}>{snippet.data.description}</p>

      <Code snippet={snippet}/>

      <div className={styles.links}>
        <a>Edit</a>
        <a>Delete</a>
      </div>
    </div>
  )
}

Ci-dessus, nous avons inséré le langage de l'extrait, le nom et la description reçus de FaunaDB dans notre code. Pour obtenir le code de Fauna dans notre application, nous avons également dû transmettre l'extrait de code au Codecomposant.

Puis dans le Codecomposant :

function Code({snippet}){
  ...
  <div>
    <CopyToClipboard text={snippet.data.code}>
      <button className={styles.btn}>Copy</button>
    </CopyToClipboard>
    <SyntaxHighlighter language="javascript" style={atomDark}>
      {snippet.data.code}
    </SyntaxHighlighter>
  </div>
  ...
}

Nous en avons maintenant terminé avec la GetSnippetfonctionnalité. Si nous revenons à FaunaDB et créons un nouvel extrait, nous voyons ce qui est illustré ci-dessous.

ajouté un autre document à FaunaDB

{
  "name": "console.log()",
  "language": "javascript",
  "description": "logs out data",
  "code": "console.log('Hello, world!')"'
}

Exécuter le code

Pour exécuter dans la CLI :

npm run dev

Si vous ouvrez la page dans votre navigateur, vous aurez un résultat similaire à l'image ci-dessous.

Page d'affichage d'extraits

Nous avons créé avec succès une page d'affichage d'extrait de code avec des fonctionnalités permettant d'afficher et de masquer le code et de copier l'extrait de code.

La page de téléchargement d'extraits

Nous devrons créer un lien vers la page de téléchargement à partir de notre composant d'accueil. Next.js a des dispositions qui facilitent le routage sans que vous ayez à installer react-routeret d'autres dépendances comme vous le feriez si vous utilisiez du code React natif.

Dans index.js, nous importerons le Linkmodule depuis next:

import Link from "next/link"

Ajoutez-le ensuite à notre bouton Créer un nouvel extrait :

- <button>Create new snippet</button>

+ <Link href="/upload">
+   <button>Create new snippet</button>
+ </Link>

Nous allons créer une nouvelle page dans notre pagesdossier et la nommer upload.js.

De retour dans notre Fauna.jsfichier, nous allons créer et également exporter une fonction pour créer des extraits dans notre application :

const createSnippet = async (code, language, description, name) => {
  return await faunaClient.query(q.Create(q.Collection("codesnippet"), {
    data:{code, language, description, name}
  }))
}

module.exports = {
  getResponse,
  createSnippet,
}

Ici, nous avons créé la fonction createSnippet, qui prendra certains paramètres et les transmettra en tant que données dans le nouveau document qui sera créé dans la base de données.

Ajout d'une fonction pour créer des extraits

Nous allons également configurer notre point de terminaison pour créer des extraits. Créez un nouveau fichier appelé createSnippet.jsdans le apidossier et remplissez-le avec le code suivant :

import { createSnippet } from "../../Fauna"

export default async function handler(req, res) {
  const { code, language, description, name } = req.body
  if (req.method !== "POST") {
    return res.status(405).json({msg:"unauthorized"})
  }
  try {
    const createdSnippet = await createSnippet(code, language, description, name)
    return res.status(200).json(createdSnippet)
  } catch (error) {
    console.log(error)
    res.status(500).json({msg:"unauthorized"})
  }
}

Création de notre page de téléchargement

Nous allons maintenant créer la page de téléchargement dans notre upload.jsfichier. Pour que notre formulaire crée des extraits, nous utiliserons le react-hook-form . Nous allons l'installer via la CLI :

npm install react-hook-form

Ensuite, dans notre upload.jsdossier :

import React from "react"
import { useForm } from "react-hook-form"
import { useRouter } from "next/router"
import style from "../styles/form.module.css"
import { Link } from "next/link"

function upload({ snippet }) {
  const { register, handleSubmit, errors, reset } = useForm()
  const router = useRouter()
  const createSnippet = async (data) => {
    const { code, language, description, name } = data
    console.log(data)
    try {
      // code here to push to Fauna
    } catch (error) {
      console.log(error)
    }
  }
  return (
    <div className={style.cont}>
      <form
        className={style.form}
        onSubmit={handleSubmit(snippet ? updateSnippet : createSnippet)}
      >
        <div>
          <label htmlFor="name">Name</label>
          <input
            className={style.input}
            type="text"
            id="name"
            {...register("name", { required: true })}
          />
        </div>
        <div>
          <label className={style.label} htmlFor="language">
            language
          </label>
          <select
            className={style.select}
            type="text"
            id="language"
            {...register("language", { required: true })}
          >
            <option>Javascript</option>
            <option>Html</option>
            <option>CSS</option>
          </select>
        </div>
        <div>
          <label className={style.label} htmlFor="description">
            description
          </label>
          <textarea
            className={style.input}
            rows={7}
            type="text"
            id="description"
            placeholder="snippet description"
            {...register("description", { required: true })}
          />
        </div>
        <div>
          <label className={style.label} htmlFor="code">
            Code
          </label>
          <textarea
            className={style.input}
            rows={8}
            columns={8}
            type="text"
            id="code"
            {...register("code", { required: true })}
            placeholder="background: none;"
          />
        </div>
        <div>
          <button className={style.button}>Submit</button>
          <button className={style.button}>Cancel</button>
        </div>
      </form>
    </div>
  )
}
export default upload

Styliser notre formulaire

Ci-dessus, nous avons créé notre formulaire en utilisant le react-hook-formpackage. Nous avons utilisé la handleSubmitfonction useForm()dans l'opérateur ternaire. Lors de la soumission du formulaire, il détermine si la soumission effectuée consiste à créer ou à mettre à jour un extrait de code existant. Avec register, nous avons ajouté la propriété requise à chaque champ de notre formulaire. Nous avons également ajouté des importations pour une feuille de style nommée form.module.cssoù nous avons les styles suivants pour notre formulaire :

.form {
  max-width: 800px;
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}

.cont{
  background: rgb(48, 48, 255);
  height: 100%;
  min-height: 100vh;
  padding: 10px 0 0 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.select,
.input {
  display: block;
  box-sizing: border-box;
  width: 100%;
  border-radius: 4px;
  border: 1px solid black;
  padding: 10px 15px;
  margin-bottom: 15px;
  font-size: 14px;
}

.label{
  line-height: 2;
  text-align: left;
  display: block;
  margin: 5px;
  color: white;
  font-size: 14px;
  font-weight: 200;
}

.button {
  background : #fff;
  color: #444;
  border: none;
  border-radius: 5%;
  margin-right: 8px;
}

Création d'un point de terminaison d'API pour créer des extraits

Pour envoyer les données de notre formulaire vers la base de données FaunaDB, ajoutez le code suivant au try...catchbloc dans la createSnippetfonction dans upload.js:

try {
  await fetch("/api/createSnippet", {
    method: "POST",
    body: JSON.stringify({ code, language, description, name }),
    headers: {
      "Content-type": "application/json"
    },
  })
  router.push("/")
} catch (error) {
  console.log(error)
}

Tester notre formulaire

Exécutez le code et accédez à la page de téléchargement. Maintenant, si nous ajoutons un nouvel extrait au formulaire et cliquons sur Soumettre , nous verrons ce qui est illustré ci-dessous.

Page de téléchargement d'extraits

Lorsque nous naviguons vers notre composant d'accueil, nous pouvons voir l'extrait de code créé.

Création d'un nouvel extrait

Création d'une fonction pour modifier des extraits

Pour créer notre fonctionnalité d'édition d'extrait de code, de retour dans le Fauna.jsfichier, nous allons créer et exporter une fonction pour gérer cette tâche :

const updateSnippet = async (id, code, language, description, name) => {
  return await faunaClient.query(q.Update(q.Ref(q.Collection("codesnippet"), id), {
    data: {code, language, name, description},
  }))
}
module.exports = {
  ...
  updateSnippet,
}

Créer un point de terminaison d'API pour modifier des extraits

Cette fonction est similaire à la createSnippetfonction , mais elle prend également un paramètre de id. Il utilise cet ID pour identifier les extraits à modifier. Si le idcorrespond, nous mettons à jour les données avec les autres paramètres. Nous allons également créer un fichier de point de terminaison dans le apirépertoire appelé updateSnippet.jspour gérer les mises à jour :

import { updateSnippet } from "../../Fauna"
export default async function handler(req, res) {
  const { id, code, language, description, name } = req.body
  if (req.method !== "PUT") {
    return res.status(405).json({ msg: "unauthorized" })
  }
  try {
    const updated = await updateSnippet(
      id,
      code,
      language,
      description,
      name
    )
    return res.status(200).json(updated)
  }
  catch (error) {
    console.log(error)
    res.status(500).json({ msg: "unauthorized" })
  }
}

Lier notre bouton Modifier

Maintenant, passez au Snippetscomposant et modifiez ce composant pour utiliser cette fonction. Tout d'abord, nous allons importer le Linkmodule :

...
import Link from "next/link"

Nous modifions également notre editbouton :

- <a>Edit</a>

+ <Link href={`/edit/${snippet.id}`}>
+   <a>Edit</a>
+ </Link>

Gestion de l'extrait de modification

Lorsqu'il est cliqué, il envoie une requête à la page editavec l' idextrait de code sélectionné. Dans le pagesdossier, créez un dossier nommé editavec un fichier [id].jsà l'intérieur :

import { getSnippetById } from "../../Fauna"
import Upload from "../upload"
export default function Home({ snippet }) {
  const email = ""
  const user = ""
  return (
    <div>
      <h3>Update a snippet</h3>
      <Upload snippet={snippet} email={email} user={user}/>
    </div>
  )
}

export async function getServerSideProps(context) {
  try {
    //get and update record
    const id = context.params.id
  }
  catch (error) {
    console.log(error)
    context.res.statusCode = 302
    context.res.setHeader("Location", "/")
    return {props: {}}
  }
}

Dans [id].js, nous transmettons l'extrait de code propsà la page de téléchargement d'extraits. Cependant, cette fois, la page de téléchargement contiendra les données stockées dans l'extrait de code référencé par le id. Pour récupérer l'extrait par ID, nous devrons créer la getSnippetByIdfonction dans le Fauna.jsfichier :

const getSnippetById = async (id) => {
  const snippet = await faunaClient.query(q.Get(q.Ref(q.Collection("codesnippet"),id)))
  snippet.id = snippet.ref.id
  delete snippet.ref
  return snippet
}

module.exports = {
  getResponse,
  createSnippet,
  updateSnippet,
  getSnippetById,
}

Lorsque nous exportons la fonction, de retour dans le [id].jsfichier, nous pouvons l'utiliser pour récupérer un extrait particulier avec son ID :

try {
  const id = context.params.id;
  const snippet = await getSnippetById(id);
  return {
    props: { snippet },
  };
} catch (error) {
  // as before
}

Modification des extraits stockés

Maintenant, dans le upload.jsfichier, nous allons le modifier pour pouvoir accéder aux données stockées si un extrait doit être modifié :

- const { register, handleSubmit, errors, reset } = useForm()

+ const { register, handleSubmit, errors, reset } = useForm({
+   defaultValues: {
+     code: snippet ? snippet.data.code : "",
+     language: snippet ? snippet.data.language : "",
+     description: snippet ? snippet.data.description : "",
+     name: snippet ? snippet.data.name : "",
+   }
+ })

Le code ci-dessus vérifie si l'extrait contient des données stockées. S'il renvoie true, il renvoie les données aux paramètres : code, languageet description. codeS'il renvoie false, il renvoie une chaîne vide.

Ensuite, nous allons créer une fonction pour mettre à jour l'extrait de code :

const createSnippet = async (data) => { ... }

const updateSnippet = async (data) => {
  const { code, language, description, name } = data
  const id = snippet.id
  try {
    await fetch("/api/updateSnippet", {
      method: "PUT",
      body: JSON.stringify({ code, language, description, name, id }),
      headers: {
        "Content-Type": "application/json",
      },
    })
    router.push("/")
  }
  catch (error) {
    console.log(error)
  }
}

return ( ,,, )

Test de la fonctionnalité d'édition d'extrait de code

Si nous exécutons notre code, nous pouvons modifier les extraits de code créés précédemment en cliquant sur le bouton Modifier , en modifiant les données du formulaire et en cliquant sur Soumettre .

Ajout de la fonctionnalité de suppression d'extrait de code

Maintenant, si nous revenons au Homecomposant dans notre navigateur, nous pouvons éditer et mettre à jour des extraits de code. Nous pouvons enfin ajouter la fonctionnalité finale pour supprimer notre extrait de code. Créez et exportez une nouvelle fonction — deleteSnippet— dans le Fauna.jsfichier :

const deleteSnippet = async (id) => {
  return await faunaClient.query(q.Delete(q.Ref(q.Collection("codesnippet"),id)))
}

module.exports = {
  ...
  deleteSnippet,
}

Création d'un point de terminaison d'API pour la fonctionnalité de suppression

Nous allons créer un autre point de terminaison pour cette fonction dans notre apidossier appelé deleteSnippet.jset le remplir avec le code suivant :

import { deleteSnippet } from "../../Fauna"
export default async function handler(req, res) {
  if (req.method !== "DELETE") {
    return res.status(405).json({ msg: "unauthorized" })
  }
  const { id } = req.body
  try {
    const deleted = await deleteSnippet(id)
    return res.status(200).json(deleted)
  }
  catch (error) {
    console.log(error)
    res.status(500).join({ msg: "error occured" })
  }
}

Ensuite, nous modifions le Snippets.jsfichier pour ajouter la nouvelle fonctionnalité :

function Snippets({ snippet, snippetDeleted }) {
  ...
}

Créez ensuite une deleteSnippetfonction pour récupérer le point de terminaison à partir de apiet supprimez l'extrait de code référencé par l'ID :

function Snippets({snippet, snippetDeleted}) {
  const deleteSnippet = async () => {
    try {
      await fetch("/api/deleteSnippet", {
        method: "DELETE",
        body: JSON.stringify({ id: snippet.id }),
        headers: {
          "Content-Type": "application/json",
        },
      });
      snippetDeleted();
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <div className={styles.cont}>
      <p className={styles.lang}>{snippet.data.language}</p>
      <h3 className={styles.name}>{snippet.data.name}</h3>
      <p className={styles.descp}>{snippet.data.description}</p>

      <Code snippet={snippet}/>

      <div className={styles.links}>
        <Link href={`/edit/${snippet.id}`}>
          <a>Edit</a>
        </Link>
        <a onClick={deleteSnippet}>Delete</a>
      </div>
    </div>
  )
}

Nous avons également mis à jour l'élément d'ancrage pour appeler la deleteSnippetfonction lorsqu'il est cliqué.

Test de la fonctionnalité de suppression

Nous avons ajouté une fonctionnalité pour supprimer des extraits de code. Nous pouvons maintenant supprimer des extraits en cliquant sur le bouton Supprimer dans notre application.

Ceci conclut les fonctionnalités de l'application d'extrait de code. Nous allons maintenant ajouter des mesures d'authentification à notre application pour permettre uniquement aux utilisateurs autorisés de créer ou de modifier des extraits de code dans notre application.

Authentification d'utilisateur

Pourquoi avons-nous besoin d'une authentification ? Actuellement, les utilisateurs peuvent créer des extraits, mais ils peuvent également supprimer et modifier des extraits qu'ils n'ont pas créés. Nous devrons fournir un moyen d'autoriser les utilisateurs à accéder à notre site - et donc le besoin d'authentification des utilisateurs.

Nous allons installer next-auth pour l'authentification via notre CLI :

npm i next-auth

Nous utiliserons un jeton JWT pour notre authentification. JWT est une norme utilisée pour créer des jetons d'accès pour une application.

Créez un dossier nommé authdans votre apidossier et à l'intérieur de celui-ci, créez un fichier [...nextauth].jscontenant le code suivant :

import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code",
    })
  ],
  jwt: {
    encryption: true
  },
  secret: process.env.secret,
  callbacks: {
    async jwt(token, account) {
      if (account ?.accessToken) {
        token.accessToken = account.accessToken
      }
      return token
    },
    redirect: async (url, _baseUrl)=>{
      if (url === "/profile") {
        return Promise.resolve("/")
      }
      return  Promise.resolve("/")
    }
  }
})

Après cela, nous allons encapsuler nos composants dans le _app.jsfichier :

import '../styles/globals.css'
import {Provider} from "next-auth/client"

function MyApp({ Component, pageProps }) {
  return (
    <Provider session={pageProps.session}>
      <Component {...pageProps} />
    </Provider>
  )
}
export default MyApp

Gestion de l'authentification

Nous modifierons notre Homecomposant en index.jspour revenir à notre composant si l'utilisateur est authentifié, sinon il renvoie un bouton qui mène à la page d'authentification :

import {signIn, signOut, useSession} from "next-auth/client"
...

Puis dans Home:

export default function Home() {
  const { data:snippets, mutate }=useSWR("api/snippets")

  const [session, loadingSession] = useSession()
  if (loadingSession) {
    <>
      <p>...authenticating</p>
    </>
  }
  ...
}

Le code ci-dessus vérifie si l'application est loadingSession. Si vrai, il renvoie le pbloc de balises, sinon il renvoie le reste de notre application s'il y a session. Ensuite, nous afficherons la "connexion" s'il n'y a pas de session :

return (
  <div className={styles.container}>
    <Head> ... </Head>

    {!session && (
      <>
        <h1>Sign in to access snippet app</h1>
        <button onClick={() => signIn()}>Sign In</button>
      </>
    )}

    {session && (
      <>
        <main className={styles.main}>
          <h3>welcome {session.user.email}</h3>
          <button onClick={() => signOut()}>Sign Out</button>
          ...
        </main>
      </>
    )}
  </div>
)

Pour utiliser le "service de connexion Google", nous avons besoin d'informations d'identification d'accès à partir de la console cloud de Google. Pour l'obtenir, connectez-vous à votre compte Google et accédez à la console Google Cloud . Cliquez sur CRÉER UN PROJET sur la page, entrez le nom de votre projet et cliquez sur Créer .

Sur la nouvelle page qui s'ouvre, cliquez sur + CRÉER DES IDENTIFIANTS dans la barre de menu supérieure et enfin sélectionnez ID client OAuth dans le menu déroulant.

Console Google Cloud

Sur la page qui s'ouvre, vous recevrez une notification avec un bouton vous demandant de "Configurer l'écran de consentement". Cliquez sur ce bouton.

Sur la page suivante, sélectionnez Externe sous le type d'utilisateur et cliquez sur Créer . Entrez les champs requis pour votre "Nom de l'application" et "Email" et cliquez sur Enregistrer et continuer .

Dans les sections Portées et Tester les utilisateurs , faites défiler vers le bas et cliquez sur Enregistrer et continuer .

Enfin, cliquez sur Revenir au tableau de bord et cliquez sur le bouton Publier .

Maintenant, nous pouvons créer notre clé en cliquant sur Credentials dans le menu latéral, puis sur Create Credentials dans la barre de menu supérieure. Sélectionnez Oauth Client ID dans la liste déroulante et vous obtiendrez une page demandant le type d'application.

Sélectionnez Web Application puis, sous « Authorized JavaScript origins », cliquez sur Add URI et saisissez http://localhost. Enfin, sous « Authorized redirect URIs », cliquez sur Add URI et entrez http://localhost/api/auth/callback/googledans le champ, avant de cliquer sur Create .

Création de clé Oauth

Copiez l'ID client et le secret client depuis la fenêtre contextuelle qui s'ouvre et ajoutez-les au .envfichier :

GOOGLE_CLIENT_ID=id
GOOGLE_CLIENT_SECRET=secret

Nous pouvons maintenant nous connecter en utilisant l'authentification Google à notre application. De plus, nous allons configurer notre upload.jsfichier en tant que route protégée afin que les utilisateurs non autorisés ne puissent pas créer de nouveaux extraits :

import { getSession } from "next-auth/client"

function Upload({ snippet, user }) { ... }

export async function getServerSideProps(context) {
  const session = await getSession(context)
  if (!session) {
    context.res.writeHead(302, { Location: "/" })
    context.res.end()
    return {}
  }
  return {
    props: {
      user: session.user,
    }
  }
}

export default Upload;

Test de l'authentification de l'utilisateur

Si nous exécutons notre application avec la npm run devcommande, nous aurons d'abord une page nous demandant de "se connecter". Nous ne pouvons pas accéder à la page de téléchargement via le /uploadchemin dans notre URL. Nous ne pouvons accéder à notre application que lorsque nous utilisons la fonction de connexion Google pour nous connecter à notre application.

Autoriser les utilisateurs à créer un extrait

Enfin, nous modifierons la createSnippetfonctionnalité pour ajouter l'e-mail de l'utilisateur à la base de données, puis nous n'afficherons les boutons Modifier et Supprimer que si l'e-mail correspond.

Dans Fauna.js, modifiez la createSnippetfonction comme ceci :

const createSnippet = async (code, language, description, name, mail) => {
  return await faunaClient.query(q.Create(q.Collection("codesnippet"), {
    data:{code, language, description, name, mail}
  }))
}

Dans le createSnippet.jsfichier, apportez les modifications suivantes :

- const { code, language, description, name } = req.body;
+ const { code, language, description, name, mail } = req.body;

- const createdSnippet = await createSnippet(code, language, description, name);
+ const createdSnippet = await createSnippet(code, language, description, name, mail);

Dans upload.js:

function upload({ snippet, user }) {
+  const email = user.email;
   ...
}

Et modifiez la createSnippetfonction et la updateSnippetfonction, comme suit :

const createSnippet = async (data) => {
  const { code, language, description, name, mail } = data;
  console.log(data)
  try {
    await fetch("/api/createSnippet", {
      method: "POST",
      body: JSON.stringify({ code, language, description, name, mail:email }),
      headers: {
        "Content-type": "application/json"
      },
    })
    router.push("/")
  } catch (error) {
    console.log(error)
  }
}

const updateSnippet = async (data) => {
  const { code, language, description, name } = data
  const id = snippet.id
  try {
    await fetch("/api/updateSnippet", {
      method: "PUT",
      body: JSON.stringify({ code, language, description, name, mail:email }),
      headers: {
        "Content-Type": "application/json",
      },
    })
    router.push("/")
  }
  catch (error) {
    console.log(error)
  }
}

Nous pouvons maintenant procéder à l' affichage des boutons Modifier et Supprimer uniquement si l'e-mail correspond.

Tout d'abord, nous passons les user.mailas props au Snippetcomposant in index.js:

<Snippets
  key={snippet.id}
  snippet={snippet}
  snippetDeleted={mutate}
+ email={session.user.email}
/>

Puis dans Snippet.js:

function Snippets({ snippet, snippetDeleted, email }) {
...
  {email == snippet.data.mail && (
    <>
      <div className={styles.links}>
        <Link href={`/edit/${snippet.id}`}>
          <a>Edit</a>
        </Link>
        <a onClick={deleteSnippet}>Delete</a>
      </div>
    </>
  )}
  ...
}

Tester notre application

Exécutez npm run devdans la CLI et ouvrez l'application dans votre navigateur. Désormais, si vous créez un nouvel extrait, l'e-mail de l'utilisateur est ajouté à la base de données. Si l'e-mail ne correspond pas, les boutons Modifier et Supprimer ne s'affichent pas sur la page d'affichage de l'extrait. Vous pouvez tester cela en vous connectant avec une adresse e-mail différente de celle utilisée pour créer les extraits de code.

Conclusion

Nous sommes enfin arrivés à la fin de ce tutoriel. Nous avons appris à créer une application CRUD avec Next.js et FaunaDB, et à effectuer des opérations CRUD basées sur l'authentification de l'utilisateur.

Pour consulter le code complet, visitez le dépôt GitHub

Source : https://www.sitepoint.com/nextjs-faunadb-build-code-snippet-app/

 #nextjs #faunadb #crud 

What is GEEK

Buddha Community

Créer Une Application CRUD Avec Next.js Et FaunaDB

NBB: Ad-hoc CLJS Scripting on Node.js

Nbb

Not babashka. Node.js babashka!?

Ad-hoc CLJS scripting on Node.js.

Status

Experimental. Please report issues here.

Goals and features

Nbb's main goal is to make it easy to get started with ad hoc CLJS scripting on Node.js.

Additional goals and features are:

  • Fast startup without relying on a custom version of Node.js.
  • Small artifact (current size is around 1.2MB).
  • First class macros.
  • Support building small TUI apps using Reagent.
  • Complement babashka with libraries from the Node.js ecosystem.

Requirements

Nbb requires Node.js v12 or newer.

How does this tool work?

CLJS code is evaluated through SCI, the same interpreter that powers babashka. Because SCI works with advanced compilation, the bundle size, especially when combined with other dependencies, is smaller than what you get with self-hosted CLJS. That makes startup faster. The trade-off is that execution is less performant and that only a subset of CLJS is available (e.g. no deftype, yet).

Usage

Install nbb from NPM:

$ npm install nbb -g

Omit -g for a local install.

Try out an expression:

$ nbb -e '(+ 1 2 3)'
6

And then install some other NPM libraries to use in the script. E.g.:

$ npm install csv-parse shelljs zx

Create a script which uses the NPM libraries:

(ns script
  (:require ["csv-parse/lib/sync$default" :as csv-parse]
            ["fs" :as fs]
            ["path" :as path]
            ["shelljs$default" :as sh]
            ["term-size$default" :as term-size]
            ["zx$default" :as zx]
            ["zx$fs" :as zxfs]
            [nbb.core :refer [*file*]]))

(prn (path/resolve "."))

(prn (term-size))

(println (count (str (fs/readFileSync *file*))))

(prn (sh/ls "."))

(prn (csv-parse "foo,bar"))

(prn (zxfs/existsSync *file*))

(zx/$ #js ["ls"])

Call the script:

$ nbb script.cljs
"/private/tmp/test-script"
#js {:columns 216, :rows 47}
510
#js ["node_modules" "package-lock.json" "package.json" "script.cljs"]
#js [#js ["foo" "bar"]]
true
$ ls
node_modules
package-lock.json
package.json
script.cljs

Macros

Nbb has first class support for macros: you can define them right inside your .cljs file, like you are used to from JVM Clojure. Consider the plet macro to make working with promises more palatable:

(defmacro plet
  [bindings & body]
  (let [binding-pairs (reverse (partition 2 bindings))
        body (cons 'do body)]
    (reduce (fn [body [sym expr]]
              (let [expr (list '.resolve 'js/Promise expr)]
                (list '.then expr (list 'clojure.core/fn (vector sym)
                                        body))))
            body
            binding-pairs)))

Using this macro we can look async code more like sync code. Consider this puppeteer example:

(-> (.launch puppeteer)
      (.then (fn [browser]
               (-> (.newPage browser)
                   (.then (fn [page]
                            (-> (.goto page "https://clojure.org")
                                (.then #(.screenshot page #js{:path "screenshot.png"}))
                                (.catch #(js/console.log %))
                                (.then #(.close browser)))))))))

Using plet this becomes:

(plet [browser (.launch puppeteer)
       page (.newPage browser)
       _ (.goto page "https://clojure.org")
       _ (-> (.screenshot page #js{:path "screenshot.png"})
             (.catch #(js/console.log %)))]
      (.close browser))

See the puppeteer example for the full code.

Since v0.0.36, nbb includes promesa which is a library to deal with promises. The above plet macro is similar to promesa.core/let.

Startup time

$ time nbb -e '(+ 1 2 3)'
6
nbb -e '(+ 1 2 3)'   0.17s  user 0.02s system 109% cpu 0.168 total

The baseline startup time for a script is about 170ms seconds on my laptop. When invoked via npx this adds another 300ms or so, so for faster startup, either use a globally installed nbb or use $(npm bin)/nbb script.cljs to bypass npx.

Dependencies

NPM dependencies

Nbb does not depend on any NPM dependencies. All NPM libraries loaded by a script are resolved relative to that script. When using the Reagent module, React is resolved in the same way as any other NPM library.

Classpath

To load .cljs files from local paths or dependencies, you can use the --classpath argument. The current dir is added to the classpath automatically. So if there is a file foo/bar.cljs relative to your current dir, then you can load it via (:require [foo.bar :as fb]). Note that nbb uses the same naming conventions for namespaces and directories as other Clojure tools: foo-bar in the namespace name becomes foo_bar in the directory name.

To load dependencies from the Clojure ecosystem, you can use the Clojure CLI or babashka to download them and produce a classpath:

$ classpath="$(clojure -A:nbb -Spath -Sdeps '{:aliases {:nbb {:replace-deps {com.github.seancorfield/honeysql {:git/tag "v2.0.0-rc5" :git/sha "01c3a55"}}}}}')"

and then feed it to the --classpath argument:

$ nbb --classpath "$classpath" -e "(require '[honey.sql :as sql]) (sql/format {:select :foo :from :bar :where [:= :baz 2]})"
["SELECT foo FROM bar WHERE baz = ?" 2]

Currently nbb only reads from directories, not jar files, so you are encouraged to use git libs. Support for .jar files will be added later.

Current file

The name of the file that is currently being executed is available via nbb.core/*file* or on the metadata of vars:

(ns foo
  (:require [nbb.core :refer [*file*]]))

(prn *file*) ;; "/private/tmp/foo.cljs"

(defn f [])
(prn (:file (meta #'f))) ;; "/private/tmp/foo.cljs"

Reagent

Nbb includes reagent.core which will be lazily loaded when required. You can use this together with ink to create a TUI application:

$ npm install ink

ink-demo.cljs:

(ns ink-demo
  (:require ["ink" :refer [render Text]]
            [reagent.core :as r]))

(defonce state (r/atom 0))

(doseq [n (range 1 11)]
  (js/setTimeout #(swap! state inc) (* n 500)))

(defn hello []
  [:> Text {:color "green"} "Hello, world! " @state])

(render (r/as-element [hello]))

Promesa

Working with callbacks and promises can become tedious. Since nbb v0.0.36 the promesa.core namespace is included with the let and do! macros. An example:

(ns prom
  (:require [promesa.core :as p]))

(defn sleep [ms]
  (js/Promise.
   (fn [resolve _]
     (js/setTimeout resolve ms))))

(defn do-stuff
  []
  (p/do!
   (println "Doing stuff which takes a while")
   (sleep 1000)
   1))

(p/let [a (do-stuff)
        b (inc a)
        c (do-stuff)
        d (+ b c)]
  (prn d))
$ nbb prom.cljs
Doing stuff which takes a while
Doing stuff which takes a while
3

Also see API docs.

Js-interop

Since nbb v0.0.75 applied-science/js-interop is available:

(ns example
  (:require [applied-science.js-interop :as j]))

(def o (j/lit {:a 1 :b 2 :c {:d 1}}))

(prn (j/select-keys o [:a :b])) ;; #js {:a 1, :b 2}
(prn (j/get-in o [:c :d])) ;; 1

Most of this library is supported in nbb, except the following:

  • destructuring using :syms
  • property access using .-x notation. In nbb, you must use keywords.

See the example of what is currently supported.

Examples

See the examples directory for small examples.

Also check out these projects built with nbb:

API

See API documentation.

Migrating to shadow-cljs

See this gist on how to convert an nbb script or project to shadow-cljs.

Build

Prequisites:

  • babashka >= 0.4.0
  • Clojure CLI >= 1.10.3.933
  • Node.js 16.5.0 (lower version may work, but this is the one I used to build)

To build:

  • Clone and cd into this repo
  • bb release

Run bb tasks for more project-related tasks.

Download Details:
Author: borkdude
Download Link: Download The Source Code
Official Website: https://github.com/borkdude/nbb 
License: EPL-1.0

#node #javascript

Créer Une Application CRUD Avec Next.js Et FaunaDB

 Lors de la programmation, les développeurs rencontrent des problèmes nécessitant la réutilisation du code, ce qui entraîne une programmation répétitive qui peut faire perdre du temps et réduire la productivité. Cela donne lieu au besoin d'un code source réutilisable appelé "extraits de code". Ces extraits empêchent le code répétitif pendant la programmation, peuvent être enregistrés pour une utilisation future et sont partageables.

Dans ce didacticiel, nous allons créer un site Web pour aider les utilisateurs à enregistrer des extraits de code quotidiens à l'aide du cadre de développement Web Next.js et alimenté par la base de données Fauna pour gérer le stockage, la manipulation et l'affichage des extraits de code. En travaillant sur ce projet amusant, nous apprendrons également à créer une application CRUD de base avec Next.js et FaunaDB qui pourrait également être utilisée pour créer d'autres projets similaires.

Une version de travail de ce projet est disponible sur GitHub . Pour suivre, vous aurez besoin de Node installé sur votre machine , ainsi que d'un compte FaunaDB et d'un compte Google (pour l'authentification).

Installation des dépendances

Dans cette section, nous verrons comment installer Next.js à l'aide de la npx create-next-appcommande. Cela initialisera la Next CLI et créera une nouvelle application Next.js.

Nous installerons également les dépendances que nous utiliserons pour le back-end - FaunaDB et SWR - via la ligne de commande. SWR (state-while-revalidate) est un crochet Next.js pour récupérer des données. Nous aborderons cela en profondeur plus tard dans ce tutoriel.

Installation de Next.js

Pour installer Next.js, saisissez la commande suivante dans la CLI :

npx create-next-app snippetapp

La commande ci-dessus crée un répertoire de projet appelé snippetappavec le modèle de démarrage Next.js, qui contient les fichiers requis pour le codage avec Next. Une fois l'installation de Next terminée, accédez au répertoire nouvellement créé :

cd snippetapp

Installation de FaunaDB et SWR

Pour installer Fauna, nous utiliserons la commande suivante dans la CLI :

npm install --save faunadb

Ensuite pour installer SWR :

npm install swr@0.3.8

Avec cela, nous avons installé toutes les dépendances que nous utiliserons pour construire notre application et pouvons maintenant procéder à la configuration de notre base de données sur Fauna.

Configuration de FaunaDB

FaunaDB est une base de données en temps réel sans serveur. Il transforme une base de données traditionnelle en une API de données flexible qui conserve les capacités d'une base de données et ses performances tout en offrant un accès sécurisé et évolutif aux données des applications.

Ici, nous allons créer un compte utilisateur et configurer la base de données pour stocker les données que nous utiliserons dans notre application d'extrait de code.

Création d'un compte utilisateur

Pour créer un compte utilisateur, accédez à la page d'inscription Fauna et créez un compte.

Créer un compte FaunaDB

Après avoir créé un compte utilisateur, vous serez redirigé vers le tableau de bord.

Le tableau de bord FaunaDB

Créer la base de données et la collection d'extraits

Ici, nous allons créer une base de données avec les collections nécessaires pour gérer les extraits de code de notre application. Cliquez sur CRÉER UNE BASE DE DONNÉES . Nous allons créer une base de données appelée snippets.

Création d'une base de données d'extraits FaunaDB

Sur la nouvelle page qui s'ouvre, cliquez sur NOUVELLE COLLECTION et créez une collection appelée codesnippet.

page de collecte

Après avoir créé une collection, nous obtenons une page où nous pouvons créer un document.

Créer un document

Ici, vous cliquerez sur NOUVEAU DOCUMENT . Un document JSON s'ouvrira, dans lequel vous pourrez entrer les détails, comme illustré ci-dessous.

création de documents

{
  name: "Prompt User",
  description: "prompts the user",
  language: "javascript",
  code: "prompt('would you like to continue')"
}

Ici, nous définissons un extrait avec les attributs : name, descriptionet language. codeCliquez sur ENREGISTRER pour enregistrer la nouvelle collection. Nous avons ajouté avec succès un extrait à notre base de données. Nous pouvons maintenant procéder à l'obtention de nos identifiants d'accès à utiliser sur notre application.

Création de clé secrète

Sur le tableau de bord, cliquez sur Sécurité . Cela ouvre une nouvelle page pour créer notre clé de sécurité.

Créer une clé de sécurité FaunaDB

Ici, nous allons définir le rôle sur "serveur" au lieu de "admin", et vous pouvez donner un nom à la clé. Cliquez sur le bouton SAVE pour générer votre clé.

Création d'un .envfichier

Nous allons maintenant créer un .envfichier dans le répertoire de notre projet. Ce fichier stockera notre clé secrète générée. Dans le .envfichier nous avons ceci :

FAUNA_SECRET = paste your key here

Création d'une page d'extrait de code

Dans cette section, nous allons créer la page d'affichage et de téléchargement des extraits de code, et y ajouter des fonctionnalités.

Ouvrez le répertoire du projet dans votre éditeur de code et accédez au index.jsfichier dans votre dossier de pages. Ici, nous allons effacer le code et commencer à créer notre application :

import Head from "next/head"
import Image from "next/image"
import styles from "../styles/Home.module.css"

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>View Snippet</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>Re-usuable Code Snippets</h1>
        <p className={styles.info}>Add your code snippets here...</p>
        <button>Create new snippet</button>
      </main>
    </div>
  )
}

Création de nos composants

Nous allons maintenant créer un fichier de composant qui affichera nos extraits. Créez un dossier nommé componentdans votre répertoire de travail et créez un fichier nommé Snippets.jsà l'intérieur avec le code suivant :

import React from "react"
import styles from "../styles/Home.module.css"

function Snippets() {
  return (
    <div className={styles.cont}>
      <p className={styles.lang}>language</p>
      <h3 className={styles.name}>name of snippet</h3>
      <p className={styles.descp}>description of snippet</p>
      {/* Code will be displayed here*/}
      <div className={styles.links}>
        <a>Edit</a>
        <a>Delete</a>
      </div>
    </div>
  )
}

export default Snippets

Importation de nos composants dans l'application

Nous allons maintenant ajouter des importations pour ce fichier dans notreindex.js :

import Snippets from "../components/Snippets"

Et utilisez-le dans notre application :

<button>Create new snippet</button>
<Snippets/>

Styliser notre application

Nous pouvons maintenant styliser notre page. Accédez au Home.module.cssfichier dans le stylesdossier et remplacez les styles par ce qui suit :

.container{
  display: flex;
  height: 100%;
  min-height: 100vh;
  background: rgb(48, 48, 255);
  flex-direction: column;
  align-items: center;
  color: #fff;
  font-family: Montserrat;
}
.cont{
  color: #333;
  margin-top: 5px;
  background: rgb(214, 214, 214);
  border-radius: 15px;
  padding: 10px 15px;
}
.main button{
  width: fit-content;
  flex-grow: unset;
  display: inline-block;
  padding: 5px 10px;
  outline: none;
  border: none;
  border-radius: 5%;
  font-weight: bold;
  color: rgb(48, 48, 255);
}
.main button:hover{
  cursor: pointer;
}
.links{
  margin-top: 10px;
}
.links a{
  margin-left: 5px;
}
.links a:hover{
  cursor: pointer;
}

Affichage de notre application

À ce stade, vous devriez pouvoir démarrer le serveur de développement avec npm run dev, visiter http://localhost:3000 et voir le squelette de notre application.

Configuration de la zone d'affichage des extraits

Ensuite, nous allons créer la section d'affichage pour l'extrait de code. Créez un nouveau fichier appelé Code.jsdans le dossier des composants et importez-le dans Snippets.js:

import React from 'react'
import styles from '../styles/Home.module.css'
import Code from "./Code";

function Snippets() {
  return (
    <div className={styles.cont}>
      <p className={styles.lang}>language</p>
      <h3 className={styles.name}>name of snippet</h3>
      <p className={styles.descp}>description of snippet</p>

      {/* Code will be displayed here*/}
      <Code />

      <div className={styles.links}>
        <a>Edit</a>
        <a>Delete</a>
      </div>
    </div>
  )
}

export default Snippets

Pour la coloration syntaxique du code, nous utiliserons deux packages, à savoir react-syntax-highlighter et react-copy-to-clipboard . Nous pouvons le télécharger via la CLI :

npm install react-syntax-highlighter react-copy-to-clipboard --save

Puis dans Code.js:

import React from "react"
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"
import {atomDark} from "react-syntax-highlighter/dist/cjs/styles/prism"
import { CopyToClipboard } from "react-copy-to-clipboard"
import styles from "../styles/Home.module.css"
function Code() {
  const codeString = "npm install import react from 'react'"
  const [show, setshow] = React.useState(false)
  return (
    <div>
      <button onClick={() => setshow(!show)}>Show Code</button>
      {show ? (
        <div>
          <CopyToClipboard text={codeString}>
            <button className={styles.btn}>Copy</button>
          </CopyToClipboard>
          <SyntaxHighlighter language="javascript" style={atomDark}>
            {codeString}
          </SyntaxHighlighter>
        </div>
      ) : null}
    </div>
  )
}
export default Code

Ici, nous avons créé un composant pour afficher le code avec la coloration syntaxique. Nous avons également ajouté des fonctionnalités de copie et de basculement d'affichage. Maintenant dans le stylesfichier :

.btn{
  left: 80%;
  position: relative;
}

Test des blocs de code

Pour afficher cette modification, vous pouvez exécuter npm run devla ligne de commande et l'afficher dans votre navigateur. Nous avons la chaîne "npm install import react from 'react'" affichée avec la coloration syntaxique en tant que bloc de code. Il y a aussi un bouton pour masquer et afficher l'extrait de code, et un bouton qui nous permet de copier le code du bloc de code.

Initialisation de FaunaDB

Dans cette section, nous allons récupérer les données de notre base de données FaunaDB vers notre application. Créez un fichier appelé Fauna.jsdans votre répertoire de projet :

const faunadb = require("faunadb")
const faunaClient = new faunadb.Client({
  secret: process.env.FAUNA_SECRET
})
const q = faunadb.query
const getResponse = async () => {
  const { data } = await faunaClient.query(
    q.Map(
      q.Paginate(q.Documents(q.Collection("codesnippet"))),
      q.Lambda("doc", q.Get(q.Var("doc")))
    )
  )
  const snippets = data.map((snippet) => {
    snippet.id = snippet.ref.id
    delete snippet.ref
    return snippet
  })
  return snippets
}

module.exports = {
  getResponse,
}

Ici, nous avons initialisé FaunaDB avec notre clé secrète. Nous avons également mis en place une asyncrequête pour interroger notre collection et renvoyer les données. Nous avons stocké les données renvoyées dans une variable nommée snippetset supprimé la référence pour mieux structurer les données. D'autres fonctionnalités de création, de mise à jour et de suppression de snippets seront ajoutées ultérieurement dans ce tutoriel.

Notez que si vous obtenez une erreur non autorisée dans la console, vous devrez peut-être spécifier le nom de domaine du point de terminaison cible. La valeur par défaut est db.fauna.com, mais depuis l'introduction des groupes de régions , trois domaines cloud sont disponibles. Utilisez le domaine correct pour le groupe de régions de votre base de données :

  • Classique (États-Unis et UE) :db.fauna.com
  • États-Unis (États-Unis) :db.us.fauna.com
  • Europe (UE):db.eu.fauna.com

Exemple de code :

const faunaClient = new faunadb.Client({
  secret: process.env.FAUNA_SECRET,
  domain: "db.eu.fauna.com"
})

Traitement de nos requêtes API

Nous allons également créer un fichier pour gérer notre requête API pour notre base de données. Dans le apidossier de pages, créez un fichier appelé snippets.jsavec le code suivant :

import { getResponse } from "../../Fauna.js"
export default async function handler(req, res) {
  console.log(req)
  if (req.method !== "GET") {
    return res.status(405)
  }
  try {
    const snippets = await getResponse()
    return res.status(200).json(snippets)
  } catch (err) {
    console.log(err)
      res.status(500).json({ msg: "Something went wrong." })
  }
}

Ci-dessus, nous avons simplement mis en place une fonction pour gérer les requêtes de notre base de données. Les extraits sont renvoyés au fur et à mesure Jsonet consigneront les erreurs le cas échéant. Dans Next.js, tout fichier stocké dans le apidossier est traité comme des points de terminaison d'API plutôt que comme une page et est rendu côté serveur.

Qu'est-ce que SWR ?

Comme indiqué précédemment, SWR (state-while-revalidate) est un crochet Next.js pour récupérer des données. C'est une solution parfaite pour récupérer des données fréquemment mises à jour et convient parfaitement à notre application.

Configuration de SWR

Nous l'utiliserons pour récupérer les données de FaunaDB. Pour l'utiliser, nous devons l'importer dans index.js:

import useSWR from "swr"

export default function Home() {
  const { data:snippets, mutate }=useSWR("api/snippets")
  ...
})

Ici, nous avons importé SWR et l'avons utilisé pour récupérer les données telles que configurées dans snippets.js. Nous avons ensuite stocké ces extraits dans la snippetsvariable et les afficherons à partir de là. Nous allons maintenant passer le snippetsà notre Snippetscomposant pour afficher :

- <Snippets />

+ {snippets &&
+   snippets.map((snippet) => (
+     <Snippets
+       key={snippet.id}
+       snippet={snippet}
+       snippetDeleted={mutate}
+     />
+   ))
+ }

Ci-dessus, nous avons transmis la clé et l'extrait à Snippets. Nous avons également configuré une mutatepropriété pour mettre à jour (récupérer à nouveau) les extraits lorsqu'un extrait est supprimé. Pour utiliser les données transmises, nous modifions le Snippetscomposant avec ce qui suit :

function Snippets({snippet}) {
  return (
    <div className={styles.cont}>
      <p className={styles.lang}>{snippet.data.language}</p>
      <h3 className={styles.name}>{snippet.data.name}</h3>
      <p className={styles.descp}>{snippet.data.description}</p>

      <Code snippet={snippet}/>

      <div className={styles.links}>
        <a>Edit</a>
        <a>Delete</a>
      </div>
    </div>
  )
}

Ci-dessus, nous avons inséré le langage de l'extrait, le nom et la description reçus de FaunaDB dans notre code. Pour obtenir le code de Fauna dans notre application, nous avons également dû transmettre l'extrait de code au Codecomposant.

Puis dans le Codecomposant :

function Code({snippet}){
  ...
  <div>
    <CopyToClipboard text={snippet.data.code}>
      <button className={styles.btn}>Copy</button>
    </CopyToClipboard>
    <SyntaxHighlighter language="javascript" style={atomDark}>
      {snippet.data.code}
    </SyntaxHighlighter>
  </div>
  ...
}

Nous en avons maintenant terminé avec la GetSnippetfonctionnalité. Si nous revenons à FaunaDB et créons un nouvel extrait, nous voyons ce qui est illustré ci-dessous.

ajouté un autre document à FaunaDB

{
  "name": "console.log()",
  "language": "javascript",
  "description": "logs out data",
  "code": "console.log('Hello, world!')"'
}

Exécuter le code

Pour exécuter dans la CLI :

npm run dev

Si vous ouvrez la page dans votre navigateur, vous aurez un résultat similaire à l'image ci-dessous.

Page d'affichage d'extraits

Nous avons créé avec succès une page d'affichage d'extrait de code avec des fonctionnalités permettant d'afficher et de masquer le code et de copier l'extrait de code.

La page de téléchargement d'extraits

Nous devrons créer un lien vers la page de téléchargement à partir de notre composant d'accueil. Next.js a des dispositions qui facilitent le routage sans que vous ayez à installer react-routeret d'autres dépendances comme vous le feriez si vous utilisiez du code React natif.

Dans index.js, nous importerons le Linkmodule depuis next:

import Link from "next/link"

Ajoutez-le ensuite à notre bouton Créer un nouvel extrait :

- <button>Create new snippet</button>

+ <Link href="/upload">
+   <button>Create new snippet</button>
+ </Link>

Nous allons créer une nouvelle page dans notre pagesdossier et la nommer upload.js.

De retour dans notre Fauna.jsfichier, nous allons créer et également exporter une fonction pour créer des extraits dans notre application :

const createSnippet = async (code, language, description, name) => {
  return await faunaClient.query(q.Create(q.Collection("codesnippet"), {
    data:{code, language, description, name}
  }))
}

module.exports = {
  getResponse,
  createSnippet,
}

Ici, nous avons créé la fonction createSnippet, qui prendra certains paramètres et les transmettra en tant que données dans le nouveau document qui sera créé dans la base de données.

Ajout d'une fonction pour créer des extraits

Nous allons également configurer notre point de terminaison pour créer des extraits. Créez un nouveau fichier appelé createSnippet.jsdans le apidossier et remplissez-le avec le code suivant :

import { createSnippet } from "../../Fauna"

export default async function handler(req, res) {
  const { code, language, description, name } = req.body
  if (req.method !== "POST") {
    return res.status(405).json({msg:"unauthorized"})
  }
  try {
    const createdSnippet = await createSnippet(code, language, description, name)
    return res.status(200).json(createdSnippet)
  } catch (error) {
    console.log(error)
    res.status(500).json({msg:"unauthorized"})
  }
}

Création de notre page de téléchargement

Nous allons maintenant créer la page de téléchargement dans notre upload.jsfichier. Pour que notre formulaire crée des extraits, nous utiliserons le react-hook-form . Nous allons l'installer via la CLI :

npm install react-hook-form

Ensuite, dans notre upload.jsdossier :

import React from "react"
import { useForm } from "react-hook-form"
import { useRouter } from "next/router"
import style from "../styles/form.module.css"
import { Link } from "next/link"

function upload({ snippet }) {
  const { register, handleSubmit, errors, reset } = useForm()
  const router = useRouter()
  const createSnippet = async (data) => {
    const { code, language, description, name } = data
    console.log(data)
    try {
      // code here to push to Fauna
    } catch (error) {
      console.log(error)
    }
  }
  return (
    <div className={style.cont}>
      <form
        className={style.form}
        onSubmit={handleSubmit(snippet ? updateSnippet : createSnippet)}
      >
        <div>
          <label htmlFor="name">Name</label>
          <input
            className={style.input}
            type="text"
            id="name"
            {...register("name", { required: true })}
          />
        </div>
        <div>
          <label className={style.label} htmlFor="language">
            language
          </label>
          <select
            className={style.select}
            type="text"
            id="language"
            {...register("language", { required: true })}
          >
            <option>Javascript</option>
            <option>Html</option>
            <option>CSS</option>
          </select>
        </div>
        <div>
          <label className={style.label} htmlFor="description">
            description
          </label>
          <textarea
            className={style.input}
            rows={7}
            type="text"
            id="description"
            placeholder="snippet description"
            {...register("description", { required: true })}
          />
        </div>
        <div>
          <label className={style.label} htmlFor="code">
            Code
          </label>
          <textarea
            className={style.input}
            rows={8}
            columns={8}
            type="text"
            id="code"
            {...register("code", { required: true })}
            placeholder="background: none;"
          />
        </div>
        <div>
          <button className={style.button}>Submit</button>
          <button className={style.button}>Cancel</button>
        </div>
      </form>
    </div>
  )
}
export default upload

Styliser notre formulaire

Ci-dessus, nous avons créé notre formulaire en utilisant le react-hook-formpackage. Nous avons utilisé la handleSubmitfonction useForm()dans l'opérateur ternaire. Lors de la soumission du formulaire, il détermine si la soumission effectuée consiste à créer ou à mettre à jour un extrait de code existant. Avec register, nous avons ajouté la propriété requise à chaque champ de notre formulaire. Nous avons également ajouté des importations pour une feuille de style nommée form.module.cssoù nous avons les styles suivants pour notre formulaire :

.form {
  max-width: 800px;
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}

.cont{
  background: rgb(48, 48, 255);
  height: 100%;
  min-height: 100vh;
  padding: 10px 0 0 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.select,
.input {
  display: block;
  box-sizing: border-box;
  width: 100%;
  border-radius: 4px;
  border: 1px solid black;
  padding: 10px 15px;
  margin-bottom: 15px;
  font-size: 14px;
}

.label{
  line-height: 2;
  text-align: left;
  display: block;
  margin: 5px;
  color: white;
  font-size: 14px;
  font-weight: 200;
}

.button {
  background : #fff;
  color: #444;
  border: none;
  border-radius: 5%;
  margin-right: 8px;
}

Création d'un point de terminaison d'API pour créer des extraits

Pour envoyer les données de notre formulaire vers la base de données FaunaDB, ajoutez le code suivant au try...catchbloc dans la createSnippetfonction dans upload.js:

try {
  await fetch("/api/createSnippet", {
    method: "POST",
    body: JSON.stringify({ code, language, description, name }),
    headers: {
      "Content-type": "application/json"
    },
  })
  router.push("/")
} catch (error) {
  console.log(error)
}

Tester notre formulaire

Exécutez le code et accédez à la page de téléchargement. Maintenant, si nous ajoutons un nouvel extrait au formulaire et cliquons sur Soumettre , nous verrons ce qui est illustré ci-dessous.

Page de téléchargement d'extraits

Lorsque nous naviguons vers notre composant d'accueil, nous pouvons voir l'extrait de code créé.

Création d'un nouvel extrait

Création d'une fonction pour modifier des extraits

Pour créer notre fonctionnalité d'édition d'extrait de code, de retour dans le Fauna.jsfichier, nous allons créer et exporter une fonction pour gérer cette tâche :

const updateSnippet = async (id, code, language, description, name) => {
  return await faunaClient.query(q.Update(q.Ref(q.Collection("codesnippet"), id), {
    data: {code, language, name, description},
  }))
}
module.exports = {
  ...
  updateSnippet,
}

Créer un point de terminaison d'API pour modifier des extraits

Cette fonction est similaire à la createSnippetfonction , mais elle prend également un paramètre de id. Il utilise cet ID pour identifier les extraits à modifier. Si le idcorrespond, nous mettons à jour les données avec les autres paramètres. Nous allons également créer un fichier de point de terminaison dans le apirépertoire appelé updateSnippet.jspour gérer les mises à jour :

import { updateSnippet } from "../../Fauna"
export default async function handler(req, res) {
  const { id, code, language, description, name } = req.body
  if (req.method !== "PUT") {
    return res.status(405).json({ msg: "unauthorized" })
  }
  try {
    const updated = await updateSnippet(
      id,
      code,
      language,
      description,
      name
    )
    return res.status(200).json(updated)
  }
  catch (error) {
    console.log(error)
    res.status(500).json({ msg: "unauthorized" })
  }
}

Lier notre bouton Modifier

Maintenant, passez au Snippetscomposant et modifiez ce composant pour utiliser cette fonction. Tout d'abord, nous allons importer le Linkmodule :

...
import Link from "next/link"

Nous modifions également notre editbouton :

- <a>Edit</a>

+ <Link href={`/edit/${snippet.id}`}>
+   <a>Edit</a>
+ </Link>

Gestion de l'extrait de modification

Lorsqu'il est cliqué, il envoie une requête à la page editavec l' idextrait de code sélectionné. Dans le pagesdossier, créez un dossier nommé editavec un fichier [id].jsà l'intérieur :

import { getSnippetById } from "../../Fauna"
import Upload from "../upload"
export default function Home({ snippet }) {
  const email = ""
  const user = ""
  return (
    <div>
      <h3>Update a snippet</h3>
      <Upload snippet={snippet} email={email} user={user}/>
    </div>
  )
}

export async function getServerSideProps(context) {
  try {
    //get and update record
    const id = context.params.id
  }
  catch (error) {
    console.log(error)
    context.res.statusCode = 302
    context.res.setHeader("Location", "/")
    return {props: {}}
  }
}

Dans [id].js, nous transmettons l'extrait de code propsà la page de téléchargement d'extraits. Cependant, cette fois, la page de téléchargement contiendra les données stockées dans l'extrait de code référencé par le id. Pour récupérer l'extrait par ID, nous devrons créer la getSnippetByIdfonction dans le Fauna.jsfichier :

const getSnippetById = async (id) => {
  const snippet = await faunaClient.query(q.Get(q.Ref(q.Collection("codesnippet"),id)))
  snippet.id = snippet.ref.id
  delete snippet.ref
  return snippet
}

module.exports = {
  getResponse,
  createSnippet,
  updateSnippet,
  getSnippetById,
}

Lorsque nous exportons la fonction, de retour dans le [id].jsfichier, nous pouvons l'utiliser pour récupérer un extrait particulier avec son ID :

try {
  const id = context.params.id;
  const snippet = await getSnippetById(id);
  return {
    props: { snippet },
  };
} catch (error) {
  // as before
}

Modification des extraits stockés

Maintenant, dans le upload.jsfichier, nous allons le modifier pour pouvoir accéder aux données stockées si un extrait doit être modifié :

- const { register, handleSubmit, errors, reset } = useForm()

+ const { register, handleSubmit, errors, reset } = useForm({
+   defaultValues: {
+     code: snippet ? snippet.data.code : "",
+     language: snippet ? snippet.data.language : "",
+     description: snippet ? snippet.data.description : "",
+     name: snippet ? snippet.data.name : "",
+   }
+ })

Le code ci-dessus vérifie si l'extrait contient des données stockées. S'il renvoie true, il renvoie les données aux paramètres : code, languageet description. codeS'il renvoie false, il renvoie une chaîne vide.

Ensuite, nous allons créer une fonction pour mettre à jour l'extrait de code :

const createSnippet = async (data) => { ... }

const updateSnippet = async (data) => {
  const { code, language, description, name } = data
  const id = snippet.id
  try {
    await fetch("/api/updateSnippet", {
      method: "PUT",
      body: JSON.stringify({ code, language, description, name, id }),
      headers: {
        "Content-Type": "application/json",
      },
    })
    router.push("/")
  }
  catch (error) {
    console.log(error)
  }
}

return ( ,,, )

Test de la fonctionnalité d'édition d'extrait de code

Si nous exécutons notre code, nous pouvons modifier les extraits de code créés précédemment en cliquant sur le bouton Modifier , en modifiant les données du formulaire et en cliquant sur Soumettre .

Ajout de la fonctionnalité de suppression d'extrait de code

Maintenant, si nous revenons au Homecomposant dans notre navigateur, nous pouvons éditer et mettre à jour des extraits de code. Nous pouvons enfin ajouter la fonctionnalité finale pour supprimer notre extrait de code. Créez et exportez une nouvelle fonction — deleteSnippet— dans le Fauna.jsfichier :

const deleteSnippet = async (id) => {
  return await faunaClient.query(q.Delete(q.Ref(q.Collection("codesnippet"),id)))
}

module.exports = {
  ...
  deleteSnippet,
}

Création d'un point de terminaison d'API pour la fonctionnalité de suppression

Nous allons créer un autre point de terminaison pour cette fonction dans notre apidossier appelé deleteSnippet.jset le remplir avec le code suivant :

import { deleteSnippet } from "../../Fauna"
export default async function handler(req, res) {
  if (req.method !== "DELETE") {
    return res.status(405).json({ msg: "unauthorized" })
  }
  const { id } = req.body
  try {
    const deleted = await deleteSnippet(id)
    return res.status(200).json(deleted)
  }
  catch (error) {
    console.log(error)
    res.status(500).join({ msg: "error occured" })
  }
}

Ensuite, nous modifions le Snippets.jsfichier pour ajouter la nouvelle fonctionnalité :

function Snippets({ snippet, snippetDeleted }) {
  ...
}

Créez ensuite une deleteSnippetfonction pour récupérer le point de terminaison à partir de apiet supprimez l'extrait de code référencé par l'ID :

function Snippets({snippet, snippetDeleted}) {
  const deleteSnippet = async () => {
    try {
      await fetch("/api/deleteSnippet", {
        method: "DELETE",
        body: JSON.stringify({ id: snippet.id }),
        headers: {
          "Content-Type": "application/json",
        },
      });
      snippetDeleted();
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <div className={styles.cont}>
      <p className={styles.lang}>{snippet.data.language}</p>
      <h3 className={styles.name}>{snippet.data.name}</h3>
      <p className={styles.descp}>{snippet.data.description}</p>

      <Code snippet={snippet}/>

      <div className={styles.links}>
        <Link href={`/edit/${snippet.id}`}>
          <a>Edit</a>
        </Link>
        <a onClick={deleteSnippet}>Delete</a>
      </div>
    </div>
  )
}

Nous avons également mis à jour l'élément d'ancrage pour appeler la deleteSnippetfonction lorsqu'il est cliqué.

Test de la fonctionnalité de suppression

Nous avons ajouté une fonctionnalité pour supprimer des extraits de code. Nous pouvons maintenant supprimer des extraits en cliquant sur le bouton Supprimer dans notre application.

Ceci conclut les fonctionnalités de l'application d'extrait de code. Nous allons maintenant ajouter des mesures d'authentification à notre application pour permettre uniquement aux utilisateurs autorisés de créer ou de modifier des extraits de code dans notre application.

Authentification d'utilisateur

Pourquoi avons-nous besoin d'une authentification ? Actuellement, les utilisateurs peuvent créer des extraits, mais ils peuvent également supprimer et modifier des extraits qu'ils n'ont pas créés. Nous devrons fournir un moyen d'autoriser les utilisateurs à accéder à notre site - et donc le besoin d'authentification des utilisateurs.

Nous allons installer next-auth pour l'authentification via notre CLI :

npm i next-auth

Nous utiliserons un jeton JWT pour notre authentification. JWT est une norme utilisée pour créer des jetons d'accès pour une application.

Créez un dossier nommé authdans votre apidossier et à l'intérieur de celui-ci, créez un fichier [...nextauth].jscontenant le code suivant :

import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code",
    })
  ],
  jwt: {
    encryption: true
  },
  secret: process.env.secret,
  callbacks: {
    async jwt(token, account) {
      if (account ?.accessToken) {
        token.accessToken = account.accessToken
      }
      return token
    },
    redirect: async (url, _baseUrl)=>{
      if (url === "/profile") {
        return Promise.resolve("/")
      }
      return  Promise.resolve("/")
    }
  }
})

Après cela, nous allons encapsuler nos composants dans le _app.jsfichier :

import '../styles/globals.css'
import {Provider} from "next-auth/client"

function MyApp({ Component, pageProps }) {
  return (
    <Provider session={pageProps.session}>
      <Component {...pageProps} />
    </Provider>
  )
}
export default MyApp

Gestion de l'authentification

Nous modifierons notre Homecomposant en index.jspour revenir à notre composant si l'utilisateur est authentifié, sinon il renvoie un bouton qui mène à la page d'authentification :

import {signIn, signOut, useSession} from "next-auth/client"
...

Puis dans Home:

export default function Home() {
  const { data:snippets, mutate }=useSWR("api/snippets")

  const [session, loadingSession] = useSession()
  if (loadingSession) {
    <>
      <p>...authenticating</p>
    </>
  }
  ...
}

Le code ci-dessus vérifie si l'application est loadingSession. Si vrai, il renvoie le pbloc de balises, sinon il renvoie le reste de notre application s'il y a session. Ensuite, nous afficherons la "connexion" s'il n'y a pas de session :

return (
  <div className={styles.container}>
    <Head> ... </Head>

    {!session && (
      <>
        <h1>Sign in to access snippet app</h1>
        <button onClick={() => signIn()}>Sign In</button>
      </>
    )}

    {session && (
      <>
        <main className={styles.main}>
          <h3>welcome {session.user.email}</h3>
          <button onClick={() => signOut()}>Sign Out</button>
          ...
        </main>
      </>
    )}
  </div>
)

Pour utiliser le "service de connexion Google", nous avons besoin d'informations d'identification d'accès à partir de la console cloud de Google. Pour l'obtenir, connectez-vous à votre compte Google et accédez à la console Google Cloud . Cliquez sur CRÉER UN PROJET sur la page, entrez le nom de votre projet et cliquez sur Créer .

Sur la nouvelle page qui s'ouvre, cliquez sur + CRÉER DES IDENTIFIANTS dans la barre de menu supérieure et enfin sélectionnez ID client OAuth dans le menu déroulant.

Console Google Cloud

Sur la page qui s'ouvre, vous recevrez une notification avec un bouton vous demandant de "Configurer l'écran de consentement". Cliquez sur ce bouton.

Sur la page suivante, sélectionnez Externe sous le type d'utilisateur et cliquez sur Créer . Entrez les champs requis pour votre "Nom de l'application" et "Email" et cliquez sur Enregistrer et continuer .

Dans les sections Portées et Tester les utilisateurs , faites défiler vers le bas et cliquez sur Enregistrer et continuer .

Enfin, cliquez sur Revenir au tableau de bord et cliquez sur le bouton Publier .

Maintenant, nous pouvons créer notre clé en cliquant sur Credentials dans le menu latéral, puis sur Create Credentials dans la barre de menu supérieure. Sélectionnez Oauth Client ID dans la liste déroulante et vous obtiendrez une page demandant le type d'application.

Sélectionnez Web Application puis, sous « Authorized JavaScript origins », cliquez sur Add URI et saisissez http://localhost. Enfin, sous « Authorized redirect URIs », cliquez sur Add URI et entrez http://localhost/api/auth/callback/googledans le champ, avant de cliquer sur Create .

Création de clé Oauth

Copiez l'ID client et le secret client depuis la fenêtre contextuelle qui s'ouvre et ajoutez-les au .envfichier :

GOOGLE_CLIENT_ID=id
GOOGLE_CLIENT_SECRET=secret

Nous pouvons maintenant nous connecter en utilisant l'authentification Google à notre application. De plus, nous allons configurer notre upload.jsfichier en tant que route protégée afin que les utilisateurs non autorisés ne puissent pas créer de nouveaux extraits :

import { getSession } from "next-auth/client"

function Upload({ snippet, user }) { ... }

export async function getServerSideProps(context) {
  const session = await getSession(context)
  if (!session) {
    context.res.writeHead(302, { Location: "/" })
    context.res.end()
    return {}
  }
  return {
    props: {
      user: session.user,
    }
  }
}

export default Upload;

Test de l'authentification de l'utilisateur

Si nous exécutons notre application avec la npm run devcommande, nous aurons d'abord une page nous demandant de "se connecter". Nous ne pouvons pas accéder à la page de téléchargement via le /uploadchemin dans notre URL. Nous ne pouvons accéder à notre application que lorsque nous utilisons la fonction de connexion Google pour nous connecter à notre application.

Autoriser les utilisateurs à créer un extrait

Enfin, nous modifierons la createSnippetfonctionnalité pour ajouter l'e-mail de l'utilisateur à la base de données, puis nous n'afficherons les boutons Modifier et Supprimer que si l'e-mail correspond.

Dans Fauna.js, modifiez la createSnippetfonction comme ceci :

const createSnippet = async (code, language, description, name, mail) => {
  return await faunaClient.query(q.Create(q.Collection("codesnippet"), {
    data:{code, language, description, name, mail}
  }))
}

Dans le createSnippet.jsfichier, apportez les modifications suivantes :

- const { code, language, description, name } = req.body;
+ const { code, language, description, name, mail } = req.body;

- const createdSnippet = await createSnippet(code, language, description, name);
+ const createdSnippet = await createSnippet(code, language, description, name, mail);

Dans upload.js:

function upload({ snippet, user }) {
+  const email = user.email;
   ...
}

Et modifiez la createSnippetfonction et la updateSnippetfonction, comme suit :

const createSnippet = async (data) => {
  const { code, language, description, name, mail } = data;
  console.log(data)
  try {
    await fetch("/api/createSnippet", {
      method: "POST",
      body: JSON.stringify({ code, language, description, name, mail:email }),
      headers: {
        "Content-type": "application/json"
      },
    })
    router.push("/")
  } catch (error) {
    console.log(error)
  }
}

const updateSnippet = async (data) => {
  const { code, language, description, name } = data
  const id = snippet.id
  try {
    await fetch("/api/updateSnippet", {
      method: "PUT",
      body: JSON.stringify({ code, language, description, name, mail:email }),
      headers: {
        "Content-Type": "application/json",
      },
    })
    router.push("/")
  }
  catch (error) {
    console.log(error)
  }
}

Nous pouvons maintenant procéder à l' affichage des boutons Modifier et Supprimer uniquement si l'e-mail correspond.

Tout d'abord, nous passons les user.mailas props au Snippetcomposant in index.js:

<Snippets
  key={snippet.id}
  snippet={snippet}
  snippetDeleted={mutate}
+ email={session.user.email}
/>

Puis dans Snippet.js:

function Snippets({ snippet, snippetDeleted, email }) {
...
  {email == snippet.data.mail && (
    <>
      <div className={styles.links}>
        <Link href={`/edit/${snippet.id}`}>
          <a>Edit</a>
        </Link>
        <a onClick={deleteSnippet}>Delete</a>
      </div>
    </>
  )}
  ...
}

Tester notre application

Exécutez npm run devdans la CLI et ouvrez l'application dans votre navigateur. Désormais, si vous créez un nouvel extrait, l'e-mail de l'utilisateur est ajouté à la base de données. Si l'e-mail ne correspond pas, les boutons Modifier et Supprimer ne s'affichent pas sur la page d'affichage de l'extrait. Vous pouvez tester cela en vous connectant avec une adresse e-mail différente de celle utilisée pour créer les extraits de code.

Conclusion

Nous sommes enfin arrivés à la fin de ce tutoriel. Nous avons appris à créer une application CRUD avec Next.js et FaunaDB, et à effectuer des opérations CRUD basées sur l'authentification de l'utilisateur.

Pour consulter le code complet, visitez le dépôt GitHub

Source : https://www.sitepoint.com/nextjs-faunadb-build-code-snippet-app/

 #nextjs #faunadb #crud 

Eva  Murphy

Eva Murphy

1625674200

Google analytics Setup with Next JS, React JS using Router Events - 14

In this video, we are going to implement Google Analytics to our Next JS application. Tracking page views of an application is very important.

Google analytics will allow us to track analytics information.

Frontend: https://github.com/amitavroy/video-reviews
API: https://github.com/amitavdevzone/video-review-api
App link: https://video-reviews.vercel.app

You can find me on:
Twitter: https://twitter.com/amitavroy7​
Discord: https://discord.gg/Em4nuvQk

#next js #js #react js #react #next #google analytics

Benefits of Angular JS based Applications

AngularJS was introduced in the year 2009, by Google. AngularJS is a software framework used worldwide by developers. The entire base of this framework is open source. AngularJS has gained popularity among developers because of how it has become for them to create web applications. AngularJS helps in building apps that require less work and reduces the use of unnecessary codes. AngularJS application development is a javascript framework. AngularJS has a clear goal to make the entire process simpler, it also helps app development process and operations as much as it could. AngularJS is used for building applications that support MVC (model view controller) and SPAs (single page web apps) coding and programming structures. AngularJS has been used by some of the top companies in the world to simplify their app development process, like, Google, Paypal, Udemy, mobile site in iPad for HBO, etc. To read more click on the link.

#hire angular js developer #hire dedicated angular js developer #angular js application development #hire dedicated angular js team #hire best angular js application developer

Node JS Development Company| Node JS Web Developers-SISGAIN

Top organizations and start-ups hire Node.js developers from SISGAIN for their strategic software development projects in Illinois, USA. On the off chance that you are searching for a first rate innovation to assemble a constant Node.js web application development or a module, Node.js applications are the most appropriate alternative to pick. As Leading Node.js development company, we leverage our profound information on its segments and convey solutions that bring noteworthy business results. For more information email us at hello@sisgain.com

#node.js development services #hire node.js developers #node.js web application development #node.js development company #node js application