Building A Dropserver App
Applicable Versions:
This page is valid for version 0.13.1 and above.
This page gives a high level overview of how a Dropserver app is built. See the tutorial for a hands-on example.
A Dropserver app is made up of:
dropapp.json
app manifest.- One JavaScript or TypeScript “entrypoint” file that creates the application (see below).
- Any additional backend code. All JS, TS or WASM files must run on Deno.
- Any additional asset needed, such as images and frontend HTML, CSS and JS.
Directory structure
You’re free to arrange your app’s code directory structure anyway you like, however there should be a directory that you can point the packaging system to that satisfies the following requirements:
- This directory must have a manifest file called
dropapp.json
at its root. See the Dropserver app manifest reference. - All files that need to be packaged with the app must be inside that directory.
- Do not use symlinks in the directory.
While a trivial app may have the package directory at the root of the code repository, like the tutorial does. A more typical application will have an app
subdirectory in the repo that is the root of the package. It includes app/dropapp.json
and any backend code and assets that do not need to be built. (Any other assets that need to be built have their sources outside the app/
dir and the build process puts the built assets in app/
.)
The entrypoint file is usually app/app.ts
or app/app.js
. If you use a different file name or if it’s not in the root directory of your package, then you must declare it in the entrypoint
field of your dropapp.json
.
If there is frontend that must be built, the source might be in frontend/
, and the build target might be app/frontend/
.
The Backend code
This “entrypoint” file should import createApp
from a pinned version of the dropserver_app
library. It should call the createApp
function with appropriate parameters to create the app. The value returned from createApp
can be exported and used to interact with the app elsewhere.
Note:
The dropserver_app library makes it convenient to write a Dropserver app without knowing its ever-changing internals.
Deno 2 Support
Dropserver can work with Deno 1 or Deno 2. To support Deno 2:
- Use
ds-host
andds-dev
version 0.13.3 or higher. - Use
dropserver_app
version 0.2.2 or higher.
Naturally your own code must run under Deno 2.
Backend Basics
Below are some aspects of writing a Dropserver application that differ from other code you may have written before. It’s a high level overview. For specifics refer to the tutorial and the dropserver_app docs.
No server
You do not create a server in a Dropserver app. The server is run by Dropserver. You just declare routes with callbacks (or other parameters, see below) in createApp
.
Note:
In the current version of Dropserver, app routes can only be created by using createApp
. This means you can not create or remove routes of a running app. Use wildcard routes to serve generated assets.
Different route types
When declaring routes, you can specify a route type. Currently there are just two route types:
- regular callback (which calls a function in the sandbox to respond to a request)
- static file handler (which serves files from
@app
,@appspace
or other valid directory).
Local file access
Your app will always run in a sandbox therefore it will not have access to just any part of the file system. In fact it is strictly restricted to the following subdirectories:
@app
Read-only. This is the contents of the app package.@appspace
Read-write. This is where the appspace gets to store its data files.@avatars
Read-only. A special place for getting avatars for users.
The “current working directory” of the sandbox is the appspace files directory. To access a file in the appspace directory simply refer to it with a relative path: somePath/someFile.txt
.
To operate in any of the other areas, you must first get the actual directory path by calling the appropriate method on your app instance, such as app.appPath()
. You should call this every time you need it because it may change if the app gets upgraded or appspace gets moved around.
Users
User management is handled by Dropserver. Your app should not have any code to handle new user registration, passwords or any other auth, etc… You can get a full list of users from your app instance using app.getUsers()
or get a specific user with app.getUser(<proxyID>)
.
A “proxy-id” is a string that uniquely identifies a user in your appspace. Your app does not have access to private identifiers such as email, etc…
Backend Code Caveats
localStorage ⚠️
Data stored in localStorage
from the backend code may disappear. Deno stores that data at a location that is different from the appspace data files, as a result it will not be in appspace backups. The data may also disappear if the directory structure of the instance changes, or if bubblewrap is enabled or disabled. See this issue.
Note:
This is strictly about backend code. Feel free to use localStorage in your frontend.
Specify a file for Deno Key-Value DB
Similarly to the above, do not use Deno KV store without specifying a file path. When used in that manner, Deno stashes the data in the same obscure location as session and local storage, and it may be lost.
Open a KV by specifying a path in your appspace data directory, like this:
const kv = await Deno.openKv(app.appspacePath("kv.db"));
No Deno.json and import map 🚫
Do not use a deno.json
file. or an import map. For now Dropserver uses import maps internally, and does not currently attempt to merge any other import maps.
Note:
Proper handling for import maps and deno.json
by Dropserver is on the roadmap.
Keep it lean
If no sandbox is running when a request is received, ds-host
must start one and then forward the request. Sandbox start time can become long if the app code imports large number of libraries. For this reason use good judgement when adding code dependencies.
If you do need to depend on a large library for some operations, consider loading asynchronously on an as-needed basis.
Your app code will exit
Assume your code will exit between requests. Any data not stored in the appspace directory may be lost as soon as you are done sending a response to a request.
No outbound requests 🚫
Dropserver apps can not make outbound requests (meaning they can not initiate contact with other servers).
Note:
This is a temporary restriction that will be lifted as soon as an adequate permission model is written. See this blog post for the gory details.
Frontend and clients
You are free to build any frontend you like for your Dropserver app. From server-side HTML to PWA SPA to a native app, anything is possible so long as it makes HTTP requests to the backend.
Strict Content Security Policy
One caveat to keep in mind is that all responses from a Dropserver appspace have a strict Content Security Policy! The policy in the current version of Dropserver is: default-src: 'self'
.
Note:
This strict CSP even prevents inline styles and a <style>
tags from working in your HTML. I’m contemplating loosening the CSP to allow this. Please comment if you feel strongly one way or another.
Example frontend and clients:
Generate HTML server-side
Generate HTML server-side using a template language of your choice and serve CSS as static files. See the Secret Santa app code for an example using Mustache and one static CSS file.
Progressive Web App
Create a PWA Single-Page Application that is built when packaging the app and served statically. See the Leftovers code for an example PWA SPA using Vue.
This is a great way of providing near-native app experience without dealing with app stores.
Native app
Create a native app for your target environment (smartphone, desktop OS, or CLI) and interact with your app via a JSON API. Your native app would be packaged and distributed separately and should include a way to specify the appspace domain.
Note: authentication of appspace users from a native app has not been developed yet.
Server-Side Rendered 🤔
An SSR app may work as a Dropserver application, but it’s untested. First your SSR code has to run in Deno, while most assume Node. Second, routing may be an issue.