import { json, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node";
import { useLoaderData, useSubmit } from "@remix-run/react";
import { Page, Layout, Card, Button } from "@shopify/polaris";
import { authenticate } from "../shopify.server";
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { admin, session } = await authenticate.admin(request);
const response = await admin.graphql(
`#graphql
query getShop {
shop {
name
currencyCode
}
}`
);
const responseJson = await response.json();
return json({ shop: responseJson.data.shop, shopDomain: session.shop });
};
export default function Settings() {
const { shop } = useLoaderData<typeof loader>();
return (
<Page title="Setări Aplicație">
<Layout>
<Layout.Section>
<Card>
<p>Conectat la magazinul: <strong>{shop.name}</strong></p>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}Am trimis recent a patra aplicație în Shopify App Store, un plugin de upsell pentru un client cu peste 12k de comenzi lunare. După ce am trecut prin vechiul stack cu Node/Express și React embedded, trecerea la Remix cu Polaris a fost și o ușurare, dar și o sursă nouă de draci.
Dacă vrei să scrii o aplicație custom pentru Shopify în 2024, ăsta e stack-ul recomandat oficial. Îți zic din start: economisești cam 40% din timpul de boilerplate, dar te lovești de niște reguli rigide la review-ul lor pe care nu le găsești clar scrise în docs.
De ce Remix + Polaris și unde e capcana
Shopify a cumpărat Remix acum ceva timp și se vede. Pachetul @shopify/shopify-app-remix se ocupă automat de rotația de session tokens, OAuth, webhook-uri și inserarea de Content Security Policy (CSP) headers. Fără asta, gestionarea iframe-ului în Shopify Admin era un coșmar curat cu cookieless auth.
Polaris, pe de altă parte, e design system-ul lor. Arată impecabil și oferă comercianților senzația că aplicația ta e o bucată nativă din Shopify. Trade-off-ul? Flexibilitate aproape zero. Dacă vrei un layout mai atipic sau un dashboard cu grafice customizate, o să te lupți cu componentele lor. Am pierdut 6 ore doar încercând să aliniem un tabel complex cu filtre custom pentru că Polaris își impune propriile spacing-uri și nu te lasă ușor să le suprascrii.
Cursa din procesul de App Review
Review-ul lor durează de obicei între 3 și 7 zile lucrătoare. La ultimul submission am luat reject de două ori la rând. O dată a fost o chestie legitimă, a doua oară o chichiță de UX.
Primul reject l-am luat pentru că nu am gestionat fallback-ul când un comerciant are adblocker agresiv sau Safari cu ITP (Intelligent Tracking Prevention) pornit. Iframe-ul rămânea alb. Trebuie neapărat să folosești utilitarul lor de App Bridge pentru re-autentificare în window-ul părinte.
Al doilea reject a fost pur UX: am pus un buton clasic de "Salvează" în josul paginii de setări, în loc să folosesc componenta <SaveBar> din App Bridge care apare în header-ul nativ din Shopify Admin. Regula lor nescrisă e simplă: dacă există un pattern UI nativ în Shopify, ești obligat să-l folosești pe ăla, altfel nu treci.
Setup-ul de loader/action care funcționează curat
În Remix, fiecare rută protejată trebuie să apeleze authenticate.admin(request). Asta îți garantează că ai un context valid de GraphQL API și că request-ul vine într-adevăr din adminul Shopify.
Atenție mare la tratarea erorilor: dacă GraphQL îți întoarce userErrors, aplicația nu trebuie să crape cu un 500 generoc. Reviewerii testează intenționat introducerea de date invalide (ex: un ID de produs care nu există) și se așteaptă să vadă bannere de eroare Polaris inline.
În concluzie, Remix + Polaris e cea mai rapidă cale să ajungi în App Store, dar te obligă să te pliezi 100% pe regulile lor de design. Câștigi timp la backend, pierzi timp la UI tweaking.
Voi ce experiențe aveți cu review-ul de la Shopify? A scăpat cineva fără reject din prima încercare?