Getting started
Edit on GitHubWhat is Primate?
Primate is a framework for building web applications primarily focused on flexibility and developer freedom.
Other frameworks lock you in. Primate sets you free, offering you unlimited choice in mixing and matching different tools that work best for you, rather than forcing to you accept the limitations and design decisions made by others.
First steps
To create a simple app that responds with a 200 OK
saying "Hello, world!"
at its index route (/
), create a project directory and a file in routes
.
export default {
get() {
return "Hello, world!";
},
};
To start your app, run npx -y primate@latest
and point your browser to
http://localhost:6161.
bun --bun x primate
.
If you're a Deno user, use deno run -A npm:primate
to run Primate.{ "type": "module" }
to your
package.json
file for Node to treat JavaScript files as ESM.Multi-language support in routes
Primate isn't limited to JavaScript for your backend code. If you're a Go developer, you can easily do the same with a Go route.
func Get(request Request) any {
return "Hello, world!";
}
Or with a Python route if you're a Python developer.
def get(request):
return "Hello, world!"
Same as before, run npx -y primate@latest
and point your browser to
http://localhost:6161 to run your route.
Serving HTML
Before we continue, we recommend installing Primate by issuing
npm install primate
in your project directory, both to make its exports
available within your project and to pin the version you're using.
Building on the last example, imagine you wanted to add a form to your page and redirect users who have submitted the form to a success page. This requires first changing the previous route to show a form.
import view from "primate/handler/view";
export default {
get() {
return view("form.html");
},
};
We also need to create an HTML component for the form.
<form>
<label for="name">Enter name</label>
<input type="text" id="name" required />
<label for="name">Enter age</label>
<input type="number" id="age" required />
</form>
You may have noticed in our first example that we simply returned a string from
the route function, which was then translated to a 200 OK
response with
content type text/plain
. Primate can detect the content type to use based on
the return type, but only where it makes sense. To return HTML (content type
text/html
), we need to use the explicit request handler view
which we
imported. It accepts the name of a component file and renders it from the
components
directory.
If you now go to http://localhost:6161, you will see an HTML form.
Next we need to handle the form submission. We'll do that by adding a post
function to our route.
import view from "primate/handler/view";
import redirect from "primate/handler/redirect";
export default {
get() {
return view("form.html");
},
post(request) {
const { name, age } = request.body;
if (name !== undefined && age !== undefined) {
return redirect("/success");
}
return redirect("/");
},
};
Every route function in Primate accepts a request
parameter that contains
request data, including the request body if applicable. Here, Primate
deserialized the form for us into request.body
so we can easily get its
fields.
In case both name
and age
are set, we redirect the user to /success
by
using the redirect
handler Primate provides.
While we did specify in our HTML that both name
and age
are required
fields, we need to account for a possible client-side manipulation, so in the
alternative case that the fields aren't set, we simply redirect back to our
form.
All that's left is the success page, which we will handle by creating an additional route file.
export default {
get() {
return "Thank you for submitting your data, we will get back to you.";
},
};
Using a frontend framework
Beyond pure HTML, Primate supports a variety of frontend frameworks. Here is the same code as before, in Svelte.
First add Svelte support by issuing npm install @primate/svelte
and
loading it your configuration file.
import svelte from "@primate/svelte";
export default {
modules: [
svelte(),
],
};
For Svelte, you will also need to install Svelte itself, npm install svelte@4
.
Now change your route to serve a Svelte component.
import view from "primate/handler/view";
export default {
get() {
return view("Form.svelte", { name: "Donald" });
},
post(request) {
const { name, age } = request.body;
if (name !== undefined && age !== undefined) {
return redirect("/success");
}
return redirect("/");
},
};
Create the Svelte component.
<script>
export let name;
</script>
<form>
<label for="name">Enter name</label>
<input type="text" id="name" bind:value={name} required />
<label for="name">Enter age</label>
<input type="number" id="age" required />
</form>
If you now go to http://localhost:6161, you will see an HTML form -- rendered by Svelte.
Deeper dive
Now that we've built a trivial use case with form submission, we can start diving a bit deeper into the platform itself and what it offers.
You don't have to read this entire guide to get productive with Primate. If you prefer a hands-on approach, you can jump in directly into coding and refer back to it as necessary.
By running npm create primate@latest
, you can scaffold a fresh project. This
TUI will walk you step by step in creating a project from scratch, generating a
configuration file and including additional modules.
Alternatively you can clone the Primate template app repository and start looking around. It features an exhausive example app that includes various additional frontend frameworks as well as a bundler, a session manager and a data store.
Goals
Primate strives for technical excellence, with a small core codebase and a variety of officially supported modules that extend it. This translates into three goals.
Expressive
- Routes are pure functions that transform requests into responses
- Route functions accept a prepared request object with easily accessible
body
,path
,query
,cookies
andheaders
fields
Minimal
- No dependencies aside from
rcompat
, a JavaScript interoperability & runtime compatibility layer - Under 1K lines of JavaScript code (Express > 1.8K, Fastify > 5.3K)
Extensible
- Different hooks available (
load
,init
,build
,serve
,handle
,route
,context
) - Officially supported modules that are updated alongside Primate itself
Resources
If you have a question that this guide doesn't cover, consider consulting the code itself, asking in chat, or raising an issue.
Code
Primate's monorepo contains the core platform code under
packages/primate
as well code for the official modules and the
website under packages
.
Chat
Primate has an IRC channel at #primate
on irc.libera.chat. You can use the
Libera web client if you don't have an IRC client installed.
Issues
Feel free to open an issue on Primate's issue tracker if you find a bug or have a feature request.