Write a schema only absolutely no code backend server with Node.js and Teo!
Tags: node, teo, graphql, prisma
Published: 2024-02-20 21:46:00

---
title: Write a schema only absolutely no code backend server with Node.js and Teo!
published: true
description: This article guides you to create a schema only web server with no code under 5 minutes.
tags: 
  - nodejs
  - Teo
  - GraphQL
  - Prisma
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hysv64nxeh9jov8thhh8.png
# published_at: 2024-02-20 21:51 +0000
---

Let's start with the simplest use case: Install Teo and start a schema only app. By the end of this tutorial, you gain understanding of how Teo schema composites and how to write them.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hysv64nxeh9jov8thhh8.png)

## Installation

Install Node.js if it hasn't been installed. There are several ways to install Node.js. You may download the installer from the [official website](https://nodejs.org/), or install it with tools like [NVM](https://github.com/nvm-sh/nvm). After installation, run this command to verify its installation.

```sh
node --version
```
```txt
v21.6.2
```

If you see some outputs like `v21.6.2`, great! Node.js is installed successfully.

Although now you can install Teo globally with the `npm` package manager. We highly recommand you to init a Node.js project and install Teo locally. This won't affect other projects that use Teo. To create a directory, init a 
Node.js project, and install Teo, follow these commands.

```sh
mkdir hello-teo-schema-only
cd hello-teo-schema-only
npm init -y
npm install @teocloud/teo
```

Use the `npx` command to verify whether Teo is installed successfully.

```sh
npx teo --version
```
```txt
teo Teo 0.2.4 (Node.js v21.6.2) [CLI]
```

If you see this output, congratulations! And let's move on.

## Editor plugin

We highly recommand you to use [VSCode](https://code.visualstudio.com) as we created 
a plugin for syntax highlight, code diagnostics, auto completion and jump to definition.
Download our plugin from [VSCode marketplace](https://marketplace.visualstudio.com/items?itemName=yeannylam.teo-vscode).

## Write your first schema

In the current working directory, create a file named `schema.teo`.

```sh
touch schema.teo
```

Paste these content into the file.

```teo
connector {
  provider: .sqlite,
  url: "sqlite::memory:"
}

server {
  bind: ("0.0.0.0", 5050)
}

model User {
  @id @autoIncrement @readonly
  id: Int
  @unique @onSet($if($presents, $isEmail))
  email: String
  name: String?
  @relation(fields: .id, references: .authorId)
  posts: Post[]
}
 
model Post {
  @id @autoIncrement @readonly
  id: Int
  title: String
  content: String?
  @default(false)
  published: Bool
  @foreignKey
  authorId: Int
  @relation(fields: .authorId, references: .id)
  author: User
}
```

Let's explain what this schema describes.

* The `connector` block defines to which database this server connects. The content of the block is just a plain object literal with some predefined key value pairs. The expression starts with a `.` is enum variant literal in Teo schema. It's inspired by Apple's Swift programming language and is widely used in schema. The benefits of prefixing with a dot is that it's easy to write and 
complete. Obviously, this server app connects to an in-memory SQLite database.

* The `server` block defines how the server behaves. The syntax is the same with `connector`. These blocks are for configuration, and they are called config blocks in Teo. Guess what it does? The answer is that the server will listen at port `5050`. The thing wrapped by a pair of parenthesis is tuple type. Teo supports a bunch of builtin types including ranges and tuples. Anyway, there are mostly used in config blocks. 

* The `model` keyword defines models. A model represents a database table and a set of auto synthesized HTTP requests to interact with it. The things start with a `@` is called decorator. In Teo schema, plenty of things can be decorated including models, model fields, model relations. A decorator modifies the behavior of the decorated item. The thing start with a `$` is called pipeline. There are one or more pipeline items in a pipeline. A pipeline validates and transforms its input value into its output value, or throw errors if value is invalid. Look at this part: `$if($presents, $isEmail)`. It means when an email value is set, if the value is not null, which is presented, validate the value against `$isEmail`. If the value passed in is not a valid email address, revoke this value and throw an error to the frontend requester. The `@relation` decorator defines a model relation. In this case, a user has many posts and a post has an author. The `fields` argument and `references` argument takes 1 or more primitive fields on the model. These argument describes by which fields the models are connected. The trailing `?` in a type expression indicates the value is optional.

In Teo schema, trailing comma is allowed wherever comma is used. The whole schema langauge is inspired by a lot of languages like TypeScript, Swift, GraphQL and Prisma.
When we design this, we want developers to feel familiar. We take advantages from different programming languages, combine them with our descriptive declarations. Teo schema is quite easy and not something to learn with. If you have trouble with understanding Teo schema, take it easy, join our [Discord group](https://teocloud.io/discord), there 
are people available to help you.

## Add models with many-to-many relations

Append these code to the schema file.

```teo
model Artist {
  @id @autoIncrement @readonly
  id: Int
  name: String
  @relation(through: Perform, local: .artist, foreign: .song)
  songs: Song[]
}

model Song {
  @id @autoIncrement @readonly
  id: Int
  name: String
  @relation(through: Perform, local: .song, foreign: .artist)
  artists: Artist[]
}

@id([.songId, .artistId])
model Perform {
  @foreignKey
  artistId: Int
  @foreignKey
  songId: Int
  @relation(fields: .songId, references: .id)
  song: Song
  @relation(fields: .artistId, references: .id)
  artist: Artist
}
```

In Teo, unlike Ruby on Rails or Prisma, join table of a many-to-many relation is declared explicitly. In a many-to-many relation, `@relation` takes a join model and two direct relations defined on join model. It's quite clear to write everything out in such a short lines of code. Join tables sometimes is meaningful on their own. With it explicitly declared, database migration won't be hard when you are adding fields and contents to it. Notice the id of the model `Perform` is declared at the level of model. This is a compound primary key.

## Run the server

Now run this command to start the server.

```sh
npx teo serve
```
```txt
[2023-12-20 04:58:40] sqlite connector connected for `main` at "sqlite::memory:"
[2023-12-20 04:58:40] Teo 0.2.4 (Node.js v21.6.2, CLI)
[2023-12-20 04:58:40] listening on port 5050
```

If you see the output likt above, congratulation, it starts successfully.
If it cannot start successfully, most of the times, the reason is the 
database setup and configuration.

Now our server is ready to listen and handle requests.

## Create request

Send this request body to `/User/create`.

```json
{
  "create": {
    "email": "ada@teocloud.io",
    "name": "Ada",
    "posts": {
      "create": [
        {
          "title": "Introducing Teo",
          "content": "This post introduces Teo."
        },
        {
          "title": "The next generation framework",
          "content": "Use the next generation technology."
        }
      ]
    }
  },
  "include": {
    "posts": true
  }
}
```
```json
{
  "data": {
    "id": 1,
    "email": "ada@teocloud.io",
    "name": "Ada",
    "posts": [
      {
        "id": 1,
        "title": "Introducing Teo",
        "content": "This post introduces Teo.",
        "published": false,
        "authorId": 1
      },
      {
        "id": 2,
        "title": "The next generation framework",
        "content": "Use the next generation technology.",
        "published": false,
        "authorId": 1
      }
    ]
  }
}
```

In this request, we've created a user with two related posts. If you request this again, you will get a unique constraint violation error. This is the designated behavior as we've declared `email` field of user to be `@unique`.

```text
send again...
```
```json
{
  "error": {
    "type": "ValueError",
    "message": "value is invalid",
    "fields": {
      "create": "unique value duplicated: email"
    }
  }
}
```

Send this request to `/Artist/createMany`.

```json
{
  "create": [
    {
      "name": "Ed Sheeran",
      "songs": {
        "create": [
          {
            "name": "Perfect"
          },
          {
            "name": "Eyes Closed"
          }
        ]
      }
    },
    {
      "name": "Taylor Swift",
      "songs": {
        "create": [
          {
            "name": "Love Story"
          },
          {
            "name": "Red"
          }
        ]
      }
    }
  ],
  "include": {
    "songs": true
  }
}
```
```json
{
  "data": [
    {
      "id": 1,
      "name": "Ed Sheeran",
      "songs": [
        {
          "id": 1,
          "name": "Perfect"
        },
        {
          "id": 2,
          "name": "Eyes Closed"
        }
      ]
    },
    {
      "id": 2,
      "name": "Taylor Swift",
      "songs": [
        {
          "id": 3,
          "name": "Love Story"
        },
        {
          "id": 4,
          "name": "Red"
        }
      ]
    }
  ],
  "meta": {
    "count": 2
  }
}
```

We created two artists with two songs each. In this request, instead of using `create` handler, we use `createMany`. With `createMany`, the arguments become an array of inputs. We can create many database records in a single batch. If any record creation failed, the transaction mechanism is triggered.

## Find many request

Let's query what we've created before. Send this request body to `/Song/findMany`.

```json
{
  "include": {
    "artists": {
      "select": {
        "name": true
      }
    }
  }
}
```
```json  
{
  "data": [
    {
      "id": 1,
      "name": "Perfect",
      "artists": [
        {
          "name": "Ed Sheeran"
        }
      ]
    },
    {
      "id": 2,
      "name": "Eyes Closed",
      "artists": [
        {
          "name": "Ed Sheeran"
        }
      ]
    },
    {
      "id": 3,
      "name": "Love Story",
      "artists": [
        {
          "name": "Taylor Swift"
        }
      ]
    },
    {
      "id": 4,
      "name": "Red",
      "artists": [
        {
          "name": "Taylor Swift"
        }
      ]
    }
  ],
  "meta": {
    "count": 4
  }
}
```

We limited the returned value with `select`. Only selected fields are returned. We can query further by specify some `where` conditions.

```json
{
  "where": {
    "name": {
      "startsWith": "Perfect"
    }
  },
  "include": {
    "artists": {
      "where": {
        "name": "Not exist"
      },
      "select": {
        "name": true
      }
    }
  }
}
```
```json     
{
  "data": [
    {
      "id": 1,
      "name": "Perfect",
      "artists": []
    }
  ],
  "meta": {
    "count": 1
  }
}
```

In this request, only 1 song is returned. Although we include the artists in the response, the only artist that is related to the song is filtered out by a where condition.

## Update requests

To perform a update request, combine `where` and `update` together in the request body and send it to `/Song/update`.

```json
{
  "where": {
    "id": 1
  },
  "update": {
    "name": "Perfect Symphony",
    "artists": {
      "update": {
        "where": {
          "id": 1
        },
        "update": {
          "name": "Richard Clayderman"
        }
      }
    }
  },
  "include": {
    "artists": true
  }
}
```
```json
{
  "data": {
    "id": 1,
    "name": "Perfect Symphony",
    "artists": [
      {
        "id": 1,
        "name": "Richard Clayderman"
      }
    ]
  }
}
```

We can nested update related records with Teo. If any update is invalid, it rollbacks.

## Summary

There are a lot of builtin model route handlers including `delete`, `upsert`, `copy`, `createMany`, `findFirst`, `findUnique` and so on. Relation queries are supported except `delete` and `deleteMany`.

Hope it's not hard to catch up with us. Teo is quite easy and simple to use. Find us in [our Discord group](https://teocloud.io/discord) if you have any troubles.

In the next tutorial in this series, you'll learn how to write route handlers with Teo. Programming code is necessary to write custom business logics.