Come ospitare un’applicazione Remix?
In questo articolo scoprirete tutto quello che c’è da sapere per iniziare a usare Remix. Ne esamineremo i pro e i contro, mostreremo come costruire un’applicazione Remix e, infine, come distribuire Remix.
Contents
Che cos’è il Remix?
Remix è un moderno framework web full-stack per la realizzazione di applicazioni veloci, efficienti e resistenti. Sfrutta la potenza del rendering lato server (SSR), consentendo tempi di caricamento più rapidi e una migliore SEO per le applicazioni web dinamiche.
Con Remix, gli sviluppatori possono integrare perfettamente la logica lato client e lato server in una base di codice unificata. Rispetto ad altri popolari framework React, Remix fa molte cose in modo diverso. Tra queste:
- Routing basato su file (un file nella directory routes rappresenta un percorso specifico)
- Gestione tradizionale dei moduli (utilizzando HTML e richieste HTTP, simile a PHP)
- Gestione dello stato (lo stato è memorizzato solo sul server)
- Caricamento dei dati (disaccoppiato dai componenti)
Remix è stato creato nel 2020 e inizialmente era disponibile con una licenza annuale. Successivamente, nell’ottobre del 2021, il team di Remix ha deciso di rendere il progetto open-source. Ora è disponibile con la licenza MIT.
Alla fine del 2022, Remix è stata acquisita da Shopify per 2,1 miliardi di dollari.
Vantaggi di Remix
Vediamo i principali vantaggi dell’utilizzo del framework Remix.
Remix è costruito sulla base di React Router, una potente soluzione di routing lato client. Infatti, Remix è stato creato dallo stesso team di sviluppatori che ha creato React Router.
Il framework utilizza un sistema di navigazione basato sui file, che semplifica l’organizzazione del codice. Consente agli sviluppatori di associare rotte, componenti e risorse a file o directory specifiche.
Ecco un esempio:
app/
└── routes/
├── $noteId.tsx // matches: /<noteId>/
├── $noteId_.destroy.tsx // matches: /<noteId>/destroy
├── $noteId_.edit.tsx // matches: /<noteId>/edit
├── _index.tsx // matches: /
└── create.tsx // matches: /create
Soprattutto, supporta l’instradamento nidificato via . Questo si traduce in tempi di caricamento più brevi, migliore gestione degli errori e altro ancora!
Rendering lato server (SSR)
Remix sfrutta la potenza del rendering lato server.
Nelle applicazioni React di base, i dati vengono solitamente recuperati sul lato client e poi iniettati nel DOM. Questa operazione è nota come rendering lato client (CSR).
Remix, invece, adotta un approccio diverso. Prima recupera i dati sul backend, poi esegue il rendering dell’HTML con i dati recuperati e quindi li serve al client.
Il rendering lato server tende a garantire prestazioni migliori e applicazioni più SEO-friendly.
Gestione dei moduli
Remix riporta la manipolazione delle forme alle basi.
Invece di utilizzare un mucchio di componenti controllati e JavaScript, utilizza i tradizionali moduli HTML e le richieste HTTP.
Quando si invia un modulo, si invia una richiesta HTTP a una rotta specifica, che viene poi elaborata sul lato server tramite la funzione action()
. Il funzionamento è simile a quello del buon vecchio PHP.
Ciò significa che Remix non richiede assolutamente JavaScript per elaborare i moduli. Se da un lato questo è ottimo, dall’altro potrebbe rendere un po’ più complicata la convalida dei moduli e la visualizzazione degli errori.
Gestione dello Stato
In questo contesto, lo stato si riferisce alla sincronizzazione dei dati del server e del client.
Remix rende la gestione dello stato un gioco da ragazzi. Elimina la necessità di Redux, Zustand, React Query, Apollo o qualsiasi altra libreria di gestione dello stato sul lato client.
Quando si usa Remix, tutto lo stato è gestito dal server. Il client non detiene praticamente alcuno stato; pertanto, il processo di sincronizzazione non è necessario.
I dati dal server al client vengono passati attraverso varie funzioni, come loader()
e action()
.
Transizioni e UI ottimistiche
Le transizioni di Remix rendono il passaggio da una pagina all’altra fluido e veloce, caricando i dati in anticipo e utilizzando le animazioni.
L’interfaccia utente ottimistica riflette istantaneamente le azioni dell’utente, rendendo i siti web più reattivi e mostrando le modifiche prima che vengano confermate.
Le transizioni e l’interfaccia utente ottimistica migliorano notevolmente l’esperienza dell’utente, riducendo la latenza percepita e fornendo un feedback immediato.
Limiti del Remix
Anche se Remix è ottimo, presenta alcune limitazioni.
Complesso
Remix non è il framework più facile da usare e la sua documentazione (al momento in cui scriviamo) non è delle migliori.
Il framework fa anche molte cose in modo diverso da React puro e semplice. Se siete sviluppatori React, potreste aver bisogno di un po’ di tempo per capire i diversi concetti di Remix.
Meno popolare
Remix è ancora un framework relativamente nuovo. Il suo rilascio pubblico risale solo all’ottobre 2021. È meno maturo e collaudato rispetto ad alcuni concorrenti, come Next.js.
Osservando le stelle di GitHub, possiamo notare che Remix (27k stelle) è molto meno popolare di Next.js (120k stelle). Al momento in cui scriviamo, Remix non è stato adottato da nessun gigante tecnologico, tranne Shopify.
Accoppiata stretta
Le applicazioni Remix hanno un frontend e un backend strettamente accoppiati. Sebbene questo approccio vada bene per i progetti più piccoli, alcuni sviluppatori preferiscono la flessibilità di un frontend e di un backend separati.
Inoltre, separare il frontend dal backend può rendere il codice più manutenibile e più facile da testare.
Nessun SSG o ISR
Remix non supporta la generazione di siti statici (SSG) o la rigenerazione statica incrementale (ISR).
Come distribuire un’applicazione Remix?
In questa sezione, costruiremo e distribuiremo un’applicazione Remix.
Prerequisiti
Per seguirci, avrete bisogno di:
- Conoscenza di base di TypeScript
- Esperienza con Docker (e tecnologie di containerizzazione)
- Node.js e un IDE JavaScript installati sulla propria macchina
- Docker Desktop installato sulla macchina
Panoramica del progetto
Nel corso dell’articolo, lavoreremo su una web app per le note. L’applicazione web consentirà agli utenti di creare, recuperare, modificare ed eliminare le note.
Per il backend utilizzeremo Back4app BaaS e per il frontend il framework Remix. Una volta che il frontend è stato codificato, lo distribuiremo su Back4app Containers.
Il prodotto finale avrà un aspetto simile a questo:
Vi suggerisco di seguire prima l’applicazione web Notes. Dopo l’articolo, dovreste essere in grado di distribuire le vostre applicazioni Remix.
Backend
In questa sezione dell’articolo, useremo Back4app per costruire il backend della nostra applicazione.
Creare un’applicazione
Iniziate accedendo al vostro account Back4app (o creandone uno se dovete ancora ottenerlo).
Una volta effettuato l’accesso, si verrà reindirizzati al portale “Le mie applicazioni”. Per creare un backend è necessario creare un’applicazione Back4app. Per farlo, cliccate su “Build new app”.
Back4app offre due soluzioni:
- Backend as a Service (BaaS): una soluzione backend a tutti gli effetti
- Containers as a Service (CaaS) — piattaforma di orchestrazione di container basata su Docker
Poiché stiamo lavorando su un backend, selezionare “Backend as a Service”.
Dare un nome all’applicazione, lasciare tutto il resto come predefinito e fare clic su “Crea”.
La piattaforma richiede un po’ di tempo per configurare tutto ciò che è necessario per il backend. Ciò include il database, l’interfaccia dell’applicazione, la scalabilità, la sicurezza, ecc.
Una volta che l’applicazione è pronta, si verrà reindirizzati alla vista del database in tempo reale dell’applicazione.
Database
Procediamo con la gestione del database.
Poiché stiamo costruendo un’applicazione relativamente semplice, avremo bisogno di un solo modello: chiamiamolo Nota
. Per crearlo, fare clic sul pulsante “Crea una classe” in alto a sinistra dello schermo.
Nominarla Note
, abilitare “Lettura e scrittura pubbliche” e fare clic su “Crea classe e aggiungi colonne”.
Quindi, aggiungere le seguenti colonne:
+-----------+--------------+----------------+----------+
| Data type | Name | Default value | Required |
+-----------+--------------+----------------+----------+
| String | emoji | <leave blank> | yes |
+-----------+--------------+----------------+----------+
| String | title | <leave blank> | yes |
+-----------+--------------+----------------+----------+
| File | content | <leave blank> | no |
+-----------+--------------+----------------+----------+
Una volta creata la classe, popolate il database con i dati di esempio. Creare alcune note fornendo le emoji, i titoli e il contenuto. In alternativa, importare l’esportazione del database.
Ottimo, è tutto!
Abbiamo creato con successo un backend senza scrivere alcun codice.
Per saperne di più su Backend as a Service, consultate Cos’è Backend as a Service?
Frontend
In questa sezione dell’articolo, costruiremo il frontend della nostra applicazione utilizzando il framework Remix.
creare-remix
Il modo più semplice per avviare un progetto Remix è l’utility create-remix
. Questo strumento crea un progetto Remix pronto per la produzione.
Si occupa della struttura delle cartelle e delle dipendenze, imposta il bundler, ecc.
Create un nuovo progetto Remix eseguendo il seguente comando:
$ npx create-remix@latest remix-notes
Initialize a new git repository? Yes
Install dependencies with npm? Yes
Se non avete mai usato
create-remix
, verrà installato automaticamente.
Una volta creato il progetto, modificate la vostra directory attiva in esso:
$ cd remix-notes
Infine, avviare il server di sviluppo:
$ npm run dev
Aprite il vostro browser web preferito e visitate il sito http://localhost:5173. Sullo schermo dovrebbe apparire la pagina di destinazione predefinita di Remix.
TailwindCSS
Per renderci la vita un po’ più facile, useremo TailwindCSS. TailwindCSS è un framework orientato all’utilità che consente di costruire rapidamente frontend senza scrivere alcun CSS.
Per prima cosa, installarlo usando npm:
$ npm install -D tailwindcss postcss autoprefixer
Quindi, eseguire tailwindcss init:
$ npx tailwindcss init --ts -p
Questo configura il progetto e crea un file tailwind.config.ts nella radice del progetto.
Modificare la proprietà content
in questo modo per far sapere a Tailwind in quali file verranno utilizzate le classi di utilità:
// tailwind.config.ts
import type {Config} from "tailwindcss";
export default {
content: ["./app/**/*.{js,jsx,ts,tsx}"], // new
theme: {
extend: {},
},
plugins: [],
} satisfies Config;
Creare un nuovo file chiamato tailwind.css nella cartella dell’applicazione con i seguenti contenuti:
/* app/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Infine, importarlo in root.tsx tramite i link
:
// app/root.tsx
// other imports
import {LinksFunction} from "@remix-run/node";
import stylesheet from "~/tailwind.css?url";
export const links: LinksFunction = () => [
{ rel: "stylesheet", href: stylesheet },
];
// ...
Ecco fatto!
Abbiamo installato TailwindCSS con successo.
Percorsi
La nostra applicazione web avrà i seguenti endpoint:
/
visualizza tutte le note/create
consente agli utenti di creare note/
visualizza una nota specifica/
/cancella consente agli utenti di cancellare una nota specifica.
Per definire queste rotte, creare la seguente struttura di directory nella cartella app:
app/
└── routes/
├── $noteId.tsx
├── $noteId_.destroy.tsx
├── $noteId_.edit.tsx
├── _index.tsx
└── create.tsx
Come si può intuire, il prefisso $
viene usato per i parametri dinamici e .
viene usato al posto di /
.
Visualizzazioni
Procediamo con l’implementazione delle viste.
Per rendere il nostro codice più sicuro dal punto di vista tipologico, creeremo un’interfaccia chiamata Note
che assomiglia alla nostra classe di database Note
.
Creare una cartella denominata store e al suo interno creare NoteModel.ts con i seguenti contenuti:
// app/store/NoteModel.ts
export default interface NoteModel {
objectId: string;
emoji: string;
title: string;
content: string;
createdAt: Date;
updatedAt: Date;
}
Quindi incollare il codice della vista per _index.tsx, $noteId.tsx e create.tsx:
// app/routes/_index.tsx
import {Link, NavLink} from "@remix-run/react";
import NoteModel from "~/store/NoteModel";
const notes = [
{objectId: "1", emoji: "📝", title: "My First Note"},
{objectId: "2", emoji: "📓", title: "My Second Note"},
{objectId: "3", emoji: "📔", title: "My Third Note"},
] as NoteModel[];
export default function Index() {
return (
<>
<Link to={`/create`}>
<div className="bg-blue-500 hover:bg-blue-600 text-lg font-semibold text-white
px-4 py-3 mb-2 border-2 border-blue-600 rounded-md"
>
+ Create
</div>
</Link>
{notes.map(note => (
<NavLink key={note.objectId} to={`/${note.objectId}`}>
<div className="hover:bg-slate-200 text-lg font-semibold
px-4 py-3 mb-2 border-2 border-slate-300 rounded-md"
>
{note.emoji} {note.title}
</div>
</NavLink>
))}
</>
);
}
// app/routes/$noteId.tsx
import {Form} from "@remix-run/react";
import NoteModel from "~/store/NoteModel";
const note = {
objectId: "1", emoji: "📝", title: "My First Note", content: "Content here.",
createdAt: new Date(), updatedAt: new Date(),
} as NoteModel;
export default function NoteDetails() {
return (
<>
<div className="mb-4">
<p className="text-6xl">{note.emoji}</p>
</div>
<div className="mb-4">
<h2 className="font-semibold text-2xl">{note.title}</h2>
<p>{note.content}</p>
</div>
<div className="space-x-2">
<Form
method="post" action="destroy"
onSubmit={(event) => event.preventDefault()}
className="inline-block"
>
<button
type="submit"
className="bg-red-500 hover:bg-red-600 font-semibold text-white
p-2 border-2 border-red-600 rounded-md"
>
Delete
</button>
</Form>
</div>
</>
);
}
// app/routes/create.tsx
import {Form} from "@remix-run/react";
export default function NoteCreate() {
return (
<>
<div className="mb-4">
<h2 className="font-semibold text-2xl">Create Note</h2>
</div>
<Form method="post" className="space-y-4">
<div>
<label htmlFor="emoji" className="block">Emoji</label>
<input
type="text" id="emoji" name="emoji"
className="w-full border-2 border-slate-300 p-2 rounded"
/>
</div>
<div>
<label htmlFor="title" className="block">Title</label>
<input
type="text" id="title" name="title"
className="w-full border-2 border-slate-300 p-2 rounded"
/>
</div>
<div>
<label htmlFor="content" className="block">Content</label>
<textarea
id="content" name="content"
className="w-full border-2 border-slate-300 p-2 rounded"
/>
</div>
<div>
<button
type="submit"
className="bg-blue-500 hover:bg-blue-600 font-semibold
text-white p-2 border-2 border-blue-600 rounded-md"
>
Create
</button>
</div>
</Form>
</>
);
}
Non c’è nulla di particolare in questo codice. Tutto ciò che abbiamo fatto è stato utilizzare JSX in combinazione con TailwindCSS per creare l’interfaccia utente.
Come avrete notato, tutti i componenti non sono controllati (non stiamo usando useState()
). Inoltre, stiamo usando un vero modulo HTML.
Questo perché il framework Remix gestisce i moduli in modo simile a PHP utilizzando le richieste HTTP, a differenza di React.
Parse
Esistono diversi modi per collegarsi al backend basato su Back4app. È possibile utilizzare:
- API RESTful
- API GraphQL
- SDK Parse
Il modo più semplice e robusto è certamente Parse SDK. Parse SDK è un kit di sviluppo software che fornisce una serie di classi e metodi di utilità per interrogare e manipolare facilmente i dati.
Si inizia installando Parse tramite npm:
$ npm install -i parse @types/parse
Creare un file .env nella radice del progetto in questo modo:
# .env
PARSE_APPLICATION_ID=<your_parse_app_id>
PARSE_JAVASCRIPT_KEY=<your_parse_js_key>
Assicurarsi di sostituire
<your_parse_app_id>
e<your_parse_js_key>
con le credenziali effettive. Per ottenere le credenziali, accedere alla propria applicazione e selezionare “Impostazioni dell’applicazione > Server e sicurezza” nella barra laterale.
Quindi, inizializzare Parse all’inizio del file root.tsx in questo modo:
// app/root.tsx
// other imports
import Parse from "parse/node";
const PARSE_APPLICATION_ID = process.env.PARSE_APPLICATION_ID || "";
const PARSE_HOST_URL = "https://parseapi.back4app.com/";
const PARSE_JAVASCRIPT_KEY = process.env.PARSE_JAVASCRIPT_KEY || "";
Parse.initialize(PARSE_APPLICATION_ID, PARSE_JAVASCRIPT_KEY);
Parse.serverURL = PARSE_HOST_URL;
Creeremo un file separato chiamato api/backend.ts per mantenere le nostre viste pulite dalla logica di comunicazione del backend.
Creare api/backend.ts con i seguenti contenuti:
// app/api/backend.ts
import Parse from "parse/node";
import NoteModel from "~/store/NoteModel";
export const serializeNote = (note: Parse.Object<Parse.Attributes>): NoteModel => {
return {
objectId: note.id,
emoji: note.get("emoji"),
title: note.get("title"),
content: note.get("content"),
createdAt: new Date(note.get("createdAt")),
updatedAt: new Date(note.get("updatedAt")),
};
}
export const getNotes = async (): Promise<NoteModel[]> => {
// ...
}
export const getNote = async (objectId: string): Promise<NoteModel | null> => {
// ...
}
// Grab the entire file from:
// https://github.com/duplxey/back4app-containers-remix/blob/master/app/api/backend.ts
Infine, modificare le viste per recuperare e manipolare i dati del backend:
// app/routes/index.tsx
import {json} from "@remix-run/node";
import {Link, NavLink, useLoaderData} from "@remix-run/react";
import {getNotes} from "~/api/backend";
export const loader = async () => {
const notes = await getNotes();
return json({notes});
};
export default function Index() {
const {notes} = useLoaderData<typeof loader>();
return (
// ...
);
}
// app/routes/$noteId.tsx
import {getNote} from "~/api/backend";
import {json, LoaderFunctionArgs} from "@remix-run/node";
import {Form, useLoaderData} from "@remix-run/react";
import {invariant} from "@remix-run/router/history";
export const loader = async ({params}: LoaderFunctionArgs) => {
invariant(params.noteId, "Missing noteId param");
const note = await getNote(params.noteId);
if (note == null) throw new Response("Not Found", {status: 404});
return json({note});
};
export default function NoteDetails() {
const {note} = useLoaderData<typeof loader>();
return (
// ...
);
}
// app/routes/create.tsx
import {ActionFunctionArgs, redirect} from "@remix-run/node";
import {Form} from "@remix-run/react";
import {createNote} from "~/api/backend";
export const action = async ({request}: ActionFunctionArgs) => {
const formData = await request.formData();
const {emoji, title, content} = Object.fromEntries(formData)
as Record<string, string>;
const note = await createNote(emoji, title, content);
return redirect(`/${note?.objectId}`);
};
export default function NoteCreate() {
return (
// ...
);
}
// app/routes/$noteId_.destroy.tsx
import type {ActionFunctionArgs} from "@remix-run/node";
import {redirect} from "@remix-run/node";
import {invariant} from "@remix-run/router/history";
import {deleteNote} from "~/api/backend";
export const action = async ({params}: ActionFunctionArgs) => {
invariant(params.noteId, "Missing noteId param");
await deleteNote(params.noteId);
return redirect(`/`);
};
Codice di riferimento:
- Abbiamo usato la funzione Remix
loader()
per caricare i dati. Una volta caricati i dati, li abbiamo passati alla vista come JSON tramite la funzionejson()
.
- Abbiamo usato la funzione
action()
di Remix per gestire l’invio del modulo (ad esempio,POST
).
- L’
ID nota
è stato passato alle viste come parametro.
L’applicazione dovrebbe ora essere completamente funzionante e sincronizzata con il backend di Back4app. Per verificare che tutto funzioni, creare alcune note, modificarle e poi cancellarle.
App Dockerize
In questa sezione, faremo la dockerizzazione del nostro frontend di Remix.
Profilo Docker
Un Dockerfile è un file di testo semplice che delinea i passaggi che il motore Docker deve eseguire per costruire un’immagine.
Questi passaggi comprendono l’impostazione della directory di lavoro, la specificazione dell’immagine di base, il trasferimento dei file, l’esecuzione dei comandi e altro ancora.
Le istruzioni sono tipicamente rappresentate in maiuscolo e sono immediatamente seguite dai rispettivi argomenti.
Per saperne di più su tutte le istruzioni, consultate il riferimento al Dockerfile.
Creare un file Docker nella root del progetto con i seguenti contenuti:
# Dockerfile
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start"]
Questo file Docker è basato sull’immagine di node:20
. Per prima cosa imposta la directory di lavoro, copia il file package.json e installa le dipendenze.
Dopodiché, costruisce il progetto, espone la porta 3000
e serve l’applicazione.
.dockerignore
Quando si lavora con Docker, di solito si cerca di creare immagini il più possibile piccole.
Poiché il nostro progetto contiene alcuni file che non devono essere presenti nell’immagine (ad esempio, .git,
build,
impostazioni IDE), li escluderemo. Per farlo, creeremo un file .dockerignore che funziona in modo simile a un file .gitignore.
Creare un file .dockerignore nella radice del progetto:
# .dockerignore
.idea/
.cache/
build/
node_modules/
Adattare il file .dockerignore in base alle esigenze del progetto.
Costruire e testare
Prima di spingere un’immagine Docker nel cloud, è una buona idea testarla localmente.
Per prima cosa, costruire l’immagine:
$ docker build -t remix-notes:1.0 .
Quindi, creare un contenitore usando l’immagine appena creata:
$ docker run -it -p 3000:3000
-e PARSE_APPLICATION_ID=<your_parse_app_id>
-e PARSE_JAVASCRIPT_KEY=<your_parse_javascript_key>
-d remix-notes:1.0
Assicurarsi di sostituire
<your_parse_app_id>
e<your_parse_javascript_key>
con le credenziali reali.
L’applicazione dovrebbe ora essere accessibile all’indirizzo http://localhost:3000. Dovrebbe comportarsi come prima del processo di dockerizzazione.
Spingere su GitHub
Per distribuire un’applicazione su Back4app Containers, è necessario prima inviare il codice sorgente a GitHub. Per farlo, si possono seguire i seguenti passaggi:
- Accedere al proprio account GitHub (o registrarsi).
- Creare un nuovo repository GitHub.
- Navigare nel progetto locale e inizializzarlo:
git init
- Aggiungere tutto il codice al sistema di controllo della versione:
git add .
- Aggiungere l’origine remota tramite
git remote add origin
- Eseguire il commit di tutto il codice tramite
git commit -m "commit iniziale".
- Spingere il codice su GitHub
git push origin master
Distribuire l’applicazione
In quest’ultima sezione, distribuiremo il frontend su Back4app Containers.
Accedere al proprio account Back4app e fare clic su “Build new app” per avviare il processo di creazione dell’applicazione.
Poiché ora stiamo distribuendo un’applicazione containerizzata, selezionare “Containers as a Service”.
Successivamente, è necessario collegare il proprio account GitHub con Back4app e importare il repository creato in precedenza. Una volta collegati, selezionare il repository.
Back4app Containers consente una configurazione avanzata. Tuttavia, per la nostra semplice applicazione, le seguenti impostazioni saranno sufficienti:
- Nome app: remix-notes (o scegli il tuo nome)
- Variabili d’ambiente:
PARSE_APPLICATION_ID
,PARSE_JAVASCRIPT_KEY
Per le variabili d’ambiente, utilizzare i valori usati nel file .env.
Una volta terminata la configurazione dell’installazione client, fare clic su “Deploy”.
Attendere qualche istante per il completamento dell’installazione. Una volta distribuita, fate clic sul link verde sul lato sinistro dello schermo per aprire l’applicazione nel browser.
Per un’esercitazione dettagliata, consultate la Documentazione di Container Remix di Back4app.
Ecco fatto! La vostra applicazione è ora distribuita con successo e accessibile attraverso il link fornito. Inoltre, Back4app ha rilasciato un certificato SSL gratuito per la vostra applicazione.
Conclusione
Nonostante sia un framework relativamente nuovo, Remix permette agli sviluppatori di costruire potenti applicazioni web full-stack.
Il framework affronta molte complessità delle applicazioni web, come la gestione dei moduli, la gestione degli stati e altro ancora.
In questo articolo avete imparato a costruire e distribuire un’applicazione Remix. Ora dovreste essere in grado di utilizzare Back4app per creare un semplice backend e Back4app Containers per distribuire le vostre applicazioni containerizzate.
Il codice sorgente del progetto è disponibile sul repo back4app-containers-remix.