The Problem
Let’s say that you create a simple web application with the Nim Heroku Buildpack.
Project structure:
├── nim_heroku.nimble
├── Procfile
├── src
│ ├── nim_heroku
│ ├── nim_heroku.nim
│ └── views
│ └── general.nim
├── static_dir
│ ├── favicon.ico
│ └── style.css
└── tags
3 directories, 8 files
Bin file (src/nim_heroku.nim
):
import jester, asyncdispatch, os, strutils
import views/general
var settings = newSettings()
if existsEnv("PORT"):
settings.port = Port(parseInt(getEnv("PORT")))
settings.staticDir = "./static_dir"
routes:
get "/":
resp renderMain()
runForever()
View (src/views/general.nim
):
#? stdtmpl(subsChar = '$', metaChar = '#')
#
#proc renderMain*(): string =
## result = ""
<!DOCTYPE html>
<html>
<head>
<title>Nim Heroku Test</title>
<link rel="stylesheet" href="https://unpkg.com/modern-css-reset/dist/reset.min.css" />
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<div class="center text-center">
<div class="[ flow ]">
<p>Hello World</p>
<p>This is a test</p>
</div>
</div>
</body>
</html>
#end proc
We load a css file and a favicon in the HTML head
.
That works fine in local development. But when you deploy to Heroku, you won’t see those files with a HTTP status error code of 403.
The Solution
You can bundle assets like CSS or JavaScript with nimassets.
Install nimassets locally
nimble install nimassets
Use your terminal to create the assets file:
nimassets -d=static_dir -o= src/views/assetsfile.nim
Import the assets file into your view. Add the CSS file to your HTML with the
<style>
tag.#? stdtmpl(subsChar = '$', metaChar = '#') #import assetsfile # #let css = assetsFile.getAsset("static_dir/style.css") # #proc renderMain*(): string = ## result = "" <!DOCTYPE html> <html> <head> <title>Nim Heroku Test</title> <link rel="stylesheet" href="https://unpkg.com/modern-css-reset/dist/reset.min.css" /> <style type="text/css"> ${css} </style> <link rel="shortcut icon" href="favicon.ico"> </head> <body> <div class="center text-center"> <div class="[ flow ]"> <p>Hello World</p> <p>This is a test</p> </div> </div> </body> </html> #end proc
Serve the favicon icon directly with Jester. You have to find the file with
os.walkdir
(src/views/static_assets.nim
):import os, sequtils, re var filepaths: seq[string] for kind, path in walkdir("./static_dir/"): filepaths.add(path) let icoFile = filter(filepaths, proc (x: string): bool = contains(x, re".ico"))[0] let ico* = readFile(icoFile)
Create a route for serving the favicon:
import jester, asyncdispatch, os, strutils import views/general, views/static_assets var settings = newSettings() settings.staticDir = "./static_dir" if existsEnv("PORT"): settings.port = Port(parseInt(getEnv("PORT"))) template corsResp(code, message: untyped): untyped = mixin resp resp code, {"Access-Control-Allow-Origin": "*"}, message routes: get "/": corsResp(Http200, renderMain()) get "/favicon.ico": corsResp(Http200, ico) runForever()
Push to Heroku and it should work.
Project structure:
.
├── nim_heroku.nimble
├── Procfile
├── src
│ ├── nim_heroku
│ ├── nim_heroku.nim
│ └── views
│ ├── assetsfile.nim
│ ├── general.nim
│ ├── static_assets
│ └── static_assets.nim
├── static_dir
│ ├── favicon.ico
│ └── style.css
└── tags
3 directories, 11 files