Zero configuration web framework.
Features | Installation | Getting Started | Examples | Running on Cloud | Docs
Zero is a web framework to simplify modern web development. It allows you to build your application without worrying about package management or routing. It's as simple as writing your code in a mix of Node.js, React, HTML, MDX, Vue, Svelte, Python, and static files and putting them all in a folder. Zero will serve them all. Zero abstracts the usual project configuration for routing, bundling, and transpiling to make it easier to get started.
An example project with different types of pages, all in one folder:
Auto Configuration: Your project folder doesn't require config files. You just place your code and it's automatically compiled, bundled and served.
File-system Based Routing: If your code resides in ./api/login.js
it's exposed at http://<SERVER>/api/login
. Inspired by good ol' PHP days.
Auto Dependency Resolution: If a file does require('underscore')
, it is automatically installed and resolved. You can always create your own package.json
file to install a specific version of a package.
Multiple Languages: Zero is designed to support code written in many languages all under a single project. Imagine this:
- Exposing your Tensorflow model as a python API.
- Using React pages to consume it.
- Writing the user login code in Node.js.
- Your landing pages in a mix of HTML or Markdown/MDX.
All under a single project folder as a single web application.
You can play with Zero without installing it locally. Click the button below:
You can install zero
globally by:
npm install -g zero
Let's start by making a website that tells us server time.
First we need to create an API endpoint in Node.js to tell us time in JSON.
Create a new folder and add a new file time.js
in that folder. In this file, export a function that accepts Request
and Response
objects (like Express):
// time.js
const moment = require("moment");
module.exports = (req, res) => {
var time = moment().format("LT"); // 11:51 AM
res.send({ time: time });
};
Once saved, you can cd
into that folder and start the server like this:
zero
Running this command will automatically install any dependencies (like momentjs here) and start the web server.
Open this URL in the browser: http://localhost:3000/time
You just created an API endpoint π:
Keep the server running. Now let's consume our API from a React page, create a new file index.jsx
and add the following code:
// index.jsx
import React from "react";
export default class extends React.Component {
static async getInitialProps() {
var json = await fetch("/time").then(resp => resp.json());
return { time: json.time };
}
render() {
return <p>Current time is: {this.props.time}</p>;
}
}
This is a standard React component. With one additional hook for initial data population:
getInitialProps
is an async
static method which is called by zero
when the page loads. This method can return a plain object which populates props
.
Now go to this URL: http://localhost:3000/
and you should see the current server time rendered by React while fetch
-ing an API endpoint you created earlier:
zero
automatically bundles your code and supports server-side rendering. You don't need to fiddle with webpack anymore.
That's it! You just created a web application.
Zero serves routes based on file structure. If you write a function that resides in ./api/login.js
it's exposed at http://<SERVER>/api/login
. Similarly if you put a React page under ./about.jsx
it will be served at http://<SERVER>/about
Create a ./404.js
file (or a .jsx
, .vue
, .py
, etc) to catch all requests to pages that don't exist.
Zero decides routes based on file structure. Most projects also require dynamic routes like /user/luke
and /user/anakin
. Where luke
and anakin
are parameters. Zero natively supports this type of routes: any file or folder that starts with $ is considered a dynamic route.
So if you create ./user/$username.js
and then from browser visit /user/luke
, Zero will send that request to $username.js
file and set req.params
to {username: 'luke'}
. Code for this:
/*
project/
βββ user/
βββ $username.js <- this file
*/
module.exports = function(req, res) {
console.log(req.params); // = {username: 'luke'} when user visits /user/luke
res.send({ params: req.params });
};
Parameters apply to folder-names too. Another example: if you want to cater /user/luke/messages
route, you can handle this with following directory structure:
project/
βββ user/
βββ $username/
βββ index.js
βββ messages.js
index.js
handles/user/:username
routes.messages.js
handles/user/:username/messages
routes.
Tip: $
is used by Bash for variables. So it might be confusing when you do cd $username
or mkdir $username
and nothing happens. The right way to do this is escaping the $
ie. cd \$username
or mkdir \$username
.
- Node.js (JavaScript & TypeScript)
- React (JavaScript & TypeScript)
- HTML
- Markdown / MDX
- Vue
- Svelte
- Python
If a file does require('underscore')
, the latest version of that package is automatically installed from NPM and resolved.
But sometimes you want to use a specific version or a dependency from a private repository. You can do that by creating a package.json
in your project folder and adding dependencies to it. Zero will install those versions instead.
Example (package.json):
{
"name": "myapp",
"dependencies": {
"underscore": "^1.4.0",
"private_ui_pkg": "git+https://github.com/user/repo.git"
}
}
By default, Zero compiles and serves all files in your project. But most of the time, you don't want to expose a certain file or folder. You can add .zeroignore
file to prevent Zero from serving that file as a valid route.
In a project with following structure:
- project/
-- components/
-- api/
-- index.jsx
You probably don't want to expose components
directory. To do this, you can create .zeroignore
file with the following text:
components
This will prevent your users from accessing /components
path directly.
A Zero app is a regular Node.js server. But zero doesn't create package.json
in your project folder. For most clouds (Heroku, EC2, etc) you probably need package.json
. You can create one similar to this:
{
"name": "my-zero-app",
"scripts": {
"dev": "zero",
"build": "zero build",
"start": "NODE_ENV=production zero"
},
"dependencies": {
"zero": "latest"
}
}
- We add dependency
zero
, so the cloud builder can installzero
on your server. - Add a
"start"
command and also setNODE_ENV
toproduction
so zero generates minified builds and disabled HMR etc. - Add a
"build"
command to pre-build all files to speed up cold boots. Don't forget to runnpm run build
in your build step (in your Dockerfile,heroku-postbuild
, etc) - If your cloud has an option to set environment variables / config variables (to store secrets, API keys, etc), these variables are automatically passed to your code and can be accessed as you normally would. In node, using
process.env.MY_SECRET
. You should never commit your local.env
file to your code.
After this, you can follow the instructions from your cloud provider for deploying a Node.js app.
We provide an official Buildpack for Heroku. If you are deploying on Heroku, this is the easiest way.
By default zero runs on port 3000
. You can change this by setting the PORT
environment variable, like this: PORT=8080 zero
Here is a basic Dockerfile
you can add to your zero application to Docker-ize it:
FROM node:alpine
# Install dependencies via apk
RUN apk update && apk upgrade \
&& apk add --no-cache python python3 g++ make \
&& rm -rf /var/cache/apk/*
# Install zero globally
RUN npm install --quiet --no-progress --unsafe-perm -g zero
# Add current folder to /app
ADD . /app
# Run zero in production mode
ENV NODE_ENV production
# Generate bundles
RUN zero build
# Expose port
ENV PORT 80
EXPOSE 80
WORKDIR /app
CMD ["zero"]
Please see our CONTRIBUTING.md
Zero is Apache-2.0 licensed.