Skip to content

Commit 95b8772

Browse files
committed
switch all samples to use the defang gh action
0 parents  commit 95b8772

File tree

14 files changed

+286
-0
lines changed

14 files changed

+286
-0
lines changed

.github/workflows/deploy.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Deploy
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
id-token: write
14+
15+
steps:
16+
- name: Checkout Repo
17+
uses: actions/checkout@v4
18+
19+
- name: Deploy
20+
uses: DefangLabs/defang-github-action@v1.0.4

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Sveltekit & MongoDB
2+
3+
This is a project that demonstrate both client side component rendering and hydration as well as serverside rendering with external API route configuration. Furthermore, there is also a mongodb connection (not hosted on the atlas) to cache the queried results.
4+
5+
## NOTE
6+
7+
This sample showcases how you could deploy a full-stack application with Defang and Sveltekit. However, it deploys mongodb as a defang service. Defang [services](https://12factor.net/processes) are ephemeral and should not be used to run stateful workloads in production as they will be reset on every deployment. For production use cases you should use a managed database like RDS, Aiven, or others. In the future, Defang will help you provision and connect to managed databases.
8+
9+
## Essential Setup Files
10+
11+
1. Download [Defang CLI] (https://github.com/DefangLabs/defang)
12+
2. (optional) If you are using [Defang BYOC] (https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) authenticated your AWS account.
13+
3. (optional for local development) [Docker CLI] (https://docs.docker.com/engine/install/)
14+
15+
## Prerequisite
16+
17+
1. Download [Defang CLI] (https://github.com/DefangLabs/defang)
18+
2. (optional) If you are using [Defang BYOC](https://docs.defang.io/docs/concepts/defang-byoc) make sure you have properly
19+
3. [Docker CLI] (https://docs.docker.com/engine/install/)
20+
21+
4. [NodeJS] (https://nodejs.org/en/download/package-manager)
22+
23+
## Development
24+
25+
For development, we use a local container. This can be seen in the compose.yaml and /src/routes/api/songs/+server.js file and the server.js file where we create a pool of connections. To run the sample locally after clonging the respository, you can run on docker by doing
26+
27+
1. docker compose up --build
28+
29+
## A Step-by-Step Guide
30+
31+
1. Open the terminal and type `defang login`
32+
2. Type `defang compose up` in the CLI
33+
3. Your app should be up and running with Defang in minutes!
34+
35+
---
36+
37+
Title: SvelteKit & MongoDB
38+
39+
Short Description: A full-stack application using SvelteKit for the frontend and MongoDB for the database.
40+
41+
Tags: SvelteKit, MongoDB, Full-stack, Node.js, JavaScript
42+
43+
Languages: nodejs

app/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine-strict=true

app/Dockerfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use the official Node.js image as a base image
2+
FROM node:18-alpine
3+
4+
# Set the working directory
5+
WORKDIR /app
6+
7+
# Copy package.json and package-lock.json
8+
COPY package*.json ./
9+
10+
# Install dependencies
11+
RUN npm install
12+
13+
# Copy the rest of the application code
14+
COPY . .
15+
16+
# Build the SvelteKit application
17+
ARG MONGODB_URI
18+
ENV MONGODB_URI=$MONGODB_URI
19+
RUN npm run build
20+
21+
# Expose the port the app runs on
22+
EXPOSE 3000
23+
24+
# Command to run the app
25+
CMD ["node", "build"]

app/package.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "sveltekit",
3+
"version": "0.0.1",
4+
"private": true,
5+
"scripts": {
6+
"dev": "vite dev",
7+
"build": "vite build",
8+
"preview": "vite preview"
9+
},
10+
"devDependencies": {
11+
"@sveltejs/adapter-auto": "^3.0.0",
12+
"@sveltejs/adapter-node": "^5.0.1",
13+
"@sveltejs/kit": "^2.5.8",
14+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
15+
"svelte": "^4.2.7",
16+
"svelte-preprocess": "^5.1.4",
17+
"vite": "^5.2.11"
18+
},
19+
"type": "module",
20+
"dependencies": {
21+
"mongodb": "^6.6.1",
22+
"node-fetch": "^3.3.2"
23+
}
24+
}

app/src/app.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
%sveltekit.head%
8+
</head>
9+
<body data-sveltekit-preload-data="hover">
10+
<div style="display: contents">%sveltekit.body%</div>
11+
</body>
12+
</html>

app/src/lib/db.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { MongoClient } from 'mongodb';
2+
3+
const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017/musicdb';
4+
const client = new MongoClient(uri);
5+
6+
let cachedClient = null;
7+
let cachedDb = null;
8+
9+
export async function connectToDatabase() {
10+
if (cachedClient && cachedDb) {
11+
return { client: cachedClient, db: cachedDb };
12+
}
13+
14+
// Connect the client
15+
await client.connect();
16+
const db = client.db(); // Connects to the specified database in the URI
17+
18+
// Ensure the collections exist (this step is optional because MongoDB will create collections on first use)
19+
await db.createCollection('songs').catch(() => {});
20+
await db.createCollection('searches').catch(() => {});
21+
22+
cachedClient = client;
23+
cachedDb = db;
24+
return { client: cachedClient, db: cachedDb };
25+
}

app/src/lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// place files you want to import through the `$lib` alias in this folder.

app/src/routes/+page.svelte

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<script>
2+
import { onMount } from "svelte";
3+
import { writable } from "svelte/store";
4+
5+
let query = "";
6+
let songs = writable([]);
7+
8+
async function searchSongs() {
9+
const res = await fetch(`/api/songs?query=${query}`);
10+
const data = await res.json();
11+
songs.set(data.songs);
12+
}
13+
14+
onMount(() => {
15+
// Optionally, perform an initial search
16+
});
17+
</script>
18+
19+
<input bind:value={query} placeholder="Search for a song..." />
20+
<button on:click={searchSongs}>Search</button>
21+
22+
<ul>
23+
{#each $songs as song}
24+
<li>{song.title} by {song.artist}</li>
25+
{/each}
26+
</ul>

app/src/routes/api/songs/+server.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { connectToDatabase } from '$lib/db';
2+
import fetch from 'node-fetch';
3+
import { ObjectId } from 'mongodb';
4+
import { json } from '@sveltejs/kit'; // Import the json utility
5+
6+
export async function GET({ url }) {
7+
const query = url.searchParams.get('query');
8+
if (!query) {
9+
return json({ error: 'Query parameter is required' }, { status: 400 });
10+
}
11+
12+
const { db } = await connectToDatabase();
13+
14+
// Check if the search query is already cached
15+
const cachedSearch = await db.collection('searches').findOne({ query });
16+
17+
if (cachedSearch) {
18+
// Fetch cached results from the Songs collection
19+
const songIds = cachedSearch.results.map(id => new ObjectId(id));
20+
const songs = await db.collection('songs').find({ _id: { $in: songIds } }).toArray();
21+
return json({ songs });
22+
}
23+
24+
// If not cached, fetch from MusicBrainz API
25+
const response = await fetch(`https://musicbrainz.org/ws/2/recording?query=${query}&fmt=json`);
26+
const data = await response.json();
27+
28+
// Ensure the data is in the expected format
29+
if (!data.recordings || !Array.isArray(data.recordings)) {
30+
return json({ error: 'Invalid data format from MusicBrainz API' }, { status: 500 });
31+
}
32+
33+
const songs = data.recordings.map(recording => ({
34+
title: recording.title,
35+
artist: recording['artist-credit']?.[0]?.name || 'Unknown artist',
36+
album: recording.releases?.[0]?.title || 'Unknown album',
37+
musicbrainz_id: recording.id,
38+
data: recording
39+
}));
40+
41+
// Save the songs in the Songs collection
42+
const result = await db.collection('songs').insertMany(songs);
43+
const songIds = Object.values(result.insertedIds);
44+
45+
// Cache the search query and results in the Searches collection
46+
await db.collection('searches').insertOne({ query, results: songIds, timestamp: new Date() });
47+
48+
return json({ songs });
49+
}

0 commit comments

Comments
 (0)