Typetta | The TypeScript ORM for type lovers

Mattia Minotti
Dev Genius
Published in
5 min readJan 17, 2022

--

Typetta is an open-source ORM written in TypeScript that aims to allow seamless access to data in a typed fashion to all main SQL databases (MySQL, PostgreSQL, Microsoft SQL Server, SQLLite3, CockroachDB, MariaDB, Oracle e Amazon Redshift) and also to the NoSQL database MongoDB.

Typetta

Typetta is pluggable into any TypeScript backend (including serverless applications and microservices) but in terms of gain we totally suggest using it in all GraphQL backends, because… because we simply love GraphQL.

How do I use Typetta?

With Typetta everything revolves around the data model, the entities that describe the application domain and all underlying relationships between them. This model is described in standard GraphQL, using all basic concepts (scalars, types, enumerations, etc…) and some custom directives.

Starting from the model output of the domain analysis, Typetta provides a range of code generators for:

  • Type definitions in TypeScript language for each entity in the model.
  • Data Access Object (aka DAO) for each entity type that has a corresponding data source. Each DAO is an object that the developer can also query with advanced CRUD operations.
  • A DAOContext, a contextual object where the developer can configure each data source and retrieve the reference of any DAO.Main Functionalities

Below is a brief description of what makes Typetta awesome:

  • Complete support of main SQL databases and also MongoDB.
  • Multiple databases, including the ability to cross query different databases.
  • Multiple connections and connection pooling.
  • Entity relationships: 1–1, 1-n, n-m.
  • Dynamic typing and corresponding data projections.
  • Pagination.
  • Can be extended using middlewares.
  • Customised scalars and serialisation of the database.
  • Autogenerated IDs.
  • Validation rules.
  • Virtual, computed and calculated fields.
  • Aggregation queries.
  • Ability to build custom queries.
  • Define data access security policies.
  • Embedded documents supported on MongoDB as well as SQL.
  • Automated code generation.
  • Effortless integration with GraphQL backends.
  • Transactions.
  • Logging.
  • Mocking.
  • Auditing.
  • Multi-tenancy partitioning.
  • Soft-delete.

Why Typetta?

Typetta fulfills the need of having a typed ORM connected to SQL and NoSQL databases designed with productivity and flexibility in mind.

The philosophy behind Typetta components has been to ensure ease of use and optimisation of development time, adding complexity (with direct access to data source) only when strictly needed.

In case you are still unsure, why use Typetta instead of other ORMs?

  • It’s the ONLY TypeScript ORM that has full support for SQL and MongoDB databases.
  • A very strict typing system that 100% leverages TypeScript in providing types as responses based on the requested data type.
  • Using standard GraphQL, you can define the model. Using this standard opens the door to a whole set of instruments and third party frameworks that are based on this standard.

Getting Started

Typetta is a small sized package that can be installed using npm on any TypeScript project. The only explicit dependency is GraphQL. Both can be very simply added to a project using npm:

npm install @twinlogix/typetta — save

Project structure

Typetta does not depend on a specific project structure and therefore can be fully configurable. For our example we can think of having a TypeScript project structure like the following:

MyProject
┣ src
┃ ┗ index.ts
┣ package.json
┗ tsconfig.json

All you need is to add an applicative model in GraphQL language, typically found in src directory in a file named schema.graphql and also a config file named codegen.yml in the root for code generation purposes.

The updated project structure becomes the following:

MyProject
┣ src
┃ ┣ schema.graphql
┃ ┗ index.ts
┣ package.json
┣ tsconfig.json
codegen.yml

The data model

Inside the file schema.graphql you will have to insert the applicative model in GraphQL language. For a complete GraphQL syntax guide please refer directly to the official web site graphql.org.

Typetta relies on customized directives to extend the standard model definitions allowing the developer to specify very useful details in binding to the data source.

Here below you can see the simple definition of a User with a first and a last name.

type User @entity @mongo {
id: ID! @id
firstName: String
lastName: String
}

Code generation

With Typetta, using a standard language such as GraphQL to model the application enables the developer to use many third-party tools and libraries. As a consequence, developing an application becomes a much quicker and more productive experience.

Code generation in Typetta relies on GraphQL Code Generator, a very customizable and extensible library. GraphQL Code Generator enables the developer to choose from many standard generators as well as our embedded generator that automatically provides DAO in TypeScript language.

Typetta already includes all default dependencies to use the TypeScript generator, but it’s also possible to add other generators following the official guide.

It’s now time to configure our first codegen.yml with a minimum of effort. This operation will enable the code generation.

schema: "src/schema.graphql"
generates:
src/models.ts:
plugins:
— "typescript"
src/dao.ts:
config:
schemaImport: "./schema.graphql"
tsTypesImport: "./models"
plugins:
— "@twinlogix/typetta"

To allow code generation it’s also useful to edit the file package.json inserting the following script:

{
"scripts": {
"generate": "graphql-codegen"
}
}

Now let’s do our first code generation running the following command:

npm run generate

Using the above setup, the code generation command will create two files: src/models.ts with all TypeScript types included in schema.graphql and another file src/dao.ts with DAO and DAOContext. This two files will be useful for later.

A simple application

The following initial Typetta example writes and reads an entity on a MongoDB database (on ab SQL DB would be pretty much the same). This and other examples are available in a dedicated repository.

First things first… open a connection to the MongoDB database using the official Driver:

import { MongoClient } from 'mongodb';
import { DAOContext } from './dao';
const main = async () => {
const mongoConnection =
await MongoClient.connect(process.env.MONGODB_URL!);
const mongoDb =
mongoConnection.db(process.env.MONGODB_DATABASE_NAME);
};
main();

Let’s do our first DAOContext, our first Typetta code generated class. This class represents the central repo for all entity of the applicative model.

const daoContext = new DAOContext({
mongo: {
default: mongoDb
}
});

It’s now time do do our first CRUD simple operations on User entity.

const user1 = await daoContext.user.insertOne({
record: {
firstName: "Mattia",
lastName: "Minotti"
}
});
const user2 = await daoContext.user.insertOne({
record: {
firstName: "Edoardo",
lastName: "Barbieri"
}
});
const users = await daoContext.user.findAll();
users.forEach(user =>
console.log(`${user.firstName} ${user.lastName}`)
);

Our personal version of Hello World example will print on console the following 2 names:

Mattia Minotti
Edoardo Barbieri

This is the complete source code for this initial Typetta example connected to a MongoDB:

import { MongoClient } from 'mongodb';
import { DAOContext } from './dao';
const main = async () => { const mongoConnection =
await MongoClient.connect(process.env.MONGODB_URL!);
const mongoDb =
mongoConnection.db(process.env.MONGODB_DATABASE_NAME);
const daoContext = new DAOContext({
mongo: {
default: mongoDb
}
});
const user1 = await daoContext.user.insertOne({
record: {
firstName: "Mattia",
lastName: "Minotti"
}
});
const user2 = await daoContext.user.insertOne({
record: {
firstName: “Edoardo”,
lastName: “Barbieri”
}
});
const users = await daoContext.user.findAll();
users.forEach(user =>
console.log(`${user.firstName} ${user.lastName}`)
);
};
main();

--

--