Salta ai contenuti

Firebase & Astro

Firebase è una piattaforma di sviluppo di app che fornisce un database NoSQL, autenticazione, sottoscrizioni in tempo reale, funzioni e archiviazione.

Consulta la nostra guida separata per il deployment su Firebase hosting (EN).

  • Un progetto Firebase con un’app web già configurata.
  • Un progetto Astro con server-side rendering (SSR) (EN) abilitato.
  • Credenziali Firebase: Avrai bisogno di due set di credenziali per collegare Astro a Firebase:
    • Credenziali dell’app web: Queste credenziali saranno utilizzate dal client della tua app. Puoi trovarle nella console di Firebase in Impostazioni del progetto > Generali. Scorri verso il basso fino alla sezione Le tue app e clicca sull’icona App web.
    • Credenziali del progetto: Queste credenziali saranno utilizzate dal server della tua app. Puoi generarle nella console di Firebase in Impostazioni del progetto > Account di servizio > Firebase Admin SDK > Genera nuova chiave privata.

Aggiunta delle credenziali di Firebase

Sezione intitolata Aggiunta delle credenziali di Firebase

Per aggiungere le credenziali di Firebase nel tuo progetto con Astro, crea un file .env nella cartella principale del tuo progetto con le seguenti variabili:

.env
FIREBASE_PRIVATE_KEY_ID=TUO_ID_PRIVATO
FIREBASE_PRIVATE_KEY=TUO_TOKEN_DI_ACCESSO
FIREBASE_PROJECT_ID=TUO_ID_PROGETTO
FIREBASE_CLIENT_EMAIL=EMAIL_DEL_TUO_CLIENT
FIREBASE_CLIENT_ID=ID_DEL_TUO_CLIENT
FIREBASE_AUTH_URI=URI_DI_AUTENTICAZIONE
FIREBASE_TOKEN_URI=URI_DEL_TOKEN
FIREBASE_AUTH_CERT_URL=URL_DEL_CERTIFICATO_DI_AUTENTICAZIONE
FIREBASE_CLIENT_CERT_URL=URL_DEL_CERTIFICATO_DEL_CLIENT

Ora queste variabili d’ambiente sono pronte per essere utilizzate nel tuo progetto.

Se desideri avere IntelliSense per le tue variabili d’ambiente di Firebase, modifica o crea il file env.d.ts nella cartella src/ del tuo progetto e configura i tipi:

src/env.d.ts
interface ImportMetaEnv {
readonly FIREBASE_PRIVATE_KEY_ID: string;
readonly FIREBASE_PRIVATE_KEY: string;
readonly FIREBASE_PROJECT_ID: string;
readonly FIREBASE_CLIENT_EMAIL: string;
readonly FIREBASE_CLIENT_ID: string;
readonly FIREBASE_AUTH_URI: string;
readonly FIREBASE_TOKEN_URI: string;
readonly FIREBASE_AUTH_CERT_URL: string
readonly FIREBASE_CLIENT_CERT_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

Il tuo progetto dovrebbe ora includere questi nuovi file:

  • Cartellasrc/
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Per collegare Astro a Firebase, installa i seguenti pacchetti utilizzando il comando corretto qui sotto in base al tuo gestore di pacchetti preferito:

  • firebase - Firebase SDK per il lato client
  • firebase-admin - Firebase Admin SDK per il lato server
Terminal window
npm install firebase firebase-admin

Successivamente, crea una cartella chiamata firebase nella cartella src/ e aggiungi due nuovi file: client.ts e server.ts.

Nel client.ts, aggiungi il seguente codice per inizializzare Firebase nel client utilizzando le credenziali dell’app web e il pacchetto firebase:

src/firebase/client.ts
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: "tua-api-key-pubblica",
authDomain: "tuo-dominio-di-autenticazione",
projectId: "tuo-id-progetto",
storageBucket: "tuo-bucket-di-archiviazione",
messagingSenderId: "tuo-id-mittente",
appId: "tuo-id-app",
};
export const app = initializeApp(firebaseConfig);

Nel server.ts, aggiungi il seguente codice per inizializzare Firebase nel server, utilizzando le credenziali del progetto e il pacchetto firebase-admin:

src/firebase/server.ts
import type { ServiceAccount } from "firebase-admin";
import { initializeApp, cert } from "firebase-admin/app";
const serviceAccount = {
type: "service_account",
project_id: import.meta.env.FIREBASE_PROJECT_ID,
private_key_id: import.meta.env.FIREBASE_PRIVATE_KEY_ID,
private_key: import.meta.env.FIREBASE_PRIVATE_KEY,
client_email: import.meta.env.FIREBASE_CLIENT_EMAIL,
client_id: import.meta.env.FIREBASE_CLIENT_ID,
auth_uri: import.meta.env.FIREBASE_AUTH_URI,
token_uri: import.meta.env.FIREBASE_TOKEN_URI,
auth_provider_x509_cert_url: import.meta.env.FIREBASE_AUTH_CERT_URL,
client_x509_cert_url: import.meta.env.FIREBASE_CLIENT_CERT_URL,
};
export const app = initializeApp({
credential: cert(serviceAccount as ServiceAccount),
});

Infine, il tuo progetto ora dovrebbe includere questi nuovi file:

  • Cartellasrc
    • env.d.ts
    • Cartellafirebase
      • client.ts
      • server.ts
  • .env
  • astro.config.mjs
  • package.json

Aggiunta dell’autenticazione con Firebase

Sezione intitolata Aggiunta dell’autenticazione con Firebase
  • Un progetto Astro inizializzato con Firebase.
  • Un progetto Firebase con l’autenticazione tramite email/password abilitata nella console di Firebase in Autenticazione > Metodo di accesso.

Creazione degli endpoint del server per l’autenticazione

Sezione intitolata Creazione degli endpoint del server per l’autenticazione

L’autenticazione di Firebase in Astro richiede i seguenti endpoint del server di Astro:

  • GET /api/auth/signin - per effettuare l’accesso di un utente
  • GET /api/auth/signout - per disconnettere un utente
  • POST /api/auth/register - per registrare un utente

Crea tre endpoint correlati all’autenticazione in una nuova cartella src/pages/api/auth/: signin.ts, signout.ts e register.ts.

signin.ts contiene il codice per effettuare l’accesso di un utente utilizzando Firebase:

src/pages/api/auth/signin.ts
import type { APIRoute } from "astro";
import { app } from "../../../firebase/server";
import { getAuth } from "firebase-admin/auth";
export const GET: APIRoute = async ({ request, cookies, redirect }) => {
const auth = getAuth(app);
/* Ottieni il token dagli headers della richiesta */
const idToken = request.headers.get("Authorization")?.split("Bearer ")[1];
if (!idToken) {
return new Response(
"Nessun token trovato",
{ status: 401 }
);
}
/* Verifica il token */
try {
await auth.verifyIdToken(idToken);
} catch (error) {
return new Response(
"Token non valido",
{ status: 401 }
);
}
/* Crea e imposta il cookie per la sessione */
const fiveDays = 60 * 60 * 24 * 5 * 1000;
const sessionCookie = await auth.createSessionCookie(idToken, {
expiresIn: fiveDays,
});
cookies.set("session", sessionCookie, {
path: "/",
});
return redirect("/dashboard");
};

signout.ts contiene il codice per disconnettere un utente eliminando il cookie per la sessione:

src/pages/api/auth/signout.ts
import type { APIRoute } from "astro";
export const GET: APIRoute = async ({ redirect, cookies }) => {
cookies.delete("session", {
path: "/",
});
return redirect("/signin");
};

register.ts contiene il codice per registrare un utente utilizzando Firebase:

src/pages/api/auth/register.ts
import type { APIRoute } from "astro";
import { getAuth } from "firebase-admin/auth";
import { app } from "../../../firebase/server";
export const POST: APIRoute = async ({ request, redirect }) => {
const auth = getAuth(app);
/* Ottieni i dati del form */
const formData = await request.formData();
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
const name = formData.get("name")?.toString();
if (!email || !password || !name) {
return new Response(
"Alcuni dati del form mancano",
{ status: 400 }
);
}
/* Crea l'utente */
try {
await auth.createUser({
email,
password,
displayName: name,
});
} catch (error: any) {
return new Response(
"Qualcosa è andato storto",
{ status: 400 }
);
}
return redirect("/signin");
};

Dopo aver creato gli endpoint del server per l’autenticazione, la cartella del tuo progetto dovrebbe ora includere questi nuovi file:

  • Cartellasrc
    • env.d.ts
    • Cartellafirebase
      • client.ts
      • server.ts
    • Cartellapages
      • Cartellaapi
        • Cartellaauth
          • login.ts
          • logout.ts
          • register.ts
  • .env
  • astro.config.mjs
  • package.json

Crea le pagine che utilizzeranno gli endpoint di Firebase:

  • src/pages/register - conterrà un form per registrare un utente
  • src/pages/signin - conterrà un form per effettuare l’accesso di un utente
  • src/pages/dashboard - conterrà una dashboard a cui solo gli utenti autenticati possono accedere

L’esempio di src/pages/register.astro qui sotto include un form che invierà una richiesta POST all’endpoint /api/auth/register. Questo endpoint creerà un nuovo utente utilizzando i dati del form e quindi reindirizzerà l’utente alla pagina /signin.

src/pages/register.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Registrati">
<h1>Registrati</h1>
<p>Hai già un account? <a href="/signin">Accedi</a></p>
<form action="/api/auth/register" method="post">
<label for="name">Nome</label>
<input type="text" name="name" id="name" />
<label for="email" for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Registrati</button>
</form>
</Layout>

src/pages/signin.astro utilizza l’app server di Firebase per verificare il cookie per la sessione dell’utente. Se l’utente è autenticato, la pagina reindirizzerà l’utente alla pagina /dashboard.

La pagina di esempio qui sotto contiene un form che invierà una richiesta POST all’endpoint /api/auth/signin con il token ID generato dall’app client di Firebase.

L’endpoint verificherà il token ID e creerà un nuovo cookie per la sessione dell’utente. Quindi, l’endpoint reindirizzerà l’utente alla pagina /dashboard.

src/pages/signin.astro
---
import { app } from "../firebase/server";
import { getAuth } from "firebase-admin/auth";
import Layout from "../layouts/Layout.astro";
/* Controlla se l'utente è autenticato */
const auth = getAuth(app);
if (Astro.cookies.has("session")) {
const sessionCookie = Astro.cookies.get("session").value;
const decodedCookie = await auth.verifySessionCookie(sessionCookie);
if (decodedCookie) {
return Astro.redirect("/dashboard");
}
}
---
<Layout title="Accedi">
<h1>Accedi</h1>
<p>Sei nuovo? <a href="/register">Crea un account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email" for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Accedi</button>
</form>
</Layout>
<script>
import {
getAuth,
inMemoryPersistence,
signInWithEmailAndPassword,
} from "firebase/auth";
import { app } from "../firebase/client";
const auth = getAuth(app);
// Questo impedirà al browser di memorizzare i dati della sessione
auth.setPersistence(inMemoryPersistence);
const form = document.querySelector("form") as HTMLFormElement;
form.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(form);
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return;
}
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
const idToken = await userCredential.user.getIdToken();
const response = await fetch("/api/auth/signin", {
method: "GET",
headers: {
Authorization: `Bearer ${idToken}`,
},
});
if (response.redirected) {
window.location.assign(response.url);
}
});
</script>

src/pages/dashboard.astro verificherà il cookie per la sessione dell’utente utilizzando l’app server di Firebase. Se l’utente non è autenticato, la pagina reindirizzerà l’utente alla pagina /signin.

La pagina d’esempio qui sotto mostrerà il nome dell’utente e un pulsante per disconnettersi. Cliccando sul pulsante invierà una richiesta GET all’endpoint /api/auth/signout.

L’endpoint eliminerà il cookie per la sessione dell’utente e reindirizzerà l’utente alla pagina /signin.

src/pages/dashboard.astro
---
import { app } from "../firebase/server";
import { getAuth } from "firebase-admin/auth";
import Layout from "../layouts/Layout.astro";
const auth = getAuth(app);
/* Verifica la sessione corrente */
if (!Astro.cookies.has("session")) {
return Astro.redirect("/signin");
}
const sessionCookie = Astro.cookies.get("session").value;
const decodedCookie = await auth.verifySessionCookie(sessionCookie);
const user = await auth.getUser(decodedCookie.uid);
if (!user) {
return Astro.redirect("/signin");
}
---
<Layout title="Dashboard">
<h1>Benvenuto {user.displayName}</h1>
<p>Siamo felici di vederti qui</p>
<form action="/api/auth/signout">
<button type="submit">Disconnettiti</button>
</form>
</Layout>

Per aggiungere provider OAuth all’app, è necessario abilitarli nella console di Firebase.

Nella console di Firebase, vai nella sezione Autenticazione e clicca sulla scheda Metodo di accesso. Quindi, clicca sul pulsante Aggiungi un nuovo provider e abilita i provider che desideri utilizzare.

L’esempio seguente utilizza il provider Google.

Modifica la pagina signin.astro per aggiungere:

  • un pulsante per effettuare l’accesso con Google sotto il form esistente
  • un listener degli eventi per pulsante per gestire il processo di accesso nel tag <script>.
src/pages/signin.astro
---
import { app } from "../firebase/server";
import { getAuth } from "firebase-admin/auth";
import Layout from "../layouts/Layout.astro";
/* Controlla se l'utente è autenticato */
const auth = getAuth(app);
if (Astro.cookies.has("session")) {
const sessionCookie = Astro.cookies.get("session").value;
const decodedCookie = await auth.verifySessionCookie(sessionCookie);
if (decodedCookie) {
return Astro.redirect("/dashboard");
}
}
---
<Layout title="Accedi">
<h1>Accedi</h1>
<p>Sei nuovo? <a href="/register">Crea un account</a></p>
<form action="/api/auth/signin" method="post">
<label for="email" for="email">Email</label>
<input type="email" name="email" id="email" />
<label for="password">Password</label>
<input type="password" name="password" id="password" />
<button type="submit">Accedi</button>
</form>
<button id="google">Accedi con Google</button>
</Layout>
<script>
import {
getAuth,
inMemoryPersistence,
signInWithEmailAndPassword,
GoogleAuthProvider,
signInWithPopup,
} from "firebase/auth";
import { app } from "../firebase/client";
const auth = getAuth(app);
auth.setPersistence(inMemoryPersistence);
const form = document.querySelector("form") as HTMLFormElement;
form.addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(form);
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
if (!email || !password) {
return;
}
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
const idToken = await userCredential.user.getIdToken();
const response = await fetch("/api/auth/signin", {
headers: {
Authorization: `Bearer ${idToken}`,
},
});
if (response.redirected) {
window.location.assign(response.url);
}
});
const googleSignin = document.querySelector("#google") as HTMLButtonElement;
googleSignin.addEventListener("click", async () => {
const provider = new GoogleAuthProvider();
const userCredential = await signInWithPopup(auth, provider);
const idToken = await userCredential.user.getIdToken();
const res = await fetch("/api/auth/signin", {
headers: {
Authorization: `Bearer ${idToken}`,
},
});
if (res.redirected) {
window.location.assign(res.url);
}
});
</script>

Quando viene cliccato, il pulsante di accesso a Google aprirà una finestra popup per effettuare l’accesso. Una volta che l’utente effettua l’accesso, invierà una richiesta POST all’endpoint /api/auth/signin con il token ID generato dal provider OAuth.

L’endpoint verificherà il token ID e creerà un nuovo cookie per la sessione dell’utente. Quindi, l’endpoint reindirizzerà l’utente alla pagina /dashboard.

In questa guida, la collection Firestore si chiamerà friends e conterrà documenti con i seguenti campi:

  • id: generato automaticamente da Firestore
  • name: un campo di tipo stringa
  • age: un campo di tipo numero
  • isBestFriend: un campo di tipo booleano

Crea due nuovi file in una nuova cartella src/pages/api/friends/: index.ts e [id].ts. Questi creeranno due punti finali server per interagire con il database Firestore nei seguenti modi:

  • POST /api/friends: per creare un nuovo documento nella collection friends.
  • POST /api/friends/:id: per aggiornare un documento nella collection friends.
  • DELETE /api/friends/:id: per eliminare un documento nella collection friends.

index.ts conterrà il codice per creare un nuovo documento nella collection friends:

src/pages/api/friends/index.ts
import type { APIRoute } from "astro";
import { app } from "../../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData();
const name = formData.get("name")?.toString();
const age = formData.get("age")?.toString();
const isBestFriend = formData.get("isBestFriend") === "on";
if (!name || !age) {
return new Response("Mancano aluni parametri", {
status: 400,
});
}
try {
const db = getFirestore(app);
const friendsRef = db.collection("friends");
await friendsRef.add({
name,
age: parseInt(age),
isBestFriend,
});
} catch (error) {
return new Response("Qualcosa è andato storto", {
status: 500,
});
}
return redirect("/dashboard");
};

[id].ts conterrà il codice per aggiornare ed eliminare un documento nella collection friends:

src/pages/api/friends/[id].ts
import type { APIRoute } from "astro";
import { app } from "../../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
const db = getFirestore(app);
const friendsRef = db.collection("friends");
export const POST: APIRoute = async ({ params, redirect, request }) => {
const formData = await request.formData();
const name = formData.get("name")?.toString();
const age = formData.get("age")?.toString();
const isBestFriend = formData.get("isBestFriend") === "on";
if (!name || !age) {
return new Response("Missing required fields", {
status: 400,
});
}
if (!params.id) {
return new Response("Cannot find friend", {
status: 404,
});
}
try {
await friendsRef.doc(params.id).update({
name,
age: parseInt(age),
isBestFriend,
});
} catch (error) {
return new Response("Something went wrong", {
status: 500,
});
}
return redirect("/dashboard");
};
export const DELETE: APIRoute = async ({ params, redirect }) => {
if (!params.id) {
return new Response("Cannot find friend", {
status: 404,
});
}
try {
await friendsRef.doc(params.id).delete();
} catch (error) {
return new Response("Something went wrong", {
status: 500,
});
}
return redirect("/dashboard");
};

Dopo aver creato gli endpoint server per Firestore, la cartella del tuo progetto dovrebbe ora includere questi nuovi file:

  • Cartellasrc
    • env.d.ts
    • Cartellafirebase
      • client.ts
      • server.ts
    • Cartellapages
      • Cartellaapi
        • Cartellafriends
          • index.ts
          • [id].ts
  • .env
  • astro.config.mjs
  • package.json

Crea le pagine che utilizzeranno gli endpoint di Firestore:

  • src/pages/add.astro - conterrà un form per aggiungere un nuovo amico.
  • src/pages/edit/[id].ts - conterrà un form per modificare un amico e un pulsante per eliminare un amico.
  • src/pages/friend/[id].ts - conterrà i dettagli di un amico.
  • src/pages/dashboard.astro - mostrerà un elenco con tutti gli amici.

L’esempio di src/pages/add.astro qui sotto include un form che invierà una richiesta POST all’endpoint /api/friends. Questo endpoint creerà un nuovo amico utilizzando i dati dal form e quindi reindirizzerà l’utente alla pagina /dashboard.

src/pages/add.astro
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="Aggiungi un nuovo amico">
<h1>Aggiungi un nuovo amico</h1>
<form method="post" action="/api/friends">
<label for="name">Nome</label>
<input type="text" id="name" name="name" />
<label for="age">Età</label>
<input type="number" id="age" name="age" />
<label for="isBestFriend">Migliore amico?</label>
<input type="checkbox" id="isBestFriend" name="isBestFriend" />
<button type="submit">Aggiungi amico</button>
</form>
</Layout>

src/pages/edit/[id].astro conterrà un form per modificare i dati di un amico e un pulsante per eliminarlo. All’invio, questa pagina invierà una richiesta POST all’endpoint /api/friends/:id per aggiornare i dati di un amico.

Se l’utente clicca il pulsante per eliminare un amico, questa pagina invierà una richiesta DELETE all’endpoint /api/friends/:id per eliminare un amico.

src/pages/edit/[id].astro
---
import Layout from "../../layouts/Layout.astro";
import { app } from "../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
interface Friend {
name: string;
age: number;
isBestFriend: boolean;
}
const { id } = Astro.params;
if (!id) {
return Astro.redirect("/404");
}
const db = getFirestore(app);
const friendsRef = db.collection("friends");
const friendSnapshot = await friendsRef.doc(id).get();
if (!friendSnapshot.exists) {
return Astro.redirect("/404");
}
const friend = friendSnapshot.data() as Friend;
---
<Layout title="Modifica {friend.name}">
<h1>Modifica {friend.name}</h1>
<p>Qui puoi modificare o eliminare i dati del tuo amico.</p>
<form method="post" action={`/api/friends/${id}`}>
<label for="name">Nome</label>
<input type="text" id="name" name="name" value={friend.name} />
<label for="age">Età</label>
<input type="number" id="age" name="age" value={friend.age} />
<label for="isBestFriend">Migliore amico?</label>
<input
type="checkbox"
id="isBestFriend"
name="isBestFriend"
checked={friend.isBestFriend}
/>
<button type="submit">Modifica amico</button>
</form>
<button type="button" id="delete-document">Elimina</button>
</Layout>
<script>
const deleteButton = document.getElementById(
"delete-document"
) as HTMLButtonElement;
const url = document.querySelector("form")?.getAttribute("action") as string;
deleteButton.addEventListener("click", async () => {
const response = await fetch(url, {
method: "DELETE",
});
if (response.redirected) {
window.location.assign(response.url);
}
});
</script>

src/pages/friend/[id].astro visualizzerà i dettagli di un solo amico.

src/pages/friend/[id].astro
---
import Layout from "../../layouts/Layout.astro";
import { app } from "../../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
interface Friend {
name: string;
age: number;
isBestFriend: boolean;
}
const { id } = Astro.params;
if (!id) {
return Astro.redirect("/404");
}
const db = getFirestore(app);
const friendsRef = db.collection("friends");
const friendSnapshot = await friendsRef.doc(id).get();
if (!friendSnapshot.exists) {
return Astro.redirect("/404");
}
const friend = friendSnapshot.data() as Friend;
---
<Layout title={friend.name}>
<h1>{friend.name}</h1>
<p>Età: {friend.age}</p>
<p>Migliore amico: {friend.isBestFriend ? "" : "No"}</p>
</Layout>

Mostra una lista di record con un bottone per modificarli

Sezione intitolata Mostra una lista di record con un bottone per modificarli

Infine, src/pages/dashboard.astro mostrerà una lista degli amici. Ogni amico avrà un link alla loro pagina e un bottone che li manderà sulla pagina per modificare l’amico.

src/pages/dashboard.astro
---
import { app } from "../firebase/server";
import { getFirestore } from "firebase-admin/firestore";
import Layout from "../layouts/Layout.astro";
interface Friend {
id: string;
name: string;
age: number;
isBestFriend: boolean;
}
const db = getFirestore(app);
const friendsRef = db.collection("friends");
const friendsSnapshot = await friendsRef.get();
const friends = friendsSnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
})) as Friend[];
---
<Layout title="I miei amici">
<h1>Amici</h1>
<ul>
{
friends.map((friend) => (
<li>
<a href={`/friend/${friend.id}`}>{friend.name}</a>
<span>({friend.age})</span>
<strong>{friend.isBestFriend ? "Migliore Amico" : "Amico"}</strong>
<a href={`/edit/${friend.id}`}>Modifica</a>
</li>
))
}
</ul>
</Layout>

Dopo aver creato tutti i file il tuo progetto dovrebbe avere una struttura come questa:

  • Cartellasrc
    • env.d.ts
    • Cartellafirebase
      • client.ts
      • server.ts
    • Cartellapages
      • dashboard.astro
      • add.astro
      • Cartellaedit
        • [id].astro
      • Cartellafriend
        • [id].astro
      • Cartellaapi
        • Cartellafriends
          • index.ts
          • [id].ts
  • .env
  • astro.config.mjs
  • package.json

Altre guide per servizi backend