Skip to content

basebox Product Tour

Introduction

This page describes the steps to create a simple TODO app with basebox as the API server and a Web client based on Vue.
If you want, you can skip the description and jump right to the Demo by clicking the button below.

Skip to Demo

Step 1 - Create a GraphQL Schema

You start by defining the app's data types and operations in a standard GraphQL schema file, with some basebox-specific annotations.

To get more info about basebox GraphQL Annotations, read the basebox compiler (bbc) guide.

Click me to view the GraphQL schema
GraphQL
directive @bb_primaryKey on FIELD_DEFINITION
directive @bb_resolver on FIELD_DEFINITION

"""
Task list.
"""
type List {
  id: ID!
  title: String!
  tasks: [Task]
  user: User!
}

"""
Task or todo item.
"""
type Task {
  id: ID!
  title: String!
  description: String,
  completed: Boolean!
  user: User!
  list: List!
}

"""
User type; owner of lists and tasks
"""
type User {
  username: String! @bb_primaryKey
  name: String
  tasks: [Task]
  lists: [List]
}


type Query {

  """
  Get a user, this will be used to get the current user as well as the user's lists and tasks.
  """
  getUser(
    username: String!
  ): User @bb_resolver(_type: SELECT, _object: User, _filter: { username: { _eq: "$username" } })

}

type Mutation {

  createUser(
    username: String!,
    name: String!
  ): User  @bb_resolver(_type: INSERT, _object: User, _fields: { 
    username: "$username", 
    name: "$name" 
    })

  createList(
    title: String!
    user: User! # username needs to be specified as it's non-nullable
  ): List @bb_resolver(_type: INSERT, _object: List, _fields: { 
    title: "$title", 
    user: "$user" 
  })

  updateList(
    id: ID!,
    title: String!
  ): List @bb_resolver(_type: UPDATE, _object: List, _filter: { id: { _eq: "$id" } },  
                       _fields: { 
                        title: "$title" 
                      })

  deleteList(id: ID!): List @bb_resolver(_type: DELETE, _object: List, _filter: { id: { _eq: "$id" } })

  createTask(
    title: String!,
    description: String,
    completed: Boolean!, # default not implemented yet, this needs to be added as it's non-nullable
    list: List! # list needs to be specified as it's non-nullable
    user: User! # username needs to be specified as it's non-nullable
  ): Task @bb_resolver(_type: INSERT, _object: Task, _fields: { 
    title: "$title", 
    description: "$description", 
    completed: "$completed", 
    list: "$list", 
    user: "$user" 
  })

  updateTask(
    id: ID!,
    title: String,
    description: String,
    completed: Boolean,
    list: List
  ): Task @bb_resolver(_type: UPDATE, _object: Task, _filter: { id: { _eq: "$id" } }, _fields: { 
    title: "$title", 
    description: "$description", 
    completed: "$completed", 
    list: "$list" 
  })

  deleteTask(id: ID!): Task @bb_resolver(_type: DELETE, _object: Task, _filter: { id: { _eq: "$id" } })

}
directive @bb_primaryKey on FIELD_DEFINITION
directive @bb_resolver on FIELD_DEFINITION

"""
Task list.
"""
type List {
  id: ID!
  title: String!
  tasks: [Task]
  user: User!
}

"""
Task or todo item.
"""
type Task {
  id: ID!
  title: String!
  description: String,
  completed: Boolean!
  user: User!
  list: List!
}

"""
User type; owner of lists and tasks
"""
type User {
  username: String! @bb_primaryKey
  name: String
  tasks: [Task]
  lists: [List]
}


type Query {

  """
  Get a user, this will be used to get the current user as well as the user's lists and tasks.
  """
  getUser(
    username: String!
  ): User @bb_resolver(_type: SELECT, _object: User, _filter: { username: { _eq: "$username" } })

}

type Mutation {

  createUser(
    username: String!,
    name: String!
  ): User  @bb_resolver(_type: INSERT, _object: User, _fields: { 
    username: "$username", 
    name: "$name" 
    })

  createList(
    title: String!
    user: User! # username needs to be specified as it's non-nullable
  ): List @bb_resolver(_type: INSERT, _object: List, _fields: { 
    title: "$title", 
    user: "$user" 
  })

  updateList(
    id: ID!,
    title: String!
  ): List @bb_resolver(_type: UPDATE, _object: List, _filter: { id: { _eq: "$id" } },  
                       _fields: { 
                        title: "$title" 
                      })

  deleteList(id: ID!): List @bb_resolver(_type: DELETE, _object: List, _filter: { id: { _eq: "$id" } })

  createTask(
    title: String!,
    description: String,
    completed: Boolean!, # default not implemented yet, this needs to be added as it's non-nullable
    list: List! # list needs to be specified as it's non-nullable
    user: User! # username needs to be specified as it's non-nullable
  ): Task @bb_resolver(_type: INSERT, _object: Task, _fields: { 
    title: "$title", 
    description: "$description", 
    completed: "$completed", 
    list: "$list", 
    user: "$user" 
  })

  updateTask(
    id: ID!,
    title: String,
    description: String,
    completed: Boolean,
    list: List
  ): Task @bb_resolver(_type: UPDATE, _object: Task, _filter: { id: { _eq: "$id" } }, _fields: { 
    title: "$title", 
    description: "$description", 
    completed: "$completed", 
    list: "$list" 
  })

  deleteTask(id: ID!): Task @bb_resolver(_type: DELETE, _object: Task, _filter: { id: { _eq: "$id" } })

}

Step 2 - Compile the Schema

Let's compile the schema with the basebox compiler (bbc):

bbc --prefix=bb_todo -fbasebox compiler (bbc) version 1.0.0  Writing output files to '/home/username/basebox/schema'    Data model: 'bb_todo-datamodel.sql'    Resolver:   'bb_todo-resolver.toml'    Type map:   'bb_todo-typemap.json'Done.

The compiler creates, among other things, an SQL script that you can use to initialize your database, e.g. it creates the required tables etc.

The other two files become important later; they are used by basebox to translate incoming GraphQL requests to SQL automatically at blazing speed - without you having to write resolver functions!

Click me to view the generated SQL script
sql
CREATE EXTENSION IF NOT EXISTS pgcrypto; 

CREATE TABLE "List" (
  "id" UUID DEFAULT gen_random_uuid() NOT NULL,
  "title" VARCHAR NOT NULL,
  "user_username" VARCHAR NOT NULL 
);

CREATE TABLE "Task" (
  "id" UUID DEFAULT gen_random_uuid() NOT NULL,
  "title" VARCHAR NOT NULL,
  "description" VARCHAR,
  "completed" BOOLEAN NOT NULL,
  "user_username" VARCHAR NOT NULL,
  "list_id" UUID NOT NULL 
);

CREATE TABLE "User" (
  "username" VARCHAR NOT NULL,
  "name" VARCHAR 
);

ALTER TABLE "List" ADD PRIMARY KEY ("id");
ALTER TABLE "Task" ADD PRIMARY KEY ("id");
ALTER TABLE "User" ADD PRIMARY KEY ("username");
ALTER TABLE "List" ADD CONSTRAINT fk_list_1 FOREIGN KEY ("user_username") REFERENCES "User" ("username");
ALTER TABLE "Task" ADD CONSTRAINT fk_task_2 FOREIGN KEY ("user_username") REFERENCES "User" ("username");
ALTER TABLE "Task" ADD CONSTRAINT fk_task_3 FOREIGN KEY ("list_id") REFERENCES "List" ("id");
CREATE EXTENSION IF NOT EXISTS pgcrypto; 

CREATE TABLE "List" (
  "id" UUID DEFAULT gen_random_uuid() NOT NULL,
  "title" VARCHAR NOT NULL,
  "user_username" VARCHAR NOT NULL 
);

CREATE TABLE "Task" (
  "id" UUID DEFAULT gen_random_uuid() NOT NULL,
  "title" VARCHAR NOT NULL,
  "description" VARCHAR,
  "completed" BOOLEAN NOT NULL,
  "user_username" VARCHAR NOT NULL,
  "list_id" UUID NOT NULL 
);

CREATE TABLE "User" (
  "username" VARCHAR NOT NULL,
  "name" VARCHAR 
);

ALTER TABLE "List" ADD PRIMARY KEY ("id");
ALTER TABLE "Task" ADD PRIMARY KEY ("id");
ALTER TABLE "User" ADD PRIMARY KEY ("username");
ALTER TABLE "List" ADD CONSTRAINT fk_list_1 FOREIGN KEY ("user_username") REFERENCES "User" ("username");
ALTER TABLE "Task" ADD CONSTRAINT fk_task_2 FOREIGN KEY ("user_username") REFERENCES "User" ("username");
ALTER TABLE "Task" ADD CONSTRAINT fk_task_3 FOREIGN KEY ("list_id") REFERENCES "List" ("id");

This is the command that you would use to initialize your database with the SQL script above:

sh
psql -U username -d dbname -f bb_todo-datamodel.sql
psql -U username -d dbname -f bb_todo-datamodel.sql

Step 3 - Install basebox

Note

Since basebox runs on your own machines, you have to install it yourself. We provide a set of Docker containers to make this as easy as possible.

basebox basically consists of two components: broker, the GraphQL server and dbproxy, the database proxy. broker receives GraphQL requests from your frontend apps and translates them to SQL. The database proxy receives the SQL requests and forwards them to the database.

  • This tutorial's purpose is to give an overview about what basebox does and how it works. For a detailed installation guide, see our Installation Guide.
  • An installation script that handles most of these steps is being prepared, but for now you have to do it manually. For the sake of this tutorial, we assume that you have installed basebox in /opt/basebox.
  • We are also working on a hosted version of basebox, so you don't have to install it yourself. If you are interested in this, please contact us.

Step 4 - Configure basebox

broker Configuration File

The broker is the party that handles authorization, so its config file is where this is configured. basebox uses OpenID Connect, and so far we have tested Keycloak and Auth0. See the [auth] section in the example configuration below; of course, you have to substitute your own values for iss and aud (audience).

Click me to view the config file
toml
[generic]
# log level; can be error, warn, info, debug, trace
log_level = "trace"

[graphql]
# path and file name to GraphQL schema file
schema_file = "todo_schema.graphql"
allow_introspection = true

[proxy]
# host name or IP of basebox DB proxy
host = "localhost"
port = 8081
# Whether to use http or https to connect to the proxy
tls = false

[server]
# Host name of the broker (GraphQL server)
host = "127.0.0.1"

# Port number; default is 80 for http, 443 for https
port = 8080

# number of HTTP server threads to spawn; default is one per CPU core
workers = 2

[auth]
# Base URL to the identity provider (OAuth2/OpenID Connect server, e.g. Keycloak)
iss = "https://basebox-test-1.eu.auth0.com/"
aud = "basebox-todo"
[generic]
# log level; can be error, warn, info, debug, trace
log_level = "trace"

[graphql]
# path and file name to GraphQL schema file
schema_file = "todo_schema.graphql"
allow_introspection = true

[proxy]
# host name or IP of basebox DB proxy
host = "localhost"
port = 8081
# Whether to use http or https to connect to the proxy
tls = false

[server]
# Host name of the broker (GraphQL server)
host = "127.0.0.1"

# Port number; default is 80 for http, 443 for https
port = 8080

# number of HTTP server threads to spawn; default is one per CPU core
workers = 2

[auth]
# Base URL to the identity provider (OAuth2/OpenID Connect server, e.g. Keycloak)
iss = "https://basebox-test-1.eu.auth0.com/"
aud = "basebox-todo"

dbproxy Configuration File

The dbproxy is the party that connects to the database. Its configuration file is where you configure the database connection; see the example configuration below.

Click me to view the dbproxy config file
toml
[generic]
# log level; can be error, warn, info, debug, trace
log_level = "trace"

[oidc_config]
# Incoming ID tokens are validated using, among other, the following fields.
# Contents of 'iss' field, usually the URL of the authetnication realm
iss = "https://basebox-test-1.eu.auth0.com/"
# Access token audience field
aud = "basebox-todo"

[graphql]
# path and file name to GraphQL schema file
schema_file = "todo_schema.graphql"
# Path and file name of the resolver map file
resolver_map_file = "bb_todo-resolver.toml"
# Path and file name of the type map file
type_map_file = "bb_todo-typemap.json"

[database]
# Type of database; currently, only "postgres" is suppoerted
db_type = "postgres"
# The host where the DB server is runnung
host = "localhost"
# Port the DB server is listening at
port = 5432
# Database name
db_name = "bb_todo"
username = "bb_todo"
password = "basebox"
ssl_mode = "no"

[server]
# Host name of (this) proxy server
host = "localhost"

# Port number; default is 80 for http, 443 for https
port = 8081
[generic]
# log level; can be error, warn, info, debug, trace
log_level = "trace"

[oidc_config]
# Incoming ID tokens are validated using, among other, the following fields.
# Contents of 'iss' field, usually the URL of the authetnication realm
iss = "https://basebox-test-1.eu.auth0.com/"
# Access token audience field
aud = "basebox-todo"

[graphql]
# path and file name to GraphQL schema file
schema_file = "todo_schema.graphql"
# Path and file name of the resolver map file
resolver_map_file = "bb_todo-resolver.toml"
# Path and file name of the type map file
type_map_file = "bb_todo-typemap.json"

[database]
# Type of database; currently, only "postgres" is suppoerted
db_type = "postgres"
# The host where the DB server is runnung
host = "localhost"
# Port the DB server is listening at
port = 5432
# Database name
db_name = "bb_todo"
username = "bb_todo"
password = "basebox"
ssl_mode = "no"

[server]
# Host name of (this) proxy server
host = "localhost"

# Port number; default is 80 for http, 443 for https
port = 8081

Step 5 - Try it!

With everything in place, you can start sending GraphQL requests to basebox. Assuming your basebox instance runs on localhost, you can send GraphQL requests to http://localhost:8080/graphql.

We prepared a demo that shows everything in action:


Launch basebox Demo