diff --git a/versioned_docs/version-0.0.13/entity.md b/versioned_docs/version-0.0.13/entity.md deleted file mode 100644 index 7d4733de9c..0000000000 --- a/versioned_docs/version-0.0.13/entity.md +++ /dev/null @@ -1,3 +0,0 @@ -# Entity - -TBC \ No newline at end of file diff --git a/versioned_docs/version-0.0.13/intro.md b/versioned_docs/version-0.0.13/intro.md deleted file mode 100644 index f0116da463..0000000000 --- a/versioned_docs/version-0.0.13/intro.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Hello Platformatic diff --git a/versioned_docs/version-0.0.13/mapper.md b/versioned_docs/version-0.0.13/mapper.md deleted file mode 100644 index 12669122cf..0000000000 --- a/versioned_docs/version-0.0.13/mapper.md +++ /dev/null @@ -1,53 +0,0 @@ -# Mapper - -Basegraph's Mapper will look into your db schema and return an object containing - -- `db`: a Database abstraction layer from [@Databases](https://www.atdatabases.org/) -- `sql`: The SQL builder from [@Databases](https://www.atdatabases.org/) -- `entities` and object containing a key for each table found in the schema, with basic CRUD operations. See [entity.md](./entity.md) for details. - -It exports a function that accepts following parameters - -- `connectionString`: The Database connection string -- `log`: A logger object (like [Pino](https://getpino.io)) -- `onDatabaseLoad`: An async function that is called after the connection is established. It will receive `db` and `sql` as parameter. -- `ignore`: Object used to ignore some tables from building entities. (i.e. `{ 'versions': true }` will ignore `versions` table) -- `autoTimestamp`: Generate timestamp automatically when inserting/updating records. -- `hooks`: For each entity name (like `Page`) you can customize any of the entity API function. Your custom function will receive the original function as first parameter, and then all the other parameters passed to it. - -## Code samples - -```javascript -const { pino } = require('pino') - -const logger = pino() - -async function onDatabaseLoad (db, sql) { - await db.query(sql`CREATE TABLE pages ( - id SERIAL PRIMARY KEY, - title VARCHAR(255) NOT NULL - );`) -} -const connectionString = - 'postgres://postgres:postgres@localhost:5432/postgres' -const mapper = await connect({ - connectionString, - log: logger, - onDatabaseLoad, - ignore: {}, - hooks: { - Page: { - find: async function(_find, opts) { - console.log('hook called'); - return await _find(opts) - } - } - } -}) -const pageEntity = mapper.entities.page - -await mapper.db.query(mapper.sql`SELECT * FROM pages`) -await mapper.db.find('option1', 'option2') -... - -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.14/entity.md b/versioned_docs/version-0.0.14/entity.md deleted file mode 100644 index 7d4733de9c..0000000000 --- a/versioned_docs/version-0.0.14/entity.md +++ /dev/null @@ -1,3 +0,0 @@ -# Entity - -TBC \ No newline at end of file diff --git a/versioned_docs/version-0.0.14/intro.md b/versioned_docs/version-0.0.14/intro.md deleted file mode 100644 index f0116da463..0000000000 --- a/versioned_docs/version-0.0.14/intro.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Hello Platformatic diff --git a/versioned_docs/version-0.0.14/mapper.md b/versioned_docs/version-0.0.14/mapper.md deleted file mode 100644 index 12669122cf..0000000000 --- a/versioned_docs/version-0.0.14/mapper.md +++ /dev/null @@ -1,53 +0,0 @@ -# Mapper - -Basegraph's Mapper will look into your db schema and return an object containing - -- `db`: a Database abstraction layer from [@Databases](https://www.atdatabases.org/) -- `sql`: The SQL builder from [@Databases](https://www.atdatabases.org/) -- `entities` and object containing a key for each table found in the schema, with basic CRUD operations. See [entity.md](./entity.md) for details. - -It exports a function that accepts following parameters - -- `connectionString`: The Database connection string -- `log`: A logger object (like [Pino](https://getpino.io)) -- `onDatabaseLoad`: An async function that is called after the connection is established. It will receive `db` and `sql` as parameter. -- `ignore`: Object used to ignore some tables from building entities. (i.e. `{ 'versions': true }` will ignore `versions` table) -- `autoTimestamp`: Generate timestamp automatically when inserting/updating records. -- `hooks`: For each entity name (like `Page`) you can customize any of the entity API function. Your custom function will receive the original function as first parameter, and then all the other parameters passed to it. - -## Code samples - -```javascript -const { pino } = require('pino') - -const logger = pino() - -async function onDatabaseLoad (db, sql) { - await db.query(sql`CREATE TABLE pages ( - id SERIAL PRIMARY KEY, - title VARCHAR(255) NOT NULL - );`) -} -const connectionString = - 'postgres://postgres:postgres@localhost:5432/postgres' -const mapper = await connect({ - connectionString, - log: logger, - onDatabaseLoad, - ignore: {}, - hooks: { - Page: { - find: async function(_find, opts) { - console.log('hook called'); - return await _find(opts) - } - } - } -}) -const pageEntity = mapper.entities.page - -await mapper.db.query(mapper.sql`SELECT * FROM pages`) -await mapper.db.find('option1', 'option2') -... - -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.15/getting-started/architecture.md b/versioned_docs/version-0.0.15/getting-started/architecture.md deleted file mode 100644 index 7fb2ee7ae1..0000000000 --- a/versioned_docs/version-0.0.15/getting-started/architecture.md +++ /dev/null @@ -1,3 +0,0 @@ -# Architecture - -TODO diff --git a/versioned_docs/version-0.0.15/how-to/authentication-authorization.md b/versioned_docs/version-0.0.15/how-to/authentication-authorization.md deleted file mode 100644 index fd0a862319..0000000000 --- a/versioned_docs/version-0.0.15/how-to/authentication-authorization.md +++ /dev/null @@ -1,3 +0,0 @@ -# Authentication & Authorization - -TODO diff --git a/versioned_docs/version-0.0.15/how-to/custom-functionality.md b/versioned_docs/version-0.0.15/how-to/custom-functionality.md deleted file mode 100644 index 0504551a07..0000000000 --- a/versioned_docs/version-0.0.15/how-to/custom-functionality.md +++ /dev/null @@ -1,3 +0,0 @@ -# Adding custom functionality - -TODO diff --git a/versioned_docs/version-0.0.15/how-to/deployment.md b/versioned_docs/version-0.0.15/how-to/deployment.md deleted file mode 100644 index e0b064d345..0000000000 --- a/versioned_docs/version-0.0.15/how-to/deployment.md +++ /dev/null @@ -1,3 +0,0 @@ -# Deployment - -TODO diff --git a/versioned_docs/version-0.0.15/how-to/integration-testing.md b/versioned_docs/version-0.0.15/how-to/integration-testing.md deleted file mode 100644 index 909882f04e..0000000000 --- a/versioned_docs/version-0.0.15/how-to/integration-testing.md +++ /dev/null @@ -1,3 +0,0 @@ -# Integration testing - -TODO diff --git a/versioned_docs/version-0.0.15/how-to/monitoring-observability.md b/versioned_docs/version-0.0.15/how-to/monitoring-observability.md deleted file mode 100644 index 215edd3ffd..0000000000 --- a/versioned_docs/version-0.0.15/how-to/monitoring-observability.md +++ /dev/null @@ -1,3 +0,0 @@ -# Monitoring & Observability - -TODO diff --git a/versioned_docs/version-0.0.15/how-to/seed.md b/versioned_docs/version-0.0.15/how-to/seed.md deleted file mode 100644 index d9fc694bae..0000000000 --- a/versioned_docs/version-0.0.15/how-to/seed.md +++ /dev/null @@ -1,17 +0,0 @@ -# Seeding a database - -A database is as useful as the data that it contains: a fresh, empty database is not really useful. -While we can add a few rows using SQL within our migrations, we might need to use JavaScript from time to time. -Therefore, we can run `platformatic db seed myfile.js`, where `myfile.js` exports a `Function` that accepts an argument: -an instance of `@platformatic/sql-mapper`. Here is an example: - -```js -'use strict' - -module.exports = async function ({ entities, db, sql }) { - await entities.graph.save({ input: { name: 'Hello' } }) - await db.query(sql` - INSERT INTO graphs (name) VALUES ('Hello 2'); - `) -} -``` diff --git a/versioned_docs/version-0.0.15/intro.md b/versioned_docs/version-0.0.15/intro.md deleted file mode 100644 index 79e72bfc39..0000000000 --- a/versioned_docs/version-0.0.15/intro.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Platformatic - -Platformatic is a new set of tools that will enable you to develop HTTP APIs without frictions. \ No newline at end of file diff --git a/versioned_docs/version-0.0.15/reference/cli.md b/versioned_docs/version-0.0.15/reference/cli.md deleted file mode 100644 index 343c5c6763..0000000000 --- a/versioned_docs/version-0.0.15/reference/cli.md +++ /dev/null @@ -1,3 +0,0 @@ -# CLI - -TODO diff --git a/versioned_docs/version-0.0.15/reference/configuration.md b/versioned_docs/version-0.0.15/reference/configuration.md deleted file mode 100644 index 7dd4eb6903..0000000000 --- a/versioned_docs/version-0.0.15/reference/configuration.md +++ /dev/null @@ -1,186 +0,0 @@ -# Configuration - -## File support - -Platformatic DB supports JSON, YAML and TOML file format. The format will be inferred by file extension (for YAML, both `.yml` and `.yaml` extension are supported). - -We'll use JSON format in the following examples. - -## Format - -Following root object's properties are supported - - [`authorization`](#authorization) - - [`core`](#core) (**required**) - - [`dashboard`](#dashboard) - - [`metrics`](#metrics) - - [`migrations`](#migrations) - - [`plugin`](#plugin) - - [`server`](#server) (**required**) - -### `authorization` - - `adminSecret` (string): if defined, it will be the password used to access the dashboard and the string to send within the `x-platformatic-admin-secret` header when performing GraphQL/REST API calls. - - `rules` (array): authorization rules that describe which CRUD action every user can/can't perform. - - The format of this array is TBD. - -### `core` - - `connectionString` (string) (**required**): This is the connection string used to connect to the database (i.e. `postgres://user:password@my-database:5432/db-name`). - - Platformatic DB supports Mysql/MariaDB, Postgresql and SQLite. - - `graphql` (boolean or object): enables the GraphQL support - - _Examples_ - - Enables GraphQL support - - ```JSON - { - "core": { - ... - "graphql": true - } - } - ``` - - Enables GraphQL support with GraphiQL - - ```JSON - { - "core": { - ... - "graphql": { - "graphiql": true - } - } - } - ``` - - `openapi` (boolean or object): enables OpenAPI REST support. If the value is an object, all [OpenAPI v3](https://swagger.io/specification/) properties can be detailed. - - Platformatic DB uses [`@fastify/swagger`](https://github.com/fastify/fastify-swagger) under the hood to manage this configuration. - - _Examples_ - - Enables OpenAPI - - ```JSON - { - "core": { - ... - "openapi": true - } - } - ``` - - Enables OpenAPI with options - - ```JSON - { - "core": { - ... - "openapi": { - "info": { - "title": "Platformatic DB", - "description": "Exposing a SQL database as REST" - } - } - } - } - ``` - - `ignore` (object): key/value object that define which tables should be ignored, meaning they are not entities. - - _Examples_ - - ```JSON - { - "core": { - ... - "ignore": { - "versions": true // "versions" table will be not mapped with GraphQL/REST APIs - } - } - } - ``` - -### `dashboard` - - `rootPath` (boolean): Whether the dashboard will be available at `/` location. Default is `true`. - -### `metrics` - Configuration for a [Prometheus](https://prometheus.io/) server that will export monitoring metrics for the current server instance. It uses [`fastify-metrics`](https://github.com/SkeLLLa/fastify-metrics) under the hodd. - - It can be a boolean or an object. If value is `true` this server will listen to `http://0.0.0.0:9090` - - - `hostname` (string): The hostname where Prometheus server will listen for connections. - - `port` (number): The port where Prometheus server will listen for connections. - - `auth` (object): Basic Auth configuration. `username` and `password` are required here. - - -### `migrations` - Configures [Postgrator](https://github.com/rickbergfalk/postgrator) to run migrations over the database. - - - `dir` (string) (**required**): Relative path to the migrations directory. - - `autoApply` (boolean): Whether to automatically apply migrations when running the migrate command. Default `true` - -### `plugin` - Defines a plugin that will be loaded with [`fastify-isolate`](https://github.com/mcollina/fastify-isolate). - - It is an object and all properties will be passed to `fastify-isolate` - - - `path` (string) (**required**): Relative path to plugin's entry point. - -### `server` - - - `hostname` (string) (**required**): The hostname where Platformatic DB server will listen for connections. - - `port` (number) (**required**): The port where Platformatic DB server will listen for connections. - - `healthCheck` (boolean or object): Enables the Health Check. Powered by [`@fastify/under-pressure`](https://github.com/fastify/under-pressure). - The value can be an object, used to specify the interval between checks (default 5000ms) - - _Example_ - - ```JSON - { - "server": { - ... - "healthCheck": { - "interval": 2000 - } - } - } - ``` - - `cors` (object): the value of this property will be passed to [`@fastify/cors`](https://github.com/fastify/fastify-cors) so all keys documented there are supported. - -## Environment Variables - -Each value can be replaced by an environment variable. - -To avoid messing/exposing system's variables, only `PLT_` prefixed ones will be replaced. - -All placeholders must be replaced. - -_Examples_ - - -```JSON -{ - "core": { - "connectionString": "{PLT_CONNECTION_STRING}" - }, - "server": { - "port": "{MY_PORT}" - } -} -``` - -Platformatic will look at the `PLT_CONNECTION_STRING` environment variable and will replace it in the config. - -Anyway it will throw an error because it can't replace `{PORT}` because it's not prefixed with `PLT_`. The error message will suggest to use `PLT_PORT` instead. - -Variables can be set via terminal (i.e. `PLT_PORT=4321 npx platformatic db --config db.json`) or via a `.env` file (loaded by [`dotenv`](https://github.com/motdotla/dotenv)). -The `.env` file can be located in the same folder of the config file or in the current working directory. - -### Environment Variables Whitelist - -Users can override default behavior, and use variables that are not prefixed with `PLT_`, using the `--allow-env` argument. - -The value is a comma separated list of strings. `--allow-env=PORT,HOST` will allow `{PORT}` and `{HOST}` in the config file. - -Default variables already supported are: `PORT`, `DATABASE_URL`. If the user pass `--allow-env` argument, it will replace the default list. diff --git a/versioned_docs/version-0.0.15/reference/entity.md b/versioned_docs/version-0.0.15/reference/entity.md deleted file mode 100644 index 88e28131a3..0000000000 --- a/versioned_docs/version-0.0.15/reference/entity.md +++ /dev/null @@ -1,38 +0,0 @@ -# Entity - -TBC - -## addEntityHooks (entityName, hooks) - -Wraps an entity with all the hooks defined in hooks. - -```js -app.platformatic.addEntityHooks(entityName, { - async find (originalFind, { where, ctx, fields }) { - // Do something... - - return originalFind({ where, ctx, fields }) - }, - - async save (originalSave, { input, ctx, fields }) { - // Do something... - - return originalSave({ input, ctx, fields }) - }, - - async insert (originalInsert, { inputs, ctx, fields }) { - // Do something... - - return originalInsert({ inputs, ctx, fields }) - }, - - async delete (originalDelete, { where, ctx, fields }) { - // Do something... - - return originalDelete({ where, ctx, fields }) - } -}) -``` - -Note the use of `ctx`. This is defined inside [Mercurius](https://github.com/mercurius-js/mercurius/blob/master/docs/context.md). -and exposed with the same API also by `sql-openapi`. diff --git a/versioned_docs/version-0.0.15/reference/graphql-api.md b/versioned_docs/version-0.0.15/reference/graphql-api.md deleted file mode 100644 index 20eafe8f34..0000000000 --- a/versioned_docs/version-0.0.15/reference/graphql-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# GraphQL API - -TODO diff --git a/versioned_docs/version-0.0.15/reference/mapper.md b/versioned_docs/version-0.0.15/reference/mapper.md deleted file mode 100644 index 12669122cf..0000000000 --- a/versioned_docs/version-0.0.15/reference/mapper.md +++ /dev/null @@ -1,53 +0,0 @@ -# Mapper - -Basegraph's Mapper will look into your db schema and return an object containing - -- `db`: a Database abstraction layer from [@Databases](https://www.atdatabases.org/) -- `sql`: The SQL builder from [@Databases](https://www.atdatabases.org/) -- `entities` and object containing a key for each table found in the schema, with basic CRUD operations. See [entity.md](./entity.md) for details. - -It exports a function that accepts following parameters - -- `connectionString`: The Database connection string -- `log`: A logger object (like [Pino](https://getpino.io)) -- `onDatabaseLoad`: An async function that is called after the connection is established. It will receive `db` and `sql` as parameter. -- `ignore`: Object used to ignore some tables from building entities. (i.e. `{ 'versions': true }` will ignore `versions` table) -- `autoTimestamp`: Generate timestamp automatically when inserting/updating records. -- `hooks`: For each entity name (like `Page`) you can customize any of the entity API function. Your custom function will receive the original function as first parameter, and then all the other parameters passed to it. - -## Code samples - -```javascript -const { pino } = require('pino') - -const logger = pino() - -async function onDatabaseLoad (db, sql) { - await db.query(sql`CREATE TABLE pages ( - id SERIAL PRIMARY KEY, - title VARCHAR(255) NOT NULL - );`) -} -const connectionString = - 'postgres://postgres:postgres@localhost:5432/postgres' -const mapper = await connect({ - connectionString, - log: logger, - onDatabaseLoad, - ignore: {}, - hooks: { - Page: { - find: async function(_find, opts) { - console.log('hook called'); - return await _find(opts) - } - } - } -}) -const pageEntity = mapper.entities.page - -await mapper.db.query(mapper.sql`SELECT * FROM pages`) -await mapper.db.find('option1', 'option2') -... - -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.15/reference/rest-api.md b/versioned_docs/version-0.0.15/reference/rest-api.md deleted file mode 100644 index 7f17569717..0000000000 --- a/versioned_docs/version-0.0.15/reference/rest-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# REST API - -TODO diff --git a/versioned_docs/version-0.0.17/contributing/_category_.json b/versioned_docs/version-0.0.17/contributing/_category_.json deleted file mode 100644 index 88df27c409..0000000000 --- a/versioned_docs/version-0.0.17/contributing/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Contributing", - "position": 4 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/contributing/intro.md b/versioned_docs/version-0.0.17/contributing/intro.md deleted file mode 100644 index b60ddf51f8..0000000000 --- a/versioned_docs/version-0.0.17/contributing/intro.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contributing - -Work in progres... \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/contributing/style-guide.md b/versioned_docs/version-0.0.17/contributing/style-guide.md deleted file mode 100644 index 05a90068eb..0000000000 --- a/versioned_docs/version-0.0.17/contributing/style-guide.md +++ /dev/null @@ -1,237 +0,0 @@ ---- -credits: https://github.com/fastify/fastify/blob/main/docs/Guides/Style-Guide.md ---- -## Welcome - -Welcome to *Platformatic Style Guide*. This guide is here to provide you with a -conventional writing style for users writing developer documentation on our Open -Source framework. Each topic is precise and well explained to help you write -documentation users can easily understand and implement. - -## Who is this guide for? - -This guide is for anyone who loves to build with Platformatic or wants to contribute -to our documentation. You do not need to be an expert in writing technical -documentation. This guide is here to help you. - -Visit [CONTRIBUTING.md](https://github.com/platformatic/platformatic/blob/main/CONTRIBUTING.md) -file on GitHub to join our Open Source folks. - -## Before you write - -You should have a basic understanding of: - -* JavaScript -* Node.js -* Git -* GitHub -* Markdown -* HTTP -* NPM - -### Consider your Audience - -Before you start writing, think about your audience. In this case, your audience -should already know HTTP, JavaScript, NPM, and Node.js. It is necessary to keep -your readers in mind because they are the ones consuming your content. You want -to give as much useful information as possible. Consider the vital things they -need to know and how they can understand them. Use words and references that -readers can relate to easily. Ask for feedback from the community, it can help -you write better documentation that focuses on the user and what you want to -achieve. - -### Get straight to the point - -Give your readers a clear and precise action to take. Start with what is most -important. This way, you can help them find what they need faster. Mostly, -readers tend to read the first content on a page, and many will not scroll -further. - -**Example** - -Less like this: Colons are very important to register a parametric path. It lets -the framework know there is a new parameter created. You can place the colon -before the parameter name so the parametric path can be created. - -More Like this: To register a parametric path, put a colon before the parameter -name. Using a colon lets the framework know it is a parametric path and not a -static path. - -### Images and video should enhance the written documentation - - -Images and video should only be added if they complement the written -documentation, for example to help the reader form a clearer mental model of a -concept or pattern. - -Images can be directly embedded, but videos should be included by linking to an -external site, such as YouTube. You can add links by using -`[Title](https://www.websitename.com)` in the Markdown. - - - - -### Avoid plagiarism - -Make sure you avoid copying other people's work. Keep it as original as -possible. You can learn from what they have done and reference where it is from -if you used a particular quote from their work. - - -## Word Choice - -There are a few things you need to use and avoid when writing your documentation -to improve readability for readers and make documentation neat, direct, and -clean. - - -### When to use the second person "you" as the pronoun - -When writing articles or guides, your content should communicate directly to -readers in the second person ("you") addressed form. It is easier to give them -direct instruction on what to do on a particular topic. To see an example, visit -the [Quick Start Guide](../getting-started/quick-start-guide.md). - -**Example** - -Less like this: we can use the following plugins. - -More like this: You can use the following plugins. - -> According to [Wikipedia](#), ***You*** is usually a second person pronoun. -> Also, used to refer to an indeterminate person, as a more common alternative -> to a very formal indefinite pronoun. - -## When to avoid the second person "you" as the pronoun - -One of the main rules of formal writing such as reference documentation, or API -documentation, is to avoid the second person ("you") or directly addressing the -reader. - -**Example** - -Less like this: You can use the following recommendation as an example. - -More like this: As an example, the following recommendations should be -referenced. - -To view a live example, refer to the [Decorators](../reference/configuration.md) -reference document. - - -### Avoid using contractions - -Contractions are the shortened version of written and spoken forms of a word, -i.e. using "don't" instead of "do not". Avoid contractions to provide a more -formal tone. - -### Avoid using condescending terms - -Condescending terms are words that include: - -* Just -* Easy -* Simply -* Basically -* Obviously - -The reader may not find it easy to use Platformatic; avoid -words that make it sound simple, easy, offensive, or insensitive. Not everyone -who reads the documentation has the same level of understanding. - -### Starting with a verb - -Mostly start your description with a verb, which makes it simple and precise for -the reader to follow. Prefer using present tense because it is easier to read -and understand than the past or future tense. - -**Example** - - Less like this: There is a need for Node.js to be installed before you can be - able to use Platformatic. - - More like this: Install Node.js to make use of Platformatic. - -### Grammatical moods - -Grammatical moods are a great way to express your writing. Avoid sounding too -bossy while making a direct statement. Know when to switch between indicative, -imperative, and subjunctive moods. - - -**Indicative** - Use when making a factual statement or question. - -Example: Since there is no testing framework available, "Platformatic recommends ways -to write tests". - -**Imperative** - Use when giving instructions, actions, commands, or when you -write your headings. - -Example: Install dependencies before starting development. - - -**Subjunctive** - Use when making suggestions, hypotheses, or non-factual -statements. - -Example: Reading the documentation on our website is recommended to get -comprehensive knowledge of the framework. - -### Use **active** voice instead of **passive** - -Using active voice is a more compact and direct way of conveying your -documentation. - -**Example** - - -Passive: The node dependencies and packages are installed by npm. - -Active: npm installs packages and node dependencies. - -## Writing Style - -### Documentation titles - -When creating a new guide, API, or reference in the `/docs/` directory, use -short titles that best describe the topic of your documentation. Name your files -in kebab-cases and avoid Raw or camelCase. To learn more about kebab-case you -can visit this medium article on [Case -Styles](https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841). - -**Examples**: - ->`hook-and-plugins.md`, - - `adding-test-plugins.md`, - - `removing-requests.md`. - -### Hyperlinks - -Hyperlinks should have a clear title of what it references. Here is how your -hyperlink should look: - -```MD - - -// Add clear & brief description -[Fastify Plugins] (https://www.fastify.io/docs/latest/Plugins/) - - - -// incomplete description -[Fastify] (https://www.fastify.io/docs/latest/Plugins/) - -// Adding title in link brackets -[](https://www.fastify.io/docs/latest/Plugins/ "fastify plugin") - -// Empty title -[](https://www.fastify.io/docs/latest/Plugins/) - -// Adding links localhost URLs instead of using code strings (``) -[http://localhost:3000/](http://localhost:3000/) - -``` - -Include in your documentation as many essential references as possible, but -avoid having numerous links when writing for beginners to avoid distractions. diff --git a/versioned_docs/version-0.0.17/getting-started/_category_.json b/versioned_docs/version-0.0.17/getting-started/_category_.json deleted file mode 100644 index 87121b812f..0000000000 --- a/versioned_docs/version-0.0.17/getting-started/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Getting Started", - "position": 1 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/getting-started/architecture.md b/versioned_docs/version-0.0.17/getting-started/architecture.md deleted file mode 100644 index 183ea474d7..0000000000 --- a/versioned_docs/version-0.0.17/getting-started/architecture.md +++ /dev/null @@ -1,28 +0,0 @@ -# Architecture - -Platformatic is a collection of Open Source tools designed to eliminate friction -in backend development. The first of those tools is Platformatic DB, which is developed -as `@platformatic/db`. - -## Platformatic DB - -Platformatic DB can expose a SQL database by dynamically mapping it to REST/OpenAPI -and GraphQL endpoints. It supports a limited subset of the SQL query language, but -also allows developers to add their own custom routes and resolvers. - -![Platformatic DB Architecture](./platformatic-architecture.png) - -Platformatic DB is composed of a few key libraries: - -1. `@platformatic/sql-mapper` - follows the [Data Mapper pattern](https://en.wikipedia.org/wiki/Data_mapper_pattern) to build an API on top of a SQL database. - Internally it uses the [`@database` project](https://www.atdatabases.org/). -1. `@platformatic/sql-openapi` - uses `sql-mapper` to create a series of REST routes and matching OpenAPI definitions. - Internally it uses [`@fastify/swagger`](https://github.com/fastify/fastify-swagger). -1. `@platformatic/sql-graphql` - uses `sql-mapper` to create a GraphQL endpoint and schema. `sql-graphql` also support Federation. - Internally it uses [`mercurius`](https://github.com/mercurius-js/mercurius). - -Platformatic DB allows you to load a [Fastify plugin](https://www.fastify.io/docs/latest/Reference/Plugins/) during server startup that contains your own application-specific code. -The plugin can add more routes or resolvers — these will automatically be shown in the OpenAPI and GraphQL schemas. - -SQL database migrations are also supported. They're implemented internally with the [`postgrator`](https://www.npmjs.com/package/postgrator) library. - diff --git a/versioned_docs/version-0.0.17/getting-started/quick-start-guide.md b/versioned_docs/version-0.0.17/getting-started/quick-start-guide.md deleted file mode 100644 index e77462ba45..0000000000 --- a/versioned_docs/version-0.0.17/getting-started/quick-start-guide.md +++ /dev/null @@ -1,221 +0,0 @@ -# Quick start guide - -In this guide you'll learn how to create and run your first API with -Platformatic DB. Let's get started! - -:::info - -This guide uses [SQLite](https://www.sqlite.org/) for the database, but -Platformatic DB also supports [PostgreSQL](https://www.postgresql.org/), -[MySQL](https://www.mysql.com/) and [MariaDB](https://mariadb.org/) databases. - -::: - -## Requirements - -Platformatic supports macOS, Linux and Windows ([WSL](https://docs.microsoft.com/windows/wsl/) recommended). - -To follow along with this guide you'll need to have these things installed: - -- [Node.js](https://nodejs.org/) v16 or v18 (latest of the series) -- [npm](https://docs.npmjs.com/cli/v8/) v7 or later -- A code editor, for example [Visual Studio Code](https://code.visualstudio.com/) -- Python and a compiler toolchain is recommended - to experience the hot-reloading capabilities; - Check the requirements for [Unix](https://github.com/nodejs/node-gyp#on-unix), - [macOS](https://github.com/nodejs/node-gyp#on-macos) or - [Windows](https://github.com/nodejs/node-gyp#on-windows)) - -In case the compiler toolchain is missing, there is a fallback that supports only commonjs modules. -ESM support is available only via the native addon. - -## Create a new API project - -Create a directory for your new API project: - -```sh -mkdir quick-start - -cd quick-start -``` - -Then create a `package.json` file with the npm default values: - -```sh -npm init --yes -``` - -And install the [platformatic](https://www.npmjs.com/package/platformatic) CLI -as a project dependency: - -```sh -npm install platformatic -``` - -## Add a database schema - -In your project directory (`quick-start`), create a `migrations` directory to -store your database migration files: - -```sh -mkdir migrations -``` - -Then create a new migration file named **`001.do.sql`** in the **`migrations`** -directory. - -Copy and paste this SQL query into the migration file: - -```sql -CREATE TABLE pages ( - id INTEGER PRIMARY KEY, - title VARCHAR(255) NOT NULL -); -``` - -When it's run by Platformatic, this query will create a new database table -named `pages`. - -:::tip - -You can check syntax for SQL queries on the [Database.Guide SQL Reference](https://database.guide/sql-reference-for-beginners/). - -::: - -## Configure your API - -In your project directory, create a new Platformatic configuration file named -**`db.json`**. - -Copy and paste in this configuration: - -```json -{ - "server": { - "logger": { - "level": "info" - }, - "hostname": "127.0.0.1", - "port": "3042" - }, - "core": { - "connectionString": "sqlite://pages.db", - "graphiql": true - }, - "migrations": { - "dir": "./migrations" - } -} -``` - -This configuration tells Platformatic to: - -- Run an API server on `http://127.0.0.1:3042/` -- Configure the API server to log messages with an `info` level or above -- Connect to an SQLite database stored in a file named `pages.db` -- Enable the [GraphiQL](https://www.npmjs.com/package/graphiql) web UI -- Look for database migration files in the `migrations` directory - -:::tip - -The [Configuration reference](/reference/configuration.md) explains all of the -supported configuration options. - -::: - -## Start your API server - -In your project directory, use the Platformatic CLI to start your API server: - -```sh -npx platformatic db start -``` - -This will: - -1. Run your SQL migration file and create a `pages` table in the SQLite database. -1. Automatically map your SQL database to REST and GraphQL API interfaces. -1. Start the Platformatic API server. - -Your Platformatic API is now up and running! - -:::tip - -You can display human-readable logs in development by using -[`pino-pretty`](https://github.com/pinojs/pino-pretty): - -```sh -npm install --save-dev pino-pretty - -npx platformatic db start | npx pino-pretty -``` - -::: - -## Next steps - -### Use the REST API interface - -You can use cURL to make requests to the REST interface of your API, for example: - -#### Create a new page - -```bash -curl -X POST -H "Content-Type: application/json" \ - -d "{ \"title\": \"Hello Platformatic DB\" }" \ - http://localhost:3042/pages -``` - -You should receive a response from your API like this: - -```json -{"id":1,"title":"Hello Platformatic DB"} -``` - -#### Get all pages - -```bash -curl http://localhost:3042/pages -``` - -You should receive a response from your API like this, with an array -containing all the pages in your database: - -```json -[{"id":1,"title":"Hello Platformatic DB"}] -``` - -:::tip - -Take a look at the [REST API reference](/reference/rest-api.md) for a complete -overview of the REST interface that your API provides. - -::: - -#### OpenAPI documentation - -You can explore the OpenAPI documentation for your REST API at -[http://localhost:3042/documentation](http://localhost:3042/documentation) - -### Use the GraphQL API interface - -Open [http://localhost:3042/graphiql](http://localhost:3042/graphiql) in your -web browser to explore the GraphQL interface of your API. - -Try out this GraphQL query to retrieve all pages from your API: - -```graphql -{ - pages { - id - title - } -} -``` - -:::tip - -Learn more about your API's GraphQL interface in the -[GraphQL API reference](/reference/graphql-api.md). - -::: diff --git a/versioned_docs/version-0.0.17/how-to/_category_.json b/versioned_docs/version-0.0.17/how-to/_category_.json deleted file mode 100644 index 792570104d..0000000000 --- a/versioned_docs/version-0.0.17/how-to/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "How To", - "position": 2 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/how-to/authentication-authorization.md b/versioned_docs/version-0.0.17/how-to/authentication-authorization.md deleted file mode 100644 index fd0a862319..0000000000 --- a/versioned_docs/version-0.0.17/how-to/authentication-authorization.md +++ /dev/null @@ -1,3 +0,0 @@ -# Authentication & Authorization - -TODO diff --git a/versioned_docs/version-0.0.17/how-to/custom-functionality.md b/versioned_docs/version-0.0.17/how-to/custom-functionality.md deleted file mode 100644 index 0504551a07..0000000000 --- a/versioned_docs/version-0.0.17/how-to/custom-functionality.md +++ /dev/null @@ -1,3 +0,0 @@ -# Adding custom functionality - -TODO diff --git a/versioned_docs/version-0.0.17/how-to/deployment.md b/versioned_docs/version-0.0.17/how-to/deployment.md deleted file mode 100644 index 98374362b9..0000000000 --- a/versioned_docs/version-0.0.17/how-to/deployment.md +++ /dev/null @@ -1,218 +0,0 @@ -# Deployment - -Requirements: - -1. Dockerfile with access to `platformatic` CLI -2. A fly.io account -3. A platformatic app that works locally - -## On Fly.io - -1. Need a fly.io account and the CLI tool: https://fly.io/docs/hands-on/ -2. Navigate to your project on your local machine -1. Create a **Dockerfile**: - ```dockerfile - FROM platformatic/platformatic:latest - - USER root - - WORKDIR /opt/ - COPY migrations migrations - COPY db.json db.json - - EXPOSE 3042 - - CMD ["platformatic", "db"] - ``` -1. Create an app on fly: `fly launch --no-deploy --generate-name --org personal --region mad` - * or just `fly launch` and follow the prompts - * if there is no database at this point, `--no-deploy` can be removed -4. Expose the correct port, matching **db.json** and **Dockerfile**: - ```diff - [[services]] - http_checks = [] - - internal_port = 8080 - + internal_port = 3042 - processes = ["app"] - protocol = "tcp" - script_checks = [] - ``` -9. Now deploy: `fly deploy` - -### With sqlite - -1. Follow steps above, skipping deployment until the end -2. Create a volume for database storage: `fly volumes create data` - * will create storage in the same region as application - * defaults to 3GB size, use `-s` to change: `-s 10` is 10GB -3. Update mount in **fly.toml**, replacing ``: - ```toml - [mounts] - source = "data" - destination = "/opt//.platformatic/data" - ``` -4. Create directory in project, this will be where the sqlite database goes: - ```sh - mkdir -p .platformatic/data - touch .platformatic/data/.gitkeep - ``` -5. Make sure sqlite databases are ignored to avoid inconsistencies in - deployment: - ```sh - echo "*.db" >> .gitignore - ``` -6. Update connection string to the sqlite database, replacing ``: - ```json - { - "core": { - "connectionString": "sqlite://.platformatic/data/.db" - } - } - ``` -7. Add migrations folder, migrations, and configuration. _Note_ app will not run - if there is a migrations folder and no migrations. - 1. Create folder and simple migration if not already available: - ```sh - mkdir migrations - echo "CREATE TABLE demo (id uuid PRIMARY KEY);" > migrations/001.do.sql - ``` - 2. Update configuration: - ```json - { - "migrations": { - "dir": "./migrations" - } - } - ``` -8. Optionally, [add `sqlite` to the **Dockerfile** to help with debugging](#adding-sqlite-for-debugging) -10. Deploy the app `fly deploy` - -#### Adding `sqlite` for debugging - -Create a script for launching the database, call it **db-cli.sh**: -```sh -#!/bin/sh -set -x -# DSN will be defined in the Dockerfile -sqlite3 $DSN -``` - -Add the following snippet to the **Dockerfile**: -```dockerfile -# Setup sqlite viewer -# Replace with your app name -RUN apk add sqlite -ENV DSN "/opt//.platformatic/data/demo.db" -COPY db-cli.sh /usr/local/bin/db-cli -RUN chmod +x /usr/local/bin/db-cli -``` - -With fly.io, it becomes easy to boot directly into the database by running the -following command from the local machine: - -```sh -fly ssh console -C db-cli -``` - -#### Adding Litestream and S3 for backups - -This requires an AWS account and the appropriate setup in AWS. Follow the -[Litestream guide for configuring an AWS user](https://litestream.io/guides/s3/) and then come back here to -integrate with Platformatic and Fly. - -Once AWS is setup, store the credentials on Fly: -```sh -fly secrets set \ - AWS_ACCESS_KEY_ID=some-access-key \ - AWS_SECRET_ACCESS_KEY=some-access-secret -``` - -Update **fly.toml** with the bucket name: -```toml -[env] - AWS_BACKUP_BUCKET = "bucket-name" -``` - -Configuration of Litestream will be done through the standard yaml file, create -a **litestream.yml** file in the project with the following contents: -```yml -dbs: - # make sure to replace - - path: /opt//.platformatic/data/.db - replicas: - - url: s3://${AWS_BACKUP_BUCKET} - access-key-id: ${AWS_ACCESS_KEY_ID} - secret-access-key: ${AWS_SECRET_ACCESS_KEY} -``` - -To get automatic database replication and restoration, a small Bash script is -used as the **Dockerfile** `CMD`: -```sh -#!/bin/bash - -if [ ! -f "$DSN" ] -then - echo "Restoring database" - litestream restore -v "$DSN" -fi - -# TODO change -echo "Starting Litestream & application" -litestream replicate -exec "platformatic db --config /opt//db.json" -``` - -Finally, the existing Dockerfile needs a number of changes. Start with the -Litestream base image: - -```dockerfile -FROM litestream/litestream:0.3.9 AS litestream - -FROM registry.fly.io/platformatic-private:latest -``` - -Copy Litestream into the platformatic image: -```dockerfile -USER root -COPY --from=litestream /usr/local/bin/litestream /usr/local/bin/litestream -``` - -Copy the runner and configuration: -```dockerfile -COPY run.sh /run.sh -COPY litestream.yml /etc/litestream.yml -``` - -Last of all, run from **run.sh**: -```dockerfile -CMD /run.sh -``` - -With Litestream and the database tools, the final image should look something -like this: -```dockerfile -FROM litestream/litestream:0.3.9 AS litestream - -FROM registry.fly.io/platformatic-private:latest - -USER root -COPY --from=litestream /usr/local/bin/litestream /usr/local/bin/litestream - -RUN apk add sqlite bash ca-certificates curl - -# Set environment variables. -ENV DSN "/opt//.platformatic/data/.db" -COPY image/db-cli /usr/local/bin/db-cli -RUN chmod +x /usr/local/bin/db-cli - -EXPOSE 3042 - -ADD litestream.yml /etc/litestream.yml -ADD run.sh /run.sh - -# Application specific files -WORKDIR /opt/ -COPY migrations migrations -COPY db.json db.json - -CMD /run.sh -``` diff --git a/versioned_docs/version-0.0.17/how-to/integration-testing.md b/versioned_docs/version-0.0.17/how-to/integration-testing.md deleted file mode 100644 index 909882f04e..0000000000 --- a/versioned_docs/version-0.0.17/how-to/integration-testing.md +++ /dev/null @@ -1,3 +0,0 @@ -# Integration testing - -TODO diff --git a/versioned_docs/version-0.0.17/how-to/monitoring-observability.md b/versioned_docs/version-0.0.17/how-to/monitoring-observability.md deleted file mode 100644 index 215edd3ffd..0000000000 --- a/versioned_docs/version-0.0.17/how-to/monitoring-observability.md +++ /dev/null @@ -1,3 +0,0 @@ -# Monitoring & Observability - -TODO diff --git a/versioned_docs/version-0.0.17/how-to/seed.md b/versioned_docs/version-0.0.17/how-to/seed.md deleted file mode 100644 index d9fc694bae..0000000000 --- a/versioned_docs/version-0.0.17/how-to/seed.md +++ /dev/null @@ -1,17 +0,0 @@ -# Seeding a database - -A database is as useful as the data that it contains: a fresh, empty database is not really useful. -While we can add a few rows using SQL within our migrations, we might need to use JavaScript from time to time. -Therefore, we can run `platformatic db seed myfile.js`, where `myfile.js` exports a `Function` that accepts an argument: -an instance of `@platformatic/sql-mapper`. Here is an example: - -```js -'use strict' - -module.exports = async function ({ entities, db, sql }) { - await entities.graph.save({ input: { name: 'Hello' } }) - await db.query(sql` - INSERT INTO graphs (name) VALUES ('Hello 2'); - `) -} -``` diff --git a/versioned_docs/version-0.0.17/intro.md b/versioned_docs/version-0.0.17/intro.md deleted file mode 100644 index b79f7279c2..0000000000 --- a/versioned_docs/version-0.0.17/intro.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_position: 0 -title: 'Welcome' ---- - -# Welcome - -Platformatic is a new set of tools that will enable you to develop HTTP APIs without frictions. \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/_category_.json b/versioned_docs/version-0.0.17/reference/_category_.json deleted file mode 100644 index 9185910a67..0000000000 --- a/versioned_docs/version-0.0.17/reference/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Reference", - "position": 3 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/cli.md b/versioned_docs/version-0.0.17/reference/cli.md deleted file mode 100644 index bb758d1c5a..0000000000 --- a/versioned_docs/version-0.0.17/reference/cli.md +++ /dev/null @@ -1,160 +0,0 @@ -# Platformatic CLI - -The following is a list of all the help commands part of the Platformatic CLI. - -You can install the CLI with `npm install -g platformatic` or `yarn global add platformatic`. -You can then run `platformatic` to see the list of commands. - -## Help - - -``` -Welcome to Platformatic. Available commands are: - -* help - Display this message -* help - shows more information about a command. -* db - start Platformatic DB; type `platformatic db help` to know more. - -``` - - -## DB - - - ### help - Available commands: - -* `help` - show this help message. -* `help ` - shows more information about a command. -* `start` - start the server. -* `migrate` - run migrations. -* `seed` - run a seed file. - - - ### migrate - Apply all configurated migrations to the database: - - $ platformatic db migrate - -The migrations will be applied in the order they are specified in the -folder defined in the configuration file. If you want to apply a specific migration, -you can use the `--to` option: - - $ platformatic db migrate --to 001 - -Here is an example migration: - - CREATE TABLE graphs ( - id SERIAL PRIMARY KEY, - name TEXT - ); - -You can always rollback to a specific migration with: - - $ platformatic db migrate --to VERSION - -Use 000 to reset to the initial state. - -Options: - - * `-c, --config `: Path to the configuration file. - * `-t, --to `: Migrate to a specific version. - -If not specified, the configuration specified will be loaded from -`db.json`, `db.yml`, or `db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - - ### schema - Generate a schema from the database and prints it to standard output: - -* `schema graphql` - generate the GraphQL schema -* `schema openapi` - generate the OpenAPI schema - -Options: - - -c, --config FILE Specify a configuration file to use - -If not specified, the configuration specified will be loaded from -`db.json`, `db.yml`, or `db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - - ### seed - Load a seed into the database. This is a convenience method that loads -a JavaScript file and configure @platformatic/sql-mapper to connect to -the database specified in the configuration file. - -Here is an example of a seed file: - - 'use strict' - - module.exports = async function ({ entities, db, sql }) { - await entities.graph.save({ input: { name: 'Hello' } }) - await db.query(sql` - INSERT INTO graphs (name) VALUES ('Hello 2'); - `) - } - -You can run this using the `seed` command: - - $ platformatic db seed seed.js - -Options: - - * `--config` - Path to the configuration file. - -If not specified, the configuration specified will be loaded from -`db.json`, `db.yml`, or `db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - - ### start - Start the Platformatic DB server with the following command: - - $ platformatic db start - -You will need a configuration file. Here is an example to get you -started, save the following as `db.json`: - - { - "server": { - "hostname": "127.0.0.1", - "port": 0, - "logger": { - "level": "info" - } - }, - "core": { - "connectionString": "sqlite://./db" - }, - "migrations": { - "dir": "./migrations" - } - } - - -Remeber to create a migration, run the `db help migrate` command -to know more. - -All outstanding migrations will be applied to the database -unless the `migrations.autoApply` configuration option is set to -false. - -By sending the SIGUSR2 signal, the server can be reloaded. - -Options: - - * `-c --config FILE` Specify a configuration file to use - * `-watch-ignore=FILE1,FILE2` Specify which files should not be watched (and cause server restart) - -If not specified, the configuration specified will be loaded from -`db.json`, `db.yml`, or `db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - - \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/configuration.md b/versioned_docs/version-0.0.17/reference/configuration.md deleted file mode 100644 index 3457bb8c7b..0000000000 --- a/versioned_docs/version-0.0.17/reference/configuration.md +++ /dev/null @@ -1,186 +0,0 @@ -# Configuration - -## File support - -Platformatic DB supports JSON, JSON5, YAML and TOML file format. The format will be inferred by file extension (for YAML, both `.yml` and `.yaml` extension are supported). - -We'll use JSON format in the following examples. - -## Format - -Following root object's properties are supported - - [`authorization`](#authorization) - - [`core`](#core) (**required**) - - [`dashboard`](#dashboard) - - [`metrics`](#metrics) - - [`migrations`](#migrations) - - [`plugin`](#plugin) - - [`server`](#server) (**required**) - -### `authorization` - - `adminSecret` (string): if defined, it will be the password used to access the dashboard and the string to send within the `x-platformatic-admin-secret` header when performing GraphQL/REST API calls. - - `rules` (array): authorization rules that describe which CRUD action every user can/can't perform. - - The format of this array is TBD. - -### `core` - - `connectionString` (string) (**required**): This is the connection string used to connect to the database (i.e. `postgres://user:password@my-database:5432/db-name`). - - Platformatic DB supports Mysql/MariaDB, Postgresql and SQLite. - - `graphql` (boolean or object): enables the GraphQL support - - _Examples_ - - Enables GraphQL support - - ```JSON - { - "core": { - ... - "graphql": true - } - } - ``` - - Enables GraphQL support with GraphiQL - - ```JSON - { - "core": { - ... - "graphql": { - "graphiql": true - } - } - } - ``` - - `openapi` (boolean or object): enables OpenAPI REST support. If the value is an object, all [OpenAPI v3](https://swagger.io/specification/) properties can be detailed. - - Platformatic DB uses [`@fastify/swagger`](https://github.com/fastify/fastify-swagger) under the hood to manage this configuration. - - _Examples_ - - Enables OpenAPI - - ```JSON - { - "core": { - ... - "openapi": true - } - } - ``` - - Enables OpenAPI with options - - ```JSON - { - "core": { - ... - "openapi": { - "info": { - "title": "Platformatic DB", - "description": "Exposing a SQL database as REST" - } - } - } - } - ``` - - `ignore` (object): key/value object that define which tables should be ignored, meaning they are not entities. - - _Examples_ - - ```JSON - { - "core": { - ... - "ignore": { - "versions": true // "versions" table will be not mapped with GraphQL/REST APIs - } - } - } - ``` - -### `dashboard` - - `rootPath` (boolean): Whether the dashboard will be available at `/` location. Default is `true`. - -### `metrics` - Configuration for a [Prometheus](https://prometheus.io/) server that will export monitoring metrics for the current server instance. It uses [`fastify-metrics`](https://github.com/SkeLLLa/fastify-metrics) under the hodd. - - It can be a boolean or an object. If value is `true` this server will listen to `http://0.0.0.0:9090` - - - `hostname` (string): The hostname where Prometheus server will listen for connections. - - `port` (number): The port where Prometheus server will listen for connections. - - `auth` (object): Basic Auth configuration. `username` and `password` are required here. - - -### `migrations` - Configures [Postgrator](https://github.com/rickbergfalk/postgrator) to run migrations over the database. - - - `dir` (string) (**required**): Relative path to the migrations directory. - - `autoApply` (boolean): Whether to automatically apply migrations when running the migrate command. Default `true` - -### `plugin` - Defines a plugin that will be loaded with [`fastify-isolate`](https://github.com/mcollina/fastify-isolate). - - It is an object and all properties will be passed to `fastify-isolate` - - - `path` (string) (**required**): Relative path to plugin's entry point. - -### `server` - - - `hostname` (string) (**required**): The hostname where Platformatic DB server will listen for connections. - - `port` (number) (**required**): The port where Platformatic DB server will listen for connections. - - `healthCheck` (boolean or object): Enables the Health Check. Powered by [`@fastify/under-pressure`](https://github.com/fastify/under-pressure). - The value can be an object, used to specify the interval between checks (default 5000ms) - - _Example_ - - ```JSON - { - "server": { - ... - "healthCheck": { - "interval": 2000 - } - } - } - ``` - - `cors` (object): the value of this property will be passed to [`@fastify/cors`](https://github.com/fastify/fastify-cors) so all keys documented there are supported. - -## Environment Variables - -Each value can be replaced by an environment variable. - -To avoid messing/exposing system's variables, only `PLT_` prefixed ones will be replaced. - -All placeholders must be replaced. - -_Examples_ - - -```JSON -{ - "core": { - "connectionString": "{PLT_CONNECTION_STRING}" - }, - "server": { - "port": "{MY_PORT}" - } -} -``` - -Platformatic will look at the `PLT_CONNECTION_STRING` environment variable and will replace it in the config. - -Anyway it will throw an error because it can't replace `{PORT}` because it's not prefixed with `PLT_`. The error message will suggest to use `PLT_PORT` instead. - -Variables can be set via terminal (i.e. `PLT_PORT=4321 npx platformatic db --config db.json`) or via a `.env` file (loaded by [`dotenv`](https://github.com/motdotla/dotenv)). -The `.env` file can be located in the same folder of the config file or in the current working directory. - -### Environment Variables Whitelist - -Users can override default behavior, and use variables that are not prefixed with `PLT_`, using the `--allow-env` argument. - -The value is a comma separated list of strings. `--allow-env=PORT,HOST` will allow `{PORT}` and `{HOST}` in the config file. - -Default variables already supported are: `PORT`, `DATABASE_URL`. If the user pass `--allow-env` argument, it will replace the default list. diff --git a/versioned_docs/version-0.0.17/reference/db-authorization/_category_.json b/versioned_docs/version-0.0.17/reference/db-authorization/_category_.json deleted file mode 100644 index a1c00451ec..0000000000 --- a/versioned_docs/version-0.0.17/reference/db-authorization/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Authorization and Authentication" -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/db-authorization/intro.md b/versioned_docs/version-0.0.17/reference/db-authorization/intro.md deleted file mode 100644 index 58f11e8425..0000000000 --- a/versioned_docs/version-0.0.17/reference/db-authorization/intro.md +++ /dev/null @@ -1,6 +0,0 @@ -# Auth - - -## Context - -Each time we invoke the entity API we should pass a `ctx` object that comes from [mercurius](https://github.com/mercurius-js/mercurius/blob/master/docs/context.md), since GraphQL is a first-class citizen in Platformatic DB. diff --git a/versioned_docs/version-0.0.17/reference/entity.md b/versioned_docs/version-0.0.17/reference/entity.md deleted file mode 100644 index 88e28131a3..0000000000 --- a/versioned_docs/version-0.0.17/reference/entity.md +++ /dev/null @@ -1,38 +0,0 @@ -# Entity - -TBC - -## addEntityHooks (entityName, hooks) - -Wraps an entity with all the hooks defined in hooks. - -```js -app.platformatic.addEntityHooks(entityName, { - async find (originalFind, { where, ctx, fields }) { - // Do something... - - return originalFind({ where, ctx, fields }) - }, - - async save (originalSave, { input, ctx, fields }) { - // Do something... - - return originalSave({ input, ctx, fields }) - }, - - async insert (originalInsert, { inputs, ctx, fields }) { - // Do something... - - return originalInsert({ inputs, ctx, fields }) - }, - - async delete (originalDelete, { where, ctx, fields }) { - // Do something... - - return originalDelete({ where, ctx, fields }) - } -}) -``` - -Note the use of `ctx`. This is defined inside [Mercurius](https://github.com/mercurius-js/mercurius/blob/master/docs/context.md). -and exposed with the same API also by `sql-openapi`. diff --git a/versioned_docs/version-0.0.17/reference/graphql-api.md b/versioned_docs/version-0.0.17/reference/graphql-api.md deleted file mode 100644 index 20eafe8f34..0000000000 --- a/versioned_docs/version-0.0.17/reference/graphql-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# GraphQL API - -TODO diff --git a/versioned_docs/version-0.0.17/reference/rest-api.md b/versioned_docs/version-0.0.17/reference/rest-api.md deleted file mode 100644 index 7f17569717..0000000000 --- a/versioned_docs/version-0.0.17/reference/rest-api.md +++ /dev/null @@ -1,3 +0,0 @@ -# REST API - -TODO diff --git a/versioned_docs/version-0.0.17/reference/sql-graphql/_category_.json b/versioned_docs/version-0.0.17/reference/sql-graphql/_category_.json deleted file mode 100644 index 2a8641a660..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-graphql/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "SQL-to-GraphQL", - "position": 1 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-graphql/intro.md b/versioned_docs/version-0.0.17/reference/sql-graphql/intro.md deleted file mode 100644 index 934b00e474..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-graphql/intro.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_position: 1 ---- -# GraphQL API - -Platformatic DB GraphQL plugin automatically starts a GraphQL server (powered by [mercurius](https://mercurius.dev)) ready to run queries and mutations over your entities on the `/graphql` endpoint - - -## GraphiQL - -[GraphiQL](https://github.com/graphql/graphiql) is already integrated into Platformatic DB. -To enable that you just need to pass it as an option to the `sql-graphql` plugin. - -```js -... -app.register(graphqlPlugin, { graphiql: true }) - -``` - -GraphiQL interface will be available at `/graphiql` endpoint. \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-graphql/mutations.md b/versioned_docs/version-0.0.17/reference/sql-graphql/mutations.md deleted file mode 100644 index e239004748..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-graphql/mutations.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Mutations - -When the GraphQL plugin is loaded, some mutations are set up for you. - -## `save[ENTITY]` - -Saves a new entity in the database or updates an existing entity - -### Example - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres', - log: logger, - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - savePage(input: { id: 3 title: "Platformatic is cool!" }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { savePage: { id: '3', title: 'Platformatic is cool!' } } - await app.close() -} - -main() -``` - -## `insert[ENTITY]` - -Inserts a new entity in the database - -### Example - - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres', - log: logger, - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - savePage(input: { title: "Platformatic is cool!" }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { savePage: { id: '4', title: 'Platformatic is cool!' } } - await app.close() -} - -main() -``` - -## `delete[ENTITIES]` - -Deletes one or more entity from the database, based on the `where` clause passed as input to the mutation. - -### Example - - - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres', - log: logger, - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - deletePages(where: { id: { eq: "3" } }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { deletePages: [ { id: '3', title: 'Platformatic is cool!' } ] } - await app.close() -} - -main() -``` diff --git a/versioned_docs/version-0.0.17/reference/sql-graphql/queries.md b/versioned_docs/version-0.0.17/reference/sql-graphql/queries.md deleted file mode 100644 index 871aafcc77..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-graphql/queries.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Queries - -For each table (such as `pages`) a query is created, and all fields are mapped. - -## Example - - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - query{ - pages{ - id, - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) - await app.close() -} -main() -``` - -## Advanced Queries - -For each entity these other queries are created - -### `get[ENTITY]by[PRIMARY_KEY]` - -If you have a table `pages` with field `id` as primary key, you can run a query called `getPageById` - -#### Example - -```js -... -const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - query{ - getPageById(id: 3) { - id, - title - } - } - ` - } -}) -const result = await res.json() -console.log(result.data) // { getPageById: { id: '3', title: 'A fiction' } } -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/_category_.json b/versioned_docs/version-0.0.17/reference/sql-mapper/_category_.json deleted file mode 100644 index 842a89952f..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "SQL-Mapper", - "position": 1 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/_category_.json b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/_category_.json deleted file mode 100644 index 64295896fe..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Entity", - "position": 3, - "link": { - "type": "generated-index", - "description": "Entity Reference" - } -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/api.md b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/api.md deleted file mode 100644 index d4757a45ab..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/api.md +++ /dev/null @@ -1,245 +0,0 @@ ---- -sidebar_position: 3 ---- - -# API - -A set of methods are available on each entity: [`find`](#find), [`insert`](#insert), [`save`](#save) and [`delete`](#delete) - - -## Returned fields - -The entity methods accept a `fields` option that can specify an array of field names to be returned. If not specified, all fields will be returned. - - - -## Where clause - -The entity methods accept a `where` option to allow limiting of the database rows that will be affected by the operation. - -The `where` object's key is the field you want to check, the value is a key/value map where the key is an operator (see the table below) and the value is the value you want to run the operator against. - -| Platformatic operator | SQL operator | -|--- | ---| -| eq | `'='` | -| in | `'IN'` | -| nin | `'NOT IN'` | -| neq | `'<>'` | -| gt | `'>'` | -| gte | `'>='` | -| lt | `'<'` | -| lte | `'<='` | - -### Examples - -#### Selects row with `id = 1` -``` -{ - ... - "where": { - id: { - eq: 1 - } - } -} -``` - -#### Select all rows with id less than 100 -``` -{ - ... - "where": { - id: { - lt: 100 - } - } -} -``` - -#### Select all rows with id 1, 3, 5 or 7 -``` -{ - ... - "where": { - id: { - in: [1, 3, 5, 7] - } - } -} -``` - -## Reference - -### `find` - -Retrieve data for an entity from the database. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `where` | `Object` | [Where clause 🔗](#where-clause) -| `orderBy` | Array of `Object` | Object like `{ field: 'counter', direction: 'ASC' }` -| `limit` | `Number` | Limits the number of returned elements -| `offset` | `Number` | The offset to start looking for rows from - - -#### Usage - - -```js -'use strict' - -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() -``` - -### `insert` - -Insert one or more entity rows in the database. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `inputs` | Array of `Object` | Each object is a new row - -#### Usage - -```js -'use strict' - -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const res = await mapper.entities.page.insert({ - fields: ['id', 'title' ], - inputs: [ - { title: 'Foobar' }, - { title: 'FizzBuzz' } - ], - }) - logger.info(res) - /** - 0: { - "id": "16", - "title": "Foobar" - } - 1: { - "id": "17", - "title": "FizzBuzz" - } - */ - await mapper.db.dispose() -} -main() -``` - -### `save` - -Create a new entity row in the database or update an existing one. - -To update an existing entity, the `id` field (or equivalent primary key) must be included in the `input` object. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `input` | `Object` | The single row to create/update - -#### Usage - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: connectionString, - log: logger, - }) - const res = await mapper.entities.page.save({ - fields: ['id', 'title' ], - input: { id: 1, title: 'FizzBuzz' }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() -``` -### `delete` - -Delete one or more entity rows from the database, depending on the `where` option. Returns the data for all deleted objects. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `where` | `Object` | [Where clause 🔗](#where-clause) - -#### Usage - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: connectionString, - log: logger, - }) - const res = await mapper.entities.page.delete({ - fields: ['id', 'title',], - where: { - id: { - lt: 4 - } - }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() - -``` diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/example.md b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/example.md deleted file mode 100644 index 0219824bf7..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/example.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Example - -Given this PostgreSQL SQL schema: - -```sql -CREATE TABLE "categories" ( - "id" int4 NOT NULL DEFAULT nextval('categories_id_seq'::regclass), - "name" varchar(255) NOT NULL, - PRIMARY KEY ("id") -); - -CREATE TABLE "pages" ( - "id" int4 NOT NULL DEFAULT nextval('pages_id_seq'::regclass), - "title" varchar(255) NOT NULL, - "category_id" int4, - "user_id" int4, - PRIMARY KEY ("id") -); - -ALTER TABLE "pages" ADD FOREIGN KEY ("category_id") REFERENCES "categories"("id"); -``` - -`app.platformatic.entities` will contain this mapping object: - -```json -{ - "category": { - "name": "Category", - "singularName": "category", - "pluralName": "categories", - "primaryKey": "id", - "table": "categories", - "fields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "name": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "name" - } - }, - "camelCasedFields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "name": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "name" - } - }, - "relations": [], - "reverseRelationships": [ - { - "sourceEntity": "Page", - "relation": { - "constraint_catalog": "postgres", - "constraint_schema": "public", - "constraint_name": "pages_category_id_fkey", - "table_catalog": "postgres", - "table_schema": "public", - "table_name": "pages", - "constraint_type": "FOREIGN KEY", - "is_deferrable": "NO", - "initially_deferred": "NO", - "enforced": "YES", - "column_name": "category_id", - "ordinal_position": 1, - "position_in_unique_constraint": 1, - "foreign_table_name": "categories", - "foreign_column_name": "id" - } - } - ] - }, - "page": { - "name": "Page", - "singularName": "page", - "pluralName": "pages", - "primaryKey": "id", - "table": "pages", - "fields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "title": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "title" - }, - "category_id": { - "sqlType": "int4", - "isNullable": true, - "foreignKey": true, - "camelcase": "categoryId" - }, - "user_id": { - "sqlType": "int4", - "isNullable": true, - "camelcase": "userId" - } - }, - "camelCasedFields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "title": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "title" - }, - "categoryId": { - "sqlType": "int4", - "isNullable": true, - "foreignKey": true, - "camelcase": "categoryId" - }, - "userId": { - "sqlType": "int4", - "isNullable": true, - "camelcase": "userId" - } - }, - "relations": [ - { - "constraint_catalog": "postgres", - "constraint_schema": "public", - "constraint_name": "pages_category_id_fkey", - "table_catalog": "postgres", - "table_schema": "public", - "table_name": "pages", - "constraint_type": "FOREIGN KEY", - "is_deferrable": "NO", - "initially_deferred": "NO", - "enforced": "YES", - "column_name": "category_id", - "ordinal_position": 1, - "position_in_unique_constraint": 1, - "foreign_table_name": "categories", - "foreign_column_name": "id" - } - ], - "reverseRelationships": [] - } -} -``` - diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/fields.md b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/fields.md deleted file mode 100644 index 920c6ed593..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/fields.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Fields - -When Platformatic DB inspects a database's schema, it creates an object for each table that contains a mapping of their fields. - -These objects contain the following properties: -- `singularName`: singular entity name, based on table name. Uses [inflected](https://www.npmjs.com/package/inflected) under the hood. -- `pluralName`: plural entity name (i.e `'pages'`) -- `primaryKey`: the field which is identified as primary key. -- `table`: original table name -- `fields`: an object containing all fields details. Object key is the field name. -- `camelCasedFields`: an object containing all fields details in camelcase. If you have a column named `user_id` you can access it using both `userId` or `user_id` - -## Fields detail - -- `sqlType`: The original field type. It may vary depending on the underlying DB Engine -- `isNullable`: Whether the field can be `null` or not -- `primaryKey`: Whether the field is the primary key or not -- `camelcase`: The _camelcased_ value of the field - -## Example -Given this SQL Schema (for PostgreSQL): -```SQL -CREATE SEQUENCE IF NOT EXISTS pages_id_seq; -CREATE TABLE "public"."pages" ( - "id" int4 NOT NULL DEFAULT nextval('pages_id_seq'::regclass), - "title" varchar, - "body_content" text, - "category_id" int4, - PRIMARY KEY ("id") -); -``` - -The resulting mapping object will be: - -```js -{ - singularName: 'page', - pluralName: 'pages', - primaryKey: 'id', - table: 'pages', - fields: { - id: { - sqlType: 'int4', - isNullable: false, - primaryKey: true, - camelcase: 'id' - }, - title: { - sqlType: 'varchar', - isNullable: true, - camelcase: 'title' - }, - body_content: { - sqlType: 'text', - isNullable: true, - camelcase: 'bodyContent' - }, - category_id: { - sqlType: 'int4', - isNullable: true, - foreignKey: true, - camelcase: 'categoryId' - } - } - camelCasedFields: { - id: { - sqlType: 'int4', - isNullable: false, - primaryKey: true, - camelcase: 'id' - }, - title: { - sqlType: 'varchar', - isNullable: true, - camelcase: 'title' - }, - bodyContent: { - sqlType: 'text', - isNullable: true, - camelcase: 'bodyContent' - }, - categoryId: { - sqlType: 'int4', - isNullable: true, - foreignKey: true, - camelcase: 'categoryId' - } - }, - relations: [] -} -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/hooks.md b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/hooks.md deleted file mode 100644 index 888d2607a6..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/hooks.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Hooks - -Entity hooks are a way to wrap the [API methods](./api) for an entity and add custom behaviour. - -The Platformatic DB SQL Mapper provides an `addEntityHooks(entityName, spec)` function that can be used to add hooks for an entity. - -## How to use hooks - -`addEntityHooks` accepts two arguments: - -1. A string representing the entity name (singularized), for example `'page'`. -1. A key/value object where the key is one of the API methods (`find`, `insert`, `save`, `delete`) and the value is a callback function. The callback will be called with the _original_ API method and the options that were passed to that method. See the example below. - -### Usage - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - mapper.addEntityHooks('page', { - find: async (originalFind, opts) => { - // Add a `foo` field with `bar` value to each row - const res = await originalFind(opts) - return res.map((row) => { - row.foo = 'bar' - return row - }) - } - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - /** - [ - 0: { - "id": "5", - "title": "Page 1", - "foo": "bar" - }, - 1: { - "id": "6", - "title": "Page 2", - "foo": "bar" - } - ] - */ - await mapper.db.dispose() -} -main() -``` - - -## Multiple Hooks - -Multiple hooks can be added for the same entity and API method, for example: - - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - mapper.addEntityHooks('page', { - find: async function firstHook(previousFunction, opts) { - // Add a `foo` field with `bar` value to each row - const res = await previousFunction(opts) - return res.map((row) => { - row.foo = 'bar' - return row - }) - } - }) - mapper.addEntityHooks('page', { - find: async function secondHook(previousFunction, opts) { - // Add a `bar` field with `baz` value to each row - const res = await previousFunction(opts) - return res.map((row) => { - row.bar = 'baz' - return row - }) - } - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - /** - [ - 0: { - "id": "5", - "title": "Page 1", - "foo": "bar", - "bar": "baz" - }, - 1: { - "id": "6", - "title": "Page 2", - "foo": "bar", - "bar": "baz" - } - ] - */ - await mapper.db.dispose() -} -main() -``` - -Since hooks are wrappers, they are being called in reverse order, like the image below - -![Hooks Lifecycle](../images/plt-db-hooks.svg) - -So even though we defined two hooks, the Database will be hit only once. - -Query result will be processed by `firstHook`, which will pass the result to `secondHook`, which will, finally, send the processed result to the original `.find({...})` function. - - diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/intro.md b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/intro.md deleted file mode 100644 index 21bec7ca4f..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/intro.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -sidebar_position: 1 ---- -# Introduction - -The primary goal of Platformatic DB is to read a database schema and generate REST and GraphQL endpoints that enable the execution of CRUD (Create/Retrieve/Update/Delete) operations against the database. - -Platformatic DB includes a _mapper_ that reads the schemas of database tables and then generates an _entity_ object for each table. - -Platformatic DB is a [Fastify](https://fastify.io) application. The Fastify instance object is decorated with the `platformatic` property, which exposes several APIs that handle the manipulation of data in the database. - -Platformatic DB populates the `app.platformatic.entities` object with data found in database tables. - -The keys on the `entities` object are _singularized_ versions of the table names — for example `users` becomes `user`, `categories` becomes `category` — and the values are a set of associated metadata and functions. \ No newline at end of file diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/relations.md b/versioned_docs/version-0.0.17/reference/sql-mapper/entity/relations.md deleted file mode 100644 index 2fc777383f..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/entity/relations.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -sidebar_position: 5 ---- - -# Relations - -When Platformatic DB is reading your database schema, it will pick up also relations between tables and put those info into a `relations` field in the entity object. - -We do this by querying the database internal metadata - -## Example - -Giving this PostgreSQL schema: - -```SQL -CREATE SEQUENCE IF NOT EXISTS categories_id_seq; - -CREATE TABLE "categories" ( - "id" int4 NOT NULL DEFAULT nextval('categories_id_seq'::regclass), - "name" varchar(255) NOT NULL, - PRIMARY KEY ("id") -); - -CREATE SEQUENCE IF NOT EXISTS pages_id_seq; - -CREATE TABLE "pages" ( - "id" int4 NOT NULL DEFAULT nextval('pages_id_seq'::regclass), - "title" varchar(255) NOT NULL, - "body_content" text, - "category_id" int4, - PRIMARY KEY ("id") -); - -ALTER TABLE "pages" ADD FOREIGN KEY ("category_id") REFERENCES "categories"("id"); -``` - -And running this code - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const pageEntity = mapper.entities.page - console.log(pageEntity.relations) - await mapper.db.dispose() -} -main() -``` - -This will be the output -``` -[ - { - constraint_catalog: 'postgres', - constraint_schema: 'public', - constraint_name: 'pages_category_id_fkey', - table_catalog: 'postgres', - table_schema: 'public', - table_name: 'pages', - constraint_type: 'FOREIGN KEY', - is_deferrable: 'NO', - initially_deferred: 'NO', - enforced: 'YES', - column_name: 'category_id', - ordinal_position: 1, - position_in_unique_constraint: 1, - foreign_table_name: 'categories', - foreign_column_name: 'id' - } -] -``` - -Since Platformatic DB runs against many database engines, that object might be different in MySQL or PostgreSQL or SQLite. - -There are, although, these fields that are common to all databases - -- `column_name`: the column that stores the foreign key -- `foreign_table_name`: the table hosting the related row -- `foreign_column_name`: the column in foreign table that identifies the row - - diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/fastify-plugin.md b/versioned_docs/version-0.0.17/reference/sql-mapper/fastify-plugin.md deleted file mode 100644 index 6660517489..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/fastify-plugin.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -sidebar_position: 2 ---- -# Fastify Plugin -The `@platformatic/sql-mapper` package exports a [Fastify](https://fastify.io) plugin that can be used out-of the box in a server application. - -A `connectionString` option must be passed to connect to your database. - -The plugin decorates the server with a `platformatic` object that has the following properties: - -- `db` — the DB wrapper object provided by [`@databases`](https://www.atdatabases.org/) -- `sql` — the SQL query mapper object provided by [`@databases`](https://www.atdatabases.org/) -- `entities` — all entity objects with their [API methods](./entity/api) -- `addEntityHooks` — a function to add a [hook](./entity/hooks) to an entity API method. - -#### Usage - -```js -'use strict' - -const Fastify = require('fastify') -const mapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(mapper.plugin, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - - app.get('/all-pages', async (req, reply) => { - // Will return all rows from 'pages' table - const res = await app.platformatic.entities.page.find() - return res - }) - - await app.listen({ port: 3333 }) -} - -main() -``` diff --git a/versioned_docs/version-0.0.17/reference/sql-mapper/intro.md b/versioned_docs/version-0.0.17/reference/sql-mapper/intro.md deleted file mode 100644 index 7c954e1b97..0000000000 --- a/versioned_docs/version-0.0.17/reference/sql-mapper/intro.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Introduction - -The Platformatic DB Mapper will inspect a database schema and return an object containing: - -- `db`: a Database abstraction layer from [@Databases](https://www.atdatabases.org/) -- `sql`: The SQL builder from [@Databases](https://www.atdatabases.org/) -- `entities` and object containing a key for each table found in the schema, with basic CRUD operations. See [Entity Reference](./entity/intro.md) for details. - -It exports a function that accepts following parameters - -- `connectionString`: The Database connection string -- `log`: A logger object (like [Pino](https://getpino.io)) -- `onDatabaseLoad`: An async function that is called after the connection is established. It will receive `db` and `sql` as parameter. -- `ignore`: Object used to ignore some tables from building entities. (i.e. `{ 'versions': true }` will ignore `versions` table) -- `autoTimestamp`: Generate timestamp automatically when inserting/updating records. -- `hooks`: For each entity name (like `Page`) you can customize any of the entity API function. Your custom function will receive the original function as first parameter, and then all the other parameters passed to it. - -## Code samples - -```javascript -const { pino } = require('pino') - -const logger = pino() - -async function onDatabaseLoad (db, sql) { - await db.query(sql`CREATE TABLE pages ( - id SERIAL PRIMARY KEY, - title VARCHAR(255) NOT NULL - );`) -} -const connectionString = - 'postgres://postgres:postgres@localhost:5432/postgres' -const mapper = await connect({ - connectionString, - log: logger, - onDatabaseLoad, - ignore: {}, - hooks: { - Page: { - find: async function(_find, opts) { - console.log('hook called'); - return await _find(opts) - } - } - } -}) -const pageEntity = mapper.entities.page - -await mapper.db.query(mapper.sql`SELECT * FROM pages`) -await mapper.db.find('option1', 'option2') -... - -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/contributing/_category_.json b/versioned_docs/version-0.0.21/contributing/_category_.json deleted file mode 100644 index 88df27c409..0000000000 --- a/versioned_docs/version-0.0.21/contributing/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Contributing", - "position": 4 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/contributing/intro.md b/versioned_docs/version-0.0.21/contributing/intro.md deleted file mode 100644 index b60ddf51f8..0000000000 --- a/versioned_docs/version-0.0.21/contributing/intro.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contributing - -Work in progres... \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/contributing/style-guide.md b/versioned_docs/version-0.0.21/contributing/style-guide.md deleted file mode 100644 index fbb6c09891..0000000000 --- a/versioned_docs/version-0.0.21/contributing/style-guide.md +++ /dev/null @@ -1,238 +0,0 @@ ---- -credits: https://github.com/fastify/fastify/blob/main/docs/Guides/Style-Guide.md ---- - -# Documentation Style Guide - -Welcome to the *Platformatic Documentation Style Guide*. This guide is here to provide -you with a conventional writing style for users writing developer documentation on -our Open Source framework. Each topic is precise and well explained to help you write -documentation users can easily understand and implement. - -## Who is this guide for? - -This guide is for anyone who loves to build with Platformatic or wants to contribute -to our documentation. You do not need to be an expert in writing technical -documentation. This guide is here to help you. - -Visit [CONTRIBUTING.md](https://github.com/platformatic/platformatic/blob/main/CONTRIBUTING.md) -file on GitHub to join our Open Source folks. - -## Before you write - -You should have a basic understanding of: - -* JavaScript -* Node.js -* Git -* GitHub -* Markdown -* HTTP -* NPM - -### Consider your Audience - -Before you start writing, think about your audience. In this case, your audience -should already know HTTP, JavaScript, NPM, and Node.js. It is necessary to keep -your readers in mind because they are the ones consuming your content. You want -to give as much useful information as possible. Consider the vital things they -need to know and how they can understand them. Use words and references that -readers can relate to easily. Ask for feedback from the community, it can help -you write better documentation that focuses on the user and what you want to -achieve. - -### Get straight to the point - -Give your readers a clear and precise action to take. Start with what is most -important. This way, you can help them find what they need faster. Mostly, -readers tend to read the first content on a page, and many will not scroll -further. - -**Example** - -Less like this: Colons are very important to register a parametric path. It lets -the framework know there is a new parameter created. You can place the colon -before the parameter name so the parametric path can be created. - -More Like this: To register a parametric path, put a colon before the parameter -name. Using a colon lets the framework know it is a parametric path and not a -static path. - -### Images and video should enhance the written documentation - - -Images and video should only be added if they complement the written -documentation, for example to help the reader form a clearer mental model of a -concept or pattern. - -Images can be directly embedded, but videos should be included by linking to an -external site, such as YouTube. You can add links by using -`[Title](https://www.websitename.com)` in the Markdown. - - - - -### Avoid plagiarism - -Make sure you avoid copying other people's work. Keep it as original as -possible. You can learn from what they have done and reference where it is from -if you used a particular quote from their work. - - -## Word Choice - -There are a few things you need to use and avoid when writing your documentation -to improve readability for readers and make documentation neat, direct, and -clean. - - -### When to use the second person "you" as the pronoun - -When writing articles or guides, your content should communicate directly to -readers in the second person ("you") addressed form. It is easier to give them -direct instruction on what to do on a particular topic. To see an example, visit -the [Quick Start Guide](../getting-started/quick-start-guide.md). - -**Example** - -Less like this: we can use the following plugins. - -More like this: You can use the following plugins. - -> According to [Wikipedia](#), ***You*** is usually a second person pronoun. -> Also, used to refer to an indeterminate person, as a more common alternative -> to a very formal indefinite pronoun. - -## When to avoid the second person "you" as the pronoun - -One of the main rules of formal writing such as reference documentation, or API -documentation, is to avoid the second person ("you") or directly addressing the -reader. - -**Example** - -Less like this: You can use the following recommendation as an example. - -More like this: As an example, the following recommendations should be -referenced. - -To view a live example, refer to the [Decorators](../reference/configuration.md) -reference document. - - -### Avoid using contractions - -Contractions are the shortened version of written and spoken forms of a word, -i.e. using "don't" instead of "do not". Avoid contractions to provide a more -formal tone. - -### Avoid using condescending terms - -Condescending terms are words that include: - -* Just -* Easy -* Simply -* Basically -* Obviously - -The reader may not find it easy to use Platformatic; avoid -words that make it sound simple, easy, offensive, or insensitive. Not everyone -who reads the documentation has the same level of understanding. - -### Starting with a verb - -Mostly start your description with a verb, which makes it simple and precise for -the reader to follow. Prefer using present tense because it is easier to read -and understand than the past or future tense. - -**Example** - - Less like this: There is a need for Node.js to be installed before you can be - able to use Platformatic. - - More like this: Install Node.js to make use of Platformatic. - -### Grammatical moods - -Grammatical moods are a great way to express your writing. Avoid sounding too -bossy while making a direct statement. Know when to switch between indicative, -imperative, and subjunctive moods. - - -**Indicative** - Use when making a factual statement or question. - -Example: Since there is no testing framework available, "Platformatic recommends ways -to write tests". - -**Imperative** - Use when giving instructions, actions, commands, or when you -write your headings. - -Example: Install dependencies before starting development. - - -**Subjunctive** - Use when making suggestions, hypotheses, or non-factual -statements. - -Example: Reading the documentation on our website is recommended to get -comprehensive knowledge of the framework. - -### Use **active** voice instead of **passive** - -Using active voice is a more compact and direct way of conveying your -documentation. - -**Example** - - -Passive: The node dependencies and packages are installed by npm. - -Active: npm installs packages and node dependencies. - -## Writing Style - -### Documentation titles - -When creating a new guide, API, or reference in the `/docs/` directory, use -short titles that best describe the topic of your documentation. Name your files -in kebab-cases and avoid Raw or camelCase. To learn more about kebab-case you -can visit this medium article on [Case -Styles](https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841). - -**Examples**: - ->`hook-and-plugins.md`, - - `adding-test-plugins.md`, - - `removing-requests.md`. - -### Hyperlinks - -Hyperlinks should have a clear title of what it references. Here is how your -hyperlink should look: - -```MD - - -// Add clear & brief description -[Fastify Plugins] (https://www.fastify.io/docs/latest/Plugins/) - - - -// incomplete description -[Fastify] (https://www.fastify.io/docs/latest/Plugins/) - -// Adding title in link brackets -[](https://www.fastify.io/docs/latest/Plugins/ "fastify plugin") - -// Empty title -[](https://www.fastify.io/docs/latest/Plugins/) - -// Adding links localhost URLs instead of using code strings (``) -[http://localhost:3000/](http://localhost:3000/) - -``` - -Include in your documentation as many essential references as possible, but -avoid having numerous links when writing for beginners to avoid distractions. diff --git a/versioned_docs/version-0.0.21/getting-started/_category_.json b/versioned_docs/version-0.0.21/getting-started/_category_.json deleted file mode 100644 index 87121b812f..0000000000 --- a/versioned_docs/version-0.0.21/getting-started/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Getting Started", - "position": 1 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/getting-started/architecture.md b/versioned_docs/version-0.0.21/getting-started/architecture.md deleted file mode 100644 index 183ea474d7..0000000000 --- a/versioned_docs/version-0.0.21/getting-started/architecture.md +++ /dev/null @@ -1,28 +0,0 @@ -# Architecture - -Platformatic is a collection of Open Source tools designed to eliminate friction -in backend development. The first of those tools is Platformatic DB, which is developed -as `@platformatic/db`. - -## Platformatic DB - -Platformatic DB can expose a SQL database by dynamically mapping it to REST/OpenAPI -and GraphQL endpoints. It supports a limited subset of the SQL query language, but -also allows developers to add their own custom routes and resolvers. - -![Platformatic DB Architecture](./platformatic-architecture.png) - -Platformatic DB is composed of a few key libraries: - -1. `@platformatic/sql-mapper` - follows the [Data Mapper pattern](https://en.wikipedia.org/wiki/Data_mapper_pattern) to build an API on top of a SQL database. - Internally it uses the [`@database` project](https://www.atdatabases.org/). -1. `@platformatic/sql-openapi` - uses `sql-mapper` to create a series of REST routes and matching OpenAPI definitions. - Internally it uses [`@fastify/swagger`](https://github.com/fastify/fastify-swagger). -1. `@platformatic/sql-graphql` - uses `sql-mapper` to create a GraphQL endpoint and schema. `sql-graphql` also support Federation. - Internally it uses [`mercurius`](https://github.com/mercurius-js/mercurius). - -Platformatic DB allows you to load a [Fastify plugin](https://www.fastify.io/docs/latest/Reference/Plugins/) during server startup that contains your own application-specific code. -The plugin can add more routes or resolvers — these will automatically be shown in the OpenAPI and GraphQL schemas. - -SQL database migrations are also supported. They're implemented internally with the [`postgrator`](https://www.npmjs.com/package/postgrator) library. - diff --git a/versioned_docs/version-0.0.21/getting-started/platformatic-architecture.png b/versioned_docs/version-0.0.21/getting-started/platformatic-architecture.png deleted file mode 100644 index e19a8cdd18..0000000000 Binary files a/versioned_docs/version-0.0.21/getting-started/platformatic-architecture.png and /dev/null differ diff --git a/versioned_docs/version-0.0.21/getting-started/platformatid-db-architecture.excalidraw b/versioned_docs/version-0.0.21/getting-started/platformatid-db-architecture.excalidraw deleted file mode 100644 index 04d7f461f5..0000000000 --- a/versioned_docs/version-0.0.21/getting-started/platformatid-db-architecture.excalidraw +++ /dev/null @@ -1,1777 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": [ - { - "type": "rectangle", - "version": 551, - "versionNonce": 253868331, - "isDeleted": false, - "id": "1zlVmmd_Y9S9Oz0S_fTUH", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 408.666015625, - "y": 254.265625, - "strokeColor": "#000000", - "backgroundColor": "#fab005", - "width": 522.21875, - "height": 73.2265625, - "seed": 418516020, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "zfmfD9pZLlP_G075cvUTv", - "type": "arrow" - }, - { - "id": "mzCbJHPkBmRZyt8z7yCAe", - "type": "arrow" - } - ], - "updated": 1662501577385, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 973, - "versionNonce": 199542836, - "isDeleted": false, - "id": "4CCO4Ro-Gy5uYIPERvP7H", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 785.0390625, - "y": 636.41796875, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 135.51171875, - "height": 74.8515625, - "seed": 906185780, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "zfmfD9pZLlP_G075cvUTv", - "type": "arrow" - }, - { - "id": "rQggHgShjGGXQ0hiwBQVd", - "type": "arrow" - } - ], - "updated": 1662412597026, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 184, - "versionNonce": 668075276, - "isDeleted": false, - "id": "R_U11fvrIlgCS1IlhmWIQ", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 585.58984375, - "y": 286.25390625, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 136, - "height": 25, - "seed": 553319692, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Your Frontend", - "baseline": 18, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Your Frontend" - }, - { - "type": "rectangle", - "version": 1250, - "versionNonce": 2047189428, - "isDeleted": false, - "id": "W06kzPlnPRgvKtfyagn_y", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 410.49609375, - "y": 381.2578125, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 519.26171875, - "height": 180.80078124999997, - "seed": 1222202124, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "7lr5UwO6mqEbEA4WdbjS3", - "type": "arrow" - }, - { - "id": "zfmfD9pZLlP_G075cvUTv", - "type": "arrow" - }, - { - "id": "rQggHgShjGGXQ0hiwBQVd", - "type": "arrow" - } - ], - "updated": 1662412597026, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 1593, - "versionNonce": 1162225739, - "isDeleted": false, - "id": "b8p9mgZWBw2sgu28jjLm-", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 754.9140625, - "y": 350.640625, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 165, - "height": 25, - "seed": 759852684, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662501578885, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Platformatic DB", - "baseline": 18, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Platformatic DB" - }, - { - "type": "line", - "version": 4826, - "versionNonce": 188026420, - "isDeleted": false, - "id": "WkWYX211VfpfEJZakoT-m", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 415.6994711197414, - "y": 636.1128296351798, - "strokeColor": "#0a11d3", - "backgroundColor": "#228be6", - "width": 88.21658171083376, - "height": 113.8575037534261, - "seed": 1401759284, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 0.29089298333313673, - 86.05288422061678 - ], - [ - 0.013613108737802165, - 95.84963140781468 - ], - [ - 4.543349062013738, - 100.08268472409586 - ], - [ - 20.317928500125443, - 103.66521849306073 - ], - [ - 46.98143617553956, - 104.78076599153316 - ], - [ - 72.45665455006592, - 102.9996310009587 - ], - [ - 85.99182564238487, - 98.74007888522631 - ], - [ - 87.90077837148979, - 95.14923176741362 - ], - [ - 88.16888387182134, - 87.26194204835767 - ], - [ - 87.95845222911922, - 7.219356674957439 - ], - [ - 87.48407176050935, - -0.3431928547433216 - ], - [ - 81.81967725989045, - -4.569951534960701 - ], - [ - 69.89167127292335, - -7.017866506201685 - ], - [ - 42.70935725136615, - -9.076737761892943 - ], - [ - 20.91603533578692, - -7.849028196182914 - ], - [ - 3.775735655469765, - -3.684787148572539 - ], - [ - -0.047697839012426885, - -0.0517060607782156 - ], - [ - 0, - 0 - ] - ] - }, - { - "type": "line", - "version": 2560, - "versionNonce": 957423372, - "isDeleted": false, - "id": "gEFv_XFXnFnIvMtMWGW8s", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 416.38683270923883, - "y": 701.4029576659674, - "strokeColor": "#0a11d3", - "backgroundColor": "transparent", - "width": 88.30808627974527, - "height": 9.797916664247975, - "seed": 1407616780, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 2.326538897826852, - 3.9056133261361587 - ], - [ - 12.359939318521995, - 7.182387014695761 - ], - [ - 25.710950037209347, - 9.166781347006062 - ], - [ - 46.6269757640547, - 9.347610268342288 - ], - [ - 71.03526003420632, - 8.084235941711592 - ], - [ - 85.2899738827162, - 3.4881086608341767 - ], - [ - 88.30808627974527, - -0.45030639590568633 - ] - ] - }, - { - "type": "line", - "version": 2647, - "versionNonce": 1935484852, - "isDeleted": false, - "id": "vt3ifycjA7xAUXWBfhMbE", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 415.2930911971775, - "y": 668.3288031038957, - "strokeColor": "#0a11d3", - "backgroundColor": "transparent", - "width": 88.30808627974527, - "height": 9.797916664247975, - "seed": 686964660, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 2.326538897826852, - 3.9056133261361587 - ], - [ - 12.359939318521995, - 7.182387014695761 - ], - [ - 25.710950037209347, - 9.166781347006062 - ], - [ - 46.6269757640547, - 9.347610268342288 - ], - [ - 71.03526003420632, - 8.084235941711592 - ], - [ - 85.2899738827162, - 3.4881086608341767 - ], - [ - 88.30808627974527, - -0.45030639590568633 - ] - ] - }, - { - "type": "ellipse", - "version": 5667, - "versionNonce": 1513184652, - "isDeleted": false, - "id": "ogy32Rih_TrO63WbbEXuy", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 414.1722685110156, - "y": 628.0881334080838, - "strokeColor": "#0a11d3", - "backgroundColor": "#fff", - "width": 87.65074610854188, - "height": 17.72670397681366, - "seed": 1176094092, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "sharp", - "boundElements": [ - { - "type": "arrow", - "id": "bxuMGTzXLn7H-uBCptINx" - }, - { - "id": "7lr5UwO6mqEbEA4WdbjS3", - "type": "arrow" - } - ], - "updated": 1662412597026, - "link": null, - "locked": false - }, - { - "type": "ellipse", - "version": 1034, - "versionNonce": 575017268, - "isDeleted": false, - "id": "blhO-aMedBW56hjM8iBeb", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 485.68200081159625, - "y": 652.6639362852167, - "strokeColor": "#0a11d3", - "backgroundColor": "#fff", - "width": 12.846057046979809, - "height": 13.941904362416096, - "seed": 360196404, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false - }, - { - "type": "ellipse", - "version": 1083, - "versionNonce": 432393228, - "isDeleted": false, - "id": "OO8WqGjES_ZtV1HgusFX3", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 485.68200081159625, - "y": 683.2663901442771, - "strokeColor": "#0a11d3", - "backgroundColor": "#fff", - "width": 12.846057046979809, - "height": 13.941904362416096, - "seed": 1013003276, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false - }, - { - "type": "ellipse", - "version": 1137, - "versionNonce": 659933876, - "isDeleted": false, - "id": "XYWNN5j1eug8c7TYVSHsR", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 485.68200081159625, - "y": 716.5271913187738, - "strokeColor": "#0a11d3", - "backgroundColor": "#fff", - "width": 12.846057046979809, - "height": 13.941904362416096, - "seed": 478470836, - "groupIds": [ - "6bLMR27dyecCy-nls2xaX", - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 814, - "versionNonce": 2043177612, - "isDeleted": false, - "id": "gMXOy183cvSYG2XLH1Nps", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 523.53515625, - "y": 651.0625000000001, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 114, - "height": 75, - "seed": 1990988468, - "groupIds": [ - "u14TNEkdqVuYB_Aj5ykEw" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "MySQL\nPostgreSQL\nSQLite", - "baseline": 68, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "MySQL\nPostgreSQL\nSQLite" - }, - { - "type": "rectangle", - "version": 246, - "versionNonce": 1377935909, - "isDeleted": false, - "id": "PvIWYwKCctyqauI5pTaz2", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 437.234375, - "y": 395.46484375, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 214, - "height": 49, - "seed": 1003500172, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "type": "text", - "id": "ePp9eFiz5m2-X9DieNpXq" - }, - { - "id": "zfmfD9pZLlP_G075cvUTv", - "type": "arrow" - } - ], - "updated": 1662501565240, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 164, - "versionNonce": 2126526348, - "isDeleted": false, - "id": "ePp9eFiz5m2-X9DieNpXq", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 442.234375, - "y": 407.46484375, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 204, - "height": 25, - "seed": 785206068, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "REST", - "baseline": 18, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "PvIWYwKCctyqauI5pTaz2", - "originalText": "REST" - }, - { - "type": "rectangle", - "version": 486, - "versionNonce": 936510213, - "isDeleted": false, - "id": "p8xcTPjfpNzqdS2Bi6oAZ", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 683.826171875, - "y": 394.291015625, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 205, - "height": 49, - "seed": 994139020, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "NCCySoN90b0h8dl252sC8", - "type": "text" - }, - { - "type": "text", - "id": "NCCySoN90b0h8dl252sC8" - } - ], - "updated": 1662501577385, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 409, - "versionNonce": 996763148, - "isDeleted": false, - "id": "NCCySoN90b0h8dl252sC8", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 688.826171875, - "y": 406.291015625, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 195, - "height": 25, - "seed": 415036212, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "GraphQL", - "baseline": 18, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "p8xcTPjfpNzqdS2Bi6oAZ", - "originalText": "GraphQL" - }, - { - "type": "arrow", - "version": 2380, - "versionNonce": 382783668, - "isDeleted": false, - "id": "7lr5UwO6mqEbEA4WdbjS3", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 454.9013510980401, - "y": 570.55078125, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 0, - "height": 50.53939662172979, - "seed": 646336524, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "startBinding": { - "elementId": "W06kzPlnPRgvKtfyagn_y", - "focus": 0.828967721884312, - "gap": 8.4921875 - }, - "endBinding": { - "elementId": "ogy32Rih_TrO63WbbEXuy", - "focus": -0.07065063572675613, - "gap": 7.019406618337827 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 0, - 50.53939662172979 - ] - ] - }, - { - "type": "arrow", - "version": 1800, - "versionNonce": 1766871115, - "isDeleted": false, - "id": "zfmfD9pZLlP_G075cvUTv", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 662.849929238139, - "y": 334.2265625, - "strokeColor": "#000000", - "backgroundColor": "#40c057", - "width": 0, - "height": 42.35937499999994, - "seed": 740245132, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662501582196, - "link": null, - "locked": false, - "startBinding": { - "elementId": "1zlVmmd_Y9S9Oz0S_fTUH", - "focus": 0.026523219960451445, - "gap": 6.734375 - }, - "endBinding": { - "elementId": "W06kzPlnPRgvKtfyagn_y", - "focus": -0.028028347263413595, - "gap": 4.671875000000057 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 0, - 42.35937499999994 - ] - ] - }, - { - "type": "rectangle", - "version": 502, - "versionNonce": 154619883, - "isDeleted": false, - "id": "9WfekBK46yxJpRloc_OZg", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 688.9609375, - "y": 504.76953125, - "strokeColor": "#000000", - "backgroundColor": "#fff", - "width": 172, - "height": 39, - "seed": 1128027188, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "type": "text", - "id": "K86bABr6PKIfF1LYTHu3e" - } - ], - "updated": 1662501566593, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 303, - "versionNonce": 1588802484, - "isDeleted": false, - "id": "K86bABr6PKIfF1LYTHu3e", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 693.9609375, - "y": 511.76953125, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 162, - "height": 25, - "seed": 1515850252, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Your Code", - "baseline": 18, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "9WfekBK46yxJpRloc_OZg", - "originalText": "Your Code" - }, - { - "type": "rectangle", - "version": 432, - "versionNonce": 333233835, - "isDeleted": false, - "id": "jti7SjH-dppIs0Sfz7P-6", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 461.6171875, - "y": 505.3828125, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 172, - "height": 39, - "seed": 644178100, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [ - { - "id": "QbZUz4LyUXI8NfqStLibW", - "type": "text" - }, - { - "type": "text", - "id": "QbZUz4LyUXI8NfqStLibW" - } - ], - "updated": 1662501565241, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 257, - "versionNonce": 1325047092, - "isDeleted": false, - "id": "QbZUz4LyUXI8NfqStLibW", - "fillStyle": "hachure", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 466.6171875, - "y": 512.3828125, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 162, - "height": 25, - "seed": 1705325708, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "sql-mapper", - "baseline": 18, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "jti7SjH-dppIs0Sfz7P-6", - "originalText": "sql-mapper" - }, - { - "type": "text", - "version": 275, - "versionNonce": 867285428, - "isDeleted": false, - "id": "7EfMfgnZWfNvLQtr-x3uF", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 803.3828125, - "y": 662.5234375, - "strokeColor": "#000000", - "backgroundColor": "#fff", - "width": 100, - "height": 25, - "seed": 1646758924, - "groupIds": [], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Migrations", - "baseline": 18, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Migrations" - }, - { - "type": "arrow", - "version": 104, - "versionNonce": 1312318260, - "isDeleted": false, - "id": "rQggHgShjGGXQ0hiwBQVd", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 849.7734375, - "y": 568.66015625, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 0.03125, - "height": 66.19140625, - "seed": 578856588, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412597026, - "link": null, - "locked": false, - "startBinding": { - "elementId": "W06kzPlnPRgvKtfyagn_y", - "focus": -0.6916403151899281, - "gap": 6.6015625 - }, - "endBinding": { - "elementId": "4CCO4Ro-Gy5uYIPERvP7H", - "focus": -0.043849355501159114, - "gap": 1.56640625 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 0.03125, - 66.19140625 - ] - ] - }, - { - "type": "ellipse", - "version": 1690, - "versionNonce": 361510412, - "isDeleted": false, - "id": "lFQ5wDy-KUnUz3oDzkI25", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 239.69356547871712, - "y": 67.47880783610327, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 21.49669286347574, - "height": 23.588242019607666, - "seed": 1348447412, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false - }, - { - "type": "line", - "version": 1660, - "versionNonce": 16974004, - "isDeleted": false, - "id": "JMyz9WWOhsSP478-dH8kL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 249.34114010330978, - "y": 91.1380667584855, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 1.2113207534300774, - "height": 27.54598873639832, - "seed": 675653772, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -1.2113207534300774, - 27.54598873639832 - ] - ] - }, - { - "type": "line", - "version": 1613, - "versionNonce": 1160877196, - "isDeleted": false, - "id": "-f4M1kqgJJ1DqIHSarxxq", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 248.18103511512425, - "y": 119.44818324746751, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 10.904198461697792, - "height": 16.79898457094717, - "seed": 649066036, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 10.904198461697792, - 16.79898457094717 - ] - ] - }, - { - "type": "line", - "version": 1589, - "versionNonce": 264817204, - "isDeleted": false, - "id": "wTdHoAnzcbiBjRED9_-VP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 246.67659186550435, - "y": 118.31986521987952, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 9.329943818404125, - "height": 15.60615726413436, - "seed": 1427683084, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -9.329943818404125, - 15.60615726413436 - ] - ] - }, - { - "type": "line", - "version": 1566, - "versionNonce": 1168581388, - "isDeleted": false, - "id": "lWzkvf-pHYnTTwDCCoYUU", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 238.90401457661704, - "y": 96.50986296364448, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 10.592121550031646, - "height": 10.166772277834836, - "seed": 792399796, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 10.592121550031646, - 10.166772277834836 - ] - ] - }, - { - "type": "line", - "version": 1586, - "versionNonce": 639198132, - "isDeleted": false, - "id": "eZyQeLBRJefnO4eEmCIOm", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 250.16954159533526, - "y": 106.46768276298613, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 15.26402850171518, - "height": 8.608699400331258, - "seed": 1099298188, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 15.26402850171518, - -8.608699400331258 - ] - ] - }, - { - "type": "line", - "version": 1817, - "versionNonce": 1835458956, - "isDeleted": false, - "id": "nRSmHuDmrd58aq7nRjD9v", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 259.25536801028943, - "y": 73.70638903822018, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 18.012785949061733, - "height": 8.792983231273032, - "seed": 2049374516, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -5.551156049147498, - 4.156441847180949 - ], - [ - -14.767067932950507, - 1.7161569081851142 - ], - [ - -18.012785949061733, - 8.792983231273032 - ] - ] - }, - { - "type": "line", - "version": 2089, - "versionNonce": 706399540, - "isDeleted": false, - "id": "aXuUF4qv85paleGvjq1Vf", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 241.12989079109474, - "y": 73.78023080944443, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 7.256241079962556, - "height": 20.702676936887435, - "seed": 1523016716, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -0.7145030549373154, - -3.2217494351862745 - ], - [ - -3.6837258052759845, - -3.9328337752254474 - ], - [ - -7.256241079962556, - 1.6608630719016904 - ], - [ - -5.283976448978563, - 16.76984316166199 - ], - [ - -2.813724840310993, - 9.33657487598008 - ], - [ - 0, - 0 - ] - ] - }, - { - "type": "rectangle", - "version": 1055, - "versionNonce": 866832396, - "isDeleted": false, - "id": "vPmumowJzhJh6C166dtT6", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0.32340402082123276, - "x": 234.39442736485796, - "y": 90.43231788867585, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 6.043745042549293, - "height": 12.566983256668966, - "seed": 584692404, - "groupIds": [ - "BigUhShJehOiqWIjcDoW9" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false - }, - { - "type": "ellipse", - "version": 2205, - "versionNonce": 1295189684, - "isDeleted": false, - "id": "xApIn7h3gZf3eY10bjrUb", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 299.3665088217107, - "y": 71.88057076815883, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 24.744967041785156, - "height": 22.77537981587287, - "seed": 1744387124, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false - }, - { - "type": "line", - "version": 2267, - "versionNonce": 409792140, - "isDeleted": false, - "id": "RED9kg0RWDjfRC331frur", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 310.36246414441894, - "y": 94.66231306364153, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 1.2141925957388797, - "height": 27.611295745848107, - "seed": 477024524, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -1.2141925957388797, - 27.611295745848107 - ] - ] - }, - { - "type": "line", - "version": 2220, - "versionNonce": 604508212, - "isDeleted": false, - "id": "4tmwbSvpDvV5L9eOL3w5W", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 309.318726188599, - "y": 122.87810392550332, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 10.93005052309211, - "height": 16.83881220082897, - "seed": 1131124148, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 10.93005052309211, - 16.83881220082897 - ] - ] - }, - { - "type": "line", - "version": 2234, - "versionNonce": 208092428, - "isDeleted": false, - "id": "BhFX6QUNp6ZRAaC14lRsq", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 309.2530763831227, - "y": 122.12933640276594, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 10.893068867789728, - "height": 15.119678146262912, - "seed": 721997708, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - -10.893068867789728, - 15.119678146262912 - ] - ] - }, - { - "type": "line", - "version": 2207, - "versionNonce": 1048570292, - "isDeleted": false, - "id": "T5q_miW2zK8Z6_GHMzHuy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 299.79149008152416, - "y": 99.89494714462973, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 9.29401757679057, - "height": 9.667397272649206, - "seed": 39239476, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 9.29401757679057, - 9.667397272649206 - ] - ] - }, - { - "type": "line", - "version": 2274, - "versionNonce": 1494465420, - "isDeleted": false, - "id": "7eDKh8D3tRxN9AnkQNJCH", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 309.9030355081573, - "y": 109.63564914257302, - "strokeColor": "#000000", - "backgroundColor": "#ced4da", - "width": 16.61048353051358, - "height": 8.347356780081856, - "seed": 48168460, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 16.61048353051358, - -8.347356780081856 - ] - ] - }, - { - "type": "line", - "version": 2214, - "versionNonce": 1069585204, - "isDeleted": false, - "id": "-MsR8pFblQ_RWCQ50tTvg", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 295.3274989522442, - "y": 73.87763473934886, - "strokeColor": "#000000", - "backgroundColor": "transparent", - "width": 35.55274923479633, - "height": 7.233182720600468, - "seed": 1562425524, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": null, - "points": [ - [ - 0, - 0 - ], - [ - 35.55274923479633, - 7.233182720600468 - ] - ] - }, - { - "type": "rectangle", - "version": 2521, - "versionNonce": 145517068, - "isDeleted": false, - "id": "mxV4CeWFqORnIifYgQTpP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0.1875122815022081, - "x": 303.27728681036575, - "y": 69.14462472277884, - "strokeColor": "#000000", - "backgroundColor": "#fff", - "width": 21.279164889176773, - "height": 7.3403547293505, - "seed": 216751244, - "groupIds": [ - "-ijW-sGctTCBEecgCTOPZ" - ], - "strokeSharpness": "sharp", - "boundElements": [], - "updated": 1662412601919, - "link": null, - "locked": false - }, - { - "type": "arrow", - "version": 1412, - "versionNonce": 647641268, - "isDeleted": false, - "id": "mzCbJHPkBmRZyt8z7yCAe", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 282.90625, - "y": 142.60546875, - "strokeColor": "#000000", - "backgroundColor": "#fa5252", - "width": 378.79296875, - "height": 102.58203125, - "seed": 1011840012, - "groupIds": [], - "strokeSharpness": "round", - "boundElements": [], - "updated": 1662412613395, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "1zlVmmd_Y9S9Oz0S_fTUH", - "focus": 0.056278788827343405, - "gap": 9.078125 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 151.6953125, - 69.025390625 - ], - [ - 333.46484375, - 15.73046875 - ], - [ - 378.79296875, - 102.58203125 - ] - ] - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/getting-started/quick-start-guide.md b/versioned_docs/version-0.0.21/getting-started/quick-start-guide.md deleted file mode 100644 index dde9724e78..0000000000 --- a/versioned_docs/version-0.0.21/getting-started/quick-start-guide.md +++ /dev/null @@ -1,238 +0,0 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Quick Start Guide - -In this guide you'll learn how to create and run your first API with -Platformatic DB. Let's get started! - -:::info - -This guide uses [SQLite](https://www.sqlite.org/) for the database, but -Platformatic DB also supports [PostgreSQL](https://www.postgresql.org/), -[MySQL](https://www.mysql.com/) and [MariaDB](https://mariadb.org/) databases. - -::: - -## Requirements - -Platformatic supports macOS, Linux and Windows ([WSL](https://docs.microsoft.com/windows/wsl/) recommended). - -To follow along with this guide you'll need to have these things installed: - -- [Node.js](https://nodejs.org/) >= v16.17.0 or >= v18.8.0 -- [npm](https://docs.npmjs.com/cli/) v7 or later -- A code editor, for example [Visual Studio Code](https://code.visualstudio.com/) - -## Create a new API project - -Create a directory for your new API project: - -```bash -mkdir quick-start - -cd quick-start -``` - -Then create a `package.json` file and install the [platformatic](https://www.npmjs.com/package/platformatic) -CLI as a project dependency: - - - - -```bash -npm init --yes - -npm install platformatic -``` - - - - -```bash -yarn init --yes - -yarn add platformatic -``` - - - - -```bash -pnpm init - -pnpm add platformatic -``` - - - - - -## Add a database schema - -In your project directory (`quick-start`), create a `migrations` directory to -store your database migration files: - -```bash -mkdir migrations -``` - -Then create a new migration file named **`001.do.sql`** in the **`migrations`** -directory. - -Copy and paste this SQL query into the migration file: - -```sql title="migrations/001.do.sql" -CREATE TABLE pages ( - id INTEGER PRIMARY KEY, - title VARCHAR(255) NOT NULL -); -``` - -When it's run by Platformatic, this query will create a new database table -named `pages`. - -:::tip - -You can check syntax for SQL queries on the [Database.Guide SQL Reference](https://database.guide/sql-reference-for-beginners/). - -::: - -## Configure your API - -In your project directory, create a new Platformatic configuration file named -**`platformatic.db.json`**. - -Copy and paste in this configuration: - -```json title="platformatic.db.json" -{ - "server": { - "logger": { - "level": "info" - }, - "hostname": "127.0.0.1", - "port": "3042" - }, - "core": { - "connectionString": "sqlite://pages.db", - "graphiql": true - }, - "migrations": { - "dir": "./migrations" - } -} -``` - -This configuration tells Platformatic to: - -- Run an API server on `http://127.0.0.1:3042/` -- Configure the API server to log messages with an `info` level or above -- Connect to an SQLite database stored in a file named `pages.db` -- Enable the [GraphiQL](https://www.npmjs.com/package/graphiql) web UI -- Look for database migration files in the `migrations` directory - -:::tip - -The [Configuration reference](/reference/configuration.md) explains all of the -supported configuration options. - -::: - -## Start your API server - -In your project directory, use the Platformatic CLI to start your API server: - -```bash -npx platformatic db start -``` - -This will: - -1. Run your SQL migration file and create a `pages` table in the SQLite database. -1. Automatically map your SQL database to REST and GraphQL API interfaces. -1. Start the Platformatic API server. - -Your Platformatic API is now up and running! 🌟 - -## Next steps - -### Use the REST API interface - -You can use cURL to make requests to the REST interface of your API, for example: - -#### Create a new page - -```bash -curl -X POST -H "Content-Type: application/json" \ - -d "{ \"title\": \"Hello Platformatic DB\" }" \ - http://localhost:3042/pages -``` - -You should receive a response from your API like this: - -```json -{"id":1,"title":"Hello Platformatic DB"} -``` - -#### Get all pages - -```bash -curl http://localhost:3042/pages -``` - -You should receive a response from your API like this, with an array -containing all the pages in your database: - -```json -[{"id":1,"title":"Hello Platformatic DB"}] -``` - - - -#### OpenAPI documentation - -You can explore the OpenAPI documentation for your REST API at -[http://localhost:3042/documentation](http://localhost:3042/documentation) - -### Use the GraphQL API interface - -Open [http://localhost:3042/graphiql](http://localhost:3042/graphiql) in your -web browser to explore the GraphQL interface of your API. - -Try out this GraphQL query to retrieve all pages from your API: - -```graphql -{ - pages { - id - title - } -} -``` - -:::tip - -Learn more about your API's GraphQL interface in the -[GraphQL API reference](reference/sql-graphql/intro.md). - -::: - - diff --git a/versioned_docs/version-0.0.21/how-to/_category_.json b/versioned_docs/version-0.0.21/how-to/_category_.json deleted file mode 100644 index 792570104d..0000000000 --- a/versioned_docs/version-0.0.21/how-to/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "How To", - "position": 2 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/how-to/custom-functionality.md b/versioned_docs/version-0.0.21/how-to/custom-functionality.md deleted file mode 100644 index 0504551a07..0000000000 --- a/versioned_docs/version-0.0.21/how-to/custom-functionality.md +++ /dev/null @@ -1,3 +0,0 @@ -# Adding custom functionality - -TODO diff --git a/versioned_docs/version-0.0.21/how-to/deployment.md b/versioned_docs/version-0.0.21/how-to/deployment.md deleted file mode 100644 index a38ec2361c..0000000000 --- a/versioned_docs/version-0.0.21/how-to/deployment.md +++ /dev/null @@ -1,218 +0,0 @@ -# Deployment - -Requirements: - -1. Dockerfile with access to `platformatic` CLI -2. A fly.io account -3. A platformatic app that works locally - -## On Fly.io - -1. Need a fly.io account and the CLI tool: https://fly.io/docs/hands-on/ -2. Navigate to your project on your local machine -1. Create a **Dockerfile**: - ```dockerfile - FROM platformatic/platformatic:latest - - USER root - - WORKDIR /opt/ - COPY migrations migrations - COPY platformatic.db.json platformatic.db.json - - EXPOSE 3042 - - CMD ["platformatic", "db"] - ``` -1. Create an app on fly: `fly launch --no-deploy --generate-name --org personal --region mad` - * or just `fly launch` and follow the prompts - * if there is no database at this point, `--no-deploy` can be removed -4. Expose the correct port, matching **platformatic.db.json** and **Dockerfile**: - ```diff - [[services]] - http_checks = [] - - internal_port = 8080 - + internal_port = 3042 - processes = ["app"] - protocol = "tcp" - script_checks = [] - ``` -9. Now deploy: `fly deploy` - -### With sqlite - -1. Follow steps above, skipping deployment until the end -2. Create a volume for database storage: `fly volumes create data` - * will create storage in the same region as application - * defaults to 3GB size, use `-s` to change: `-s 10` is 10GB -3. Update mount in **fly.toml**, replacing ``: - ```toml - [mounts] - source = "data" - destination = "/opt//.platformatic/data" - ``` -4. Create directory in project, this will be where the sqlite database goes: - ```bash - mkdir -p .platformatic/data - touch .platformatic/data/.gitkeep - ``` -5. Make sure sqlite databases are ignored to avoid inconsistencies in - deployment: - ```bash - echo "*.db" >> .gitignore - ``` -6. Update connection string to the sqlite database, replacing ``: - ```json - { - "core": { - "connectionString": "sqlite://.platformatic/data/.db" - } - } - ``` -7. Add migrations folder, migrations, and configuration. _Note_ app will not run - if there is a migrations folder and no migrations. - 1. Create folder and simple migration if not already available: - ```bash - mkdir migrations - echo "CREATE TABLE demo (id uuid PRIMARY KEY);" > migrations/001.do.sql - ``` - 2. Update configuration: - ```json - { - "migrations": { - "dir": "./migrations" - } - } - ``` -8. Optionally, [add `sqlite` to the **Dockerfile** to help with debugging](#adding-sqlite-for-debugging) -10. Deploy the app `fly deploy` - -#### Adding `sqlite` for debugging - -Create a script for launching the database, call it **db-cli.sh**: -```bash -#!/bin/sh -set -x -# DSN will be defined in the Dockerfile -sqlite3 $DSN -``` - -Add the following snippet to the **Dockerfile**: -```dockerfile -# Setup sqlite viewer -# Replace with your app name -RUN apk add sqlite -ENV DSN "/opt//.platformatic/data/demo.db" -COPY db-cli.sh /usr/local/bin/db-cli -RUN chmod +x /usr/local/bin/db-cli -``` - -With fly.io, it becomes easy to boot directly into the database by running the -following command from the local machine: - -```bash -fly ssh console -C db-cli -``` - -#### Adding Litestream and S3 for backups - -This requires an AWS account and the appropriate setup in AWS. Follow the -[Litestream guide for configuring an AWS user](https://litestream.io/guides/s3/) and then come back here to -integrate with Platformatic and Fly. - -Once AWS is setup, store the credentials on Fly: -```bash -fly secrets set \ - AWS_ACCESS_KEY_ID=some-access-key \ - AWS_SECRET_ACCESS_KEY=some-access-secret -``` - -Update **fly.toml** with the bucket name: -```toml -[env] - AWS_BACKUP_BUCKET = "bucket-name" -``` - -Configuration of Litestream will be done through the standard yaml file, create -a **litestream.yml** file in the project with the following contents: -```yml -dbs: - # make sure to replace - - path: /opt//.platformatic/data/.db - replicas: - - url: s3://${AWS_BACKUP_BUCKET} - access-key-id: ${AWS_ACCESS_KEY_ID} - secret-access-key: ${AWS_SECRET_ACCESS_KEY} -``` - -To get automatic database replication and restoration, a small Bash script is -used as the **Dockerfile** `CMD`: -```bash -#!/bin/bash - -if [ ! -f "$DSN" ] -then - echo "Restoring database" - litestream restore -v "$DSN" -fi - -# TODO change -echo "Starting Litestream & application" -litestream replicate -exec "platformatic db --config /opt//platformatic.db.json" -``` - -Finally, the existing Dockerfile needs a number of changes. Start with the -Litestream base image: - -```dockerfile -FROM litestream/litestream:0.3.9 AS litestream - -FROM registry.fly.io/platformatic-private:latest -``` - -Copy Litestream into the platformatic image: -```dockerfile -USER root -COPY --from=litestream /usr/local/bin/litestream /usr/local/bin/litestream -``` - -Copy the runner and configuration: -```dockerfile -COPY run.sh /run.sh -COPY litestream.yml /etc/litestream.yml -``` - -Last of all, run from **run.sh**: -```dockerfile -CMD /run.sh -``` - -With Litestream and the database tools, the final image should look something -like this: -```dockerfile -FROM litestream/litestream:0.3.9 AS litestream - -FROM registry.fly.io/platformatic-private:latest - -USER root -COPY --from=litestream /usr/local/bin/litestream /usr/local/bin/litestream - -RUN apk add sqlite bash ca-certificates curl - -# Set environment variables. -ENV DSN "/opt//.platformatic/data/.db" -COPY image/db-cli /usr/local/bin/db-cli -RUN chmod +x /usr/local/bin/db-cli - -EXPOSE 3042 - -ADD litestream.yml /etc/litestream.yml -ADD run.sh /run.sh - -# Application specific files -WORKDIR /opt/ -COPY migrations migrations -COPY platformatic.db.json platformatic.db.json - -CMD /run.sh -``` diff --git a/versioned_docs/version-0.0.21/how-to/seed.md b/versioned_docs/version-0.0.21/how-to/seed.md deleted file mode 100644 index f22f14d278..0000000000 --- a/versioned_docs/version-0.0.21/how-to/seed.md +++ /dev/null @@ -1,30 +0,0 @@ -# Seed a Database - -A database is as useful as the data that it contains: a fresh, empty database -isn't always the best starting point. We can add a few rows from our migrations -using SQL, but we might need to use JavaScript from time to time. - -The [platformatic db seed](/reference/cli.md#seed) command allows us to run a -script that will populate — or "seed" — our database. - -## Example - -Our seed script should export a `Function` that accepts an argument: -an instance of [`@platformatic/sql-mapper`](/reference/sql-mapper/intro.md). - -```javascript title="seed.js" -'use strict' - -module.exports = async function ({ entities, db, sql }) { - await entities.graph.save({ input: { name: 'Hello' } }) - await db.query(sql` - INSERT INTO graphs (name) VALUES ('Hello 2'); - `) -} -``` - -We can then run the seed script with the Platformatic CLI: - -```bash -npx platformatic db seed seed.js -``` diff --git a/versioned_docs/version-0.0.21/intro.md b/versioned_docs/version-0.0.21/intro.md deleted file mode 100644 index b79f7279c2..0000000000 --- a/versioned_docs/version-0.0.21/intro.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_position: 0 -title: 'Welcome' ---- - -# Welcome - -Platformatic is a new set of tools that will enable you to develop HTTP APIs without frictions. \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/_category_.json b/versioned_docs/version-0.0.21/reference/_category_.json deleted file mode 100644 index 9185910a67..0000000000 --- a/versioned_docs/version-0.0.21/reference/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Reference", - "position": 3 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/cli.md b/versioned_docs/version-0.0.21/reference/cli.md deleted file mode 100644 index dc68ce71b1..0000000000 --- a/versioned_docs/version-0.0.21/reference/cli.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -toc_max_heading_level: 4 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import TOCInline from '@theme/TOCInline'; - -# Platformatic CLI - -## Installation and usage - -Install the Platformatic CLI as a dependency for your project: - - - - -```bash -npm install platformatic -``` - - - - -```bash -yarn add platformatic -``` - - - - -```bash -pnpm add platformatic -``` - - - - -Once it's installed you can run it with: - - - - -```bash -npx platformatic -``` - - - - -```bash -yarn platformatic -``` - - - - -```bash -pnpm platformatic -``` - - - - -:::info - -The `platformatic` package can be installed globally, but installing it as a -project dependency ensures that everyone working on the project is using the -same version of the Platformatic CLI. - -::: - -## Commands - -The Platformatic CLI provides the following commands: - - - -### help - - -``` -Welcome to Platformatic. Available commands are: - -* help - Display this message -* help - shows more information about a command. -* db - start Platformatic DB; type `platformatic db help` to know more. -``` - - -### db - -```bash -platformatic db -``` - - -#### help - -Available commands: - -* `help` - show this help message. -* `help ` - shows more information about a command. -* `init` - initiate default application. -* `start` - start the server. -* `migrate` - run migrations. -* `seed` - run a seed file. - - -#### init - -Initiate default Platformatic DB application: - - $ platformatic db init - -As a result of executing this command, the `platformatic.db.json` configuration -file and the `migrations` folder with migration examples will be generated. - -Options: - - * `-h, --hostname `: The hostname where Platformatic DB server will listen for connections. - * `-p, --port `: The port where Platformatic DB server will listen for connections. - * `-db, --database `: The name of the database to use. Default: `sqlite`. - * `-m, --migrations `: Relative path to the migrations folder. Default: `./migrations`. - - -#### migrate - -Apply all configurated migrations to the database: - - $ platformatic db migrate - -The migrations will be applied in the order they are specified in the -folder defined in the configuration file. If you want to apply a specific migration, -you can use the `--to` option: - - $ platformatic db migrate --to 001 - -Here is an example migration: - - CREATE TABLE graphs ( - id SERIAL PRIMARY KEY, - name TEXT - ); - -You can always rollback to a specific migration with: - - $ platformatic db migrate --to VERSION - -Use 000 to reset to the initial state. - -Options: - - * `-c, --config `: Path to the configuration file. - * `-t, --to `: Migrate to a specific version. - -If not specified, the configuration specified will be loaded from -`platformatic.db.json`, `platformatic.db.yml`, or `platformatic.db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - -#### schema - -Generate a schema from the database and prints it to standard output: - -* `schema graphql` - generate the GraphQL schema -* `schema openapi` - generate the OpenAPI schema - -Options: - - -c, --config FILE Specify a configuration file to use - -If not specified, the configuration specified will be loaded from -`platformatic.db.json`, `platformatic.db.yml`, or `platformatic.db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - -#### seed - -Load a seed into the database. This is a convenience method that loads -a JavaScript file and configure @platformatic/sql-mapper to connect to -the database specified in the configuration file. - -Here is an example of a seed file: - - 'use strict' - - module.exports = async function ({ entities, db, sql }) { - await entities.graph.save({ input: { name: 'Hello' } }) - await db.query(sql` - INSERT INTO graphs (name) VALUES ('Hello 2'); - `) - } - -You can run this using the `seed` command: - - $ platformatic db seed seed.js - -Options: - - * `--config` - Path to the configuration file. - -If not specified, the configuration specified will be loaded from -`platformatic.db.json`, `platformatic.db.yml`, or `platformatic.db.tml` in the current directory. -You can find more details about the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - -#### start - -Start the Platformatic DB server with the following command: - - $ platformatic db start - -You will need a configuration file. Here is an example to get you started, -save the following as `platformatic.db.json`: - - { - "server": { - "hostname": "127.0.0.1", - "port": 0, - "logger": { - "level": "info" - } - }, - "core": { - "connectionString": "sqlite://./db" - }, - "migrations": { - "dir": "./migrations" - } - } - - -Remeber to create a migration, run the `db help migrate` command to know more. - -All outstanding migrations will be applied to the database unless the -`migrations.autoApply` configuration option is set to false. - -By sending the SIGUSR2 signal, the server can be reloaded. - -Options: - - -c, --config FILE Specify a configuration file to use - --watch-ignore LIST Specify a comma separated list of glob patterns to - ignore when watching for changes - -If not specified, the configuration specified will be loaded from `platformatic.db.json`, -`platformatic.db.yml`, or `platformatic.db.tml` in the current directory. You can find more details about -the configuration format at: -https://oss.platformatic.dev/docs/reference/configuration. - - \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/configuration.md b/versioned_docs/version-0.0.21/reference/configuration.md deleted file mode 100644 index ea9482586f..0000000000 --- a/versioned_docs/version-0.0.21/reference/configuration.md +++ /dev/null @@ -1,391 +0,0 @@ -# Configuration - -Platformatic DB is configured with a configuration file. It supports the use -of environment variables as setting values with [configuration placeholders](#configuration-placeholders). - -## Configuration file - -If the Platformatic CLI finds a file in the current working directory matching -one of these filenames, it will automatically load it: - -- `platformatic.db.json` -- `platformatic.db.json5` -- `platformatic.db.yml` or `platformatic.db.yaml` -- `platformatic.db.tml` - -Alternatively, a [`--config` option](/reference/cli.md#db) with a configuration -filepath can be passed to most `platformatic db` CLI commands. - -The configuration examples in this reference use JSON. - -### Supported formats - -| Format | Extensions | -| :-- | :-- | -| JSON | `.json` | -| JSON5 | `.json5` | -| YAML | `.yml`, `.yaml` | -| TOML | `.tml` | - -Comments are supported by the JSON5, YAML and TOML file formats. - -## Settings - -Configuration settings are organised into the following groups: - -- [`core`](#core) **(required)** -- [`dashboard`](#dashboard) -- [`metrics`](#metrics) -- [`migrations`](#migrations) -- [`plugin`](#plugin) -- [`server`](#server) **(required)** -- [`authorization`](#authorization) - -Sensitive configuration settings, such as a database connection URL that contains -a password, should be set using [configuration placeholders](#configuration-placeholders). - -### `core` - -A **required** object with the following settings: - -- **`connectionString`** (**required**, `string`) — Database connection URL. - - Example: `postgres://user:password@my-database:5432/db-name` - - Platformatic DB supports MySQL, MariaDB, PostgreSQL and SQLite. -- **`graphql`** (`boolean` or `object`, default: `true`) — Controls the GraphQL API interface, with optional GraphiQL UI. - - _Examples_ - - Enables GraphQL support - - ```json - { - "core": { - ... - "graphql": true - } - } - ``` - - Enables GraphQL support with GraphiQL - - ```json - { - "core": { - ... - "graphql": { - "graphiql": true - } - } - } - ``` -- **`openapi`** (`boolean` or `object`, default: `true`) — Enables OpenAPI REST support. - - If value is an object, all [OpenAPI v3](https://swagger.io/specification/) allowed properties can be passed. - - Platformatic DB uses [`@fastify/swagger`](https://github.com/fastify/fastify-swagger) under the hood to manage this configuration. - - _Examples_ - - Enables OpenAPI - - ```json - { - "core": { - ... - "openapi": true - } - } - ``` - - Enables OpenAPI with options - - ```json - { - "core": { - ... - "openapi": { - "info": { - "title": "Platformatic DB", - "description": "Exposing a SQL database as REST" - } - } - } - } - ``` -- **`ignore`** (`object`) — Key/value object that defines which database tables should not be mapped as API entities. - - _Examples_ - - ```json - { - "core": { - ... - "ignore": { - "versions": true // "versions" table will be not mapped with GraphQL/REST APIs - } - } - } - ``` - -### `dashboard` - -An optional object with the following settings: - -- **`rootPath`** (`boolean`, default: `true`) — Make the dashboard available at the root path (`/`). - -### `metrics` - -Configuration for a [Prometheus](https://prometheus.io/) server that will export monitoring metrics -for the current server instance. It uses [`fastify-metrics`](https://github.com/SkeLLLa/fastify-metrics) -under the hood. - -This setting can be a `boolean` or an `object`. If set to `true` the Prometheus server will listen on `http://0.0.0.0:9090`. - -Supported object properties: - -- **`hostname`** (`string`) — The hostname where Prometheus server will listen for connections. -- **`port`** (`number`) — The port where Prometheus server will listen for connections. -- **`auth`** (`object`) — Basic Auth configuration. **`username`** and **`password`** are required here - (use [environment variables](#environment-variables)). - -### `migrations` - -Configures [Postgrator](https://github.com/rickbergfalk/postgrator) to run migrations against the database. - -An optional object with the following settings: - -- **`dir`** (**required**, `string`): Relative path to the migrations directory. -- **`autoApply`** (`boolean`, default: `true`): Automatically apply migrations when Platformatic DB server starts. - -### `plugin` - -An optional object that defines a plugin to be loaded with [`fastify-isolate`](https://github.com/mcollina/fastify-isolate): - -- **`path`** (**required**, `string`): Relative path to plugin's entry point. - -All properties will be passed to `fastify-isolate`. - -### `server` - -A **required** object with the following settings: - -- **`hostname`** (**required**, `string`) — Hostname where Platformatic DB server will listen for connections. -- **`port`** (**required**, `number`) — Port where Platformatic DB server will listen for connections. -- **`healthCheck`** (`boolean` or `object`) — Enables the health check endpoint. - - Powered by [`@fastify/under-pressure`](https://github.com/fastify/under-pressure). - - The value can be an object, used to specify the interval between checks in milliseconds (default: `5000`) - - _Example_ - - ```json - { - "server": { - ... - "healthCheck": { - "interval": 2000 - } - } - } - ``` -- **`cors`** (`object`) — Configuration for Cross-Origin Resource Sharing (CORS) headers. - - All options will be passed to the [`@fastify/cors`](https://github.com/fastify/fastify-cors) plugin. - -### `authorization` - -Authorization settings can be set with an optional `authorization` object, for example: - -```json - "authorization": { - "adminSecret": "platformatic", - "rules": [ - ... - ] - } -``` - -- **`adminSecret`** (`string`, optional) — If defined, it will be the password used to access the dashboard and the string to send within the `x-platformatic-admin-secret` header when performing GraphQL/REST API calls. -- **`rules`** (`array`) — Authorization rules that describe the CRUD actions that users are allowed to perform. - -Note that if an `authorization` section is present, but _**no rules**_ are specified, no CRUD operations are allowed (unless `adminSecret` is passed). - -#### Authorization rules - -Every rule must specify: -- `role` — the role name. It's a string and must match with the role(s) set by the external authentication service -- `entity` — the Platformatic DB entity -- A set of optional [`defaults`](#defaults) -- One entry for each supported CRUD operation: `find`, `save`, `delete` - -#### Operation options - -Every operation can specify `checks` used for the authorizations. -This value can be `false` (operation disabled) or `true` (operation enabled with no checks). - -To specify more fine-grained authorization controls, add a `checks` field, e.g.: - -```json -{ - "role": "user", - "entity": "page", - "find": { - "checks": { - "userId": "X-PLATFORMATIC-USER-ID" - } - }, - ... -} - -``` - -In this example, when a user with a `user` role executes a `findPage`, they can -access all the data that has `userId` equal to the value in user metadata with -key `X-PLATFORMATIC-USER-ID`. - -Note that `"userId": "X-PLATFORMATIC-USER-ID"` is syntactic sugar for: - -```json - "find": { - "checks": { - "userId": { - "eq": "X-PLATFORMATIC-USER-ID" - } - } - } -``` - -It's possible to specify more complex rules using all the [supported where clause operators](./sql-mapper/entity/api.md#where-clause). - -Note that `userId` MUST exist as a field in the database table to use this feature. - -#### Fields - -If a `fields` array is present on an operation, Platformatic DB restricts the columns on which the user can execute to that list. -For `save` operations, the configuration must specify all the not-nullable fields (otherwise, it would fail at runtime). -Platformatic does these checks at startup. - -Example: - -```json - "rule": { - "entity": "page", - "role": "user", - "find": { - "checks": { - "userId": "X-PLATFORMATIC-USER-ID" - }, - "fields": ["id", "title"] - } - ... - } -``` - -In this case, only `id` and `title` are returned for a user with a `user` role on the `page` entity. - -#### Defaults - -Defaults are used in database insert and are default fields added automatically populated from user metadata, e.g.: - -```json - "defaults": { - "userId": "X-PLATFORMATIC-USER-ID" - }, -``` - -When an entity is created, the `userId` column is used and populated using the value from user metadata. - -#### Anonymous role - -If a user has no role, the `anonymous` role is assigned automatically. It's possible to specify a rule for it: - -```json - { - "role": "anonymous", - "entity": "page", - "find": false, - "delete": false, - "save": false - } -``` - -In this case, the user that has no role (or has an explicitly `anonymous` role) has no operations allowed on the `page` entity. - -#### Role and anonymous keys - -The roles key in user metadata defaults to `X-PLATFORMATIC-ROLE`. It's possible to change it using the `roleKey` field in configuration. -Same for the `anonymous` role, which value can be changed using `anonymousRole`. - -```json - "authorization": { - "roleKey": "X-MYCUSTOM-ROLE_KEY", - "anonymousRole": "anonym", - "rules": [ - ... - ] - } -``` - -## Configuration placeholders - -The value for any configuration setting can be replaced with an environment variable -by adding a placeholder in the configuration file, for example `{PLT_SERVER_LOGGER_LEVEL}`. - -All placeholders in a configuration must be available as an environment variable -and must meet the [allowed placeholder name](#allowed-placeholder-names) rules. - -### Example - -```json title="platformatic.db.json" -{ - "core": { - "logger": { - "level": "{PLT_SERVER_LOGGER_LEVEL}" - }, - "connectionString": "{DATABASE_URL}" - }, - "server": { - "port": "{PORT}" - } -} -``` - -Platformatic will replace the placeholders in this example with the environment -variables of the same name. - -### Setting environment variables - -If a `.env` file exists it will automatically be loaded by Platformatic using -[`dotenv`](https://github.com/motdotla/dotenv). For example: - -```plaintext title=".env" -PLT_SERVER_LOGGER_LEVEL=info -PORT=8080 -``` - -The `.env` file must be located in the same folder as the Platformatic configuration -file or in the current working directory. - -Environment variables can also be set directly on the commmand line, for example: - -```bash -PLT_SERVER_LOGGER_LEVEL=debug npx platformatic db -``` - -### Allowed placeholder names - -Only placeholder names prefixed with `PLT_`, or that are in this allow list, will be -dynamically replaced in the configuration file: - -- `PORT` -- `DATABASE_URL` - -This restriction is to avoid accidentally exposing system environment variables. -An error will be raised by Platformatic if it finds a configuration placeholder -that isn't allowed. - -The default allow list can be extended by passing a `--allow-env` CLI option with a -comma separated list of strings, for example: - -```bash -npx platformatic db --allow-env=HOST,SERVER_LOGGER_LEVEL -``` - -If `--allow-env` is passed as an option to the CLI, it will be merged with the -default allow list. diff --git a/versioned_docs/version-0.0.21/reference/db-authorization/_category_.json b/versioned_docs/version-0.0.21/reference/db-authorization/_category_.json deleted file mode 100644 index 6351e33a69..0000000000 --- a/versioned_docs/version-0.0.21/reference/db-authorization/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Authorization & Authentication" -} diff --git a/versioned_docs/version-0.0.21/reference/db-authorization/intro.md b/versioned_docs/version-0.0.21/reference/db-authorization/intro.md deleted file mode 100644 index ee6d5209ce..0000000000 --- a/versioned_docs/version-0.0.21/reference/db-authorization/intro.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Introduction - -Authorization in Platformatic DB is **role-based** (see [Roles And User Information](#roles-and-user-information) for further details). -A user is supposed to have a list of roles associated. -Platformatic delegates authentication and assignment of the `roles` to an external _authentication service_. -The job of the authentication service is to authenticate users and assign their roles correctly. -Supported authentication service integrations are: -- JWT -- Webhook - -We refer to the user roles and other informations (like `userId`) as [User Metadata](#user-metadata). - -To make testing and developing easier, it's possible to bypass these checks if a `adminSecret` is set. See [HTTP Headers](#http-headers). - -## JWT -JWT is built on top of [fastify-jwt](https://github.com/fastify/fastify-jwt). - -![Platformatic DB JWT integration](./images/jwt.png) - -To configure it, the simplest way is to pass a `secret`, e.g.: - -```json - "authorization": { - ... - - "jwt": { - "secret": "" - }, - - ... - } -``` - -For more complex configurations, please check [fastify-jwt options](https://github.com/fastify/fastify-jwt#options). - -## Webhook -Platformatic can use a webhook to authenticate the requests. - -![Platformatic DB Webhook integration](./images/webhook.png) - -In this case, the URL is configured on authorization: - -```json - "authorization": { - ... - - "webhook": { - "url": "" - }, - - ... - } -```` - -When a request is received, Platformatic sends a `POST` to the webhook, replicating the same body and headers, except for: -- `host` -- `connection` - -In the Webhook case, the HTTP response contains the roles/user information as HTTP headers. - -## HTTP Headers - -To make testing and developing easier, it's possible to bypass JWT / WebHook integration if a `adminSecret` is set. -If so, and if a request has `X-PLATFORMATIC-ADMIN-SECRET` HTTP header set with the configured `adminSecret`, the JWT/Webhook authentication is skipped, and -the role set automatically as `platformatic-admin`. - - -![Platformatic DB JWT integration](./images/http.png) - -Note that setting user roles on HTTP headers is highly insecure and should be used only within protected networks. - -### Impersonation -If a user is recognized with a `platformatic-admin` role, can also **impersonate users**. -The users/roles to impersonate are specified by: -- `X-PLATFORMATIC-USER-ID`: the `userId` of the authenticated user. Note that this key value is conventional, any key can be used as long that is the same key specified in authorization rules. -- `X-PLATFORMATIC-ROLE`: comma separated list of roles - -## User Metadata -In all cases, the roles/user information is passed to Platformatic from the external _authentication service_ as a string (JWT claims or HTTP headers). -We can refer to these as **user metadata**. Platformatic saves the user metadata for each request in a `user` object. -Roles can be set using `X-PLATFORMATIC-ROLE` as list of comma-separated roles (this key is configurable, see [References](../configuration.md#role-and-anonymous-keys)). - -Note that roles are simply strings, but some "special roles" are reserved: -- `platformatic-admin` : this identifies a user who has admin powers -- `anonymous`: set automatically when no roles are associated - - diff --git a/versioned_docs/version-0.0.21/reference/db-authorization/programmatic-rules.md b/versioned_docs/version-0.0.21/reference/db-authorization/programmatic-rules.md deleted file mode 100644 index d0d80bb4fa..0000000000 --- a/versioned_docs/version-0.0.21/reference/db-authorization/programmatic-rules.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Programmatic Rules -If it's necessary to have more control over the authorizations, it's possible to specify the rules programmatically, e.g.: - -```js - - app.register(auth, { - jwt: { - secret: 'supersecret' - }, - rules: [{ - role: 'user', - entity: 'page', - async find ({ user, ctx, where }) { - return { - ...where, - userId: { - eq: user['X-PLATFORMATIC-USER-ID'] - } - } - }, - async delete ({ user, ctx, where }) { - return { - ...where, - userId: { - eq: user['X-PLATFORMATIC-USER-ID'] - } - } - }, - defaults: { - userId: async function ({ user, ctx, input }) { - match(user, { - 'X-PLATFORMATIC-USER-ID': generated.shift(), - 'X-PLATFORMATIC-ROLE': 'user' - }) - return user['X-PLATFORMATIC-USER-ID'] - } - - }, - async save ({ user, ctx, where }) { - return { - ...where, - userId: { - eq: user['X-PLATFORMATIC-USER-ID'] - } - } - } - }] - }) - -``` - -In this example, the `user` role can delete all the posts edited before yesterday: - -```js - app.register(auth, { - jwt: { - secret: 'supersecret' - }, - roleKey: 'X-PLATFORMATIC-ROLE', - anonymousRole: 'anonymous', - rules: [{ - role: 'user', - entity: 'page', - find: true, - save: true, - async delete ({ user, ctx, where }) { - return { - ...where, - editedAt: { - lt: yesterday - } - } - }, - defaults: { - userId: 'X-PLATFORMATIC-USER-ID' - } - }] - }) -``` - - diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/_category_.json b/versioned_docs/version-0.0.21/reference/sql-graphql/_category_.json deleted file mode 100644 index 2a8641a660..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "SQL-to-GraphQL", - "position": 1 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/deleteEntity.js b/versioned_docs/version-0.0.21/reference/sql-graphql/examples/deleteEntity.js deleted file mode 100644 index fc384cd5be..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/deleteEntity.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - app.decorate('platformatic', mapper) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - deletePages(where: { id: { eq: "3" } }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { deletePages: [ { id: '3', title: 'Platformatic is cool!' } ] } - await app.close() -} - -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/insertEntity.js b/versioned_docs/version-0.0.21/reference/sql-graphql/examples/insertEntity.js deleted file mode 100644 index d3297831c3..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/insertEntity.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - app.decorate('platformatic', mapper) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - insertPage(input: { title: "Platformatic is cool!" }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { insertPage: { id: '4', title: 'Platformatic is cool!' } } - await app.close() -} - -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/query.js b/versioned_docs/version-0.0.21/reference/sql-graphql/examples/query.js deleted file mode 100644 index 1afd34b379..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/query.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - query{ - pages{ - id, - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) - await app.close() -} - -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/saveEntity.js b/versioned_docs/version-0.0.21/reference/sql-graphql/examples/saveEntity.js deleted file mode 100644 index f2dd78b49b..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/examples/saveEntity.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - app.decorate('platformatic', mapper) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - savePage(input: { id: 3 title: "Platformatic is cool!" }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { savePage: { id: '3', title: 'Platformatic is cool!' } } - await app.close() -} - -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/intro.md b/versioned_docs/version-0.0.21/reference/sql-graphql/intro.md deleted file mode 100644 index f40f368187..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/intro.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_position: 1 ---- -# GraphQL API - -The Platformatic DB GraphQL plugin starts a GraphQL server wand makes it available -via a `/graphql` endpoint. This endpoint is automatically ready to run queries and -mutations against your entities. This functionality is powered by -[Mercurius](https://mercurius.dev). - -## GraphiQL - -The [GraphiQL](https://github.com/graphql/graphiql) web UI is integrated into -Platformatic DB. To enable it you can pass an option to the `sql-graphql` plugin: - -```javascript -app.register(graphqlPlugin, { graphiql: true }) -``` - -The GraphiQL interface is made available under the `/graphiql` path. diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/mutations.md b/versioned_docs/version-0.0.21/reference/sql-graphql/mutations.md deleted file mode 100644 index fe247e75c2..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/mutations.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Mutations - -When the GraphQL plugin is loaded, some mutations are automatically adding to -the GraphQL schema. - -## `save[ENTITY]` - -Saves a new entity to the database or updates an existing entity. - -### Example - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres', - log: logger, - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - savePage(input: { id: 3 title: "Platformatic is cool!" }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { savePage: { id: '3', title: 'Platformatic is cool!' } } - await app.close() -} - -main() -``` - -## `insert[ENTITY]` - -Inserts a new entity in the database. - -### Example - - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres', - log: logger, - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - savePage(input: { title: "Platformatic is cool!" }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { savePage: { id: '4', title: 'Platformatic is cool!' } } - await app.close() -} - -main() -``` - -## `delete[ENTITIES]` - -Deletes one or more entities from the database, based on the `where` clause -passed as an input to the mutation. - -### Example - - - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres', - log: logger, - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - mutation { - deletePages(where: { id: { eq: "3" } }) { - id - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) // { deletePages: [ { id: '3', title: 'Platformatic is cool!' } ] } - await app.close() -} - -main() -``` diff --git a/versioned_docs/version-0.0.21/reference/sql-graphql/queries.md b/versioned_docs/version-0.0.21/reference/sql-graphql/queries.md deleted file mode 100644 index de5b0f2e39..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-graphql/queries.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Queries - -A GraphQL query is automatically added to the GraphQL schema for each database -table, along with a complete mapping for all table fields. - -## Example - - -```js -'use strict' - -const Fastify = require('fastify') -const graphqlPlugin = require('@platformatic/sql-graphql') -const sqlMapper = require('@platformatic/sql-mapper') -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(sqlMapper, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - app.register(graphqlPlugin, { - graphiql: true - }) - const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - query{ - pages{ - id, - title - } - } - ` - } - }) - const result = await res.json() - console.log(result.data) - await app.close() -} -main() -``` - -## Advanced Queries - -The following additional queries are added to the GraphQL schema for each entity: - -### `get[ENTITY]by[PRIMARY_KEY]` - -If you have a table `pages` with the field `id` as the primary key, you can run -a query called `getPageById`. - -#### Example - -```js -... -const res = await app.inject({ - method: 'POST', - url: '/graphql', - body: { - query: ` - query{ - getPageById(id: 3) { - id, - title - } - } - ` - } -}) -const result = await res.json() -console.log(result.data) // { getPageById: { id: '3', title: 'A fiction' } } -``` diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/_category_.json b/versioned_docs/version-0.0.21/reference/sql-mapper/_category_.json deleted file mode 100644 index 842a89952f..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "SQL-Mapper", - "position": 1 -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/_category_.json b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/_category_.json deleted file mode 100644 index 64295896fe..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Entity", - "position": 3, - "link": { - "type": "generated-index", - "description": "Entity Reference" - } -} \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/api.md b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/api.md deleted file mode 100644 index bf6eaa0481..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/api.md +++ /dev/null @@ -1,248 +0,0 @@ ---- -sidebar_position: 3 ---- - -# API - -A set of operation methods are available on each entity: - -- [`find`](#find) -- [`insert`](#insert) -- [`save`](#save) -- [`delete`](#delete) - - -## Returned fields - -The entity operation methods accept a `fields` option that can specify an array of field names to be returned. If not specified, all fields will be returned. - -## Where clause - -The entity operation methods accept a `where` option to allow limiting of the database rows that will be affected by the operation. - -The `where` object's key is the field you want to check, the value is a key/value map where the key is an operator (see the table below) and the value is the value you want to run the operator against. - -| Platformatic operator | SQL operator | -|--- | ---| -| eq | `'='` | -| in | `'IN'` | -| nin | `'NOT IN'` | -| neq | `'<>'` | -| gt | `'>'` | -| gte | `'>='` | -| lt | `'<'` | -| lte | `'<='` | - -### Examples - -#### Selects row with `id = 1` -``` -{ - ... - "where": { - id: { - eq: 1 - } - } -} -``` - -#### Select all rows with id less than 100 -``` -{ - ... - "where": { - id: { - lt: 100 - } - } -} -``` - -#### Select all rows with id 1, 3, 5 or 7 -``` -{ - ... - "where": { - id: { - in: [1, 3, 5, 7] - } - } -} -``` - -## Reference - -### `find` - -Retrieve data for an entity from the database. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `where` | `Object` | [Where clause 🔗](#where-clause) -| `orderBy` | Array of `Object` | Object like `{ field: 'counter', direction: 'ASC' }` -| `limit` | `Number` | Limits the number of returned elements -| `offset` | `Number` | The offset to start looking for rows from - - -#### Usage - - -```js -'use strict' - -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() -``` - -### `insert` - -Insert one or more entity rows in the database. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `inputs` | Array of `Object` | Each object is a new row - -#### Usage - -```js -'use strict' - -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const res = await mapper.entities.page.insert({ - fields: ['id', 'title' ], - inputs: [ - { title: 'Foobar' }, - { title: 'FizzBuzz' } - ], - }) - logger.info(res) - /** - 0: { - "id": "16", - "title": "Foobar" - } - 1: { - "id": "17", - "title": "FizzBuzz" - } - */ - await mapper.db.dispose() -} -main() -``` - -### `save` - -Create a new entity row in the database or update an existing one. - -To update an existing entity, the `id` field (or equivalent primary key) must be included in the `input` object. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `input` | `Object` | The single row to create/update - -#### Usage - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: connectionString, - log: logger, - }) - const res = await mapper.entities.page.save({ - fields: ['id', 'title' ], - input: { id: 1, title: 'FizzBuzz' }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() -``` -### `delete` - -Delete one or more entity rows from the database, depending on the `where` option. Returns the data for all deleted objects. - -#### Options - -| Name | Type | Description -|---|---|---| -| `fields` | Array of `string` | List of fields to be returned for each object | -| `where` | `Object` | [Where clause 🔗](#where-clause) - -#### Usage - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: connectionString, - log: logger, - }) - const res = await mapper.entities.page.delete({ - fields: ['id', 'title',], - where: { - id: { - lt: 4 - } - }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() - -``` diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/example.md b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/example.md deleted file mode 100644 index 0219824bf7..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/example.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Example - -Given this PostgreSQL SQL schema: - -```sql -CREATE TABLE "categories" ( - "id" int4 NOT NULL DEFAULT nextval('categories_id_seq'::regclass), - "name" varchar(255) NOT NULL, - PRIMARY KEY ("id") -); - -CREATE TABLE "pages" ( - "id" int4 NOT NULL DEFAULT nextval('pages_id_seq'::regclass), - "title" varchar(255) NOT NULL, - "category_id" int4, - "user_id" int4, - PRIMARY KEY ("id") -); - -ALTER TABLE "pages" ADD FOREIGN KEY ("category_id") REFERENCES "categories"("id"); -``` - -`app.platformatic.entities` will contain this mapping object: - -```json -{ - "category": { - "name": "Category", - "singularName": "category", - "pluralName": "categories", - "primaryKey": "id", - "table": "categories", - "fields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "name": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "name" - } - }, - "camelCasedFields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "name": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "name" - } - }, - "relations": [], - "reverseRelationships": [ - { - "sourceEntity": "Page", - "relation": { - "constraint_catalog": "postgres", - "constraint_schema": "public", - "constraint_name": "pages_category_id_fkey", - "table_catalog": "postgres", - "table_schema": "public", - "table_name": "pages", - "constraint_type": "FOREIGN KEY", - "is_deferrable": "NO", - "initially_deferred": "NO", - "enforced": "YES", - "column_name": "category_id", - "ordinal_position": 1, - "position_in_unique_constraint": 1, - "foreign_table_name": "categories", - "foreign_column_name": "id" - } - } - ] - }, - "page": { - "name": "Page", - "singularName": "page", - "pluralName": "pages", - "primaryKey": "id", - "table": "pages", - "fields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "title": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "title" - }, - "category_id": { - "sqlType": "int4", - "isNullable": true, - "foreignKey": true, - "camelcase": "categoryId" - }, - "user_id": { - "sqlType": "int4", - "isNullable": true, - "camelcase": "userId" - } - }, - "camelCasedFields": { - "id": { - "sqlType": "int4", - "isNullable": false, - "primaryKey": true, - "camelcase": "id" - }, - "title": { - "sqlType": "varchar", - "isNullable": false, - "camelcase": "title" - }, - "categoryId": { - "sqlType": "int4", - "isNullable": true, - "foreignKey": true, - "camelcase": "categoryId" - }, - "userId": { - "sqlType": "int4", - "isNullable": true, - "camelcase": "userId" - } - }, - "relations": [ - { - "constraint_catalog": "postgres", - "constraint_schema": "public", - "constraint_name": "pages_category_id_fkey", - "table_catalog": "postgres", - "table_schema": "public", - "table_name": "pages", - "constraint_type": "FOREIGN KEY", - "is_deferrable": "NO", - "initially_deferred": "NO", - "enforced": "YES", - "column_name": "category_id", - "ordinal_position": 1, - "position_in_unique_constraint": 1, - "foreign_table_name": "categories", - "foreign_column_name": "id" - } - ], - "reverseRelationships": [] - } -} -``` - diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/fields.md b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/fields.md deleted file mode 100644 index 920c6ed593..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/fields.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Fields - -When Platformatic DB inspects a database's schema, it creates an object for each table that contains a mapping of their fields. - -These objects contain the following properties: -- `singularName`: singular entity name, based on table name. Uses [inflected](https://www.npmjs.com/package/inflected) under the hood. -- `pluralName`: plural entity name (i.e `'pages'`) -- `primaryKey`: the field which is identified as primary key. -- `table`: original table name -- `fields`: an object containing all fields details. Object key is the field name. -- `camelCasedFields`: an object containing all fields details in camelcase. If you have a column named `user_id` you can access it using both `userId` or `user_id` - -## Fields detail - -- `sqlType`: The original field type. It may vary depending on the underlying DB Engine -- `isNullable`: Whether the field can be `null` or not -- `primaryKey`: Whether the field is the primary key or not -- `camelcase`: The _camelcased_ value of the field - -## Example -Given this SQL Schema (for PostgreSQL): -```SQL -CREATE SEQUENCE IF NOT EXISTS pages_id_seq; -CREATE TABLE "public"."pages" ( - "id" int4 NOT NULL DEFAULT nextval('pages_id_seq'::regclass), - "title" varchar, - "body_content" text, - "category_id" int4, - PRIMARY KEY ("id") -); -``` - -The resulting mapping object will be: - -```js -{ - singularName: 'page', - pluralName: 'pages', - primaryKey: 'id', - table: 'pages', - fields: { - id: { - sqlType: 'int4', - isNullable: false, - primaryKey: true, - camelcase: 'id' - }, - title: { - sqlType: 'varchar', - isNullable: true, - camelcase: 'title' - }, - body_content: { - sqlType: 'text', - isNullable: true, - camelcase: 'bodyContent' - }, - category_id: { - sqlType: 'int4', - isNullable: true, - foreignKey: true, - camelcase: 'categoryId' - } - } - camelCasedFields: { - id: { - sqlType: 'int4', - isNullable: false, - primaryKey: true, - camelcase: 'id' - }, - title: { - sqlType: 'varchar', - isNullable: true, - camelcase: 'title' - }, - bodyContent: { - sqlType: 'text', - isNullable: true, - camelcase: 'bodyContent' - }, - categoryId: { - sqlType: 'int4', - isNullable: true, - foreignKey: true, - camelcase: 'categoryId' - } - }, - relations: [] -} -``` \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/hooks.md b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/hooks.md deleted file mode 100644 index 888d2607a6..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/hooks.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Hooks - -Entity hooks are a way to wrap the [API methods](./api) for an entity and add custom behaviour. - -The Platformatic DB SQL Mapper provides an `addEntityHooks(entityName, spec)` function that can be used to add hooks for an entity. - -## How to use hooks - -`addEntityHooks` accepts two arguments: - -1. A string representing the entity name (singularized), for example `'page'`. -1. A key/value object where the key is one of the API methods (`find`, `insert`, `save`, `delete`) and the value is a callback function. The callback will be called with the _original_ API method and the options that were passed to that method. See the example below. - -### Usage - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - mapper.addEntityHooks('page', { - find: async (originalFind, opts) => { - // Add a `foo` field with `bar` value to each row - const res = await originalFind(opts) - return res.map((row) => { - row.foo = 'bar' - return row - }) - } - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - /** - [ - 0: { - "id": "5", - "title": "Page 1", - "foo": "bar" - }, - 1: { - "id": "6", - "title": "Page 2", - "foo": "bar" - } - ] - */ - await mapper.db.dispose() -} -main() -``` - - -## Multiple Hooks - -Multiple hooks can be added for the same entity and API method, for example: - - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - mapper.addEntityHooks('page', { - find: async function firstHook(previousFunction, opts) { - // Add a `foo` field with `bar` value to each row - const res = await previousFunction(opts) - return res.map((row) => { - row.foo = 'bar' - return row - }) - } - }) - mapper.addEntityHooks('page', { - find: async function secondHook(previousFunction, opts) { - // Add a `bar` field with `baz` value to each row - const res = await previousFunction(opts) - return res.map((row) => { - row.bar = 'baz' - return row - }) - } - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - /** - [ - 0: { - "id": "5", - "title": "Page 1", - "foo": "bar", - "bar": "baz" - }, - 1: { - "id": "6", - "title": "Page 2", - "foo": "bar", - "bar": "baz" - } - ] - */ - await mapper.db.dispose() -} -main() -``` - -Since hooks are wrappers, they are being called in reverse order, like the image below - -![Hooks Lifecycle](../images/plt-db-hooks.svg) - -So even though we defined two hooks, the Database will be hit only once. - -Query result will be processed by `firstHook`, which will pass the result to `secondHook`, which will, finally, send the processed result to the original `.find({...})` function. - - diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/intro.md b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/intro.md deleted file mode 100644 index 21bec7ca4f..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/intro.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -sidebar_position: 1 ---- -# Introduction - -The primary goal of Platformatic DB is to read a database schema and generate REST and GraphQL endpoints that enable the execution of CRUD (Create/Retrieve/Update/Delete) operations against the database. - -Platformatic DB includes a _mapper_ that reads the schemas of database tables and then generates an _entity_ object for each table. - -Platformatic DB is a [Fastify](https://fastify.io) application. The Fastify instance object is decorated with the `platformatic` property, which exposes several APIs that handle the manipulation of data in the database. - -Platformatic DB populates the `app.platformatic.entities` object with data found in database tables. - -The keys on the `entities` object are _singularized_ versions of the table names — for example `users` becomes `user`, `categories` becomes `category` — and the values are a set of associated metadata and functions. \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/relations.md b/versioned_docs/version-0.0.21/reference/sql-mapper/entity/relations.md deleted file mode 100644 index 31ce521c97..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/entity/relations.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -sidebar_position: 5 ---- - -# Relations - -When Platformatic DB is reading your database schema, it identifies relationships -between tables and stores metadata on them in the entity object's `relations` field. -This is achieved by querying the database's internal metadata. - -## Example - -Given this PostgreSQL schema: - -```sql -CREATE SEQUENCE IF NOT EXISTS categories_id_seq; - -CREATE TABLE "categories" ( - "id" int4 NOT NULL DEFAULT nextval('categories_id_seq'::regclass), - "name" varchar(255) NOT NULL, - PRIMARY KEY ("id") -); - -CREATE SEQUENCE IF NOT EXISTS pages_id_seq; - -CREATE TABLE "pages" ( - "id" int4 NOT NULL DEFAULT nextval('pages_id_seq'::regclass), - "title" varchar(255) NOT NULL, - "body_content" text, - "category_id" int4, - PRIMARY KEY ("id") -); - -ALTER TABLE "pages" ADD FOREIGN KEY ("category_id") REFERENCES "categories"("id"); -``` - -When this code is run: - - -```js -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const pageEntity = mapper.entities.page - console.log(pageEntity.relations) - await mapper.db.dispose() -} -main() -``` - -The output will be: - -```javascript -[ - { - constraint_catalog: 'postgres', - constraint_schema: 'public', - constraint_name: 'pages_category_id_fkey', - table_catalog: 'postgres', - table_schema: 'public', - table_name: 'pages', - constraint_type: 'FOREIGN KEY', - is_deferrable: 'NO', - initially_deferred: 'NO', - enforced: 'YES', - column_name: 'category_id', - ordinal_position: 1, - position_in_unique_constraint: 1, - foreign_table_name: 'categories', - foreign_column_name: 'id' - } -] -``` - -As Platformatic DB supports multiple database engines, the contents of the -`relations` object will vary depending on the database being used. - -The following `relations` fields are common to all database engines: - -- `column_name` — the column that stores the foreign key -- `foreign_table_name` — the table hosting the related row -- `foreign_column_name` — the column in foreign table that identifies the row diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/delete.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/delete.js deleted file mode 100644 index b02d8ba518..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/delete.js +++ /dev/null @@ -1,30 +0,0 @@ -// Referenced in docs/reference/sql-mapper/entity/api.md -'use strict' - -const { connect } = require('@platformatic/sql-mapper') - -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: connectionString, - log: logger, - }) - const res = await mapper.entities.page.delete({ - fields: ['id', 'title',], - where: { - id: { - lt: 4 - } - }, - }) - - logger.info(res) - - await mapper.db.dispose() -} - -main() diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/fastify-plugin.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/fastify-plugin.js deleted file mode 100644 index 6ac4c979cc..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/fastify-plugin.js +++ /dev/null @@ -1,25 +0,0 @@ -// Referenced in docs/reference/sql-mapper/fastify-plugin.md -'use strict' - -const Fastify = require('fastify') -const mapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(mapper.plugin, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - - app.get('/all-pages', async (req, reply) => { - const res = await app.platformatic.entities.page.find() - return res - }) - - await app.listen({ port: 3333 }) -} - -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/fields.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/fields.js deleted file mode 100644 index 5c991beda5..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/fields.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict' - -const { connect } = require('@platformatic/sql-mapper') - -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - logger.info(mapper.entities.page) - await mapper.db.dispose() -} - -main() diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/find.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/find.js deleted file mode 100644 index ac2cdb56b2..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/find.js +++ /dev/null @@ -1,30 +0,0 @@ -// Referenced in docs/reference/sql-mapper/entity/api.md -'use strict' - -const { connect } = require('@platformatic/sql-mapper') - -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - - logger.info(res) - - await mapper.db.dispose() -} - -main() diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/hooks.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/hooks.js deleted file mode 100644 index b854295fd4..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/hooks.js +++ /dev/null @@ -1,46 +0,0 @@ -// Referenced in docs/reference/sql-mapper/hooks.md -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - mapper.addEntityHooks('page', { - find: async function firstHook(previousFunction, opts) { - // Add a `foo` field with `bar` value to each row - const res = await previousFunction(opts) - return res.map((row) => { - row.foo = 'bar' - return row - }) - } - }) - mapper.addEntityHooks('page', { - find: async function secondHook(previousFunction, opts) { - // Add a `bar` field with `baz` value to each row - const res = await previousFunction(opts) - return res.map((row) => { - row.bar = 'baz' - return row - }) - } - }) - const res = await mapper.entities.page.find({ - fields: ['id', 'title',], - where: { - id: { - lt: 10 - } - }, - }) - logger.info(res) - await mapper.db.dispose() -} - -main() diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/insert.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/insert.js deleted file mode 100644 index 06e562c881..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/insert.js +++ /dev/null @@ -1,25 +0,0 @@ -// Referenced in docs/reference/sql-mapper/entity/api.md -'use strict' - -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const res = await mapper.entities.page.insert({ - fields: ['id', 'title' ], - inputs: [ - { title: 'Foobar' }, - { title: 'FizzBuzz' } - ], - }) - logger.info(res) - await mapper.db.dispose() -} -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/relations.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/relations.js deleted file mode 100644 index 1f7c5afc0b..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/relations.js +++ /dev/null @@ -1,38 +0,0 @@ -// Referenced in docs/reference/sql-mapper/entity/relations.md -'use strict' - -const { connect } = require('@platformatic/sql-mapper') - -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const pgConnectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: pgConnectionString, - log: logger, - }) - const pageEntity = mapper.entities.page - const categoryEntity = mapper.entities.category - - const newCategory = await categoryEntity.insert({ - fields: ['id', 'name'], - inputs: [{ name: 'fiction' }] - }) - { - const res = await pageEntity.insert({ - fields: ['id', 'name'], - inputs: [ - { - title: 'A fiction', bodyContent: 'This is our first fiction', category_id: newCategory[0].id - } - ] - }) - console.log(res) - } - console.log(pageEntity.relations) - await mapper.db.dispose() -} - -main() diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/save.js b/versioned_docs/version-0.0.21/reference/sql-mapper/examples/save.js deleted file mode 100644 index 46f90cd395..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/examples/save.js +++ /dev/null @@ -1,21 +0,0 @@ -// Referenced in docs/reference/sql-mapper/entity/api.md -'use strict' -const { connect } = require('@platformatic/sql-mapper') -const { pino } = require('pino') -const pretty = require('pino-pretty') -const logger = pino(pretty()) - -async function main() { - const connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres' - const mapper = await connect({ - connectionString: connectionString, - log: logger, - }) - const res = await mapper.entities.page.save({ - fields: ['id', 'title' ], - input: { id: 10, title: 'FizzBuzz' }, - }) - logger.info(res) - await mapper.db.dispose() -} -main() \ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/fastify-plugin.md b/versioned_docs/version-0.0.21/reference/sql-mapper/fastify-plugin.md deleted file mode 100644 index 6660517489..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/fastify-plugin.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -sidebar_position: 2 ---- -# Fastify Plugin -The `@platformatic/sql-mapper` package exports a [Fastify](https://fastify.io) plugin that can be used out-of the box in a server application. - -A `connectionString` option must be passed to connect to your database. - -The plugin decorates the server with a `platformatic` object that has the following properties: - -- `db` — the DB wrapper object provided by [`@databases`](https://www.atdatabases.org/) -- `sql` — the SQL query mapper object provided by [`@databases`](https://www.atdatabases.org/) -- `entities` — all entity objects with their [API methods](./entity/api) -- `addEntityHooks` — a function to add a [hook](./entity/hooks) to an entity API method. - -#### Usage - -```js -'use strict' - -const Fastify = require('fastify') -const mapper = require('@platformatic/sql-mapper') - -async function main() { - const app = Fastify({ - logger: { - level: 'info' - } - }) - app.register(mapper.plugin, { - connectionString: 'postgres://postgres:postgres@127.0.0.1/postgres' - }) - - app.get('/all-pages', async (req, reply) => { - // Will return all rows from 'pages' table - const res = await app.platformatic.entities.page.find() - return res - }) - - await app.listen({ port: 3333 }) -} - -main() -``` diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/images/plt-db-hooks.svg b/versioned_docs/version-0.0.21/reference/sql-mapper/images/plt-db-hooks.svg deleted file mode 100644 index 6471afbce3..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/images/plt-db-hooks.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
firstHook()
firstHook()
secondHook()
secondHook()
entity.find()
entity.find()
Database
Database
mapper.entities.page.find()
mapper.entities.page.find()
Text is not SVG - cannot display
\ No newline at end of file diff --git a/versioned_docs/version-0.0.21/reference/sql-mapper/intro.md b/versioned_docs/version-0.0.21/reference/sql-mapper/intro.md deleted file mode 100644 index 6ca9873a4a..0000000000 --- a/versioned_docs/version-0.0.21/reference/sql-mapper/intro.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Introduction - -The Platformatic DB Mapper will inspect a database schema and return an object containing: - -- `db` — A database abstraction layer from [`@databases`](https://www.atdatabases.org/) -- `sql` — The SQL builder from [`@databases`](https://www.atdatabases.org/) -- `entities` — An object containing a key for each table found in the schema, with basic CRUD operations. See [Entity Reference](./entity/intro.md) for details. - -It exports a function that accepts an object with the following properties: - -- `connectionString` — The Database connection string -- `log` — A logger object (like [Pino](https://getpino.io)) -- `onDatabaseLoad` — An async function that is called after the connection is established. It will receive `db` and `sql` as parameter. -- `ignore` — Object used to ignore some tables from building entities. (i.e. `{ 'versions': true }` will ignore `versions` table) -- `autoTimestamp` — Generate timestamp automatically when inserting/updating records. -- `hooks` — For each entity name (like `Page`) you can customize any of the entity API function. Your custom function will receive the original function as first parameter, and then all the other parameters passed to it. - -## Code samples - -```javascript -const { pino } = require('pino') - -const logger = pino() - -async function onDatabaseLoad (db, sql) { - await db.query(sql`CREATE TABLE pages ( - id SERIAL PRIMARY KEY, - title VARCHAR(255) NOT NULL - );`) -} -const connectionString = - 'postgres://postgres:postgres@localhost:5432/postgres' -const mapper = await connect({ - connectionString, - log: logger, - onDatabaseLoad, - ignore: {}, - hooks: { - Page: { - find: async function(_find, opts) { - console.log('hook called'); - return await _find(opts) - } - } - } -}) -const pageEntity = mapper.entities.page - -await mapper.db.query(mapper.sql`SELECT * FROM pages`) -await mapper.db.find('option1', 'option2') -``` diff --git a/versioned_docs/version-0.0.22/contributing/contributing.md b/versioned_docs/version-0.0.22/contributing/contributing.md deleted file mode 100644 index b53f66837e..0000000000 --- a/versioned_docs/version-0.0.22/contributing/contributing.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contributing - -Details coming soon. diff --git a/versioned_docs/version-0.0.22/contributing/documentation-style-guide.md b/versioned_docs/version-0.0.22/contributing/documentation-style-guide.md deleted file mode 100644 index fbb6c09891..0000000000 --- a/versioned_docs/version-0.0.22/contributing/documentation-style-guide.md +++ /dev/null @@ -1,238 +0,0 @@ ---- -credits: https://github.com/fastify/fastify/blob/main/docs/Guides/Style-Guide.md ---- - -# Documentation Style Guide - -Welcome to the *Platformatic Documentation Style Guide*. This guide is here to provide -you with a conventional writing style for users writing developer documentation on -our Open Source framework. Each topic is precise and well explained to help you write -documentation users can easily understand and implement. - -## Who is this guide for? - -This guide is for anyone who loves to build with Platformatic or wants to contribute -to our documentation. You do not need to be an expert in writing technical -documentation. This guide is here to help you. - -Visit [CONTRIBUTING.md](https://github.com/platformatic/platformatic/blob/main/CONTRIBUTING.md) -file on GitHub to join our Open Source folks. - -## Before you write - -You should have a basic understanding of: - -* JavaScript -* Node.js -* Git -* GitHub -* Markdown -* HTTP -* NPM - -### Consider your Audience - -Before you start writing, think about your audience. In this case, your audience -should already know HTTP, JavaScript, NPM, and Node.js. It is necessary to keep -your readers in mind because they are the ones consuming your content. You want -to give as much useful information as possible. Consider the vital things they -need to know and how they can understand them. Use words and references that -readers can relate to easily. Ask for feedback from the community, it can help -you write better documentation that focuses on the user and what you want to -achieve. - -### Get straight to the point - -Give your readers a clear and precise action to take. Start with what is most -important. This way, you can help them find what they need faster. Mostly, -readers tend to read the first content on a page, and many will not scroll -further. - -**Example** - -Less like this: Colons are very important to register a parametric path. It lets -the framework know there is a new parameter created. You can place the colon -before the parameter name so the parametric path can be created. - -More Like this: To register a parametric path, put a colon before the parameter -name. Using a colon lets the framework know it is a parametric path and not a -static path. - -### Images and video should enhance the written documentation - - -Images and video should only be added if they complement the written -documentation, for example to help the reader form a clearer mental model of a -concept or pattern. - -Images can be directly embedded, but videos should be included by linking to an -external site, such as YouTube. You can add links by using -`[Title](https://www.websitename.com)` in the Markdown. - - - - -### Avoid plagiarism - -Make sure you avoid copying other people's work. Keep it as original as -possible. You can learn from what they have done and reference where it is from -if you used a particular quote from their work. - - -## Word Choice - -There are a few things you need to use and avoid when writing your documentation -to improve readability for readers and make documentation neat, direct, and -clean. - - -### When to use the second person "you" as the pronoun - -When writing articles or guides, your content should communicate directly to -readers in the second person ("you") addressed form. It is easier to give them -direct instruction on what to do on a particular topic. To see an example, visit -the [Quick Start Guide](../getting-started/quick-start-guide.md). - -**Example** - -Less like this: we can use the following plugins. - -More like this: You can use the following plugins. - -> According to [Wikipedia](#), ***You*** is usually a second person pronoun. -> Also, used to refer to an indeterminate person, as a more common alternative -> to a very formal indefinite pronoun. - -## When to avoid the second person "you" as the pronoun - -One of the main rules of formal writing such as reference documentation, or API -documentation, is to avoid the second person ("you") or directly addressing the -reader. - -**Example** - -Less like this: You can use the following recommendation as an example. - -More like this: As an example, the following recommendations should be -referenced. - -To view a live example, refer to the [Decorators](../reference/configuration.md) -reference document. - - -### Avoid using contractions - -Contractions are the shortened version of written and spoken forms of a word, -i.e. using "don't" instead of "do not". Avoid contractions to provide a more -formal tone. - -### Avoid using condescending terms - -Condescending terms are words that include: - -* Just -* Easy -* Simply -* Basically -* Obviously - -The reader may not find it easy to use Platformatic; avoid -words that make it sound simple, easy, offensive, or insensitive. Not everyone -who reads the documentation has the same level of understanding. - -### Starting with a verb - -Mostly start your description with a verb, which makes it simple and precise for -the reader to follow. Prefer using present tense because it is easier to read -and understand than the past or future tense. - -**Example** - - Less like this: There is a need for Node.js to be installed before you can be - able to use Platformatic. - - More like this: Install Node.js to make use of Platformatic. - -### Grammatical moods - -Grammatical moods are a great way to express your writing. Avoid sounding too -bossy while making a direct statement. Know when to switch between indicative, -imperative, and subjunctive moods. - - -**Indicative** - Use when making a factual statement or question. - -Example: Since there is no testing framework available, "Platformatic recommends ways -to write tests". - -**Imperative** - Use when giving instructions, actions, commands, or when you -write your headings. - -Example: Install dependencies before starting development. - - -**Subjunctive** - Use when making suggestions, hypotheses, or non-factual -statements. - -Example: Reading the documentation on our website is recommended to get -comprehensive knowledge of the framework. - -### Use **active** voice instead of **passive** - -Using active voice is a more compact and direct way of conveying your -documentation. - -**Example** - - -Passive: The node dependencies and packages are installed by npm. - -Active: npm installs packages and node dependencies. - -## Writing Style - -### Documentation titles - -When creating a new guide, API, or reference in the `/docs/` directory, use -short titles that best describe the topic of your documentation. Name your files -in kebab-cases and avoid Raw or camelCase. To learn more about kebab-case you -can visit this medium article on [Case -Styles](https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841). - -**Examples**: - ->`hook-and-plugins.md`, - - `adding-test-plugins.md`, - - `removing-requests.md`. - -### Hyperlinks - -Hyperlinks should have a clear title of what it references. Here is how your -hyperlink should look: - -```MD - - -// Add clear & brief description -[Fastify Plugins] (https://www.fastify.io/docs/latest/Plugins/) - - - -// incomplete description -[Fastify] (https://www.fastify.io/docs/latest/Plugins/) - -// Adding title in link brackets -[](https://www.fastify.io/docs/latest/Plugins/ "fastify plugin") - -// Empty title -[](https://www.fastify.io/docs/latest/Plugins/) - -// Adding links localhost URLs instead of using code strings (``) -[http://localhost:3000/](http://localhost:3000/) - -``` - -Include in your documentation as many essential references as possible, but -avoid having numerous links when writing for beginners to avoid distractions. diff --git a/versioned_docs/version-0.0.22/getting-started/architecture.md b/versioned_docs/version-0.0.22/getting-started/architecture.md deleted file mode 100644 index 183ea474d7..0000000000 --- a/versioned_docs/version-0.0.22/getting-started/architecture.md +++ /dev/null @@ -1,28 +0,0 @@ -# Architecture - -Platformatic is a collection of Open Source tools designed to eliminate friction -in backend development. The first of those tools is Platformatic DB, which is developed -as `@platformatic/db`. - -## Platformatic DB - -Platformatic DB can expose a SQL database by dynamically mapping it to REST/OpenAPI -and GraphQL endpoints. It supports a limited subset of the SQL query language, but -also allows developers to add their own custom routes and resolvers. - -![Platformatic DB Architecture](./platformatic-architecture.png) - -Platformatic DB is composed of a few key libraries: - -1. `@platformatic/sql-mapper` - follows the [Data Mapper pattern](https://en.wikipedia.org/wiki/Data_mapper_pattern) to build an API on top of a SQL database. - Internally it uses the [`@database` project](https://www.atdatabases.org/). -1. `@platformatic/sql-openapi` - uses `sql-mapper` to create a series of REST routes and matching OpenAPI definitions. - Internally it uses [`@fastify/swagger`](https://github.com/fastify/fastify-swagger). -1. `@platformatic/sql-graphql` - uses `sql-mapper` to create a GraphQL endpoint and schema. `sql-graphql` also support Federation. - Internally it uses [`mercurius`](https://github.com/mercurius-js/mercurius). - -Platformatic DB allows you to load a [Fastify plugin](https://www.fastify.io/docs/latest/Reference/Plugins/) during server startup that contains your own application-specific code. -The plugin can add more routes or resolvers — these will automatically be shown in the OpenAPI and GraphQL schemas. - -SQL database migrations are also supported. They're implemented internally with the [`postgrator`](https://www.npmjs.com/package/postgrator) library. - diff --git a/versioned_docs/version-0.0.22/getting-started/movie-quotes-app-tutorial.md b/versioned_docs/version-0.0.22/getting-started/movie-quotes-app-tutorial.md deleted file mode 100644 index 04cb64c874..0000000000 --- a/versioned_docs/version-0.0.22/getting-started/movie-quotes-app-tutorial.md +++ /dev/null @@ -1,1888 +0,0 @@ -# Movie Quotes App Tutorial - -This tutorial will help you learn how to build a full stack application on top -of Platformatic DB. We're going to build an application that allows us to -save our favourite movie quotes. We'll also be building in custom API functionality -that allows for some neat user interaction on our frontend. - -You can find the complete code for the application that we're going to build -[on GitHub](https://github.com/platformatic/tutorial-movie-quotes-app). - -:::note - -We'll be building the frontend of our application with the [Astro](https://astro.build/) -framework, but the GraphQL API integration steps that we're going to cover can -be applied with most frontend frameworks. - -::: - -## What we're going to cover - -In this tutorial we'll learn how to: - -- Create a Platformatic API -- Apply database migrations -- Create relationships between our API entities -- Populate our database tables -- Build a frontend application that integrates with our GraphQL API -- Extend our API with custom functionality -- Enable CORS on our Platformatic API - -## Prerequisites - -To follow along with this tutorial you'll need to have these things installed: - -- [Node.js](https://nodejs.org/) >= v16.17.0 or >= v18.8.0 -- [npm](https://docs.npmjs.com/cli/) v7 or later -- A code editor, for example [Visual Studio Code](https://code.visualstudio.com/) - -You'll also need to have some experience with JavaScript, and be comfortable with -running commands in a terminal. - -## Build the backend - -### Create a Platformatic API - -First, let's create our project directory: - -```bash -mkdir -p tutorial-movie-quotes-app/apps/movie-quotes-api/ - -cd tutorial-movie-quotes-app/apps/movie-quotes-api/ -``` - -Then let's create a `package.json` file: - -```bash -npm init --yes -``` - -Now we can install the [platformatic](https://www.npmjs.com/package/platformatic) -CLI as a dependency: - -```bash -npm install platformatic -``` - -Let's also add some npm run scripts for convenience: - -```bash -npm pkg set scripts.start="platformatic db start" - -npm pkg set scripts.dev="npm start" -``` - -Now we're going to configure our API. Let's create our Platformatic configuration -file, **`platformatic.db.json`**: - -```json -{ - "server": { - "logger": { - "level": "{PLT_SERVER_LOGGER_LEVEL}" - }, - "hostname": "{PLT_SERVER_HOSTNAME}", - "port": "{PORT}" - }, - "core": { - "connectionString": "{DATABASE_URL}" - }, - "migrations": { - "dir": "./migrations" - } -} -``` - -Now we'll create a **`.env`** file with settings for our configuration to use: - -``` -PORT=3042 -PLT_SERVER_HOSTNAME=127.0.0.1 -PLT_SERVER_LOGGER_LEVEL=info -DATABASE_URL=sqlite://./movie-quotes.sqlite -``` - -:::info - -Take a look at the [Configuration reference](/reference/configuration.md) -to see all the supported configuration settings. - -::: - -### Define the database schema - -Let's create a new directory to store our migration files: - -```bash -mkdir migrations -``` - -Then we'll create a migration file named **`001.do.sql`** in the **`migrations`** -directory: - -```sql -CREATE TABLE quotes ( - id INTEGER PRIMARY KEY, - quote TEXT NOT NULL, - said_by VARCHAR(255) NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP -); -``` - -Let's also create `.gitignore` so that we avoid accidentally committing our -SQLite database: - -```bash -echo '*.sqlite' > .gitignore -``` - -Now we can start the Platformatic DB server: - -```bash -npm run dev -``` - -Our Platformatic DB server should start, and we'll see messages like these: - -``` -[11:26:48.772] INFO (15235): running 001.do.sql -[11:26:48.864] INFO (15235): server listening - url: "http://127.0.0.1:3042" -``` - -Let's open a new terminal and make a request to our server's REST API that -creates a new quote: - -```bash -curl --request POST --header "Content-Type: application/json" \ - -d "{ \"quote\": \"Toto, I've got a feeling we're not in Kansas anymore.\", \"saidBy\": \"Dorothy Gale\" }" \ - http://localhost:3042/quotes -``` - -We should receive a response like this from the API: - -```json -{"id":1,"quote":"Toto, I've got a feeling we're not in Kansas anymore.","saidBy":"Dorothy Gale","createdAt":"2022-09-13 10:39:35"} -``` - -### Create an entity relationship - -Now let's create a migration file named **`002.do.sql`** in the **`migrations`** -directory: - -```sql -CREATE TABLE movies ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL UNIQUE -); - --- TODO: Add a foreign key constraint so quotes.movie_id must exist in movies.id -ALTER TABLE quotes ADD COLUMN movie_id INTEGER REFERENCES movies(id); -``` - -This SQL will create a new `movies` database table and also add a `movie_id` -column to the `quotes` table. This will allow us to store movie data in the -`movies` table and then reference them by ID in our `quotes` table. - -Let's stop the Platformatic DB server with `Ctrl + C`, and then start it again: - -```bash -npm run dev -``` - -The new migration should be automatically applied and we'll see the log message -`running 002.do.sql`. - -Our Platformatic DB server also provides a GraphQL API. Let's open up the GraphiQL -application in our web browser: - -> http://localhost:3042/graphiql - -Now let's run this query with GraphiQL to add the movie for the quote that we -added earlier: - -```graphql -mutation { - saveMovie(input: { name: "The Wizard of Oz" }) { - id - } -} -``` - -We should receive a response like this from the API: - -```json -{ - "data": { - "saveMovie": { - "id": "1" - } - } -} -``` - -Now we can update our quote to reference the movie: - -```graphql -mutation { - saveQuote(input: { id: 1, movieId: 1 }) { - id - quote - saidBy - createdAt - movie { - id - name - } - } -} -``` - -We should receive a response like this from the API: - -```json -{ - "data": { - "saveQuote": { - "id": "1", - "quote": "Toto, I've got a feeling we're not in Kansas anymore.", - "saidBy": "Dorothy Gale", - "movie": { - "id": "1", - "name": "The Wizard of Oz" - } - } - } -} -``` - -Our Platformatic DB server has automatically identified the relationship -between our `quotes` and `movies` database tables. This allows us to make -GraphQL queries that retrieve quotes and their associated movies at the same -time. For example, to retrieve all quotes from our database we can run: - -```graphql -query { - quotes { - id - quote - saidBy - createdAt - movie { - id - name - } - } -} -``` - -To view the GraphQL schema that's generated for our API by Platformatic DB, -we can run this command in our terminal: - -```bash -npx platformatic db schema graphql -``` - -The GraphQL schema shows all of the queries and mutations that we can run -against our GraphQL API, as well as the types of data that it expects as input. - -### Populate the database - -Our movie quotes database is looking a little empty! We're going to create a -"seed" script to populate it with some data. - -Let's create a new file named **`seed.js`** and copy and paste in this code: - -```javascript -'use strict' - -const quotes = [ - { - quote: "Toto, I've got a feeling we're not in Kansas anymore.", - saidBy: 'Dorothy Gale', - movie: 'The Wizard of Oz' - }, - { - quote: "You're gonna need a bigger boat.", - saidBy: 'Martin Brody', - movie: 'Jaws' - }, - { - quote: 'May the Force be with you.', - saidBy: 'Han Solo', - movie: 'Star Wars' - }, - { - quote: 'I have always depended on the kindness of strangers.', - saidBy: 'Blanche DuBois', - movie: 'A Streetcar Named Desire' - } -] - -module.exports = async function ({ entities, db, sql }) { - for (const values of quotes) { - const movie = await entities.movie.save({ input: { name: values.movie } }) - - console.log('Created movie:', movie) - - const quote = { - quote: values.quote, - saidBy: values.saidBy, - movieId: movie.id - } - - await entities.quote.save({ input: quote }) - - console.log('Created quote:', quote) - } -} -``` - - - -:::info -Take a look at the [Seed a Database](/guides/seed-a-database.md) guide to learn more -about how database seeding works with Platformatic DB. -::: - -Let's stop our Platformatic DB server running and remove our SQLite database: - -``` -rm movie-quotes.db -``` - -Now let's create a fresh SQLite database by running our migrations: - -```bash -npx platformatic db migrate -``` - -And then let's populate the `quotes` and `movies` tables with data using our -seed script: - -```bash -npx platformatic db seed seed.js -``` - -Our database is full of data, but we don't have anywhere to display it. It's -time to start building our frontend! - -## Build the frontend - -We're now going to use [Astro](https://astro.build/) to build our frontend -application. If you've not used it before, you might find it helpful -to read [this overview](https://docs.astro.build/en/core-concepts/astro-components/) -on how Astro components are structured. - -:::tip -Astro provide some extensions and tools to help improve your -[Editor Setup](https://docs.astro.build/en/editor-setup/) when building an -Astro application. -::: - -### Create an Astro application - -In the root of our project, let's create a new directory for our frontent -application: - -```bash -mkdir -p apps/movie-quotes-frontend/ - -cd apps/movie-quotes-frontend/ -``` - -And then we'll create a new `package.json` file: - -```bash -npm init --yes -``` - -Now we can install [astro](https://www.npmjs.com/package/astro) as a dependency: - -```bash -npm install --save-dev astro -``` - -Then let's set up some npm run scripts for convenience: - -```bash -npm pkg delete scripts.test -npm pkg set scripts.dev="astro dev --port 3000" -npm pkg set scripts.start="astro dev --port 3000" -npm pkg set scripts.build="astro build" -``` - -Now we'll create our Astro configuration file, **`astro.config.mjs`** and -copy and paste in this code: - -```javascript -import { defineConfig } from 'astro/config' - -// https://astro.build/config -export default defineConfig({ - output: 'server' -}) -``` - -And we'll also create a **`tsconfig.json`** file and add in this configuration: - -```json -{ - "extends": "astro/tsconfigs/base", - "compilerOptions": { - "types": ["astro/client"] - } -} -``` - -> We won't be writing our frontend application with TypeScript, but adding this -> configuration file allows Astro to provide TODO -> https://docs.astro.build/en/guides/typescript/ - -Now let's create the directories where we'll be adding the components for our -frontend application: - -```bash -mkdir -p src/pages src/layouts src/components -``` - -And inside the **`src/pages`** directory let's create our first page, **`index.astro`**: - -```astro -

Movie Quotes

-``` - -Now we can start up the Astro development server with: - -```bash -npm run dev -``` - -And then load up the frontend in our browser at [http://localhost:3000](http://localhost:3000) - -### Create a layout - -In the **`src/layouts`** directory, let's create a new file named **`Layout.astro`**: - -```astro ---- -export interface Props { - title: string; - page?: string; -} -const { title, page } = Astro.props; ---- - - - - - - - {title} - - -
-

🎬 Movie Quotes

-
- -
- -
- - -``` - -The code between the `---` is known as the component script, and the -code after that is the component template. The component script will *only* run -on the server side when a web browser makes a request. The component template -is rendered server side and sent back as an HTML response to the web browser. - -Now we'll update **`src/pages/index.astro`** to use this `Layout` component. -Let's replace the contents of **`src/pages/index.astro`** with this code: - -```astro ---- -import Layout from '../layouts/Layout.astro'; ---- - - -
-

We'll list all the movie quotes here.

-
-
-``` - -### Integrate the urql GraphQL client - -We're now going to integrate the [URQL](https://formidable.com/open-source/urql/) -GraphQL client into our frontend application. This will allow us to run queries -and mutations against our Platformatic GraphQL API. - -Let's first install [@urql/core](https://www.npmjs.com/package/@urql/core) and -[graphql](https://www.npmjs.com/package/graphql) as project dependencies: - -```bash -npm install @urql/core graphql -``` - -Then let's create a new **`.env`** file and add this configuration: - -``` -PUBLIC_GRAPHQL_API_ENDPOINT=http://127.0.0.1:3042/graphql -``` - -Now we'll create a new directory: - -```bash -mkdir src/lib -``` - -And then create a new file named **`src/lib/quotes-api.js`**. In that file we'll -create a new URQL client: - -```javascript -// src/lib/quotes-api.js - -import { createClient } from '@urql/core'; - -const graphqlClient = createClient({ - url: import.meta.env.PUBLIC_GRAPHQL_API_ENDPOINT, - requestPolicy: "network-only" -}); -``` - -We'll also add a thin wrapper around the client that does some basic error -handling for us: - -```javascript -// src/lib/quotes-api.js - -async function graphqlClientWrapper(method, gqlQuery, queryVariables = {}) { - const queryResult = await graphqlClient[method]( - gqlQuery, - queryVariables - ).toPromise(); - - if (queryResult.error) { - console.error("GraphQL error:", queryResult.error); - } - - return { - data: queryResult.data, - error: queryResult.error, - }; -} - -export const quotesApi = { - async query(gqlQuery, queryVariables = {}) { - return await graphqlClientWrapper("query", gqlQuery, queryVariables); - }, - async mutation(gqlQuery, queryVariables = {}) { - return await graphqlClientWrapper("mutation", gqlQuery, queryVariables); - } -} -``` - -And lastly, we'll export `gql` from the `@urql/core` package, to make it -simpler for us to write GraphQL queries in our pages: - -```javascript -// src/lib/quotes-api.js - -export { gql } from "@urql/core"; -``` - -Stop the Astro dev server and then start it again so it picks up the **`.env`** -file: - -```bash -npm run dev -``` - -### Display all quotes - -Let's display all the movie quotes in **`src/pages/index.astro`**. - -First, we'll update the component script at the top and add in a query to -our GraphQL API for quotes: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -// highlight-start -import { quotesApi, gql } from '../lib/quotes-api'; - -const { data } = await quotesApi.query(gql` - query { - quotes { - id - quote - saidBy - createdAt - movie { - id - name - } - } - } -`); - -const quotes = data?.quotes || []; -// highlight-end ---- -``` - -Then we'll update the component template to display the quotes: - -```astro - -
-// highlight-start - {quotes.length > 0 ? quotes.map((quote) => ( -
-
-

{quote.quote}

-
-

- — {quote.saidBy}, {quote.movie?.name} -

-
- Added {new Date(quote.createdAt).toUTCString()} -
-
- )) : ( -

No movie quotes have been added.

- )} -// highlight-end -
-
-``` - -And just like that, we have all the movie quotes displaying on the page! - -### Integrate Tailwind for styling - -Automatically add the [@astrojs/tailwind integration](https://docs.astro.build/en/guides/integrations-guide/tailwind/): - -```bash -npx astro add tailwind --yes -``` - -Add the Tailwind CSS [Typography](https://tailwindcss.com/docs/typography-plugin) -and [Forms](https://github.com/tailwindlabs/tailwindcss-forms) plugins: - -```bash -npm install --save-dev @tailwindcss/typography @tailwindcss/forms -``` - -Import the plugins in our Tailwind configuration file: - -```javascript -// tailwind.config.cjs - -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], - theme: { - extend: {} - }, -// highlight-start - plugins: [ - require('@tailwindcss/forms'), - require('@tailwindcss/typography') - ] -// highlight-end -} -``` - -Stop the Astro dev server and then start it again so it picks up all the -configuration changes: - -```bash -npm run dev -``` - -### Style the listing page - -To style our listing page, let's add CSS classes to the component template in -**`src/layouts/Layout.astro`**: - -```astro ---- -export interface Props { - title: string; - page?: string; -} - -const { title, page } = Astro.props; - -// highlight-next-line -const navActiveClasses = "font-bold bg-yellow-400 no-underline"; ---- - - - - - - - {title} - -// highlight-next-line - -// highlight-next-line -
-

🎬 Movie Quotes

-
-// highlight-next-line - -// highlight-next-line -
- -
- - -``` - -Then let's add CSS classes to the component template in **`src/pages/index.astro`**: - -```astro - -
- {quotes.length > 0 ? quotes.map((quote) => ( -// highlight-next-line -
-// highlight-next-line -
-// highlight-next-line -

{quote.quote}

-
-// highlight-next-line -

- — {quote.saidBy}, {quote.movie?.name} -

-// highlight-next-line -
-// highlight-next-line - Added {new Date(quote.createdAt).toUTCString()} -
-
- )) : ( -

No movie quotes have been added.

- )} -
-
-``` - -Our listing page is now looking much more user friendly! - -### Create an add quote page - -We're going to create a form component that we can use for adding and editing -quotes. - -First let's create a new component file, **`src/components/QuoteForm.astro`**: - -```astro ---- -export interface QuoteFormData { - id?: number; - quote?: string; - saidBy?: string; - movie?: string; -} - -export interface Props { - action: string; - values?: QuoteFormData; - saveError?: boolean; - loadError?: boolean; - submitLabel: string; -} - -const { action, values = {}, saveError, loadError, submitLabel } = Astro.props; ---- - -{saveError &&

There was an error saving the quote. Please try again.

} -{loadError &&

There was an error loading the quote. Please try again.

} - -
- - - - -
-``` - -Create a new page file, **`src/pages/add.astro`**: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -import QuoteForm from '../components/QuoteForm.astro'; -import type { QuoteFormData } from '../components/QuoteForm.astro'; - -let formData: QuoteFormData = {}; -let saveError = false; ---- - - -
-

Add a quote

- -
-
-``` - -And now let's add a link to this page in the layout navigation in **`src/layouts/Layout.astro`**: - -```astro - -``` - -### Send form data to the API - -When a user submits the add quote form we want to send the form data to our API -so it can then save it to our database. Let's wire that up now. - -First we're going to create a new file, **`src/lib/request-utils.js`**: - -```javascript -export function isPostRequest (request) { - return request.method === 'POST' -} - -export async function getFormData (request) { - const formData = await request.formData() - - return Object.fromEntries(formData.entries()) -} -``` - - - -Then let's update the component script in **`src/pages/add.astro`** to use -these new request utility functions: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -import QuoteForm from '../components/QuoteForm.astro'; -import type { QuoteFormData } from '../components/QuoteForm.astro'; - -// highlight-next-line -import { isPostRequest, getFormData } from '../lib/request-utils'; - -let formData: QuoteFormData = {}; -let saveError = false; - -// highlight-start -if (isPostRequest(Astro.request)) { - formData = await getFormData(Astro.request); -} -// highlight-end ---- -``` - - - -When we create a new quote entity record via our API, we need to include a -`movieId` field that references a movie entity record. This means that when a -user submits the add quote form we need to: - -- Check if a movie entity record already exists with that movie name -- Return the movie `id` if it does exist -- If it doesn't exist, create a new movie entity record and return the movie ID - -Let's update the `import` statement at the top of **`src/lib/quotes-api.js`** - -```diff --import { createClient } from '@urql/core' -+import { createClient, gql } from '@urql/core' -``` - -And then add a new method that will return a movie ID for us: - -```javascript -async function getMovieId (movieName) { - movieName = movieName.trim() - - let movieId = null - - // Check if a movie already exists with the provided name. - const queryMoviesResult = await quotesApi.query( - gql` - query ($movieName: String!) { - movies(where: { name: { eq: $movieName } }) { - id - } - } - `, - { movieName } - ) - - if (queryMoviesResult.error) { - return null - } - - const movieExists = queryMoviesResult.data?.movies.length === 1 - if (movieExists) { - movieId = queryMoviesResult.data.movies[0].id - } else { - // Create a new movie entity record. - const saveMovieResult = await quotesApi.mutation( - gql` - mutation ($movieName: String!) { - saveMovie(input: { name: $movieName }) { - id - } - } - `, - { movieName } - ) - - if (saveMovieResult.error) { - return null - } - - movieId = saveMovieResult.data?.saveMovie.id - } - - return movieId -} -``` - -And let's export it too: - -```javascript -export const quotesApi = { - async query (gqlQuery, queryVariables = {}) { - return await graphqlClientWrapper('query', gqlQuery, queryVariables) - }, - async mutation (gqlQuery, queryVariables = {}) { - return await graphqlClientWrapper('mutation', gqlQuery, queryVariables) - }, -// highlight-next-line - getMovieId -} -``` - -Now we can wire up the last parts in the **`src/pages/add.astro`** component -script: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -import QuoteForm from '../components/QuoteForm.astro'; -import type { QuoteFormData } from '../components/QuoteForm.astro'; - -// highlight-next-line -import { quotesApi, gql } from '../lib/quotes-api'; -import { isPostRequest, getFormData } from '../lib/request-utils'; - -let formData: QuoteFormData = {}; -let saveError = false; - -if (isPostRequest(Astro.request)) { - formData = await getFormData(Astro.request); - -// highlight-start - const movieId = await quotesApi.getMovieId(formData.movie); - - if (movieId) { - const quote = { - quote: formData.quote, - saidBy: formData.saidBy, - movieId, - }; - - const { error } = await quotesApi.mutation(gql` - mutation($quote: QuoteInput!) { - saveQuote(input: $quote) { - id - } - } - `, { quote }); - - if (!error) { - return Astro.redirect('/'); - } else { - saveError = true; - } - } else { - saveError = true; - } -// highlight-end -} -``` - - - -### Add autosuggest for movies - -We can create a better experience for our users by autosuggesting the movie name -when they're adding a new quote. - -Let's open up **`src/components/QuoteForm.astro`** and import our API helper methods -in the component script: - -```astro -import { quotesApi, gql } from '../lib/quotes-api.js'; -``` - -Then let's add in a query to our GraphQL API for all movies: - -```astro -const { data } = await quotesApi.query(gql` - query { - movies { - name - } - } -`); - -const movies = data?.movies || []; -``` - -Now lets update the *Movie* field in the component template to use the -array of movies that we've retrieved from the API: - -```astro - -``` - - - -### Create an edit quote page - -Let's create a new directory, **`src/pages/edit/`**: - -```bash -mkdir src/pages/edit/ -``` - -And inside of it, let's create a new page, **`[id].astro`**: - -```astro ---- -import Layout from '../../layouts/Layout.astro'; -import QuoteForm, { QuoteFormData } from '../../components/QuoteForm.astro'; - -const id = Number(Astro.params.id); - -let formValues: QuoteFormData = {}; -let loadError = false; -let saveError = false; ---- - - -
-

Edit quote

- -
-
-``` - -You'll see that we're using the same `QuoteForm` component that our add quote -page uses. Now we're going to wire up our edit page so that it can load an -existing quote from our API and save changes back to the API when the form is -submitted. - -In the **`[id.astro]`** component script, let's add some code to take care of -these tasks: - -```astro ---- -import Layout from '../../layouts/Layout.astro'; -import QuoteForm, { QuoteFormData } from '../../components/QuoteForm.astro'; - -// highlight-start -import { quotesApi, gql } from '../../lib/quotes-api'; -import { isPostRequest, getFormData } from '../../lib/request-utils'; -// highlight-end - -const id = Number(Astro.params.id); - -let formValues: QuoteFormData = {}; -let loadError = false; -let saveError = false; - -// highlight-start -if (isPostRequest(Astro.request)) { - const formData = await getFormData(Astro.request); - formValues = formData; - - const movieId = await quotesApi.getMovieId(formData.movie); - - if (movieId) { - const quote = { - id, - quote: formData.quote, - saidBy: formData.saidBy, - movieId, - }; - - const { error } = await quotesApi.mutation(gql` - mutation($quote: QuoteInput!) { - saveQuote(input: $quote) { - id - } - } - `, { quote }); - - if (!error) { - return Astro.redirect('/'); - } else { - saveError = true; - } - } else { - saveError = true; - } -} else { - const { data } = await quotesApi.query(gql` - query($id: ID!) { - getQuoteById(id: $id) { - id - quote - saidBy - movie { - id - name - } - } - } - `, { id }); - - if (data?.getQuoteById) { - formValues = { - ...data.getQuoteById, - movie: data.getQuoteById.movie.name - }; - } else { - loadError = true; - } -} -// highlight-end ---- -``` - - - -Load up [http://localhost:3000/edit/1](http://localhost:3000/edit/1) in your -browser to test out the edit quote page. - -Now we're going to add edit links to the quotes listing page. Let's start by -creating a new component **`src/components/QuoteActionEdit.astro`**: - -```astro ---- -export interface Props { - id: number; -} - -const { id } = Astro.props; ---- - - - - - - Edit - -``` - -Then let's import this component and use it in our listing page, -**`src/pages/index.astro`**: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -// highlight-next-line -import QuoteActionEdit from '../components/QuoteActionEdit.astro'; -import { quotesApi, gql } from '../lib/quotes-api'; - -// ... ---- - - -
- {quotes.length > 0 ? quotes.map((quote) => ( -
- ... -
-// highlight-start - - - - Added {new Date(quote.createdAt).toUTCString()} -// highlight-end -
-
- )) : ( -

No movie quotes have been added.

- )} -
-
-``` - -### Add delete quote functionality - -Our Movie Quotes app can create, retrieve and update quotes. Now we're going -to implement the D in CRUD — delete! - -First let's create a new component, **`src/components/QuoteActionDelete.astro`**: - -```astro ---- -export interface Props { - id: number; -} - -const { id } = Astro.props; ---- -
- -
-``` - - - -And then we'll drop it into our listing page, **`src/pages/index.astro`**: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -import QuoteActionEdit from '../components/QuoteActionEdit.astro'; -// highlight-next-line -import QuoteActionDelete from '../components/QuoteActionDelete.astro'; -import { quotesApi, gql } from '../lib/quotes-api'; - -// ... ---- - - -
- {quotes.length > 0 ? quotes.map((quote) => ( -
- ... -
- - -// highlight-next-line - - - Added {new Date(quote.createdAt).toUTCString()} -
-
-... -``` - -At the moment when a delete form is submitted from our listing page, we get -an Astro 404 page. Let's fix this by creating a new directory, **`src/pages/delete/`**: - -```bash -mkdir src/pages/delete/ -``` - -And inside of it, let's create a new page, **`[id].astro`**: - -```astro ---- -import Layout from '../../layouts/Layout.astro'; - -import { quotesApi, gql } from '../../lib/quotes-api'; -import { isPostRequest } from '../../lib/request-utils'; - -if (isPostRequest(Astro.request)) { - const id = Number(Astro.params.id); - - const { error } = await quotesApi.mutation(gql` - mutation($id: ID!) { - deleteQuotes(where: { id: { eq: $id }}) { - id - } - } - `, { id }); - - if (!error) { - return Astro.redirect('/'); - } -} ---- - -
-

Delete quote

-

There was an error deleting the quote. Please try again.

-
-
-``` - - - -Now if we click on a delete quote button on our listings page, it should call our -GraphQL API to delete the quote. To make this a little more user friendly, let's -add in a confirmation dialog so that users don't delete a quote by accident. - - - - -Let's create a new directory, **`src/scripts/`**: - -```bash -mkdir src/scripts/ -``` - -And inside of that directory let's create a new file, **`quote-actions.js`**: - -```javascript -// src/scripts/quote-actions.js - -export function confirmDeleteQuote (form) { - if (confirm('Are you sure want to delete this quote?')) { - form.submit() - } -} -``` - -Then we can pull it in as client side JavaScript on our listing page, -**`src/pages/index.astro`**: - -```astro - - ... - - - -``` - - - -## Build a "like" quote feature - -We've built all the basic CRUD (Create, Retrieve, Update & Delete) features -into our application. Now let's build a feature so that users can interact -and "like" their favourite movie quotes. - -To build this feature we're going to add custom functionality to our API -and then add a new component, along with some client side JavaScript, to -our frontend. - -### Create an API migration - -We're now going to work on the code for API, under the **`apps/movie-quotes-api`** -directory. - -First let's create a migration that adds a `likes` column to our `quotes` -database table. We'll create a new migration file, **`migrations/003.do.sql`**: - -```sql -ALTER TABLE quotes ADD COLUMN likes INTEGER default 0; -``` - -This migration will automatically be applied when we next start our Platformatic -API. - -### Create an API plugin - -To add custom functionality to our Platformatic API, we need to create a -[Fastify plugin](https://www.fastify.io/docs/latest/Reference/Plugins/) and -update our API configuration to use it. - -Let's create a new file, **`plugin.js`**, and inside it we'll add the skeleton -structure for our plugin: - -```javascript -// plugin.js - -'use strict' - -module.exports = async function plugin (app) { - app.log.info('plugin loaded') -} -``` - -Now let's register our plugin in our API configuration file, **`platformatic.db.json`**: - -```json -{ - ... - "migrations": { - "dir": "./migrations" -// highlight-start - }, - "plugin": { - "path": "./plugin.js" - } -// highlight-end -} -``` - -And then we'll start up our Platformatic API: - -```bash -npm run dev -``` - -We should see log messages that tell us that our new migration has been -applied and our plugin has been loaded: - -``` -[10:09:20.052] INFO (146270): running 003.do.sql -[10:09:20.129] INFO (146270): plugin loaded -[10:09:20.209] INFO (146270): server listening - url: "http://127.0.0.1:3042" -``` - -Now it's time to start adding some custom functionality inside our plugin. - -### Add a REST API route - - - -We're going to add a REST route to our API that increments the count of -likes for a specific quote: `/quotes/:id/like` - -First let's add [fluent-json-schema](https://www.npmjs.com/package/fluent-json-schema) as a dependency for our API: - -```bash -npm install fluent-json-schema -``` - -We'll use `fluent-json-schema` to help us generate a JSON Schema. We can then -use this schema to validate the request path parameters for our route (`id`). - -Now let's add our REST API route in **`plugin.js`**: - -```javascript -'use strict' - -// highlight-next-line -const S = require('fluent-json-schema') - -module.exports = async function plugin (app) { - app.log.info('plugin loaded') - - // This JSON Schema will validate the request path parameters. - // It reuses part of the schema that Platormatic DB has - // automatically generated for our Quote entity. -// highlight-start - const schema = { - params: S.object().prop('id', app.getSchema('Quote').properties.id) - } - - app.post('/quotes/:id/like', { schema }, async function (request, response) { - return {} - }) -// highlight-end -} -``` - -We can now make a `POST` request to our new API route: - -```bash -curl --request POST http://localhost:3042/quotes/1/like -``` - -:::info -Learn more about how validation works in the -[Fastify validation documentation](https://www.fastify.io/docs/latest/Reference/Validation-and-Serialization/). -::: - -Our API route is currently returning an empty object (`{}`). Let's wire things -up so that it increments the number of likes for the quote with the specified ID. -To do this we'll add a new function inside of our plugin: - -```javascript -module.exports = async function plugin (app) { - app.log.info('plugin loaded') - -// highlight-start - async function incrementQuoteLikes (id) { - const { db, sql } = app.platformatic - - const result = await db.query(sql` - UPDATE quotes SET likes = likes + 1 WHERE id=${id} RETURNING likes - `) - - return result[0]?.likes - } -// highlight-end - - // ... -} -``` - -And then we'll call that function in our route handler function: - -```javascript -app.post('/quotes/:id/like', { schema }, async function (request, response) { -// highlight-next-line - return { likes: await incrementQuoteLikes(request.params.id) } -}) -``` - -Now when we make a `POST` request to our API route: - -```bash -curl --request POST http://localhost:3042/quotes/1/like -``` - -We should see that the `likes` value for the quote is incremented every time -we make a request to the route. - -```json -{"likes":1} -``` - - - -### Add a GraphQL API mutation - -We can add a `likeQuote` mutation to our GraphQL API by reusing the -`incrementQuoteLikes` function that we just created. - -Let's add this code at the end of our plugin, inside **`plugin.js`**: - -```javascript -module.exports = async function plugin (app) { - // ... - -// highlight-start - app.graphql.extendSchema(` - extend type Mutation { - likeQuote(id: ID!): Int - } - `) - - app.graphql.defineResolvers({ - Mutation: { - likeQuote: async (_, { id }) => await incrementQuoteLikes(id) - } - }) -// highlight-end -} -``` - -The code we've just added extends our API's GraphQL schema and defines -a corresponding resolver for the `likeQuote` mutation. - -We can now load up GraphiQL in our web browser and try out our new `likeQuote` -mutation with this GraphQL query: - -```graphql -mutation { - likeQuote(id: 1) -} -``` - -:::info -Learn more about how to extend the GraphQL schema and define resolvers in the -[Mercurius API documentation](https://mercurius.dev/#/docs/api/options). -::: - -### Enable CORS on the API - -When we build "like" functionality into our frontend, we'll be making a client -side HTTP request to our GraphQL API. Our backend API and our frontend are running -on different origins, so we need to configure our API to allow requests from -the frontend. This is known as Cross-Origin Resource Sharing (CORS). - -To enable CORS on our API, let's open up our API's **`.env`** file and add in -a new setting: - -``` -PLT_SERVER_CORS_ORIGIN=http://localhost:3000 -``` - -The value of `PLT_SERVER_CORS_ORIGIN` is our frontend application's origin. - -Now we can add a `cors` configuration object in our API's configuration file, -**`platformatic.db.json`**: - -```json -{ - "server": { - "logger": { - "level": "{PLT_SERVER_LOGGER_LEVEL}" - }, - "hostname": "{PLT_SERVER_HOSTNAME}", - "port": "{PORT}", -// highlight-start - "cors": { - "origin": "{PLT_SERVER_CORS_ORIGIN}" - } -// highlight-end - }, - ... -} -``` - -The HTTP responses from all endpoints on our API will now include the header: - -``` -access-control-allow-origin: http://localhost:3000 -``` - -This will allow JavaScript running on web pages under the `http://localhost:3000` -origin to make requests to our API. - -### Add like quote functionality - -Now that our API supports "liking" a quote, let's integrate it as a feature in -our frontend. - -First we'll create a new component, **`src/components/QuoteActionLike.astro`**: - -```astro ---- -export interface Props { - id: number; - likes: number; -} - -const { id, likes } = Astro.props; ---- - - - -``` - -And in our listing page, **`src/pages/index.astro`**, let's import our new -component and add it into the interface: - -```astro ---- -import Layout from '../layouts/Layout.astro'; -import QuoteActionEdit from '../components/QuoteActionEdit.astro'; -import QuoteActionDelete from '../components/QuoteActionDelete.astro'; -// highlight-next-line -import QuoteActionLike from '../components/QuoteActionLike.astro'; -import { quotesApi, gql } from '../lib/quotes-api'; - -// ... ---- - - -
- {quotes.length > 0 ? quotes.map((quote) => ( -
- ... -
- -// highlight-next-line - - - - - Added {new Date(quote.createdAt).toUTCString()} -
-
-... -``` - -Then let's update the GraphQL query in this component's script to retrieve the -`likes` field for all quotes: - -```javascript -const { data } = await quotesApi.query(gql` - query { - quotes { - id - quote - saidBy -// highlight-next-line - likes - createdAt - movie { - id - name - } - } - } -`); -``` - -Now we have the likes showing for each quote, let's wire things up so that -clicking on the like component for a quote will call our API and add a like. - -Let's open up **`src/scripts/quote-actions.js`** and add a new function that -makes a request to our GraphQL API: - -```javascript -// highlight-next-line -import { quotesApi, gql } from '../lib/quotes-api.js' - -export function confirmDeleteQuote (form) { - if (confirm('Are you sure want to delete this quote?')) { - form.submit() - } -} - -// highlight-start -export async function likeQuote (likeQuote) { - likeQuote.classList.add('liked') - likeQuote.classList.remove('cursor-pointer') - - const id = Number(likeQuote.dataset.quoteId) - - const { data } = await quotesApi.mutation(gql` - mutation($id: ID!) { - likeQuote(id: $id) - } - `, { id }) - - if (data?.likeQuote) { - likeQuote.querySelector('.likes-count').innerText = data.likeQuote - } -} -// highlight-end -``` - -And then let's attach the `likeQuote` function to the click event for each -like quote component on our listing page. We can do this by adding a little -extra code inside the ` -``` - -### Sort the listing by top quotes - -Now that users can like their favourite quotes, as a final step, we'll allow -for sorting quotes on the listing page by the number of likes they have. - -Let's update **`src/pages/index.astro`** to read a `sort` query string parameter -and use it the GraphQL query that we make to our API: - -```astro ---- -// ... - -// highlight-start -const allowedSortFields = ["createdAt", "likes"]; -const searchParamSort = new URL(Astro.request.url).searchParams.get("sort"); -const sort = allowedSortFields.includes(searchParamSort) ? searchParamSort : "createdAt"; -// highlight-end - -const { data } = await quotesApi.query(gql` - query { -// highlight-next-line - quotes(orderBy: {field: ${sort}, direction: DESC}) { - id - quote - saidBy - likes - createdAt - movie { - id - name - } - } - } -`); - -const quotes = data?.quotes || []; ---- -// highlight-next-line - -... -``` - -Then let's replace the 'All quotes' link in the `