Astro è un framework orientato ai contenuti caratterizzato da un tipo di architettura a Isole. Questo tipo di architettura riduce il sovraccarico e la complessità del codice JavaScript rispetto ad altri framework, ed è particolarmente adatto a siti di documentazione e ricchi di contenuti testuali.
Il framework sta attraversando una fase di forte sviluppo e il team rilascia regolarmente nuove funzionalità. Tra le novità più interessanti e promettenti dell'ultimo periodo c'è sicuramente Astro DB, un servizio di database SQL completamente gestito, progettato esclusivamente per il framework di Astro.
Cosa è Astro DB
Astro DB si basa su libSQL, un moderno client di database open source per JavaScript/TypeScript nato da un fork di SQLite. Grazie al nuovo servizio di database, si va ben oltre la gestione di contenuti testuali. Perché ora con Astro è possibile gestire dati, pagine, blocchi, immagini e un intero ecosistema di plugin.
È importante dire subito che il database di sviluppo non è persistente e viene creato nuovamente ogni volta che si avvia il server di sviluppo. Questo permette di avere un database vuoto ogni volta che si esegue il comando astro dev
(o npm run dev
). Ad ogni riavvio, Astro:
- crea un database vuoto nella cartella
.astro
; - legge lo schema da
db/config.ts
; - popola il database prelevando i dati da
db/seed.ts
.
Astro DB utilizza l'Object-relational mapping (ORM) di Drizzle, un ORM TypeScript headless con query API sia relazionali che SQL-like.
Astro DB dispone anche di un database libSQL hosted a cui è possibile connettersi sia durante lo sviluppo locale che in produzione, gestito tramite la piattaforma Astro Studio. Questa è stata sviluppata in partnership con Turso, azienda che gestisce la più grande piattaforma di hosting libSQL.
Dato che lo schema del database è definito nel file db/config.ts
, in Astro DB non ci sono file di migrazione da gestire. Quando si effettuano modifiche allo schema, basta inviarle al database hosted utilizzando il comando astro db push
. Quando si esegue il push delle modifiche allo schema del database, questo viene automaticamente confrontato con il database di produzione e le modifiche applicate.
Nel caso fosse necessario resettare il database, si potrà eseguire astro db --force-reset
per apportare tutte le modifiche allo schema del database, compreso il reset.
Installazione di Astro DB
È possibile configurare Astro DB manualmente o eseguire questo comando:
npx astro add db
Come anticipato, ogni volta che si esegue astro dev
(o npm run dev
), viene creato un database locale che utilizza LibSQL per gestire i dati.
Installando Astro DB con il comando astro add
, sarà aggiunto al progetto un file db/config.ts
dove saranno a definire le tabelle del database, come illustrato nella sezione che segue.
La configurazione delle tabelle
Per configurare una tabella del database, bisogna importare nel file db/config.ts
le utility defineDb
, defineTable
e column
di astro:db
. Quindi si esporta defineDb()
per rendere la tabella disponibile al progetto.
Nell'esempio che segue, definiamo le tabelle Todos
e Categories
:
import { defineDb, defineTable, column } from 'astro:db';
const Todos = defineTable({
columns: {
id: column.number({ primaryKey: true }),
task: column.text(),
catId: column.number({ references: () => Categories.columns.id }),
done: column.boolean(),
}
})
const Categories = defineTable({
columns: {
id: column.number({ primaryKey: true }),
category: column.text(),
}
})
export default defineDb({
tables: { Todos, Categories }
});
I dati sono strutturati in righe e colonne. Ogni colonna indica il tipo di ciascun valore (si veda anche la Table configuration reference).
Astro DB supporta i seguenti tipi:
- stringhe di testo:
column.text()
; - numeri interi e a virgola mobile:
column.number()
; - valori booleani:
column.boolean()
; - oggetti Data, memorizzati come stringhe ISO:
column.date()
; - oggetti JSON, convertiti in stringa per la memorizzazione:
column.json()
.
Ogni colonna può essere configurata in base ai seguenti parametri:
primaryKey
: imposta una colonna di tiponumber
otext
come chiave primaria.optional
: di default, tutte le colonne sonoNOT NULL
. Utilizzando questo parametro, si accettano valori null.default
: imposta un valore predefinito. Può essere un valore statico o una stringa SQL per valori generati, ad es. timestamp.unique
: stabilisce che i valori di una colonna devono essere univoci.references
: stabilisce un vincolo di chiave esterna che crea un riferimento ad un'altra tabella.
È anche possibile impostare un indice su una data colonna o su una combinazione di colonne e aggiungere chiavi esterne per creare relazioni tra tabelle.
Relazioni tra tabelle in Astro DB
In Astro DB è possibile definire relazioni tra tabelle e salvare queste relazioni nello schema del database. A questo scopo sarà necessario individuare:
- una colonna identificativa nella tabella di riferimento con la proprietà
primaryKey
. Normalmente sarà una colonnaid
. - Una colonna della tabella base in cui memorizzare l'id di riferimento. Per stabilire la relazione, si utilizzerà la proprietà
references
.
Nell'esempio precedente, abbiamo definito due tabelle, Todos
e Categories
. Nella tabella Categories
, è stata impostata la colonna id
come chiave primaria:
const Categories = defineTable({
columns: {
id: column.number({ primaryKey: true }),
category: column.text(),
}
})
Nella colonna Todos
, abbiamo creato una colonna catId
con un riferimento alla colonna id
della tabella Categories
:
const Todos = defineTable({
columns: {
id: column.number({ primaryKey: true }),
task: column.text(),
catId: column.number({ references: () => Categories.columns.id }),
done: column.boolean(),
}
})
Seeding del database
Abbiamo detto che il database, con tutte le tabelle, viene generato ogni volta che viene avviato il server di sviluppo. E ad ogni riavvio, possiamo popolare automaticamente il database (seeding). Questo ci consente di avere dati pronti da utilizzare per il testing e il debugging del progetto.
Per eseguire il seeding del database, creeremo un file db/seed.ts
, all'interno del quale importeremo l'oggetto db
e le tabelle da astro:db
. Infine, utilizzeremo la funzione db.insert()
per aggiungere righe alle tabelle (maggiori informazioni sulla funzione db.insert()
nella prossima lezione).
Apriamo il file db/seed.ts
e definiamo cinque righe per la tabella Categories
e due righe per la tabella Todos
:
import { db, Todos, Categories } from 'astro:db';
export default async function seed() {
await db.insert(Categories).values([
{ id: 0, category: 'General' },
{ id: 1, category: 'Learning' },
{ id: 2, category: 'Projects' },
{ id: 3, category: 'Articles' },
{ id: 4, category: 'Events' },
]),
await db.insert(Todos).values([
{ id: 0, task: 'Dummy text number one', catId: 0, done: false },
{ id: 1, task: 'Dummy text number two', catId: 2, done: false },
])
}
Il nostro database è pronto per lo sviluppo. Nella prossima lezione vedremo come eseguire le nostre prime query.