Skip to content

Commit

Permalink
docs: updates to Intro page and linked pages (#65)
Browse files Browse the repository at this point in the history
* fixes

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* fixes

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* updates

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* updates

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* final updates

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* updates

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* final updates

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* fixes

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

* fixes

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>

---------

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>
  • Loading branch information
jimezesinachi authored Nov 12, 2024
1 parent faabf10 commit cc9b07d
Show file tree
Hide file tree
Showing 37 changed files with 987 additions and 744 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@
## Show me some code

```ts
import { QueryEngine } from '@synthql/backend';
import { from } from './generated';

export const queryEngine = new QueryEngine({
url: 'postgresql://user:password@localhost:5432/dbname',
});

const query = from('films')
.columns('id', 'title', 'year')
.where({ id: { in: [1, 2, 3] } })
.many();
.filter({ id: { in: [1, 2, 3] } })
.all();

const { data } = useSynthql(query);
const data = await queryEngine.executeAndWait(query);

// `data` will resolve to
// `data` will resolve to:
[
{
id: 1,
Expand Down
16 changes: 9 additions & 7 deletions packages/backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
The SynthQL backend.

```ts
import { QueryEngine } from "@synthql/backend"
import { QueryEngine } from '@synthql/backend';

// Initialize the client
const queryEngine = new QueryEngine({...})
// Initialize the query engine
const queryEngine = new QueryEngine({
url: 'postgresql://user:password@localhost:5432/dbname',
});

// Write your query
const query = from('users')
.columns('id','first_name')
.where({id: {in:[1,2,3]}})
.many()
.columns('id', 'first_name')
.filter({ id: { in: [1, 2, 3] } })
.all();

// Execute the query
queryEngine.execute(query);
const result = await queryEngine.executeAndWait(query);
```

## Links
Expand Down
6 changes: 3 additions & 3 deletions packages/backend/src/QueryEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ export class QueryEngine<DB> {
query: TQuery,
opts?: {
/**
* When using middlewares (via the `QueryEngine` options),
* pass the data that should be used to transform
* the query, via this option
* When using `.permissions()` (in your query) and/or `middlewares`
* (via the `QueryEngine` options), use this option to pass the
* data that should be used to transform and/or check the query
*
* e.g.:
*
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/SynthqlError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export class SynthqlError extends Error {

const lines = [
'A query with a cardinality of `one` returned no results!',
'Hint: are you using .one() when you should be using .maybe()?',
'Hint: are you using .firstOrThrow() when you should be using .first()?',
];

return new SynthqlError(new Error(), type, lines.join('\n'), 404);
Expand Down
8 changes: 5 additions & 3 deletions packages/backend/src/tests/queryEngine.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import dotenv from 'dotenv';
import { config } from 'dotenv';
import { Pool } from 'pg';
import { QueryEngine } from '../QueryEngine';
import { Middleware } from '../execution/middleware';
import { DB } from './generated';
dotenv.config();

config();

export const pool = new Pool({
connectionString:
Expand All @@ -12,12 +13,13 @@ export const pool = new Pool({
});

export function createQueryEngine(data?: {
schema?: string;
middlewares?: Array<Middleware<any, any>>;
dangerouslyIgnorePermissions?: boolean;
}) {
return new QueryEngine<DB>({
pool,
schema: 'public',
schema: data?.schema ?? 'public',
middlewares: data?.middlewares,
dangerouslyIgnorePermissions:
data?.dangerouslyIgnorePermissions ?? true,
Expand Down
21 changes: 12 additions & 9 deletions packages/docs/blog/2024-05-10-why-json-schema/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors: [fhur]
tags: [devlog]
---

# Why json-schema?
## Why json-schema?

I wanted to drop a few words on why we're chosing `JSON schema` as an intermediate representation for our schemas. Putting it in writing will make it clearer, so here goes.

Expand All @@ -22,7 +22,7 @@ So we know that the query builder needs static type information. What's new is t
Let's look at a very basic example. Find an actor by ID.

```ts
from('actors').where({ id: 1 }).maybe();
from('actors').filter({ id: 1 }).first();
```

We expect this to compile to something like
Expand All @@ -39,9 +39,9 @@ Notice that I didn't write `select *`. That's intentional, because we can only s

```ts
from('actor')
.where({id})
.filter({id})
.include({ films })
.maybe()
.first()
.groupingId('actor_id') # <======= WHY DO I HAVE TO DO THIS?
```
Expand All @@ -61,11 +61,14 @@ const from = query<DB>().from;
const from = query<DB>(db).from;
```
# So... why JSON schema?
## So... why JSON schema?
Ok, now that we've talked about some of the goals we want to support: let's go back to the original question. Why is JSON schema a good choice?
1. There is great tooling support for JSON schema: We can find libraries that generate zod from JSON schema or generate TypeScript types from json schema.
1. Building a JSON schema programmatically is really easy. Converting from `pg-extract-schema` to JSON schema is trivial, and very easy to unit test.
1. JSON schema itself is available at runtime: As JSON schema is just a plain old javascript object, it's available at runtime, and so we can pass it to the query builer as input so it can use it to infer the groupingId and select all the fields.
1. Runtime type checking: In the future we will want to add something like zod to the `QueryEngine` so that it blocks malformed queries. Using JSON Schema we can get zod for free.
1. There is great tooling support for JSON schema: We can find libraries that generate zod from JSON schema or generate TypeScript types from json schema.
1. Building a JSON schema programmatically is really easy. Converting from `pg-extract-schema` to JSON schema is trivial, and very easy to unit test.
1. JSON schema itself is available at runtime: As JSON schema is just a plain old javascript object, it's available at runtime, and so we can pass it to the query builer as input so it can use it to infer the groupingId and select all the fields.
1. Runtime type checking: In the future we will want to add something like zod to the `QueryEngine` so that it blocks malformed queries. Using JSON Schema we can get zod for free.
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ const validateQueryResult = createValidator({
});

const queryEngine = new QueryEngine();
const queryResult = queryEngine.executeAndWait(query);

const queryResult = await queryEngine.executeAndWait(query);

validateQueryResult(queryResult);
```
11 changes: 3 additions & 8 deletions packages/docs/docs/100-getting-started/000-quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ npx @synthql/cli generate \

In the example above, this will generate a types file at `src/generated/db.ts`, a schema definitions file at `src/generated/schema.ts` and an index file that connects both to the query builder and exports them, at `src/generated/index.ts`.

This connection allows you to export a type-safe query builder, `from()`, which has all the table and column names with the corresponding TypeScript types, as sourced from your database.
This connection allows you to simply import a type-safe query builder, `from()`, which includes all the table and column names along with their corresponding TypeScript types, sourced directly from your database.

## Write your first query

Expand All @@ -40,6 +40,7 @@ import { from } from 'src/generated';

const findUserByIds = (ids: number[]) => {
return (
// Select table
from('users')
// Select which columns you want
// NOTE: if you want to select all columns, simply don't use
Expand All @@ -61,14 +62,8 @@ The `QueryEngine` compiles SynthQL queries into plain SQL and sends them to the
// src/queryEngine.ts
import { QueryEngine } from '@synthql/backend';

// Ensure DATABASE_URL is set in your .env file:
// DATABASE_URL=postgresql://user:password@localhost:5432/dbname
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL environment variable is required!');
}

export const queryEngine = new QueryEngine({
url: process.env.DATABASE_URL,
url: 'postgresql://user:password@localhost:5432/dbname',
});
```

Expand Down
18 changes: 9 additions & 9 deletions packages/docs/docs/100-getting-started/express.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import InstallPackage from '../../src/components/HomepageFeatures/InstallPackage

:::tip
Before reading this, first check out:
[Quick start: Node.js](./quick-start) if you haven't yet
[Quick start: Node.js](./quick-start) if you haven't already
:::

:::info
After reading this, you'll probably want to set up your frontend app to send queries to your new SynthQL server. To get started, check out:
[Getting started: React](./react) if you haven't already
:::

## Install the packages
Expand All @@ -31,14 +36,8 @@ The `QueryEngine` compiles SynthQL queries into plain SQL and sends them to the
// src/queryEngine.ts
import { QueryEngine } from '@synthql/backend';

// Ensure DATABASE_URL is set in your .env file:
// DATABASE_URL=postgresql://user:password@localhost:5432/dbname
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL environment variable is required!');
}

export const queryEngine = new QueryEngine({
url: process.env.DATABASE_URL,
url: 'postgresql://user:password@localhost:5432/dbname',
});
```

Expand All @@ -53,8 +52,9 @@ import { queryEngine } from './queryEngine';
const app = express();
const expressSynthqlRequestHandler = createExpressSynthqlHandler(queryEngine);

// Create route handler
app.post('/synthql', async (req, res) => {
return await expressSynthqlRequestHandler(req, res);
return expressSynthqlRequestHandler(req, res);
});

app.listen(3000);
Expand Down
18 changes: 18 additions & 0 deletions packages/docs/docs/100-getting-started/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Getting started

## Welcome to SynthQL! 🚀

Hey there, fellow developer! Ready to supercharge your data queries? You've landed in the right place. Let's get you up and running with SynthQL in no time!

Whether you're a "give me the code right now" kind of developer or someone building the next big full-stack application, we've got you covered. Here's what's in store:

Just want to dive in? 🏊‍♂️ Head straight to our "[Quick start](./getting-started/quick-start)" guide with Node.js - it's like a speed dating session with SynthQL!

Building a server? 🏗️ Check out our server-side guides:

Express.js fans, we've got your back with seamless [integration guide](./getting-started/express)!

Next.js enthusiasts, your Route Handlers are about to get a serious upgrade! Start [here](./getting-started/next)!

Frontend focused? 🎨 Our React [integration guide](./getting-started/react) will help you bring that SynthQL magic to your client-side applications, whether you're using plain React or any React-flavored framework.

Pick your path below and let's start building something awesome.

## Quick start: Node.js

Execute your first SynthQL query. [Read more](./getting-started/quick-start).
Expand Down
16 changes: 8 additions & 8 deletions packages/docs/docs/100-getting-started/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import InstallPackage from '../../src/components/HomepageFeatures/InstallPackage

:::tip
Before reading this, first check out:
[Quick start: Node.js](./quick-start) if you haven't yet
[Quick start: Node.js](./quick-start) if you haven't already
:::

:::info
After reading this, you'll probably want to set up your frontend app to send queries to your new SynthQL server. To get started, check out:
[Getting started: React](./react) if you haven't already
:::

## Install the packages
Expand All @@ -31,14 +36,8 @@ The `QueryEngine` compiles SynthQL queries into plain SQL and sends them to the
// src/queryEngine.ts
import { QueryEngine } from '@synthql/backend';

// Ensure DATABASE_URL is set in your .env file:
// DATABASE_URL=postgresql://user:password@localhost:5432/dbname
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL environment variable is required!');
}

export const queryEngine = new QueryEngine({
url: process.env.DATABASE_URL,
url: 'postgresql://user:password@localhost:5432/dbname',
});
```

Expand All @@ -51,6 +50,7 @@ import { queryEngine } from '../../../queryEngine';

const nextSynthqlRequestHandler = createNextSynthqlHandler(queryEngine);

// Create route handler
export async function POST(request: Request) {
return nextSynthqlRequestHandler(request);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/docs/100-getting-started/react.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import InstallPackage from '../../src/components/HomepageFeatures/InstallPackage
# React

:::info
This guide assumes that you have setup a server to receive and execute SynthQL queries. If you haven't yet, check out:
This guide assumes that you have setup a server to receive and execute SynthQL queries. If you haven't already, check out:
[Quick start: Node.js](./quick-start), [Getting started: Express.js](./express) and [Getting started: Next.js Route Handlers](./next)
:::

Expand Down Expand Up @@ -33,7 +33,7 @@ npx @synthql/cli generate \

In the example above, this will generate a types file at `src/generated/db.ts`, a schema definitions file at `src/generated/schema.ts` and an index file that connects both to the query builder and exports them, at `src/generated/index.ts`.

This connection allows you to export a type-safe query builder, `from()`, which has all the table and column names with the corresponding TypeScript types, as sourced from your database.
This connection allows you to simply import a type-safe query builder, `from()`, which includes all the table and column names along with their corresponding TypeScript types, sourced directly from your database.

## React usage

Expand Down
44 changes: 23 additions & 21 deletions packages/docs/docs/200-query-language/000-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,59 @@ SynthQL comes with a simple, but powerful query language. Let's see a few exampl
## Find user by ID

```ts
import { from } from './db';
import { from } from './generated';

const users = from('users').columns('id', 'name');

export function findUserById(id: string) {
return users.where({ id }).maybe();
return users.filter({ id }).first();
}
```

## Find users by IDs
## Find user by IDs

```tsx
import { from } from './db';
```ts
import { from } from './generated';

const users = from('users').columns('id', 'name');

export function findUserById(ids: string[]) {
return users.where({ id: { in: ids } }).maybe();
export function findUserByIds(ids: string[]) {
return users.filter({ id: { in: ids } }).first();
}
```

## Find users with pets (1 to n relation)
## Find user with pets (1 to n relation)

```tsx
import { from } from './db';
```ts
import { from } from './generated';

const pets = from('pets').columns('id', 'name', 'owner_id');

const users = from('users').columns('id', 'name');
const users = from('users').columns('id', 'name', 'address');

export function findUserByIds(ids: string[]) {
const pets = pets
.where({
export function findUserAndPetsByIds(ids: string[]) {
const userPets = pets
.filter({
owner_id: col('users.id'),
})
.many();
.all();

return users
.include({
pets,
userPets,
})
.where({ id: { in: ids } })
.maybe();
.filter({ id: { in: ids } })
.first();
}
```

This query will return the following shape:

```ts
Array<{
type UserAndPets = {
id: string;
name: string;
pets: Array<{ id: string; name: string }>;
}>;
address: string;
userPets: Array<{ id: string; name: string; owner_id: string }>;
};
```
Loading

0 comments on commit cc9b07d

Please sign in to comment.