Full Stack Starter - Svelte and Express
Get started with a full-stack app using Svelte and Express.
Svelte and Node.js
Knowing how to build either a frontend or backend is a valuable skill. I started my career in technology as a UI/UX designer, but I hated that I couldn't help turn my designs into a functional webapp. That's when I jumped into frontend development! I desperately wanted to do more than designing the interactions - I wanted to be able to build them!
Learning frontend design was a great challenge and it was amazing how much it affected my designs. Knowing more about data structures, data manipulation, and the capabilities of various frameworks forced me to think critically about the designs I was doing. It especially helped with the hand-off between myself and developers. Terms like, "I don't know how to do it, just do it", left my vocabulary and I was much more helpful in the implementation. Something my fellow developers are thankful for to this day.
However, for most people learning just frontend or just backend isn't enough. To put a website or web app on the internet for all to see, you have to know more than just one discipline. That's why I'm writing this! I hope this article helps you with your journey of building and deploying a Svelte app. There are other ways to deploy Svelte and the other SPA frameworks, but using an application server is one of my favorites if you need to do backend & API work.
Before we begin
Before we jump in let's go over some details.
You'll need Node.js installed - you can download it here if you haven't already.
You can access my template repo with the completed files here.
The plan
The plan is to create a Svelte frontend, a Node & Express backend, and wire them together so that our frontend can search for gifs from the GIPHY API. This will be a great example of why using an application server can be so great for your full-stack development. It keeps all the files in one place and sensitive data, like API keys, are hidden in the server and not your frontend.
So...Svelte is awesome, right?
As a full-stack developer of 7 years, I honestly say that Svelte is pretty amazing...but not for everything. Here is a saying that I've said far too often in my career
"Anything can be used as a hammer"
I'm a huge advocate for using the right tool for the job. Sometimes that means it's going to be Svelte, sometimes it won't. You could choose to use Svelte for all your projects and that might be a great call for you. It might not be a good call for someone else.
My personal opinion - I wouldn't use Svelte for bigger applications. It's not because Svelte can't handle bigger projects! It's because I think tools like React or Angular provide much better tooling and organizational potential than Svelte does. For our small example today, I think Svelte is perfect!
Let’s jump in
The first thing we’ll do is create our Svelte app. To do that, we’ll follow the instructions on this page and run the following:
$ npx degit sveltejs/template svelte-express-app
If you haven’t used npx
before, it’s an executable package, baked into NPM, that enables one-time use of an npm package. In this case, npx
is executing the tool degit to pull down and clone the Svelte template repo. You can read more info about the degit project here.
This command clones the Svelte template repo and names it “svelte-express-app”. Once the command completes, install the project, and you’re ready to begin developing with Svelte! Let’s run the following commands to get our Svelte app 100% ready.
$ cd svelte-express-app
$ yarn
$ yarn dev
We can now begin to develop our Svelte app! Let's move to the Node.js backend.
We’ll use Express on top of Node.js to make building the API super easy. To get this started, we'll first install Express and CORS.
$ yarn add express --save
$ yarn add cors --save
Now that we have Express added to our project, let's create the server file and get it set up so that it can serve the Svelte app. It's important that the server file is outside of the "src" folder. Since the Svelte app is within the "src" folder, everything within it gets compiled and bundled through Rollup - the bundler that Svelte uses. Since we want our server to be separate and serve the frontend to the browser, we need to make sure that the server file is outside of any directory that's being compiled.
NOTE: You could write Node.js and Express with Typescript and need it to be compiled. But that's a slightly different process and one we're not going over in this article. If that's something you'd like to see, let me know!
Let's create our file at the root of our project. I'm going to name my file "server" - I'm really creative, I know! You can create the file with the following command or however you're favorite editor allows you to.
$ touch server.js
Open that file up and add the boilerplate code for Express
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const cors = require('cors');
const path = require('path');
app.use(cors());
These lines of boilerplate do several things. They set up the express app, assign a port if the application doesn't have one, import the CORS and Path libraries from npm, and finally, it assigns the Express app to use CORS instead of the baked in security. Cors is really helpful in fine-tuning the security of your application in regards to what domains, outside of your hosting domain, can be used to access the server.
Just a few more lines before we're ready to use the server!
app.use(express.static('public'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'public', 'index.html'));
});
app.listen(port, () => {
console.log(`Server is up at port ${port}`);
});
The first two functions are used to serve the index.html in the public directory to the browser. If you'd like to read more on how the Path library works take a look here, but It's essentially a baked in library that allows Node.js to read and write files!
The last function is a listener that prints out a statement about the port number of the server! This is a really quick way to know if the server is up when developing locally.
Let's Test
Let's test each app separately to make sure they're working. Make sure you kill the process before you run another one.
yarn dev
to start the Svelte app. Go to your browser and check it out!
node server.js
to start the express app. The console should read Server is up at port 5000
Starting Both With One Command
Now that we have our frontend and backend configured, we need to spin them up! There are differences in the build process between development and production. To serve this in a production environment, it's a pretty straightforward process.
Build the Svelte app
yarn build
Start the server.
yarn start
It's so simple because we don't want the Svelte app to be hot reloading. For production, we want a super-performant, minified bundle that will load in the browser quickly.
But for development, hot reloading is a must for the Svelte side of things! We'll have to adjust the package.json scripts so we can re-compile the Svelte every time we make changes. And we need to serve the Svelte app from our Express server instead of Svelte's default serve library.
Svelte uses the library "sirv" to deliver the compiled bundle of Javascript to your browser. Let's open the package.json file and take a look at the "scripts" part of the file. You'll find a "start" command that uses the sirv library to serve the public folder. Let's change that FROM "sirv public" TO "node server.js". This creates a single command that will hot-reload the Svelte app and use our Express app as the server. Let's give it a go!
yarn dev
Go to your browser at localhost:5000 and see it work! You should also see the console print out the port number.
You're done! But there's more
Congratulations, you've built a full-stack web app that is incredibly easy to develop! But we're not going to stop there. Let's add an API endpoint to our Express app and use the GIPHY API to search for gifs.
Adding a route
The first thing we're going to do is add a new route to our Express.
app.get('/giphy', (req, res) => {
console.log(`Searching for a gif with the term: ${req.query.term}`);
res.send({
success: true,
data: []
})
});
It's incredibly important to note that this function MUST BE BEFORE the app.use() function that sends the index.html to the frontend. If you place this function after that app.use() function, nothing will happen on your request.
The first line is the Express function that declares the URL path of the endpoint and which HTTP verbs can be used for it. This endpoint can be accessed through localhost:5000/giphy and it's a "GET" function only. You can also use other HTTP verbs like POST, PUT, and DELETE. If you'd like to use an endpoint for everything, the "USE" function is the perfect choice.
The next line is a console log to print out the search term and then we have the res.send()
function that sends our data back to the frontend. There's no special sauce here - you can return whatever you'd like from your endpoint. In my experience creating an object that has a success flag and then a property with the data makes it easy to do success/fail conditionals in the front end.
Making the request from Svelte
Now to Svelte! Open up App.svelte and replace the paragraph after "Hello World" with the following:
<div class="search-block">
<input type="text" placeholder="Search for gif" bind:value={searchTerm} />
<button on:click={searchForGif}>Search</button>
</div>
<div class="gifs">
{#if gifs.length > 0}
<div class="gifs-grid">
{#each gifs as gif}
<iframe src={gif.embed_url} title={gif.title} />
{/each}
</div>
{:else}
No gifs to show yet
{/if}
</div>
Now, add this javascript in the script tag.
let gifs = [];
let searchTerm = "";
async function searchForGif(e) {
try {
const returnValue = await fetch(`/giphy?term=${searchTerm}`);
const response = await returnValue.json();
gifs = response.data;
} catch (error) {
console.error(error);
}
}
At this point, I'm assuming you know Svelte and it's flavor of syntax, so I'm not going to talk in detail about how this works. From a high level, the button calls a function that uses the value bound to the input field and calls to our Express endpoint. On return, the code is formatted and assigned to a variable. Once all that code is added, you should be able to type something in the input field, click the button and see the Express server log out the search term. Since we're not returning any data from our endpoint, the Svelte won't change or display anything.
Using the GIPHY API
Let's change that - let's add the GIPHY API. To make API calls from Express, we'll need to add a library. I like axios, but node-fetch is another popular one. Run $ yarn add axios
to install axios and then add the require() to the server const axios = require('axios');
Now we're ready to add the API call. In the /giphy
endpoint replace the contents with this:
console.log(`Searching for a gif with the term: ${req.query.term}`);
let params = req.query.term.replace(/ /g, '+');
params += '&api_key=YOUR_API_KEY';
params += '&limit=10';
axios.get(`https://api.giphy.com/v1/gifs/search?q=${params}`)
.then(function (response) {
res.send({
success: true,
data: response.data.data
})
})
.catch(function (error) {
res.send({
success: false,
data: []
})
});
The first part of this code takes the term
variable and replaces any spaces with a plus sign. The next two lines add the API key and a query limiter. These are all well documented in GIPHY's doc here.
The axios function will make a GET request to the endpoint and then resolve or reject, sending the appropriate response back to the client.
Before you can query the GIPHY API, you'll need to get your own API key. Head over to their developer page and create an account and an app. Make sure you select API when you are generating a key. Then copy and paste that bad boy after the YOUR_API_KEY
param in the code above!
Search for Gifs!
Once you've added your API key to the endpoint you're ready to use the search functionality!
Restart your server with yarn dev
and type something in the search box and hit the button! A grid of 10 gifs should popup! This project is super extensible, so feel free to hack around and explore it on your own.
You can access my template repo with the completed files here.