This article is a less technical version of my gist about how to implement Vite + Kemal.
Kemal is a minimalist Crystal web framework, inspired by Sinatra from Ruby. Its main difference from Sinatra is its speed, due to Crystal’s native compilation.
Meanwhile, in the front end world, Vite appeared as an alternative to bundlers, and quickly gained attention.
But what if we could combine both Vite and Kemal in the same application and use any modern UI framework, or even just simple HTML with some libraries, without manually copying .min.js files?
That is why this article exists!
How to combine Vite and Kemal?
First, install Crystal (if you do not have it) and Node.js (or Bun). After that, create a Crystal project:
crystal init app app
cd app
Then, edit your shard.yml and add Kemal as a dependency. Here is an example:
name: app
version: 0.1.0
authors:
- Name mymail@example.com
targets:
app:
main: src/app.cr
crystal: '>= 1.18.1'
dependencies:
kemal:
github: kemalcr/kemal
Use shards install to install the dependencies.
Writing code
Write this in src/app.cr (or in your main project file):
require "kemal"
require "json"
before_all do |env|
env.response.headers.add "Access-Control-Allow-Origin", "*" if !(Kemal.config.env == "production")
env.response.content_type = "application/json"
end
get "/api" do
{message: "From kemal!"}.to_json
end
get "/" do |env|
env.response.content_type = "text/html"
env.response.print File.read("public/index.html")
end
Kemal.run
In short, this code sets up CORS to accept all requests while in development, and configures responses to be sent as JSON, except for the "/" route, which will return HTML. The response reads from the public directory, where the Vite output will be stored.
Adding a front end
Start a Vite project inside the Crystal project. I will use Svelte, but any template supported by Vite works:
npm create vite@latest client -- --template svelte
Update client/vite.config.js | ts (or your front end file) with these example settings. This allows a better development environment:
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
server: {
proxy: {
'/api': 'http://localhost:3000',
},
},
build: { outDir: '../public' },
});
In short, server.proxy allows Vite during vite dev to assume the API URL is /api, which is very useful to avoid long URLs. The build is configured to the Kemal public folder, where it will serve the files as if it were a production server.
How to use
Edit your client. In my case it is Svelte:
<script>
const response = await fetch('api/');
const data = await response.json();
</script>
<main>
<h1>Important message:</h1>
<div>
<p>{data.message}</p>
</div>
</main>
Start the Kemal server:
crystal src/app.cr
To start the Vite development server, it is simple:
cd client
npm run dev
Deploy or testing
To deploy or test, it is simple. Just do this in the terminal:
cd client
npm run build
cd ..
crystal src/app.cr
This tutorial was inspired by the gist showing how to do something similar in this article, but using Sinatra.
Top comments (0)