Lot’s of interesting tech is dropping. svelte-kit is now in beta, and supabase launched a bunch of stuff.
So it’s a good time to investigate what e-commerce looks like with these tools.
Setup
To setup a svelte-kit
project
mkdir supacart && cd supacart
npm init svelte@next
Now add supabase-js
:
yarn add -D @supabase/supabase-js
Database
To start, it will just be a list of products:
drop table if exists products;
create table products (
id serial primary key,
permalink varchar not null unique,
sku varchar not null unique,
name varchar not null,
details varchar not null default '',
price money not null
);
create unique index products$permalink on products (permalink);
insert into products ( permalink, name, sku, price ) values ('t-shirt', 'T-Shirt', 't-shirt', '19.99');
insert into products ( permalink, name, sku, price ) values ('pants', 'Pants', 'pants', '79.99');
We’ll store env vars in .env
:
VITE_SUPABASE_URL=<supabase-url-here>
VITE_SUPABASE_PUBLIC_KEY=<supabase-public-key-here>
SUPABASE_PRIVATE_KEY=<supabase-private-key-here>
Notice the prefix VITE_
, that makes the variable available in the public bundle that is served to the browser. Since the private key should never be shared, it is not prefixed with VITE_
.
Routes
The root route /
will display a list of products:
<!-- src/routes/index.svelte -->
<script context="module">
import db from '$lib/db'
// load products from db
export async function load() {
return {
props: {
products: await db.products.all()
}
}
}
</script>
<script>
export let products
</script>
{#each products as product}
<article>
<a href="/products/{product.permalink}">{product.name}</a>
</article>
{/each}
And the product page will be at /products/:permalink
, so let’s create a page for that too:
<script context="module">
import db from '$lib/db'
export async function load({page}) {
// find the product
const product = await db.products.find(page.params.permalink)
// if we found it, return it as a prop
if (product) {
return {
props: {
product
}
}
}
// oh shoot, we didn't find a product, so return 404
return {
status: 404,
error: new Error('product not found')
}
}
</script>
<script>
// this prop will be provided by the `load` function
export let product
</script>
<h1>{product.name}</h1>
<p>SKU: {product.sku}</p>
<p>{product.details}</p>
{product.price}
Data access
The logic to access the DB is extracted to it’s own file src/lib/db.js
. That will help keep our UI components slim, because they won’t have to deal directly with the database.
// src/lib/db.js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_PUBLIC_KEY
)
export default {
products: {
async all(options = {}) {
const { data } = await supabase.from('products').select('*')
return data
},
async find(permalink) {
const { data } = await supabase
.from('products')
.select('*')
.match({permalink})
.single()
return data
}
}
}
Summary
That gives us everything we need to display a homepage and a product page. It can render server side and client side too.