Documentation

Intro

Orbitype is a managed headless CMS that supports a variety of data sources. Connect S3 compatible object storages, SQL databases and more. Use a simple RESTful API to modify your data programtically or edit it with an automatically generated visual GUI.

Getting started

After signing up, you'll be prompted to create your first free project. Choose a name, confirm and you'll be redirect to the explore page. Open the README.json file that was automatically created by clicking on it. This file serves as an example of Orbitype's capabilities. Use the toolbar to switch between the visual tree mode and the code mode. Changes in one mode will automatically be reflected in the other. Developers can define a data schema in code, that non-technical people can then view and edit visually.

When you're done, return to the explore page. Click on the three dots of the README.json to open a context menu. You may also right click anywhere on the file instead. This menu allows you to delete or rename the file, among other things. To create a new file, right click anywhere into the empty area. You may also click on the three dots in the top right corner instead. In this menu, you can choose to create a new file or folder or upload a file.

Managing Projects

Click on Project in the top navigation bar to edit the settings. Each project has a name, a list of members, connectors and API keys. The members are the users who can access the project. For each data source (S3, SQL, ...) you must add a matching connector. For programatic access, create API keys. You can create new projects and manage existing ones on your account page.

API: Basics

Orbitype exposes all full-featured RESTful API that you can connect to from your own code. Read, create, update and delete items with simple HTTP requests.

To get started, go to your project's setting page and generate a new API key. Each key is scoped to exactly one connector. Make sure to include it in each request as a custom X-API-KEY header. Sending an HTTP OPTIONS request to the API base URL will return the ids of the project and connector the key is scoped to.

OPTIONS https://core.orbitype.com/api
X-API-KEY: your-key-here
{
  "projectId": "your-project-uuid",
  "connectorId": "your-connector-uuid"
}

API: S3

For S3 connectors, the API exposes endpoints to read and write files.

Index

Pass a folder path to list the files within.

GET https://core.orbitype.com/api/s3/v1
    ?path=blog/posts/
[
  "blog/posts/alpha.json",
  "blog/posts/bravo.json",
  "blog/posts/charlie.json"
]

Show

Pass a file path to get its body.

GET https://core.orbitype.com/api/s3/v1
    ?path=blog/posts/alpha.json
{
  "title": "Alpha",
  "text": "Lorem ispum dolor."
}

Store

Create or overwrite a text file by passing a path and body.

PUT https://core.orbitype.com/api/s3/v1

{
  "path": "blog/posts/neo.json",
  "body": { "title": "Neo", "text": "Another post" }
}
blog/posts/neo.json

Destroy

Delete a file by passing a path.

DELETE https://core.orbitype.com/api/s3/v1

{
  "path": "blog/posts/alpha.json"
}
blog/posts/alpha.json

Copy

Copy a file by passing paths for from and to.

POST https://core.orbitype.com/api/s3/v1/copy

{
  "from": "blog/posts/alpha.json",
  "to": "blog/posts/alpha-copy.json"
}
blog/posts/alpha-copy.json

Upload

To create a non-text media file, send a multipart/form-data request. Pass the path as a query param and the file as part of the form request.

POST https://core.orbitype.com/api/s3/v1/upload
    ?path=blog/media/cover.jpg

Content-Type: multipart/form-data
Content-Disposition: form-data; name="file"; filename="cover.jpg"
Content-Type: image/jpeg
blog/media/cover.jpg

API: SQL

For SQL connectors, the API exposes an endpoint to execute prepared SQL statements. The HTTP Method conveys intention, but you may pass any valid combination of sql and bindings.

Index

Use a SELECT statement to fetch a list of rows.

GET https://core.orbitype.com/api/sql/v1
    ?sql=SELECT * FROM posts
[
  {
    "id": 1,
    "title": "Alpha",
  },
  {
    "id": 2,
    "title": "Bravo",
  },
  {
    "id": 3,
    "title": "Charlie",
  }
]

Show

Use a WHERE clause to filter it down.

GET https://core.orbitype.com/api/sql/v1
    ?sql=SELECT * FROM posts WHERE id = :id
    &bindings[id]=2
[
  {
    "id": 2,
    "title": "Bravo",
  }
]

Create

Use an INSERT statement to create a new row.

POST https://core.orbitype.com/api/sql/v1

{
  "sql": "INSERT INTO posts (title) VALUES (:title) RETURNING *",
  "bindings": {
    "title": "Delta"
  }
}
[
  {
    "id": 4,
    "title": "Delta",
  }
]

Update

Use an UPDATE statement to change an existing row.

POST https://core.orbitype.com/api/sql/v1

{
  "sql": "UPDATE posts SET title = :title WHERE id = 4 RETURNING *",
  "bindings": {
    "title": "New title"
  }
}
[
  {
    "id": 4,
    "title": "New title",
  }
]

Destroy

Use a DELETE statement to remove an existing row.

POST https://core.orbitype.com/api/sql/v1

{
  "sql": "DELETE FROM posts WHERE id = :id RETURNING *",
  "bindings": {
    "id": 4
  }
}
[
  {
    "id": 4,
    "title": "Delta",
  }
]

Integration: Nuxt

Orbitype can easily be integrated into the popular Vue Framework Nuxt.

Quick start

Create a new nuxt project with a pages/index.vue. For now, hardcode the title and text of the page.

<script setup>
  const page = {
    "title": "Home",
    "text": "Lorem ipsum dolor sit amet."
  }
</script>

<template>
  <main>
    <h1>{{ title }}</h1>
    <p>{{ text }}</p>
  </main>
</template>

Next, we'll move this data out of the code and into Orbitype. Create a new Orbitype project with a pages/home.json. Open the file, switch to code mode and copy over your data:

{
  "title": "Home",
  "text": "Lorem ipsum dolor sit amet."
}

Return to the Vue file and replace the hardcoded data with a fetch call:

const page = await $fetch(import.meta.env.VITE_S3_URL + "/pages/home.json") 

It is recommended to load your S3 base URL from an environment variable. You can find your S3 base URL in the project settings. Alternatively, choose Get file URL from an item's context menu.

You can now edit the page's data in Orbitype's visual tree mode. Try experimenting with more complex structures like arrays and nested objects. Orbitype supports any data structure than can be declared with JSON.

Advanced usage

For more complex use cases, you may attach a database to your project. With Orbitype's API, you can then implement all kinds of CRUD operations.

Start by adding a new connector in the project settings. Choose the SQL implementation and provide your databases's credentials. They will be AES-256 encrypted on save. Create an API key for the new connector and store it as an env variable.

In your Nuxt project, create a server/api/comments/index.get.js. Use Orbitype's API to get a list of comments:

import qs from "qs" // npm install qs

export default defineEventHandler(async (event) => {
  const bindings = getQuery(event)

  let sql = "SELECT * FROM comments"
  if (bindings.id) sql += " WHERE id = :id"
  else sql += " ORDER BY created_at DESC"

  return await $fetch(
    "https://core.orbitype.com/api/sql/v1" + qs.stringify({ sql, bindings }),
    { headers: { "X-API-KEY": import.meta.env.ORBITYPE_API_KEY } }
  )
})

Similarily, you can create a new comment with an insert statement. Just create a server/api/comments/index.post.js like this:

export default defineEventHandler(async (event) => {
  const bindings = await readBody(event)

  let sql = "INSERT INTO comments (text)"
  sql += " VALUES (:text)"

  return await $fetch(
    "https://core.orbitype.com/api/sql/v1",
    {
      method: "POST",
      body: { sql, bindings },
      headers: { "X-API-KEY": import.meta.env.ORBITYPE_API_KEY },
    }
  )
})

In your Vue templates, call those endpoints and use the data:

<script setup>
  const comments = await $fetch("/api/comments")

  async function createComment() {
    await $fetch("/api/comments", {
      method: "POST",
      body: { text: "Hello, world!" },
    })
  }
</script>

<template>
  <ul>
    <li v-for="c of comments">
      <h2>{{ c.text }}</h2>
      <time>{{ c.created_at }}</time>
    </li>
  </ul>
</template>

Cookbook: sections

Modern webpages are often composed of many different sections. Things like hero banners, testimonials, FAQs or simple text blocks. Developers implement these as reusable components with props. But how do we allow non-technical people to use them in Orbitype?

From HTML to JSON

Think of your pages as stack of sections from top to bottom. Each section is self-contained and configurable with props. Without a CMS, your templates might look something like this:

<!--home.vue-->
<template>
  <SectionHero title="Orbitype" subtitle="A modern CMS"/>
  <SectionFigure img=".../foo.png" caption="Lorem ipsum"/>
</template>

To make this dynamic, start by moving the hard-coded content over to Orbitype. Translate the HTML to an equivalent JSON structure holding the props. Define the component to render in the special _orbi field.

// home.json
{
  "sections": [
    {
      "title": "Orbitype",
      "subtitle": "Lorem ipsum",
      "_orbi": { "component": "SectionHero" }
    },
    {
      "img": ".../foo.png",
      "caption": "Lorem ipsum",
      "_orbi": { "component": "SectionFigure" }
    }
  ]
}

Dynamic templates

By using a glob import and dynamic resolution, you can create a dynamic AnySection component.

<!--AnySection.vue-->
<script setup lang="ts">
  const all = import.meta.glob("~/components/sections/*.vue", { eager: true })
  const components = Object.values(all).map((x) => x.default)
  defineProps<{ data: Record<string, any> }>()
</script>

<template>
  <component
    :is=" components.find((x) => x.__name === data._orbi.component)"
    v-bind="data"
  />
</template>

Next, replace your hard-coded list of components with a loop of AnySection. Pass the data from Orbitype as the array to loop over.

<!--home.vue-->
<script setup lang="ts">
  const page = await $fetch(import.meta.env.VITE_S3_URL + "/pages/home.json")
</script>

<template>
  <AnySection v-for="section of page.sections" :data="section"/>
</template>

And you're done! You can now add, remove, modify and reorder sections in Orbitype. All without touching a single line of code.